db 크무비

This commit is contained in:
Envy_PC 2024-10-12 15:20:07 +09:00
parent d7ddac9162
commit 0bf7a57acb
15 changed files with 7490 additions and 112 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

173
gui.py
View File

@ -1,4 +1,4 @@
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy
from PySide6.QtWidgets import QInputDialog, QWidget, QPushButton, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy
from PySide6.QtCore import Qt, QRect, QSettings, QTimer
from toggleSwitch import ToggleSwitch
from browser_control import BrowserController
@ -7,13 +7,15 @@ from clipboardImageManager import ClipboardImageManager
from vertexAI import VertexAITranslator
from option import OptionHandler
from price import PriceHandler
from title import TitleHandler
from locatorManager import LocatorManager
from src.cmb_diag import CMBSettingsDialog
from logger_module import QTextEditLogger # 추가
import logging
import asyncio
class TranslationApp(QWidget):
def __init__(self, logger=None, whale_translator=None):
def __init__(self, logger=None, app=None):
super().__init__()
self.initUI()
self.logger = logger
@ -23,7 +25,9 @@ class TranslationApp(QWidget):
self.locator_manager = LocatorManager()
self.browser_controller = BrowserController(self, self.logger, self.locator_manager)
# self.whale_translator = WhaleTranslator(self, self.logger, secret_mode=True,vd_mode=True) # 디버그 모드 켜기
self.whale_translator = whale_translator
# self.whale_translator = whale_translator
self.whale_translator = None
self.app = app
self.vertexAI = VertexAITranslator(self.logger, key_path)
self.optionHandler = None
@ -31,7 +35,8 @@ class TranslationApp(QWidget):
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, self.debug)
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.logger, self.vertexAI, self.debug)
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.vertexAI, self.debug)
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, debug=True)
self.running = False
# 변수 설정
@ -167,70 +172,6 @@ class TranslationApp(QWidget):
self.detail_progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.detail_progress_bar.setVisible(False)
# # 동작옵션 토글
# self.title_toggle = ToggleSwitch(self)
# self.title_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('title', checked))
# self.optionTrnas_toggle = ToggleSwitch(self)
# self.optionTrnas_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('optionTrnas', checked))
# self.optionIMGTrans_toggle = ToggleSwitch(self)
# self.optionIMGTrans_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('optionIMGTrans', checked))
# self.optionAutoSelect_toggle = ToggleSwitch(self)
# self.optionAutoSelect_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('optionAutoSelect', checked))
# self.price_toggle = ToggleSwitch(self)
# self.price_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('price', checked))
# self.thumb_toggle = ToggleSwitch(self)
# self.thumb_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('thumb', checked))
# self.tag_toggle = ToggleSwitch(self)
# self.tag_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('tag', checked))
# self.detail_Option_toggle = ToggleSwitch(self)
# self.detail_Option_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('detail_Option', checked))
# self.detail_IMGTrans_toggle = ToggleSwitch(self)
# self.detail_IMGTrans_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('detail_IMGTrans', checked))
# self.debug_toggle = ToggleSwitch(self)
# self.debug_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('debug', checked))
# # 상품명 토글
# self.title_toggle = ToggleSwitch(self)
# self.title_toggle.clicked.connect(self.on_title_toggle_clicked)
# self.title_toggle.setEnabled(False)
# # 옵션명 AI번역 토글
# self.optionTrnas_toggle = ToggleSwitch(self)
# self.optionTrnas_toggle.clicked.connect(self.on_optionTrnas_toggle_clicked)
# self.optionTrnas_toggle.setEnabled(True)
# # 옵션이미지 번역 토글
# self.optionIMGTrans_toggle = ToggleSwitch(self)
# self.optionIMGTrans_toggle.clicked.connect(self.on_optionIMGTrans_toggle_clicked)
# self.optionIMGTrans_toggle.setEnabled(True)
# # 옵션Auto선택 토글
# self.optionAutoSelect_toggle = ToggleSwitch(self)
# self.optionAutoSelect_toggle.clicked.connect(self.on_optionAutoSelect_toggle_clicked)
# self.optionAutoSelect_toggle.setEnabled(True)
# # 가격수정 토글
# self.price_toggle = ToggleSwitch(self)
# self.price_toggle.clicked.connect(self.on_price_toggle_clicked)
# self.price_toggle.setEnabled(True)
# # 썸네일 AI수정 토글
# self.thumb_toggle = ToggleSwitch(self)
# self.thumb_toggle.clicked.connect(self.on_thumb_toggle_clicked)
# self.thumb_toggle.setEnabled(False)
# # 태그수정 토글
# self.tag_toggle = ToggleSwitch(self)
# self.tag_toggle.clicked.connect(self.on_tag_toggle_clicked)
# self.tag_toggle.setEnabled(False)
# # 상페옵션명삽입 토글
# self.detail_Option_toggle = ToggleSwitch(self)
# self.detail_Option_toggle.clicked.connect(self.on_detail_Option_toggle_clicked)
# self.detail_Option_toggle.setEnabled(True)
# # 상페이미지번역 토글
# self.detail_IMGTrans_toggle = ToggleSwitch(self)
# self.detail_IMGTrans_toggle.clicked.connect(self.on_detail_IMGTrans_toggle_clicked)
# self.detail_IMGTrans_toggle.setEnabled(True)
# # 디버그모드 토글
# self.debug_toggle = ToggleSwitch(self)
# self.debug_toggle.clicked.connect(self.on_debug_toggle_clicked)
# self.debug_toggle.setEnabled(True)
# 동작옵션 토글 및 레이블 설정
self.toggle_layout = QGridLayout()
@ -337,14 +278,15 @@ class TranslationApp(QWidget):
self.start_chrome_button = QPushButton('크롬 실행', self)
self.translate_button = QPushButton('번역 시작', self)
self.pause_button = QPushButton('일시정지', self)
self.exit_button = QPushButton('종료', self)
self.cmb_button = QPushButton('크무비설정', self)
self.cmb_test_button = QPushButton('크무비테스트', self)
# 버튼 크기를 1.5배로 설정
button_height = int(self.start_chrome_button.sizeHint().height() * 1.5)
self.start_chrome_button.setFixedHeight(button_height)
self.translate_button.setFixedHeight(button_height)
self.pause_button.setFixedHeight(button_height)
self.exit_button.setFixedHeight(button_height)
self.cmb_button.setFixedHeight(button_height)
# 메인 레이아웃 설정
self.main_layout = QVBoxLayout()
@ -380,7 +322,8 @@ class TranslationApp(QWidget):
self.button_layout.addWidget(self.start_chrome_button)
self.button_layout.addWidget(self.translate_button)
self.button_layout.addWidget(self.pause_button)
self.button_layout.addWidget(self.exit_button)
self.button_layout.addWidget(self.cmb_button)
self.button_layout.addWidget(self.cmb_test_button)
# 로그 및 프로그레스바 레이아웃
self.log_layout = QVBoxLayout()
@ -422,7 +365,8 @@ class TranslationApp(QWidget):
self.pause_button.clicked.connect(self.pause_translation)
# self.exit_button.clicked.connect(self.close)
self.exit_button.clicked.connect(self.on_close_button_clicked)
self.cmb_button.clicked.connect(self.on_cmb_button_clicked)
self.cmb_test_button.clicked.connect(self.on_cmb_test_button_clicked)
async def run_async_tasks(self):
"""비동기 작업을 실행"""
@ -578,20 +522,38 @@ class TranslationApp(QWidget):
def on_start_chrome_button_clicked(self):
"""크롬 실행 버튼 클릭 시 호출"""
self.logger.debug('크롬 실행 버튼 클릭됨')
self.logger.debug(f'self.browser_controller.page : {self.browser_controller.page}')
# 비동기 함수 실행을 위해 asyncio.create_task 사용
asyncio.create_task(self.start_browser())
optionIMGTrans_status = self.toggle_states['vd_mode']
detail_IMGTrans_status = self.toggle_states['vd_mode']
vd_mode_status = self.toggle_states['vd_mode']
# async def on_close_button_clicked(self):
# """크롬 실행 버튼 클릭 시 호출"""
# self.logger.debug('크롬 실행 버튼 클릭됨')
# # 비동기 함수 실행을 위해 asyncio.create_task 사용
# task = asyncio.create_task(self.close())
# await task # 작업이 완료될 때까지 대기
if optionIMGTrans_status or detail_IMGTrans_status:
self.whale_translator = WhaleTranslator(self.app, self.logger, secret_mode=True, vd_mode=vd_mode_status) # 모드 켜기
self.whale_translator.start_whale_browser()
asyncio.create_task(self.start_browser())
async def on_close_button_clicked(self):
"""크롬 실행 버튼 클릭 시 호출"""
self.logger.debug('크롬 실행 버튼 클릭됨')
QApplication.quit() # QApplication을 직접 종료
# 비동기 함수 실행을 위해 asyncio.create_task 사용
task = asyncio.create_task(self.close())
await task # 작업이 완료될 때까지 대기
def on_cmb_test_button_clicked(self, test_cat):
"""크무비 설정 실행 버튼 클릭 시 호출"""
self.logger.debug('크무비 테스트 버튼 클릭됨')
text, ok = QInputDialog.getText(self, "카테고리 입력 테스트", "카테고리를 형식에 맞게 입력하세요:")
if ok and text: # 사용자가 확인 버튼을 누르고 텍스트를 입력한 경우
stage = self.cmb_diag.get_crmobi_stage(text)
self.logger.debug(f"{stage}")
def on_cmb_button_clicked(self):
"""크무비 설정 실행 버튼 클릭 시 호출"""
self.logger.debug('크무비 설정 버튼 클릭됨')
self.cmb_diag.show()
async def start_browser(self):
"""크롬 브라우저 실행 후 로그인"""
@ -624,6 +586,8 @@ class TranslationApp(QWidget):
# 옵션핸들러에 초기화된 page 객체 전달.
self.optionHandler.update_page(self.browser_controller.page)
self.titleHandler.update_page(self.browser_controller.page)
self.priceHandler.update_page(self.browser_controller.page)
def save_settings(self):
"""QSettings에 사용자 정보 저장"""
@ -725,20 +689,43 @@ class TranslationApp(QWidget):
# 상품 수정 다이얼로그 열기
await self.browser_controller.open_product_edit_dialog(button)
# 옵션 수정
# 상품명과 카테고리 수집
self.start_stage(0)
await self.edit_option(product_name)
product_name = await self.titleHandler.get_original_product_name() # 원본상품명 가져오기
product_category = await self.titleHandler.get_category(market='ss') # 카테고리 가져오기
# await self.edit_title()
self.complete_stage(0)
# 가격 수정
# self.start_stage(0)
await self.edit_price()
# self.complete_stage(0)
if self.toggle_states['optionTrnas'] or self.toggle_states['optionIMGTrans'] or self.toggle_states['optionAutoSelect']:
self.logger.debug(f"옵션수정 : {self.toggle_states['optionTrnas']} + {self.toggle_states['optionIMGTrans']} + {self.toggle_states['optionAutoSelect']}")
# 옵션 수정
self.start_stage(0)
await self.edit_option(product_name)
self.complete_stage(0)
if self.toggle_states['price']:
self.logger.debug(f"가격수정 : {self.toggle_states['price']} ")
# 가격 수정
# self.start_stage(0)
await self.edit_price(product_category)
# self.complete_stage(0)
# 상세페이지 수정
self.start_stage(1)
await self.detail_trans()
self.complete_stage(1)
if self.toggle_states['thumb']:
pass
if self.toggle_states['tag']:
pass
if self.toggle_states['title']:
pass
if self.toggle_states['detail_Option'] or self.toggle_states['detail_IMGTrans']:
self.logger.debug(f"상세페이지 수정 : {self.toggle_states['detail_Option']} + {self.toggle_states['detail_IMGTrans']}")
# 상세페이지 수정
self.start_stage(1)
await self.detail_trans()
self.complete_stage(1)
# 수정 후 저장
self.logger.debug('상품 세부사항 저장 중...')
@ -833,14 +820,14 @@ class TranslationApp(QWidget):
self.detail_progress_bar.setVisible(False)
async def edit_price(self):
async def edit_price(self, product_category):
# 상세페이지 탭 클릭
await self.browser_controller.click_option_tab()
await self.browser_controller.click_price_tab()
# await self.browser_controller.page.wait_for_load_state('networkidle', timeout=10000)
self.detail_progress_bar.setVisible(True)
# 가격 수정 프로세스
await self.priceHandler.process_price()
await self.priceHandler.process_price(category=product_category)
# 수정 후 저장
await self.browser_controller.save_product_edit()

View File

@ -66,6 +66,22 @@ class LocatorManager:
'file_input_locator': self.config.get('OptionLocators', 'file_input_locator')
}
# TitleLocators 섹션
self.selectors['TitleLocators'] = {
'product_name_input_locator': self.config.get('TitleLocators', 'product_name_input_locator'),
'product_name_suggestion_input_locator': self.config.get('TitleLocators', 'product_name_suggestion_input_locator'),
'product_name_search_button_locator': self.config.get('TitleLocators', 'product_name_search_button_locator'),
'original_product_name_locator': self.config.get('TitleLocators', 'original_product_name_locator'),
'product_name_warning_delete_button_locator': self.config.get('TitleLocators', 'product_name_warning_delete_button_locator'),
'category_suggestion_button_locator': self.config.get('TitleLocators', 'category_suggestion_button_locator'),
'category_main_selector_with_cp': self.config.get('TitleLocators', 'category_main_selector_with_cp'),
'category_main_selector_with_ss': self.config.get('TitleLocators', 'category_main_selector_with_ss'),
'category_main_selector_with_esm': self.config.get('TitleLocators', 'category_main_selector_with_esm'),
'category_certified_text_locator': self.config.get('TitleLocators', 'category_certified_text_locator'),
'category_text_with_certification_locator': self.config.get('TitleLocators', 'category_text_with_certification_locator'),
'category_text_without_certification_locator': self.config.get('TitleLocators', 'category_text_without_certification_locator'),
}
def get_locator(self, section, key):
"""

View File

@ -53,9 +53,9 @@ async def main():
logger.error(f"DPI 인식 설정 실패: {e}")
# 기존 TranslationApp UI 사용
whale_translator = WhaleTranslator(app, logger, secret_mode=True, vd_mode=True) # 모드 켜기
await whale_translator.start_whale_browser()
window = TranslationApp(logger, whale_translator) # PySide6 UI
# whale_translator = WhaleTranslator(app, logger, secret_mode=True, vd_mode=True) # 모드 켜기
# await whale_translator.start_whale_browser()
window = TranslationApp(logger, app) # PySide6 UI
window.show()
# asyncio와 PySide6 이벤트 루프를 통합

View File

@ -28,6 +28,10 @@ class PriceHandler:
self.initial_setting()
self.initialize_values() # 초기값 설정 메서드
def update_page(self, page1):
self.page = page1
self.logger.debug(f"page객체 업데이트 : {page1}")
def initialize_values(self):
"""
가격과 관련된 변수들을 초기화하는 메서드입니다.

4371
src/Percenty_SS_Code.json Normal file

File diff suppressed because it is too large Load Diff

502
src/cmb_diag.py Normal file
View File

@ -0,0 +1,502 @@
from PySide6.QtWidgets import (QDialog, QFileDialog, QVBoxLayout, QHBoxLayout, QGridLayout, QTreeWidget, QTreeWidgetItem, QLabel, QLineEdit, QPushButton,
QSpinBox, QGroupBox, QFileDialog, QMessageBox, QCheckBox)
from PySide6.QtCore import Qt
from PySide6.QtGui import QFont
import sqlite3
import shutil
import os, re
import pandas as pd
class CMBSettingsDialog(QDialog):
def __init__(self, parent=None, logger=None, debug=False):
super().__init__(parent)
self.setWindowTitle("CMB 설정")
self.resize(900, 600)
self.logger = logger
self.debug = debug
# DB 파일 경로 설정
self.user_db_path = os.path.join("src", "userDB.db")
self.initial_db_path = os.path.join("src", "initialDB.db")
# userDB 체크 및 초기화
if not os.path.exists(self.user_db_path):
shutil.copyfile(self.initial_db_path, self.user_db_path)
# DB 연결 설정
self.conn = sqlite3.connect(self.user_db_path)
self.cursor = self.conn.cursor()
# 메인 레이아웃 설정
main_layout = QHBoxLayout()
left_layout = QVBoxLayout()
mid_layout = QVBoxLayout()
right_layout = QVBoxLayout()
# 카테고리 검색 및 단계 설정 버튼
search_layout = QHBoxLayout()
search_layout.addWidget(QLabel("카테고리 검색:"))
self.search_input = QLineEdit()
self.search_input.returnPressed.connect(self.search_category)
self.search_btn = QPushButton("검색")
self.search_btn.setDefault(True)
self.search_btn.setAutoDefault(True)
self.search_btn.setDefault(False)
self.search_btn.clicked.connect(self.search_category)
search_layout.addWidget(self.search_input)
search_layout.addWidget(self.search_btn)
self.cmb_view_btn = QPushButton("모두 보기")
self.cmb_view_btn.clicked.connect(self.toggle_cmb_view)
search_layout.addWidget(self.cmb_view_btn)
left_layout.addLayout(search_layout, 1)
# 카테고리 목록 테이블
self.category_tree = QTreeWidget()
self.category_tree.setHeaderLabels(["Level1", "Level2", "Level3", "Level4", "CMB 단계"])
self.category_tree.setColumnCount(5)
self.category_tree.setRootIsDecorated(False)
self.category_tree.setAlternatingRowColors(True)
self.category_tree.setSortingEnabled(True) # 정렬 기능 활성화
# 정렬 순서 추적
self.sort_order = Qt.AscendingOrder
# 헤더 클릭 시그널 연결
self.category_tree.header().sectionClicked.connect(self.sort_by_column)
left_layout.addWidget(self.category_tree, 6)
main_layout.addLayout(left_layout,7)
if self.debug:
# DB 생성 및 초기화 버튼 추가
db_create_btn = QPushButton("DB 생성")
db_create_btn.clicked.connect(self.create_tables)
db_reset_btn = QPushButton("DB 초기화")
db_reset_btn.clicked.connect(self.reset_db)
mid_layout.addWidget(db_create_btn)
mid_layout.addWidget(db_reset_btn)
# 단계별 적용 버튼 추가
apply_1_btn = QPushButton("1단계 적용")
apply_2_btn = QPushButton("2단계 적용")
apply_3_btn = QPushButton("3단계 적용")
apply_1_btn.clicked.connect(lambda: self.apply_crmobi_stage(1))
apply_2_btn.clicked.connect(lambda: self.apply_crmobi_stage(2))
apply_3_btn.clicked.connect(lambda: self.apply_crmobi_stage(3))
mid_layout.addWidget(apply_1_btn)
mid_layout.addWidget(apply_2_btn)
mid_layout.addWidget(apply_3_btn)
# 닫기 버튼
close_btn = QPushButton("닫기")
close_btn.clicked.connect(self.close)
mid_layout.addWidget(close_btn)
# CMB 단계 설정 표시/숨기기 버튼
toggle_button = QPushButton("CMB 단계 설정 ▶")
toggle_button.setCheckable(True)
toggle_button.clicked.connect(self.toggle_cmb_settings)
mid_layout.addWidget(toggle_button)
main_layout.addLayout(mid_layout,1)
# CMB 단계 설정 그룹 추가
self.cmb_settings_group = QGroupBox("CMB 단계 설정")
cmb_settings_layout = QVBoxLayout()
# 각 단계별 설정
self.stage_widgets = []
for i in range(1, 4):
stage_group_box = QGroupBox(f"{i}단계")
stage_group_box_layout = QGridLayout()
# CustomSpinBox로 대체
min_amount_label = QLabel("원 초과시")
min_amount_label.setStyleSheet("""
QLabel {
font-size: 12pt; /* 폰트 크기 설정 */
font-weight: bold; /* 굵은 폰트 */
font-style: normal; /* 기울임 없음 */
}
""")
min_amount_spin = CustomSpinBox()
min_amount_spin.setSuffix ("")
min_amount_spin.setValue(100000 * i)
min_amount_spin.setSingleStep(1000)
unit_amount_label = QLabel("원 마다")
unit_amount_label.setStyleSheet("""
QLabel {
font-size: 12pt; /* 폰트 크기 설정 */
font-weight: bold; /* 굵은 폰트 */
font-style: normal; /* 기울임 없음 */
}
""")
unit_amount_spin = CustomSpinBox()
unit_amount_spin.setSuffix ("")
unit_amount_spin.setValue(20000)
unit_amount_spin.setSingleStep(1000)
cost_label = QLabel("원 추가")
cost_label.setStyleSheet("""
QLabel {
font-size: 12pt; /* 폰트 크기 설정 */
font-weight: bold; /* 굵은 폰트 */
font-style: normal; /* 기울임 없음 */
}
""")
cost_spin = CustomSpinBox()
cost_spin.setSuffix ("")
cost_spin.setValue(8000 + 4000 * i)
cost_spin.setSingleStep(1000)
# QGroupBox 레이아웃 구성
stage_group_box_layout.addWidget(min_amount_spin,0,0)
stage_group_box_layout.addWidget(min_amount_label,0,1)
stage_group_box_layout.addWidget(unit_amount_spin,1,0)
stage_group_box_layout.addWidget(unit_amount_label,1,1)
stage_group_box_layout.addWidget(cost_spin,2,0)
stage_group_box_layout.addWidget(cost_label,2,1)
stage_group_box.setLayout(stage_group_box_layout)
# 전체 레이아웃에 QGroupBox 추가
cmb_settings_layout.addWidget(stage_group_box)
# 각 단계별 스핀박스를 저장하여 추후 참조 가능
self.stage_widgets.append((min_amount_spin, unit_amount_spin, cost_spin))
self.cmb_settings_group.setLayout(cmb_settings_layout)
right_layout.addWidget(self.cmb_settings_group)
main_layout.addLayout(right_layout,2)
self.setLayout(main_layout)
# DB 읽어와서 테이블에 표시
self.load_db_to_table()
def create_tables(self):
"""초기 DB를 생성하고 CrMoBi 단계 테이블도 추가합니다."""
file_dialog = QFileDialog()
excel_path, _ = file_dialog.getOpenFileName(self, "엑셀 파일 선택", "", "Excel Files (*.xlsx *.xls)")
if excel_path:
try:
db_path = os.path.join("src", "initialDB.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 테이블 생성
cursor.execute('''
CREATE TABLE IF NOT EXISTS categories (
category1 TEXT,
category2 TEXT,
category3 TEXT,
category4 TEXT,
crmobi_stage INTEGER DEFAULT 0
)
''')
# 엑셀 파일에서 "스스 카테고리" 시트를 읽어옴
sheet_name = '스스 카테고리'
try:
# 시트의 첫 번째 열만 읽어옴
df = pd.read_excel(excel_path, sheet_name=sheet_name, header=None, usecols=[0])
df.columns = ["full_category"] # 열 이름 지정
# 카테고리 코드와 경로를 추출
for _, row in df.iterrows():
# 카테고리 코드 제거 및 텍스트 추출
full_text = row['full_category']
match = re.match(r'\[.*?\]\s*(.*)', full_text)
if match:
category_path = match.group(1) # 대괄호 안의 카테고리 코드를 제외한 텍스트
# 하이픈으로 카테고리 분할
category_parts = category_path.split('-')
category_levels = category_parts + [""] * (4 - len(category_parts)) # 4개로 맞추기 위해 빈 문자열 추가
# 데이터베이스에 삽입
cursor.execute('''
INSERT INTO categories (category1, category2, category3, category4)
VALUES (?, ?, ?, ?)
''', category_levels[:4])
except Exception as sheet_error:
QMessageBox.warning(self, "시트 불러오기 오류", f"'{sheet_name}' 시트에서 데이터를 읽어오는 중 오류가 발생했습니다: {sheet_error}")
# CrMoBi 단계 테이블 생성
cursor.execute('''
CREATE TABLE IF NOT EXISTS crmobi_stages (
stage INTEGER PRIMARY KEY,
threshold INTEGER,
increment_unit INTEGER,
extra_cost INTEGER
)
''')
# 초기 데이터 설정 (원하는 경우)
for i in range(1, 4):
cursor.execute('''
INSERT INTO crmobi_stages (stage, threshold, increment_unit, extra_cost)
VALUES (?, ?, ?, ?)
''', (i, 100000 * i, 20000, 5000 * i))
conn.commit()
conn.close()
QMessageBox.information(self, "DB 생성", "DB가 성공적으로 생성되었습니다.")
except Exception as e:
QMessageBox.critical(self, "DB 생성 오류", f"DB 생성 중 오류가 발생했습니다: {e}")
def load_db_to_table(self, search_text=None, cmb_stage=None):
"""DB에서 데이터를 읽어와 테이블에 표시하고 배경색을 설정합니다."""
self.category_tree.clear()
# 기본 쿼리와 조건을 설정
query = '''SELECT category1, category2, category3, category4, crmobi_stage FROM categories WHERE 1=1'''
args = []
# 검색어가 있을 경우 WHERE 조건 추가
if search_text:
query += ''' AND (category1 LIKE ? OR category2 LIKE ? OR category3 LIKE ? OR category4 LIKE ?)'''
args.extend([f"%{search_text}%"] * 4)
# CMB 단계 필터링 조건 추가
if cmb_stage:
query += " AND crmobi_stage = ?"
args.append(cmb_stage)
self.cursor.execute(query, args)
rows = self.cursor.fetchall()
for row_data in rows:
top_item = QTreeWidgetItem([str(data) if data else "" for data in row_data[:4]])
crmobi_stage = row_data[4]
top_item.setCheckState(0, Qt.Unchecked)
top_item.setText(4, f"{crmobi_stage}" if crmobi_stage > 0 else "미적용")
# 단계별 배경색 설정
if crmobi_stage == 1:
top_item.setBackground(4, Qt.yellow)
elif crmobi_stage == 2:
top_item.setBackground(4, Qt.green)
elif crmobi_stage == 3:
top_item.setBackground(4, Qt.red)
self.category_tree.addTopLevelItem(top_item)
def sort_by_column(self, index):
"""클릭된 열을 기준으로 오름차순/내림차순으로 정렬"""
self.category_tree.sortByColumn(index, self.sort_order)
# 정렬 순서를 토글
self.sort_order = Qt.DescendingOrder if self.sort_order == Qt.AscendingOrder else Qt.AscendingOrder
def apply_crmobi_stage(self, stage):
"""선택된 카테고리에 CrMoBi 단계를 적용합니다."""
for i in range(self.category_tree.topLevelItemCount()):
item = self.category_tree.topLevelItem(i)
if item.checkState(0) == Qt.Checked:
category_values = [item.text(j) for j in range(4)]
# DB 업데이트
self.cursor.execute('''UPDATE categories
SET crmobi_stage = ?
WHERE category1 = ? AND category2 = ? AND category3 = ? AND category4 = ?''',
[stage] + category_values)
# 트리뷰 업데이트
item.setText(4, str(stage))
if stage == 1:
item.setBackground(4, Qt.yellow)
elif stage == 2:
item.setBackground(4, Qt.green)
elif stage == 3:
item.setBackground(4, Qt.red)
self.conn.commit() # 변경사항 저장
self.load_db_to_table() # 트리 새로고침
def toggle_cmb_settings(self, checked):
"""CMB 단계 설정 영역 표시/숨기기"""
self.cmb_settings_group.setVisible(checked)
sender = self.sender()
if checked:
sender.setText("CMB 단계 설정 ▼")
else:
sender.setText("CMB 단계 설정 ▶")
def reset_db(self):
"""사용자 DB를 삭제하고 초기 DB를 로드합니다."""
if os.path.exists(self.user_db_path):
os.remove(self.user_db_path)
shutil.copyfile(self.initial_db_path, self.user_db_path)
QMessageBox.information(self, "DB 초기화", "사용자 DB가 삭제되었습니다. 초기 DB를 로드합니다.")
self.load_db_to_table()
def save_user_db(self):
"""사용자가 설정한 내용을 저장하여 사용자 DB로 생성합니다."""
# 테이블 생성
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category1 TEXT,
category2 TEXT,
category3 TEXT,
category4 TEXT
)
''')
# 기존 데이터를 모두 삭제
self.cursor.execute("DELETE FROM categories")
# 테이블 데이터 삽입
for row_idx in range(self.category_table.rowCount()):
row_data = [
self.category_table.item(row_idx, col_idx).text() if self.category_table.item(row_idx, col_idx) else ""
for col_idx in range(4)
]
self.cursor.execute("INSERT INTO categories (category1, category2, category3, category4) VALUES (?, ?, ?, ?)", row_data)
QMessageBox.information(self, "DB 저장", "사용자 DB가 저장되었습니다.")
def toggle_cmb_view(self):
"""CMB 보기 버튼을 클릭할 때마다 단계별로 토글하여 필터링합니다."""
current_text = self.cmb_view_btn.text()
if current_text == "모두 보기":
self.cmb_view_btn.setText("1단계 보기")
self.load_db_to_table(cmb_stage=1)
elif current_text == "1단계 보기":
self.cmb_view_btn.setText("2단계 보기")
self.load_db_to_table(cmb_stage=2)
elif current_text == "2단계 보기":
self.cmb_view_btn.setText("3단계 보기")
self.load_db_to_table(cmb_stage=3)
else:
self.cmb_view_btn.setText("모두 보기")
self.load_db_to_table()
def search_category(self):
"""카테고리 검색 기능."""
search_text = self.search_input.text().strip()
if search_text:
# 검색어가 있는 경우 필터링하여 로드
query = '''
SELECT category1, category2, category3, category4, crmobi_stage
FROM categories
WHERE category1 LIKE ? OR category2 LIKE ? OR category3 LIKE ? OR category4 LIKE ?
'''
args = [f"%{search_text}%"] * 4
self.cursor.execute(query, args)
else:
# 검색어가 없는 경우 전체 로드
query = '''
SELECT category1, category2, category3, category4, crmobi_stage
FROM categories
'''
self.cursor.execute(query)
# 검색 결과를 트리에 표시
self.category_tree.clear()
rows = self.cursor.fetchall()
for row_data in rows:
top_item = QTreeWidgetItem([str(data) if data else "" for data in row_data[:4]])
crmobi_stage = row_data[4]
top_item.setCheckState(0, Qt.Unchecked)
top_item.setText(4, f"{crmobi_stage}" if crmobi_stage > 0 else "미적용")
# 단계별 배경색 설정
if crmobi_stage == 1:
top_item.setBackground(4, Qt.yellow)
elif crmobi_stage == 2:
top_item.setBackground(4, Qt.green)
elif crmobi_stage == 3:
top_item.setBackground(4, Qt.red)
self.category_tree.addTopLevelItem(top_item)
def get_crmobi_stage(self, category):
"""
주어진 카테고리에 해당하는 CMB 단계가 설정되어 있는지 확인하고,
설정된 경우 해당 단계의 범위를 반환합니다.
Parameters:
category (str): 카테고리 이름 (: '가구/인테리어')
Returns:
tuple: (min_amount, unit_amount, extra_cost) 값이 있는 경우
None: 설정된 CMB 단계가 없는 경우
"""
try:
# DB에서 카테고리에 대한 CMB 단계 정보를 가져옴
query = '''
SELECT crmobi_stage FROM categories
WHERE category1 = ? OR category2 = ? OR category3 = ? OR category4 = ?
'''
self.logger.debug(f"category : {category}")
args = [category] * 4
self.cursor.execute(query, args)
result = self.cursor.fetchone()
if result and result[0] > 0: # CMB 단계가 설정되어 있을 때
crmobi_stage = result[0]
# 설정된 CMB 단계에 해당하는 범위 가져오기
stage_query = '''
SELECT threshold, increment_unit, extra_cost
FROM crmobi_stages
WHERE stage = ?
'''
self.cursor.execute(stage_query, (crmobi_stage,))
stage_result = self.cursor.fetchone()
self.logger.debug(f"stage_result : {stage_result}")
if stage_result:
min_amount, unit_amount, extra_cost = stage_result
return (min_amount, unit_amount, extra_cost)
# CMB 단계가 설정되지 않은 경우
return None
except Exception as e:
QMessageBox.critical(self, "DB 오류", f"DB 조회 중 오류가 발생했습니다: {e}")
return None
def closeEvent(self, event):
"""QDialog 종료 시 DB 연결 해제"""
self.conn.close()
event.accept()
def focusInEvent(self, event):
""" 다이얼로그에 포커스가 들어올 때 검색 필드에 포커스를 강제 설정 """
self.search_input.setFocus()
super().focusInEvent(event)
class CustomSpinBox(QSpinBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 폰트 설정
font = QFont()
font.setPointSize(12) # 폰트 크기 설정
font.setBold(True) # 폰트 굵게 설정
self.setFont(font)
# 스핀박스 설정
self.setRange(0, 1000000)
self.setSingleStep(10000)
def textFromValue(self, value):
"""숫자를 3자리마다 콤마 추가된 형식으로 표시"""
return f"{value:,}"

BIN
src/cmb_settings.db Normal file

Binary file not shown.

BIN
src/initialDB.db Normal file

Binary file not shown.

BIN
src/userDB.db Normal file

Binary file not shown.

View File

@ -7,8 +7,9 @@ class TitleHandler:
page (Page): Playwright의 페이지 객체로, 브라우저와의 상호작용을 담당합니다.
logger (Logger): 로깅을 위한 Logger 객체입니다.
"""
def __init__(self, page, logger, locator_manager):
self.page = page
def __init__(self, locator_manager, browser_controller, logger):
self.browser_controller = browser_controller
self.page = self.browser_controller.page
self.logger = logger
self.locator_manager = locator_manager
@ -16,16 +17,20 @@ class TitleHandler:
self.product_name_input_locator = self.locator_manager.get_locator('TitleLocators', 'product_name_input_locator')
self.suggestion_input_locator = self.locator_manager.get_locator('TitleLocators', 'suggestion_input_locator')
self.search_button_locator = self.locator_manager.get_locator('TitleLocators', 'search_button_locator')
self.original_name_locator = self.locator_manager.get_locator('TitleLocators', 'original_name_locator')
self.original_product_name_locator = self.locator_manager.get_locator('TitleLocators', 'original_product_name_locator')
self.delete_warning_button_locator = self.locator_manager.get_locator('TitleLocators', 'delete_warning_button_locator')
self.category_suggestion_button_locator = self.locator_manager.get_locator('TitleLocators', 'category_suggestion_button_locator')
self.main_category_locator_with_cp = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_cp')
self.main_category_locator_with_ss = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_ss')
self.main_category_locator_with_esm = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_esm')
self.category_main_selector_with_cp = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_cp')
self.category_main_selector_with_ss = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_ss')
self.category_main_selector_with_esm = self.locator_manager.get_locator('TitleLocators', 'category_main_selector_with_esm')
self.certified_text_locator = self.locator_manager.get_locator('TitleLocators', 'certified_text_locator')
self.category_text_locator = self.locator_manager.get_locator('TitleLocators', 'category_text_locator')
self.category_text_locator_certified = self.locator_manager.get_locator('TitleLocators', 'category_text_locator_certified')
def update_page(self, page1):
self.page = page1
self.logger.debug(f"page객체 업데이트 : {page1}")
async def get_product_name(self) -> str:
"""
노출상품명 입력칸에서 상품명을 가져오는 메서드입니다.
@ -85,7 +90,7 @@ class TitleHandler:
"""
try:
self.logger.debug("원본 상품명을 가져오는 중입니다.")
original_name_element = await self.page.query_selector(self.original_name_locator)
original_name_element = await self.page.query_selector(self.original_product_name_locator)
original_name = await original_name_element.inner_text() if original_name_element else ""
self.logger.debug(f"원본 상품명: {original_name}")
return original_name
@ -123,7 +128,7 @@ class TitleHandler:
except Exception as e:
self.logger.error(f"카테고리 추천받기 버튼 클릭 중 오류 발생: {e}", exc_info=True)
async def get_category(self, market) -> str:
async def get_category(self, market='ss') -> str:
"""
카테고리를 가져오는 메서드로 인증 필요 여부에 따라 카테고리 선택자를 다르게 처리합니다.
@ -131,16 +136,16 @@ class TitleHandler:
str: 카테고리 텍스트
"""
try:
self.logger.debug("카테고리 텍스트를 가져오는 중입니다.")
self.logger.debug(f"마켓 : {market} - 카테고리 텍스트를 가져오는 중입니다.")
if market == 'ss':
main_category_element = await self.page.query_selector(self.main_category_locator_with_ss)
self.logger.debug(f"선택 마켓 : 스마트스토어")
main_category_element = await self.page.query_selector(self.category_main_selector_with_ss)
self.logger.debug(f"선택 마켓 : 스마트스토어 , selector : {self.category_main_selector_with_ss}, element : {main_category_element}")
elif market == 'cp':
main_category_element = await self.page.query_selector(self.main_category_locator_with_cp)
self.logger.debug(f"선택 마켓 : 쿠팡")
main_category_element = await self.page.query_selector(self.category_main_selector_with_cp)
self.logger.debug(f"선택 마켓 : 쿠팡 , selector : {self.category_main_selector_with_cp}, element : {main_category_element}")
elif market == 'esm':
main_category_element = await self.page.query_selector(self.main_category_locator_with_esm)
self.logger.debug(f"선택 마켓 : ESM")
main_category_element = await self.page.query_selector(self.category_main_selector_with_esm)
self.logger.debug(f"선택 마켓 : ESM , selector : {self.category_main_selector_with_esm}, element : {main_category_element}")
if not main_category_element:
self.logger.error("카테고리 메인 선택자를 찾을 수 없습니다.")
return ""

View File

@ -28,7 +28,7 @@ class WhaleTranslator:
self.whale_hwnd = None
async def start_whale_browser(self):
def start_whale_browser(self):
"""비동기 브라우저 시작 및 가상 데스크탑 처리"""
if self.vd_mode:
self.ensure_virtual_desktop_2_exists() # 가상 데스크탑 2 생성