2504 lines
112 KiB
Python
2504 lines
112 KiB
Python
import sys
|
|
import re
|
|
import shutil
|
|
import os
|
|
import json
|
|
import logging
|
|
from logging.handlers import RotatingFileHandler
|
|
from openpyxl import load_workbook
|
|
import pandas as pd
|
|
import sqlite3
|
|
import requests
|
|
import time
|
|
from io import BytesIO
|
|
from PIL import Image
|
|
import xlwings as xw
|
|
import pyautogui
|
|
import math
|
|
|
|
from modules.message.MessageController import MessageController
|
|
from modules.text_input import TextDialog
|
|
|
|
from datetime import datetime
|
|
|
|
from baseDB import create_db, parse_data, read_excel_category_data, insert_category_data_to_db
|
|
from naverParser import parse_naver_shopping
|
|
from scraping_thread import ScrapingThread # 스크래핑 스레드 클래스 import
|
|
from image_save_thread import ImageSaveThread # 이미지 스크래핑 스레드 클래스 import
|
|
from tao_scraping_thread import TaoScrapingThread # 스크래핑 스레드 클래스 import
|
|
from modules.tao_img_save_thread import TaobaoImageSaveThread
|
|
from xlwings_dialog import LogDialog
|
|
from modules.automatch_tao import automatch
|
|
from modules.tao_card import TaoCard
|
|
from modules.full_auto import FullAuto
|
|
from modules.logger_module import setup_logger, QTextEditLogger
|
|
import logging
|
|
import ctypes
|
|
import atexit
|
|
|
|
from PyQt5.QtCore import QAbstractTableModel, Qt, QModelIndex
|
|
from PyQt5.QtGui import QColor, QFont
|
|
import pandas as pd
|
|
|
|
from PyQt5.QtCore import pyqtSignal, QObject
|
|
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
|
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, QAbstractTableModel, QTimer, QObject, pyqtSlot, QEvent, QUrl, QTimer
|
|
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QPixmap, QGuiApplication, QColor, QKeyEvent
|
|
from PyQt5.QtWidgets import QStyledItemDelegate, QLineEdit, QApplication, QFileDialog, QTableWidgetItem, QLabel, QMainWindow, QTableView, QVBoxLayout, QWidget, QComboBox, QMessageBox, QGraphicsScene, QGraphicsPixmapItem, QDialogButtonBox, QInputDialog, QMessageBox, QTabWidget, QSpinBox
|
|
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage, QWebEngineSettings
|
|
from PyQt5.QtWebChannel import QWebChannel
|
|
|
|
|
|
|
|
|
|
# 이 줄을 추가하세요.
|
|
#QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
|
|
|
|
now = datetime.now()
|
|
|
|
# 전역 변수 초기화
|
|
BASE_PATH = None
|
|
|
|
|
|
#class QTextEditLogger(logging.Handler, QObject):
|
|
#appendPlainText = pyqtSignal(str)
|
|
|
|
#def __init__(self):
|
|
# super().__init__()
|
|
#QObject.__init__(self)
|
|
|
|
#def emit(self, record):
|
|
#msg = self.format(record)
|
|
#self.appendPlainText.emit(msg)
|
|
|
|
|
|
def update_db(db_name, query, params):
|
|
""" 데이터베이스 업데이트 공통 메서드 """
|
|
with sqlite3.connect(db_name) as conn:
|
|
cursor = conn.cursor()
|
|
try:
|
|
cursor.execute(query, params)
|
|
conn.commit()
|
|
except Exception as e:
|
|
conn.rollback()
|
|
print(f"Failed to update database: {e}")
|
|
return False
|
|
return True
|
|
|
|
|
|
class CustomSpinBox(QSpinBox):
|
|
def __init__(self, delta=1000, parent=None):
|
|
super(CustomSpinBox, self).__init__(parent)
|
|
self.delta = delta
|
|
self.setSingleStep(self.delta)
|
|
|
|
def textFromValue(self, value):
|
|
# 3자리 숫자마다 콤마를 찍는 로직
|
|
return "{:,}".format(value)
|
|
|
|
class EditDelegate(QStyledItemDelegate):
|
|
def createEditor(self, parent, option, index):
|
|
# QLineEdit 위젯을 생성합니다.
|
|
editor = QLineEdit(parent)
|
|
# 현재 셀의 데이터를 가져옵니다.
|
|
current_text = index.data(Qt.DisplayRole)
|
|
# QLineEdit에 현재 데이터를 설정합니다.
|
|
editor.setText(current_text)
|
|
return editor
|
|
|
|
def setEditorData(self, editor, index):
|
|
# editor에 index 위치의 데이터를 설정합니다.
|
|
text = index.model().data(index, Qt.DisplayRole)
|
|
editor.setText(str(text))
|
|
|
|
|
|
class PandasModel(QAbstractTableModel):
|
|
def __init__(self, data, db_file, table_name):
|
|
super(PandasModel, self).__init__()
|
|
self._data = data
|
|
self.selected_cell = None # 선택된 셀 초기값 설정
|
|
self.sort_order = Qt.AscendingOrder
|
|
self.sort_column = 0
|
|
self.db_file = db_file
|
|
self.table_name = table_name
|
|
|
|
def rowCount(self, parent=None):
|
|
return self._data.shape[0]
|
|
|
|
def columnCount(self, parent=None):
|
|
return self._data.shape[1]
|
|
|
|
def data(self, index, role):
|
|
if not index.isValid():
|
|
return None
|
|
if role == Qt.DisplayRole:
|
|
return str(self._data.iloc[index.row(), index.column()])
|
|
elif role == Qt.BackgroundColorRole:
|
|
return self.background_color(index)
|
|
elif role == Qt.FontRole:
|
|
return self.font_style(index)
|
|
return None
|
|
|
|
def background_color(self, index):
|
|
matching_url = self._data.loc[index.row(), 'MatchingUrl']
|
|
if matching_url and matching_url.startswith('http'):
|
|
return QColor('lightgray')
|
|
if self.selected_cell == (index.row(), index.column()):
|
|
return QColor('yellow')
|
|
|
|
def font_style(self, index):
|
|
current_row = index.row()
|
|
current_keyword_id = self._data.iloc[current_row, 1]
|
|
previous_keyword_id = self._data.iloc[current_row - 1, 1] if current_row > 0 else ""
|
|
if current_row == 0 or current_keyword_id > previous_keyword_id:
|
|
font = QFont()
|
|
font.setBold(True)
|
|
font.setUnderline(True)
|
|
return font
|
|
|
|
def headerData(self, section, orientation, role):
|
|
if role == Qt.DisplayRole:
|
|
if orientation == Qt.Horizontal:
|
|
return self._data.columns[section]
|
|
else:
|
|
return str(self._data.index[section])
|
|
|
|
def update_selected_cell(self, cell):
|
|
self.selected_cell = cell
|
|
self.layoutChanged.emit()
|
|
|
|
def sort(self, column, order):
|
|
try:
|
|
self.layoutAboutToBeChanged.emit()
|
|
self._data.sort_values(by=self._data.columns[column], ascending=(order == Qt.AscendingOrder), inplace=True)
|
|
self._data.reset_index(drop=True, inplace=True)
|
|
self.layoutChanged.emit()
|
|
except Exception as e:
|
|
print(f"Error sorting data: {e}")
|
|
|
|
def setData(self, index, value, role=Qt.EditRole):
|
|
if not index.isValid():
|
|
return False
|
|
if role == Qt.EditRole:
|
|
row = index.row()
|
|
col = index.column()
|
|
current_value = self._data.iloc[row, col]
|
|
if current_value != value: # 값이 변경되었는지 확인
|
|
response = QMessageBox.question(None, "값 업데이트", "변경된 값을 DB에 적용할까요?",
|
|
QMessageBox.Yes | QMessageBox.No)
|
|
if response == QMessageBox.Yes:
|
|
column_name = self._data.columns[col]
|
|
self._data.iloc[row, col] = value # 데이터프레임 업데이트
|
|
self.dataChanged.emit(index, index, [role]) # 뷰 업데이트 신호
|
|
self.update_database(row, column_name, value) # 데이터베이스 업데이트
|
|
return True
|
|
else:
|
|
# 변경 사항 무시
|
|
return False
|
|
return True
|
|
return False
|
|
|
|
def update_database(self, row, column_name, value):
|
|
# 데이터베이스 업데이트 로직
|
|
logger.debug(f"value : {value}")
|
|
column_num = 0
|
|
|
|
logger.debug(f"self.table_name : {self.table_name}")
|
|
if self.table_name == 'Keywords':
|
|
column_num = 4
|
|
elif self.table_name == 'NaverShopping':
|
|
column_num = 14
|
|
|
|
logger.debug(f"self.db_file : {self.db_file}")
|
|
primary_key = self._data.columns[column_num] # 첫 번째 열을 기본 키로 가정
|
|
logger.debug(f"primary_key : {primary_key}")
|
|
primary_key_value = self._data.iloc[row][primary_key]
|
|
logger.debug(f"primary_key_value : {primary_key_value}")
|
|
|
|
query = f"""
|
|
UPDATE {self.table_name}
|
|
SET {column_name} = ?
|
|
WHERE {primary_key} = ?
|
|
"""
|
|
connection = sqlite3.connect(self.db_file)
|
|
logger.debug(f"connection : {connection}")
|
|
logger.debug(f"query : {query}")
|
|
cursor = connection.cursor()
|
|
logger.debug(f"cursor : {cursor}")
|
|
|
|
try:
|
|
# cursor.execute(f"UPDATE {self.table_name} SET {column_name} = ? WHERE {primary_key} = ?", (value, primary_key_value))
|
|
|
|
cursor.execute(query, (value, primary_key_value))
|
|
connection.commit()
|
|
except Exception as e:
|
|
connection.rollback()
|
|
print(f"Failed to update database: {e}")
|
|
finally:
|
|
connection.close()
|
|
|
|
print(f"Updated {column_name} for {primary_key}={primary_key_value} to {value}")
|
|
|
|
def flags(self, index):
|
|
defaultFlags = super(PandasModel, self).flags(index)
|
|
if index.isValid():
|
|
return defaultFlags | Qt.ItemIsEditable
|
|
return defaultFlags
|
|
|
|
|
|
class CustomWebEnginePage(QWebEnginePage):
|
|
def __init__(self, profile, parent=None, use_mobile=False):
|
|
super(CustomWebEnginePage, self).__init__(profile, parent)
|
|
self.mobile_user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1"
|
|
self.desktop_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
|
|
self.setZoomFactor(0.75) # 줌을 75%로 설정
|
|
#self.use_mobile_agent(False)
|
|
#self.profile().setHttpUserAgent(mobile_user_agent)
|
|
|
|
if use_mobile:
|
|
self.profile().setHttpUserAgent(self.mobile_user_agent)
|
|
else:
|
|
self.profile().setHttpUserAgent(self.desktop_user_agent)
|
|
|
|
# JavaScript 활성화
|
|
settings = self.settings()
|
|
settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
|
|
settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True)
|
|
settings.setAttribute(QWebEngineSettings.JavascriptCanAccessClipboard, True)
|
|
|
|
|
|
def use_mobile_agent(self, use_mobile):
|
|
if use_mobile:
|
|
self.profile().setHttpUserAgent(self.mobile_user_agent)
|
|
else:
|
|
self.profile().setHttpUserAgent(self.desktop_user_agent)
|
|
self.parent().load(QUrl("https://world.taobao.com/wow/tmg-fc/tmw/search_image?spm="))
|
|
|
|
def event(self, event):
|
|
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Backspace:
|
|
self.triggerAction(QWebEnginePage.Back)
|
|
return True
|
|
return super().event(event)
|
|
|
|
def contextMenuEvent(self, event):
|
|
menu = self.createStandardContextMenu()
|
|
menu.addAction('Open Inspector', self.inspect)
|
|
menu.exec_(event.globalPos())
|
|
|
|
def inspect(self):
|
|
self.triggerAction(QWebEnginePage.InspectElement)
|
|
|
|
class CustomWebEngineView(QWebEngineView):
|
|
def createWindow(self, window_type):
|
|
# tabWidget 인스턴스에 접근
|
|
logger.debug("createWindow 호출됨")
|
|
tab_widget = self.parent().findChild(QTabWidget, "tabWidget")
|
|
# 새 창 요청을 처리할 webEngineView_2 인스턴스에 접근
|
|
new_web_view = tab_widget.findChild(QWebEngineView, "webEngineView_2")
|
|
|
|
# 새 창 요청을 webEngineView_2로 전달하고, tab_2를 활성화
|
|
if tab_widget and new_web_view:
|
|
logger.debug("새 창 요청 처리 - tab_2 활성화")
|
|
tab_widget.setCurrentIndex(1) # tab_2를 활성화
|
|
return new_web_view
|
|
else:
|
|
logger.debug("tabWidget 또는 webEngineView_2를 찾을 수 없음")
|
|
return super(CustomWebEngineView, self).createWindow(window_type)
|
|
|
|
def keyPressEvent(self, event):
|
|
if event.key() == Qt.Key_Backspace:
|
|
self.back() # 백스페이스가 눌리면 이전 페이지로 이동
|
|
else:
|
|
super().keyPressEvent(event)
|
|
|
|
# # 새 창 요청을 처리하는 로직
|
|
# self.parent().findChild(QTabWidget, "tabWidget").setCurrentWidget(self.parent().findChild(QWidget, "tab_2"))
|
|
# return self.parent().findChild(QWebEngineView, "webEngineView_2")
|
|
|
|
#class NewWebEnginePage(QWebEnginePage):
|
|
# def __init__(self, parent=None):
|
|
# super(NewWebEnginePage, self).__init__(parent)
|
|
|
|
|
|
class Ui_Dialog(QtWidgets.QDialog):
|
|
|
|
def __init__(self, logger=None): # logger 매개변수 추가, 기본값은 None으로 설정
|
|
super(Ui_Dialog, self).__init__()
|
|
|
|
UI_PATH = os.path.join(BASE_PATH, 'outline10.ui')
|
|
uic.loadUi(UI_PATH, self)
|
|
self.logger = logger # logger 인스턴스를 클래스 속성으로 저장
|
|
|
|
|
|
# 화면 꺼짐과 절전 모드 방지 기능 활성화
|
|
prevent_sleep_mode()
|
|
|
|
# 메세지 컨트롤러 객체 생성
|
|
message_controller = MessageController("method")
|
|
self.message_controller = message_controller
|
|
|
|
if self.logger: # logger 인스턴스가 제공되었는지 확인
|
|
self.logger.debug("Ui_Dialog 초기화 시작")
|
|
|
|
self.log_browser = self.findChild(QtWidgets.QTextBrowser, 'logBrowser')
|
|
|
|
# QTextEditLogger 핸들러를 로거에 추가하여 GUI에 로그 출력
|
|
if self.logger:
|
|
gui_log_handler = QTextEditLogger()
|
|
#gui_log_handler.appendPlainText.connect(self.log_browser.append)
|
|
gui_log_handler.appendHtml.connect(self.log_browser.append) # HTML 메시지 추가
|
|
gui_log_handler.scrollToBottom.connect(lambda: self.log_browser.moveCursor(QtGui.QTextCursor.End)) # 자동 스크롤
|
|
self.logger.addHandler(gui_log_handler)
|
|
|
|
logger.debug("로거 브라우저 연결")
|
|
|
|
# self.taocard = TaoCard()
|
|
self.taocard = TaoCard(self) # UI 객체 또는 필요한 부분을 전달
|
|
|
|
|
|
# db_name 속성 초기화
|
|
#self.db_name = None # 여기서 초기값을 None 또는 기본 DB 파일 경로로 설정합니다.
|
|
|
|
# db_name 속성에 기본값 설정
|
|
baseDB_PATH = os.path.join(BASE_PATH, 'baseDB.db')
|
|
|
|
self.db_name = baseDB_PATH # 기본 DB 파일 이름. 필요에 따라 변경하세요.
|
|
|
|
# 클래스 변수
|
|
self.current_table_name = 'Keywords'
|
|
self.selected_row_id = None
|
|
self.model = None # model 속성 초기화
|
|
|
|
self.count_test1 = 0
|
|
self.count_test2 = 0
|
|
self.matching_urlss = ""
|
|
self.matching_urls = ""
|
|
self.papago_client_id =""
|
|
self.papago_client_secret = ""
|
|
self.endMatchingUrl = None
|
|
|
|
self.json_data = []
|
|
self.loadJsonData()
|
|
|
|
self.tanya_btn.clicked.connect(self.show_text_dialog)
|
|
self.tao_Img_save_Btn.clicked.connect(self.on_tao_image_save_btn_clicked)
|
|
self.automatchBtn.clicked.connect(self.automatch_clicked)
|
|
# self.full_auto.clicked.connect(self.full_automatch_clicked)
|
|
self.img_searchbtn.clicked.connect(self.img_search)
|
|
self.Excel_btn.clicked.connect(self.openxls)
|
|
self.copyman_Excel_btn.clicked.connect(self.openxls_copyman)
|
|
self.sourcingman_Excel_btn.clicked.connect(self.openxls_sourcingman)
|
|
self.image_save_btn.clicked.connect(self.click_image_save_btn)
|
|
|
|
self.matchbtn.clicked.connect(self.matchbtn_clicked) # matchbtn 버튼이 클릭되면 matchbtn_clicked 메소드 호출
|
|
self.unMatching.clicked.connect(self.unmatchbtn_clicked)
|
|
#self.ns_scraping.clicked.connect(self.on_ns_scraping_clicked)
|
|
|
|
self.webEngineView.urlChanged.connect(self.update_url)
|
|
self.dbviewer1.clicked.connect(self.on_table_clicked)
|
|
|
|
self.sc_button.clicked.connect(self.on_ns_scraping_clicked)
|
|
|
|
# Qt Designer에서 생성한 UI 클래스 내에서 버튼 클릭 이벤트 연결
|
|
self.papagoApi.clicked.connect(self.manage_papago_api_keys)
|
|
|
|
self.branch_checkBox = self.findChild(QtWidgets.QCheckBox, 'branch_checkBox')
|
|
self.branchCount = self.findChild(QtWidgets.QLineEdit, 'branchCount')
|
|
self.branchCount.setEnabled(False)
|
|
|
|
# 체크박스 상태 변화에 따라 슬롯 연결
|
|
self.branch_checkBox.stateChanged.connect(self.updatebranchCountState)
|
|
|
|
# 프로그램 초기화 부분에 API 키 관리 함수 호출
|
|
# self.manage_papago_api_keys()
|
|
|
|
# CustomWebEnginePage 설정
|
|
#profile = QWebEngineProfile.defaultProfile() # 기본 프로필 사용
|
|
# webEngineView에 CustomWebEnginePage 설정
|
|
#self.webEngineView.setPage(CustomWebEnginePage(profile, self.webEngineView))
|
|
#self.webEngineView = CustomWebEngineView(self.webEngineView)
|
|
# webEngineView_2에도 CustomWebEnginePage 설정 (필요한 경우)
|
|
#self.webEngineView_2.setPage(CustomWebEnginePage(profile, self.webEngineView_2))
|
|
#self.webEngineView_2 = CustomWebEngineView(self.webEngineView_2)
|
|
|
|
|
|
|
|
# webEngineView 인스턴스를 CustomWebEngineView로 초기화
|
|
#self.webEngineView = CustomWebEngineView(self.tabWidget) # 부모를 tabWidget으로 설정
|
|
|
|
# webEngineView_2 인스턴스를 CustomWebEngineView로 초기화
|
|
#self.webEngineView_2 = CustomWebEngineView(self.tabWidget) # 부모를 tabWidget으로 설정
|
|
|
|
# 초기 탭 설정
|
|
self.tabWidget.setCurrentIndex(0) # 첫 번째 탭을 활성화
|
|
|
|
# 페이지 설정
|
|
profile = QWebEngineProfile.defaultProfile()
|
|
|
|
# webEngineView에는 모바일 에이전트 설정
|
|
self.webEngineView.setPage(CustomWebEnginePage(profile, self.webEngineView, use_mobile=True))
|
|
logger.debug("webEngineView 페이지 설정 완료")
|
|
|
|
# webEngineView_2에는 PC 에이전트 설정
|
|
self.webEngineView_2.setPage(CustomWebEnginePage(profile, self.webEngineView_2, use_mobile=False))
|
|
logger.debug("webEngineView_2 페이지 설정 완료")
|
|
|
|
# webEngineView와 webEngineView_2 초기화
|
|
#self.webEngineView = CustomWebEngineView(self)
|
|
#self.webEngineView_2 = CustomWebEngineView(self)
|
|
|
|
# CustomWebEnginePage 설정
|
|
#profile = QWebEngineProfile.defaultProfile()
|
|
#self.webEngineView.setPage(CustomWebEnginePage(profile, self.webEngineView))
|
|
#self.webEngineView_2.setPage(CustomWebEnginePage(profile, self.webEngineView_2))
|
|
|
|
# 스위치 UI 요소 (예: 버튼)에 이벤트 연결
|
|
self.switchBtn.clicked.connect(self.toggleUserAgent)
|
|
|
|
# 기본 URL 설정 및 로드 "https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://world.taobao.com"
|
|
#default_url = "https://world.taobao.com"
|
|
default_url = "https://main.m.taobao.com"
|
|
default_url2 = "https://world.taobao.com/wow/tmg-fc/tmw/search_image?"
|
|
self.webEngineView.load(QUrl(default_url))
|
|
self.webEngineView_2.load(QUrl(default_url2))
|
|
|
|
# 기존DB불러오기 버튼 클릭이벤트
|
|
self.db_btn.clicked.connect(self.loadExistingDb)
|
|
|
|
# db를 엑셀로 저장 버튼
|
|
self.export_btn_by_branch.clicked.connect(self.export_data_by_branch)
|
|
self.export_btn_by_main.clicked.connect(self.export_data_by_main)
|
|
|
|
|
|
# 불러와진 DB에서 테이블을 읽어서 콤보박스에 표시하기
|
|
self.comboBox.currentIndexChanged.connect(self.loadTable)
|
|
|
|
# Load database and table names
|
|
#self.db_name = '202312250341.db' # Replace with your database name
|
|
self.loadTableNames()
|
|
|
|
# Load database and table names
|
|
#self.current_table_name = None # 현재 테이블 이름을 저장할 변수
|
|
|
|
# QGraphicsScene 초기화
|
|
self.scene = QGraphicsScene(self)
|
|
#self.current_img
|
|
self.graphicsView_multi.setScene(self.scene)
|
|
# QGraphicsView의 정렬을 상단 및 좌측에 맞추기
|
|
self.graphicsView_multi.setAlignment(Qt.AlignTop)
|
|
|
|
|
|
# 새로운 QGraphicsView 및 QGraphicsScene 초기화
|
|
self.scene_current_img = QGraphicsScene(self)
|
|
self.current_img.setScene(self.scene_current_img)
|
|
|
|
# searchbtn 버튼 클릭 이벤트 연결
|
|
#self.searchbtn.clicked.connect(self.on_searchbtn_clicked)
|
|
self.searchbtn_2.clicked.connect(self.search_chinese)
|
|
self.search_keyword_box.returnPressed.connect(self.search_chinese)
|
|
|
|
# urlbox와 urlgobtn 이벤트 연결
|
|
self.urlbox.returnPressed.connect(self.load_url_from_urlbox)
|
|
self.urlgobtn.clicked.connect(self.load_url_from_urlbox)
|
|
|
|
# 타이머 설정
|
|
self.timer = QTimer(self)
|
|
# self.timer.timeout.connect(self.updateTimer)
|
|
|
|
# 시작, 정지, 설정 버튼
|
|
self.timerStart.clicked.connect(self.startTimer)
|
|
self.timerStop.clicked.connect(self.stopTimer)
|
|
self.timerSet.clicked.connect(self.setTimer)
|
|
self.startTimer()
|
|
|
|
# 타이머 시간 표시 QLabel
|
|
#self.timerLabel = QLabel('60:00', self)P
|
|
|
|
# 타이머 시간
|
|
self.remainingTime = 3600 # 초 단위 (60분)
|
|
|
|
|
|
# 기존 QSpinBox 객체를 CustomSpinBox 객체로 변환
|
|
self.delivfee_spbox = self.findChild(QSpinBox, 'delivfee_spbox')
|
|
self.addpackingfee_spbox = self.findChild(QSpinBox, 'addpackingfee_spbox')
|
|
self.addmargin_spbox = self.findChild(QSpinBox, 'addmargin_spbox')
|
|
|
|
# QSpinBox 객체를 CustomSpinBox로 설정
|
|
self.updateSpinBox(self.delivfee_spbox, 1000)
|
|
self.updateSpinBox(self.addpackingfee_spbox, 1000)
|
|
self.updateSpinBox(self.addmargin_spbox, 1000)
|
|
|
|
# 각 QSpinBox의 단일 스텝 설정
|
|
# self.delivfee_spbox.setSingleStep(delta_delivfee)
|
|
# self.addpackingfee_spbox.setSingleStep(delta_addpackingfee)
|
|
# self.addmargin_spbox.setSingleStep(delta_addmargin)
|
|
|
|
def show_text_dialog(self):
|
|
|
|
# DB 파일 이름 설정
|
|
now = datetime.now()
|
|
self.db_name = now.strftime('%Y-%m-%d-%H%M') + '.db'
|
|
conn = sqlite3.connect(self.db_name)
|
|
|
|
# DB 생성 및 데이터 로드
|
|
create_db(self.db_name)
|
|
|
|
dialog = TextDialog(conn)
|
|
# self.db_name = TextDialog.getDbName(self)
|
|
dialog.exec_()
|
|
conn.close()
|
|
|
|
read_excel_category_data(self.db_name, BASE_PATH)
|
|
|
|
# DB 로드 후 테이블 이름을 콤보박스에 로드하고 첫 번째 테이블 선택
|
|
self.loadTableNames()
|
|
|
|
def automatch_clicked(self):
|
|
logger.debug("타오바오 자동매칭 쓰레드 시작")
|
|
|
|
item_count = 10
|
|
sort_order = 1
|
|
logger.debug(f"automatch_item_count : {item_count}, sort_order : {sort_order}")
|
|
|
|
try:
|
|
# AutomatchThread 스레드 시작
|
|
self.automatch_thread = TaoScrapingThread(self.db_name, item_count, self.message_controller, sort_order)
|
|
self.automatch_thread.progress_updated.connect(self.update_progress_bar) # 진행 상황 업데이트를 위한 연결
|
|
self.automatch_thread.finished.connect(self.on_automatch_finished) # automatch 작업 완료 후 처리
|
|
self.automatch_thread.start()
|
|
except Exception as e:
|
|
logger.debug(f"타오바오 자동매칭 스레드 실행 중 에러 : {e}")
|
|
|
|
def on_automatch_finished(self):
|
|
# automatch 작업 완료 후 처리
|
|
QMessageBox.information(self, "알림", "작업이 완료되었습니다.")
|
|
|
|
|
|
def updatebranchCountState(self):
|
|
if self.branch_checkBox.isChecked():
|
|
self.branchCount.setEnabled(True) # 체크박스가 체크되면 QLineEdit 활성화
|
|
else:
|
|
self.branchCount.setEnabled(False) # 체크박스가 체크되지 않으면 QLineEdit 비활성화
|
|
# self.branchCount.clear() # 선택이 해제되면 내용을 지웁니다.
|
|
|
|
# img_search 함수 구현
|
|
def img_search(self):
|
|
# 웹 페이지 로드
|
|
img_search__url = "https://world.taobao.com/wow/tmg-fc/tmw/search_image?"
|
|
#self.tab_widget.setCurrentIndex(1) # tab_2를 활성화
|
|
self.tabWidget.setCurrentIndex(2) # 2 번째 탭을 활성화
|
|
|
|
self.webEngineView_2.load(QUrl(img_search__url))
|
|
logger.debug("웹페이지 로드")
|
|
# 페이지 로드가 완료될 때까지 대기 (필요에 따라 시간 조정)
|
|
QTimer.singleShot(500, self.paste_and_search_image)
|
|
|
|
def paste_and_search_image(self):
|
|
# QWebEngineView에 포커스를 맞춥니다
|
|
self.webEngineView_2.setFocus()
|
|
logger.debug("포커스이동")
|
|
# CSS 선택자로 요소 찾아 클릭
|
|
click_input_js = """
|
|
var inputElement = document.querySelector('.rax-textinput');
|
|
if (inputElement) {
|
|
inputElement.click();
|
|
}
|
|
"""
|
|
self.webEngineView_2.page().runJavaScript(click_input_js)
|
|
logger.debug("자바클릭:검색창클릭")
|
|
|
|
# 클립보드에서 이미지 붙여넣기
|
|
|
|
# 시간 지연을 주어 웹 페이지가 준비될 수 있도록 합니다
|
|
time.sleep(0.1)
|
|
|
|
# Ctrl+V 키보드 단축키를 시뮬레이션하여 이미지를 붙여넣습니다
|
|
pyautogui.hotkey('ctrl', 'v')
|
|
logger.debug("컨트롤 브이 실행")
|
|
|
|
# 이미지 붙여넣기 후 버튼 클릭 (추가 대기 시간 필요)
|
|
QTimer.singleShot(100, self.click_search_button)
|
|
|
|
def click_search_button(self):
|
|
# .component-preview-button 클릭
|
|
click_search_js1 = """
|
|
console.log('클릭 스크립트 시작');
|
|
var searchButton = document.querySelector('.component-preview-button');
|
|
if (searchButton) {
|
|
searchButton.click();
|
|
}
|
|
"""
|
|
|
|
click_search_js = """
|
|
console.log('클릭 스크립트 시작');
|
|
|
|
// .component-preview-button 요소가 나타날 때까지 1초 간격으로 확인
|
|
function waitForButton() {
|
|
var searchButton = document.querySelector('.component-preview-button');
|
|
if (searchButton) {
|
|
console.log('버튼 찾음');
|
|
searchButton.click();
|
|
} else {
|
|
console.log('버튼 찾지 못함. 0.3초 후 다시 확인');
|
|
setTimeout(waitForButton, 600);
|
|
}
|
|
}
|
|
|
|
waitForButton();
|
|
"""
|
|
self.webEngineView_2.page().runJavaScript(click_search_js)
|
|
logger.debug("사진검색 버튼 클릭")
|
|
|
|
# 이미지 붙여넣기 후 버튼 클릭 (추가 대기 시간 필요)
|
|
# QTimer.singleShot(3500, self.login)
|
|
|
|
def login(self):
|
|
# 로그인 정보
|
|
tao_id = self.tao_id
|
|
tao_pw = self.tao_pw
|
|
logger.debug("로그인 실행")
|
|
|
|
|
|
# 로그인 스크립트
|
|
login_script = """
|
|
console.log('로그인 스크립트 시작');
|
|
var loginFrame = document.querySelector('#baxia-dialog-content');
|
|
if (loginFrame) {
|
|
console.log('로그인 프레임 발견');
|
|
var loginDocument = loginFrame.contentDocument || loginFrame.contentWindow.document;
|
|
setTimeout(function() {
|
|
console.log('iframe 내부 탐색 시작');
|
|
var idField = loginDocument.querySelector('#fm-login-id');
|
|
var pwField = loginDocument.querySelector('#fm-login-password');
|
|
var loginButton = loginDocument.querySelector('.fm-button');
|
|
|
|
if (idField && pwField && loginButton) {
|
|
console.log('로그인 필드 발견');
|
|
idField.value = '""" + tao_id + """';
|
|
pwField.value = '""" + tao_pw + """';
|
|
loginButton.click();
|
|
console.log('로그인 시도');
|
|
} else {
|
|
console.log('로그인 필드를 찾을 수 없음');
|
|
}
|
|
}, 3000); // iframe이 로드될 때까지 기다림
|
|
} else {
|
|
console.log('로그인 프레임을 찾을 수 없음');
|
|
}
|
|
"""
|
|
|
|
# 로그인 스크립트 실행
|
|
self.webEngineView_2.page().runJavaScript(login_script)
|
|
logger.debug("로그인 실행 완료")
|
|
|
|
|
|
def updateSpinBox(self, spinBox, step):
|
|
# QSpinBox 객체의 기능 확장
|
|
if spinBox:
|
|
spinBox.setSingleStep(step)
|
|
spinBox.textFromValue = lambda value: "{:,}".format(value)
|
|
|
|
|
|
def toggleUserAgent(self):
|
|
current_page = self.webEngineView_2.page() # 현재 페이지를 가져옵니다.
|
|
# 현재 사용 중인 사용자 에이전트가 모바일 버전인지 확인
|
|
is_mobile = current_page.profile().httpUserAgent() == current_page.mobile_user_agent
|
|
|
|
# 사용자 에이전트 전환
|
|
#self.webEnginePage.use_mobile_agent(not is_mobile)
|
|
current_page.use_mobile_agent(not is_mobile)
|
|
|
|
# 페이지 새로고침 (변경 사항 적용을 위해) -> class단에서 이미지검색url로 이동처리
|
|
#self.webEngineView.reload()
|
|
|
|
|
|
def manage_papago_api_keys(self):
|
|
papago_PATH = os.path.join(BASE_PATH, 'papago_api_keys.txt')
|
|
api_keys_filename = papago_PATH
|
|
current_client_id = ""
|
|
current_client_secret = ""
|
|
|
|
if os.path.exists(api_keys_filename):
|
|
with open(api_keys_filename, 'r') as file:
|
|
lines = file.readlines()
|
|
if len(lines) >= 2:
|
|
# 각 행에 대한 존재 확인 및 처리
|
|
current_client_id_line = lines[0]
|
|
current_client_secret_line = lines[1]
|
|
|
|
if ':' in current_client_id_line:
|
|
current_client_id = current_client_id_line.split(':')[1].strip().strip('"')
|
|
self.papago_client_id = current_client_id_line.split(':')[1].strip().strip('"')
|
|
if ':' in current_client_secret_line:
|
|
current_client_secret = current_client_secret_line.split(':')[1].strip().strip('"')
|
|
self.papago_client_secret = current_client_secret_line.split(':')[1].strip().strip('"')
|
|
|
|
if not current_client_id or not current_client_secret:
|
|
reply = QMessageBox.question(self, "API 키 설정", "API 키가 없습니다. 설정하시겠습니까?",
|
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
if reply == QMessageBox.Yes:
|
|
self.get_papago_api_keys_from_user(api_keys_filename)
|
|
else:
|
|
msg = QMessageBox()
|
|
msg.setIcon(QMessageBox.Question)
|
|
msg.setWindowTitle("Papago API Keys")
|
|
msg.setText(f"현재 API Keys:\nClient ID: {current_client_id}\nClient Secret: {current_client_secret}\n\nAPI키를 바꿀까요?")
|
|
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
|
|
if msg.exec_() == QMessageBox.Yes:
|
|
self.get_papago_api_keys_from_user(api_keys_filename)
|
|
|
|
else:
|
|
self.get_papago_api_keys_from_user(api_keys_filename)
|
|
|
|
def get_papago_api_keys_from_user(self, api_keys_filename):
|
|
client_id, ok1 = QInputDialog.getText(self, 'Papago API', 'Enter Client ID:')
|
|
client_secret, ok2 = QInputDialog.getText(self, 'Papago API', 'Enter Client Secret:')
|
|
|
|
if ok1 and ok2:
|
|
with open(api_keys_filename, 'w') as file:
|
|
file.write(f'ClientID: "{client_id}"\nClientSecret: "{client_secret}"\n')
|
|
self.papago_client_id = client_id
|
|
self.papago_client_secret = client_secret
|
|
QMessageBox.information(self, "알림", "Papago API 설정이 완료되었습니다.")
|
|
|
|
|
|
def translate_with_papago(self, text):
|
|
url = "https://openapi.naver.com/v1/papago/n2mt"
|
|
headers = {
|
|
"X-Naver-Client-Id": self.papago_client_id,
|
|
"X-Naver-Client-Secret": self.papago_client_secret,
|
|
}
|
|
data = {
|
|
"source": "ko",
|
|
"target": "zh-CN",
|
|
"text": text,
|
|
}
|
|
response = requests.post(url, headers=headers, data=data)
|
|
if response.status_code == 200:
|
|
translated_text = response.json().get('message', {}).get('result', {}).get('translatedText', '')
|
|
logger.debug(f"키워드변경 : {text} -> {translated_text}")
|
|
return translated_text
|
|
else:
|
|
# 오류 처리 및 사용자에게 메시지 표시
|
|
error_message = "번역 중 오류 발생"
|
|
if response.status_code == 401:
|
|
error_message = "파파고 API 키 인증 오류: 잘못된 API 키 또는 API키가 없습니다."
|
|
elif response.status_code == 429:
|
|
error_message = "파파고 API 최대 호출량(20000/Day)을 초과했습니다."
|
|
elif response.status_code == 500:
|
|
error_message = "서버 내부 오류가 발생했습니다."
|
|
# 오류 메시지 표시
|
|
QMessageBox.warning(self, "오류", error_message)
|
|
return None
|
|
|
|
#@pyqtSlot(QObject)
|
|
def on_button_clicked(self, button):
|
|
# if self.sc_button.buttonRole(button) == QDialogButtonBox.ApplyRole:
|
|
self.on_ns_scraping_clicked()
|
|
|
|
|
|
def startTimer(self):
|
|
# QLineEdit에서 시간 가져오기
|
|
minutes = int(self.timerMinute.text())
|
|
seconds = int(self.timerSecond.text())
|
|
self.remainingTime = minutes * 60 + seconds
|
|
self.timer.start(1000)
|
|
self.startMatchingUrl = self.getMatchingUrlCount()
|
|
|
|
|
|
def stopTimer(self):
|
|
self.timer.stop()
|
|
self.endMatchingUrl = self.getMatchingUrlCount()
|
|
work_done = self.endMatchingUrl - self.startMatchingUrl
|
|
QMessageBox.information(self, "정지", f"잠시 중지: 현재작업량 = {work_done}")
|
|
|
|
|
|
def setTimer(self):
|
|
self.startTimer()
|
|
|
|
# def updateTimer(self):
|
|
# self.remainingTime -= 1
|
|
# if self.remainingTime <= 0:
|
|
# self.timer.stop()
|
|
# endMatchingUrl = self.getMatchingUrlCount()
|
|
# work_done = self.endMatchingUrl - self.startMatchingUrl
|
|
# QMessageBox.information(self, "시간 완료", f"1시간이 지났습니다. 작업효율은 시간당 {work_done}개입니다.")
|
|
# self.remainingTime = 3600
|
|
# minutes, seconds = divmod(self.remainingTime, 60)
|
|
# self.timerLabel.setText(f"{minutes:02d}:{seconds:02d}")
|
|
|
|
def getMatchingUrlCount(self):
|
|
# 데이터베이스 연결 및 MatchingUrl 개수 조회
|
|
try:
|
|
conn = sqlite3.connect(self.db_name) # 데이터베이스 파일 이름을 self.db_name 변수에 저장
|
|
current_table = self.comboBox.currentText() # 현재 선택된 테이블 이름 가져오기
|
|
query = f"SELECT COUNT(*) FROM {current_table} WHERE MatchingUrl IS NOT NULL"
|
|
|
|
cursor = conn.cursor()
|
|
cursor.execute(query)
|
|
count = cursor.fetchone()[0] # 첫 번째 결과(카운트) 가져오기
|
|
conn.close()
|
|
return count
|
|
except Exception as e:
|
|
logger.debug(f"Error: {e}")
|
|
return 0 # 에러가 발생한 경우 0 반환
|
|
|
|
|
|
|
|
def load_url_from_urlbox(self):
|
|
url = self.urlbox.text()
|
|
if url:
|
|
self.webEngineView.load(QUrl(url))
|
|
|
|
def loadTableNames(self):
|
|
logger.debug("loadTableNames 메서드")
|
|
conn = sqlite3.connect(self.db_name)
|
|
cursor = conn.cursor()
|
|
logger.debug("쿼리 실행 : SELECT name FROM sqlite_master WHERE type='table';")
|
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
|
tables = cursor.fetchall()
|
|
conn.close()
|
|
|
|
self.comboBox.clear() # 기존에 콤보박스에 있는 항목들을 클리어
|
|
logger.debug("테이블 목록 읽기")
|
|
for table in tables:
|
|
self.comboBox.addItem(table[0])
|
|
logger.debug(f"테이블 {table} 읽기")
|
|
|
|
|
|
if tables:
|
|
self.comboBox.setCurrentIndex(0) # 첫 번째 테이블 선택
|
|
self.current_table_name = tables[0][0] # 첫 번째 테이블 이름을 current_table_name에 저장
|
|
self.loadTable() # 첫 번째 테이블 로드
|
|
logger.debug(f"첫 번째 테이블 로드")
|
|
|
|
self.update_match_count()
|
|
|
|
|
|
def cell_selected(self, selected, deselected):
|
|
if selected.indexes():
|
|
selected_index = selected.indexes()[0]
|
|
row, column = selected_index.row(), selected_index.column()
|
|
self.model.update_selected_cell((row, column))
|
|
self.update_match_count()
|
|
|
|
|
|
def export_data_by_branch(self):
|
|
# 저장할 파일 위치와 이름 선택
|
|
today = datetime.now().strftime('%Y%m%d_%H%M')
|
|
default_filename = f"{today}_1.xlsx"
|
|
file_name, _ = QFileDialog.getSaveFileName(self, "엑셀 파일 저장", default_filename, "Excel Files (*.xlsx)")
|
|
|
|
if file_name:
|
|
#self.save_to_excel(file_name)
|
|
self.save_to_excel_with_xlwings_by_branch(self.db_name, file_name)
|
|
|
|
def export_data_by_main(self):
|
|
# 저장할 파일 위치와 이름 선택
|
|
today = datetime.now().strftime('%Y%m%d_%H%M')
|
|
default_filename = f"{today}_1.xlsx"
|
|
file_name, _ = QFileDialog.getSaveFileName(self, "엑셀 파일 저장", default_filename, "Excel Files (*.xlsx)")
|
|
|
|
if file_name:
|
|
#self.save_to_excel(file_name)
|
|
self.save_to_excel_with_xlwings_by_main(self.db_name, file_name)
|
|
|
|
|
|
|
|
def save_to_excel_success(self, file_name):
|
|
# 기존 엑셀 파일 복사
|
|
baseXLS_PATH = os.path.join(BASE_PATH, 'baseXLS.xlsx')
|
|
|
|
base_filename = baseXLS_PATH
|
|
shutil.copy(base_filename, file_name)
|
|
logger.debug(f"파일 '{base_filename}'이 '{file_name}'으로 복사되었습니다.")
|
|
|
|
# 데이터베이스에서 데이터 로드
|
|
conn = sqlite3.connect(self.db_name)
|
|
current_table = self.comboBox.currentText()
|
|
query = f"SELECT * FROM {current_table} WHERE MatchingUrl IS NOT NULL"
|
|
df = pd.read_sql_query(query, conn)
|
|
conn.close()
|
|
|
|
# 복사된 파일에 새로운 데이터 추가
|
|
with pd.ExcelWriter(file_name, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:
|
|
# 새로운 데이터를 'export' 시트에 추가 (시트가 없으면 생성)
|
|
startrow = writer.book['export'].max_row if 'export' in writer.book.sheetnames else 0
|
|
df.to_excel(writer, sheet_name='export', startrow=startrow, header=False, index=False)
|
|
|
|
logger.debug(f"파일 '{file_name}'에 데이터가 추가되었습니다.")
|
|
|
|
|
|
def save_to_excel_ori2(self, file_name):
|
|
# 데이터베이스에서 데이터 로드
|
|
conn = sqlite3.connect(self.db_name)
|
|
current_table = self.comboBox.currentText()
|
|
query = f"SELECT * FROM {current_table} WHERE MatchingUrl IS NOT NULL"
|
|
df = pd.read_sql_query(query, conn)
|
|
conn.close()
|
|
|
|
# 50개 행씩 나누어 파일 저장
|
|
for i in range(0, len(df), 50):
|
|
df_subset = df.iloc[i:i+50]
|
|
|
|
# 부분 파일 이름 생성
|
|
part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx')
|
|
|
|
# 기존 엑셀 파일 복사
|
|
# 기존 엑셀 파일 복사
|
|
baseXLS_PATH = os.path.join(BASE_PATH, 'baseXLS.xlsx')
|
|
|
|
base_filename = baseXLS_PATH
|
|
shutil.copy(base_filename, part_file_name)
|
|
logger.debug(f"파일 '{base_filename}'이 '{part_file_name}'으로 복사되었습니다.")
|
|
|
|
# 복사된 파일에 새로운 데이터 추가
|
|
with pd.ExcelWriter(part_file_name, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:
|
|
# 새로운 데이터를 'export' 시트에 추가 (시트가 없으면 생성)
|
|
startrow = writer.book['export'].max_row if 'export' in writer.book.sheetnames else 0
|
|
df_subset.to_excel(writer, sheet_name='export', startrow=startrow, header=False, index=False)
|
|
|
|
# 'multi_ss' 시트에서 모든 함수 제거
|
|
if 'multi_ss' in writer.book.sheetnames:
|
|
logger.debug(f"시트 multi_ss를 찾았습니다.")
|
|
ws = writer.book['multi_ss']
|
|
for row in ws.iter_rows():
|
|
for cell in row:
|
|
if cell.value is not None and isinstance(cell.value, str) and cell.value.startswith('='):
|
|
cell.value = cell.value # Replace formula with its value
|
|
logger.debug(f"셀 '{cell}'에 함수를 제거하였습니다.")
|
|
|
|
logger.debug(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.")
|
|
|
|
writer._save
|
|
#writer.close()
|
|
|
|
|
|
def save_to_excel(self, file_name):
|
|
|
|
|
|
# 데이터베이스에서 필요한 데이터 로드
|
|
conn = sqlite3.connect(self.db_name)
|
|
query = "SELECT MatchingUrl, keyword, MatchingCat, delvFee, packingFee, plusFee, manuTag FROM NaverShopping WHERE MatchingUrl IS NOT NULL"
|
|
df = pd.read_sql_query(query, conn)
|
|
conn.close()
|
|
|
|
# 'manuTag' 필드에서 '오늘' 단어 제거
|
|
df['manuTag'] = df['manuTag'].apply(lambda x: ','.join([word for word in str(x).split(',') if '오늘' not in word.strip()]))
|
|
|
|
# 50개 행씩 나누어 파일 저장
|
|
for i in range(0, len(df), 50):
|
|
df_subset = df.iloc[i:i+50]
|
|
part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx')
|
|
|
|
# 기존 엑셀 파일 복사
|
|
baseXLS_Percenty_PATH = os.path.join(BASE_PATH, 'baseXLS_Percenty.xlsx')
|
|
base_filename = baseXLS_Percenty_PATH
|
|
shutil.copy(base_filename, part_file_name)
|
|
logger.debug(f"파일 '{base_filename}'이 '{part_file_name}'으로 복사되었습니다.")
|
|
|
|
# 엑셀 파일에 데이터 쓰기
|
|
#book = load_workbook(part_file_name)
|
|
#writer = pd.ExcelWriter(part_file_name, engine='openpyxl')
|
|
#writer.book = book
|
|
|
|
# 기존 워크북을 로드
|
|
book = load_workbook(part_file_name)
|
|
writer = pd.ExcelWriter(part_file_name, engine='openpyxl')
|
|
writer.book = book
|
|
#writer.sheets = {ws.title:ws for ws in book.worksheets}
|
|
|
|
#df.to_excel(writer, index = False, header=False, startrow=writer.sheets['Sheet1'].max_row)
|
|
|
|
if 'multi_ss' in book.sheetnames:
|
|
ws = book['multi_ss']
|
|
|
|
# 각 필드의 데이터를 지정된 셀에 입력
|
|
for index, row in df_subset.iterrows():
|
|
row_num = 4 + (index % 50)
|
|
ws[f'B{row_num}'] = row['MatchingUrl']
|
|
ws[f'C{row_num}'] = row['keyword']
|
|
ws[f'G{row_num}'] = row['MatchingCat']
|
|
ws[f'E{row_num}'] = row['delvFee'] + row['packingFee']
|
|
ws[f'D{row_num}'] = row['plusFee']
|
|
ws[f'F{row_num}'] = row['manuTag']
|
|
|
|
# 변경 사항 저장
|
|
writer.save()
|
|
writer.close()
|
|
|
|
logger.debug(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.")
|
|
|
|
|
|
def save_to_excel_with_xlwings_by_branch(self, db_name, file_name):
|
|
# 로그 다이얼로그 생성 및 표시
|
|
log_dialog = LogDialog()
|
|
log_dialog.show()
|
|
self.saved_files = []
|
|
|
|
logger.debug("엑셀 저장 로그기록 시작")
|
|
message = "엑셀 저장 로그기록 시작"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
|
|
# 로그 설정
|
|
logger.debug("엑셀 저장 로그기록 파일 생성")
|
|
|
|
# 데이터베이스에서 필요한 데이터 로드
|
|
conn = sqlite3.connect(db_name)
|
|
query = "SELECT MatchingUrl, keyword, MatchingCat, delvFee, packingFee, plusFee, manuTag, merged_price FROM NaverShopping WHERE MatchingUrl IS NOT NULL"
|
|
df = pd.read_sql_query(query, conn)
|
|
conn.close()
|
|
logger.debug("DB 읽기 완료")
|
|
message = "DB 읽기 완료"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
# 'manuTag' 필드에서 '오늘' 단어 제거
|
|
df['manuTag'] = df['manuTag'].apply(lambda x: ','.join([word for word in str(x).split(',') if '오늘' not in word.strip()]))
|
|
logger.debug("태그 필터링")
|
|
message = "태그 필터링"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
|
|
# Excel 애플리케이션을 백그라운드에서 실행
|
|
app = xw.App(visible=False)
|
|
logger.debug("xlwings 시작")
|
|
|
|
try:
|
|
# 50개 행씩 나누어 파일 저장
|
|
for i in range(0, len(df), 50):
|
|
df_subset = df.iloc[i:i+50]
|
|
|
|
# 기존 엑셀 파일 복사
|
|
base_filename = "baseXLS_Percenty.xlsx"
|
|
shutil.copy(base_filename, file_name)
|
|
logger.debug("저장양식 엑셀파일 열기")
|
|
message = "저장양식 엑셀파일 열기"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
# 복사된 파일 열기
|
|
wb = xw.Book(file_name)
|
|
ws = wb.sheets['multi_ss']
|
|
|
|
final_delv = 0
|
|
|
|
if 'sourcingman' in self.db_name or 'copyman' in self.db_name:
|
|
final_price = math.ceil((row['price'])*0.98 / 100) * 100 # 2%가격을 낮춘 후 100원단위 올림
|
|
|
|
else:
|
|
# final_delv = row['delvFee'] + row['packingFee']
|
|
# final_price = row['plusFee']
|
|
final_price = row['murged_price']
|
|
|
|
# 데이터 삽입
|
|
for index, row in df_subset.iterrows():
|
|
row_num = 4 + (index % 50)
|
|
ws.range(f'B{row_num}').value = row['MatchingUrl']
|
|
ws.range(f'C{row_num}').value = row['keyword']
|
|
ws.range(f'G{row_num}').value = row['MatchingCat']
|
|
ws.range(f'E{row_num}').value = final_delv
|
|
ws.range(f'D{row_num}').value = final_price
|
|
ws.range(f'F{row_num}').value = row['manuTag']
|
|
logger.debug(f"{index}번째 {row_num-3}엑셀데이터 기록")
|
|
# 저장 및 닫기
|
|
logger.debug("파일저장시작")
|
|
part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx')
|
|
wb.api.SaveCopyAs(part_file_name)
|
|
#wb.save(part_file_name)
|
|
# 저장된 파일의 이름을 self.saved_files 리스트에 추가
|
|
self.saved_files.append(part_file_name)
|
|
logger.debug("파일저장완료 및 워크북 닫기 시작")
|
|
wb.close()
|
|
logger.debug("워크북 닫기 완료")
|
|
logger.debug(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.")
|
|
message = f"파일 '{part_file_name}'에 데이터가 추가되었습니다."
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
time.sleep(1)
|
|
|
|
# 로그에 파일 저장 정보 추가
|
|
logger.debug(f"로그 '{part_file_name}'에 로그데이터가 추가되었습니다.")
|
|
except Exception as e:
|
|
logger.debug(e)
|
|
# 예외를 로그에 기록
|
|
logging.error(f"파일 저장 중 예외 발생: {str(e)}")
|
|
message = f"파일 저장 중 예외 발생: {str(e)}"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
finally:
|
|
# self.prompt_to_open_files()
|
|
#QMessageBox.information(self, "알림", "저장 프로세스가 완료되었습니다.")
|
|
|
|
# def prompt_to_open_files(self):
|
|
reply = QMessageBox.question(None, '완료', "저장 프로세스가 완료되었습니다. 저장된 엑셀 파일이 있는 폴더를 열어보시겠습니까?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
|
|
if reply == QMessageBox.Yes:
|
|
if self.saved_files:
|
|
folder_path = os.path.dirname(self.saved_files[0])
|
|
os.startfile(folder_path)
|
|
else:
|
|
logger.debug("저장된 파일이 없습니다.")
|
|
else:
|
|
logger.debug("저장 프로세스가 종료되었습니다.")
|
|
|
|
def save_to_excel_with_xlwings_by_main(self, db_name, file_name):
|
|
# 로그 다이얼로그 생성 및 표시
|
|
log_dialog = LogDialog()
|
|
log_dialog.show()
|
|
self.saved_files = []
|
|
|
|
logger.debug("엑셀 저장 로그기록 시작")
|
|
message = "엑셀 저장 로그기록 시작"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
|
|
# 로그 설정
|
|
|
|
logger.debug("엑셀 저장 로그기록 파일 생성")
|
|
|
|
# 데이터베이스에서 필요한 데이터 로드
|
|
conn = sqlite3.connect(db_name)
|
|
|
|
query = """
|
|
SELECT NS.*
|
|
FROM NaverShopping NS
|
|
INNER JOIN (
|
|
SELECT keyword_id, MIN(id) AS MinId
|
|
FROM NaverShopping
|
|
WHERE MatchingUrl IS NOT NULL
|
|
GROUP BY keyword_id
|
|
) AS UniqueNS ON NS.keyword_id = UniqueNS.keyword_id AND NS.id = UniqueNS.MinId
|
|
"""
|
|
|
|
df = pd.read_sql_query(query, conn)
|
|
conn.close()
|
|
logger.debug("DB 읽기 완료")
|
|
logger.debug(f"선택된 쿼리에 의한 데이터프레임 \n {df}")
|
|
message = "DB 읽기 완료"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
|
|
# 'manuTag' 필드에서 '오늘' 단어 제거
|
|
df['manuTag'] = df['manuTag'].apply(lambda x: ','.join([word for word in str(x).split(',') if '오늘' not in word.strip()]))
|
|
logger.debug("태그 필터링")
|
|
message = "태그 필터링"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
|
|
# Excel 애플리케이션을 백그라운드에서 실행
|
|
app = xw.App(visible=False)
|
|
logger.debug("xlwings 시작")
|
|
|
|
try:
|
|
# 50개 행씩 나누어 파일 저장
|
|
for i in range(0, len(df), 50):
|
|
df_subset = df.iloc[i:i+50]
|
|
|
|
# 기존 엑셀 파일 복사
|
|
baseXLS_Percenty_PATH = os.path.join(BASE_PATH, 'baseXLS_Percenty.xlsx')
|
|
|
|
base_filename = baseXLS_Percenty_PATH
|
|
shutil.copy(base_filename, file_name)
|
|
logger.debug("저장양식 엑셀파일 열기")
|
|
message = "저장양식 엑셀파일 열기"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
# 복사된 파일 열기
|
|
wb = xw.Book(file_name)
|
|
ws = wb.sheets['multi_ss']
|
|
|
|
# 데이터 삽입
|
|
for index, row in df_subset.iterrows():
|
|
row_num = 4 + (index % 50)
|
|
ws.range(f'B{row_num}').value = row['MatchingUrl']
|
|
ws.range(f'C{row_num}').value = row['keyword']
|
|
ws.range(f'G{row_num}').value = row['MatchingCat']
|
|
ws.range(f'E{row_num}').value = row['delvFee'] + row['packingFee']
|
|
ws.range(f'D{row_num}').value = row['plusFee']
|
|
ws.range(f'F{row_num}').value = row['manuTag']
|
|
logger.debug(f"{index}번째 {row_num-3}엑셀데이터 기록")
|
|
# 저장 및 닫기
|
|
logger.debug("파일저장시작")
|
|
part_file_name = file_name.replace('.xlsx', f'_part{i//50 + 1}.xlsx')
|
|
wb.api.SaveCopyAs(part_file_name)
|
|
#wb.save(part_file_name)
|
|
# 저장된 파일의 이름을 self.saved_files 리스트에 추가
|
|
self.saved_files.append(part_file_name)
|
|
logger.debug("파일저장완료 및 워크북 닫기 시작")
|
|
wb.close()
|
|
logger.debug("워크북 닫기 완료")
|
|
logger.debug(f"파일 '{part_file_name}'에 데이터가 추가되었습니다.")
|
|
message = f"파일 '{part_file_name}'에 데이터가 추가되었습니다."
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
time.sleep(1)
|
|
|
|
# 로그에 파일 저장 정보 추가
|
|
logger.debug(f"로그 '{part_file_name.log}'에 로그데이터가 추가되었습니다.")
|
|
except Exception as e:
|
|
logger.debug(e)
|
|
# 예외를 로그에 기록
|
|
logging.error(f"파일 저장 중 예외 발생: {str(e)}")
|
|
message = f"파일 저장 중 예외 발생: {str(e)}"
|
|
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
|
|
finally:
|
|
# self.prompt_to_open_files()
|
|
#QMessageBox.information(self, "알림", "저장 프로세스가 완료되었습니다.")
|
|
|
|
# def prompt_to_open_files(self):
|
|
reply = QMessageBox.question(None, '완료', "저장 프로세스가 완료되었습니다. 저장된 엑셀 파일이 있는 폴더를 열어보시겠습니까?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
|
|
if reply == QMessageBox.Yes:
|
|
if self.saved_files:
|
|
folder_path = os.path.dirname(self.saved_files[0])
|
|
os.startfile(folder_path)
|
|
else:
|
|
logger.debug("저장된 파일이 없습니다.")
|
|
else:
|
|
logger.debug("저장 프로세스가 종료되었습니다.")
|
|
|
|
|
|
def loadExistingDb(self):
|
|
db_file, _ = QFileDialog.getOpenFileName(self, "Open Database", "", "Database Files (*.db)")
|
|
if db_file:
|
|
self.db_name = db_file
|
|
#self.loadDataFromDb(db_file)
|
|
self.loadDataFromDb(self.db_name, "Keywords")
|
|
self.loadTableNames()
|
|
self.update_match_count()
|
|
|
|
def get_query_for_table(self, table_name):
|
|
queries = {
|
|
"Keywords": "SELECT keyword, base_category, MatchingUrl, MatchingCat, id FROM Keywords",
|
|
"NaverShopping": "SELECT keyword_id, keyword, delvFee, packingFee, plusFee, MatchingUrl, MatchingCat, productTitle, price, category1Name, category2Name, category3Name, category4Name, localImagePath, id, cat_code, tao_imageUrl FROM NaverShopping",
|
|
# "Taobao": "SELECT keyword_id, item_name, price, imageSearchUrl, keywordSearchUrl, rank FROM Taobao",
|
|
"Taobao": "SELECT * FROM Taobao",
|
|
"SubKeywords": "SELECT keyword_id, relatedTags, id FROM SubKeywords",
|
|
"Matching": "SELECT keyword_id, naver_id, taobao_id, sub_keyword_id, id FROM Matching"
|
|
}
|
|
# queries = {
|
|
# "Keywords": "SELECT * FROM Keywords",
|
|
# "NaverShopping": "SELECT * FROM NaverShopping",
|
|
# "Taobao": "SELECT * FROM Taobao",
|
|
# "SubKeywords": "SELECT * FROM SubKeywords",
|
|
# "Matching": "SELECT * FROM Matching"
|
|
# }
|
|
return queries.get(table_name, "Invalid table name")
|
|
|
|
|
|
|
|
def loadDataFromDb(self, db_file, table_name):
|
|
query = self.get_query_for_table(table_name)
|
|
logger.debug(f"loadDataFromDb_table_name : {table_name}")
|
|
|
|
|
|
if query == "Invalid table name":
|
|
logger.debug("Invalid table name: " + table_name)
|
|
# 여기에 추가적인 오류 처리 또는 사용자에게 알림을 제공하는 코드를 추가할 수 있습니다.
|
|
return
|
|
|
|
# 데이터베이스에서 데이터 로드
|
|
connection = sqlite3.connect(db_file)
|
|
df = pd.read_sql(query, connection)
|
|
connection.close()
|
|
|
|
# PandasModel로 데이터 설정
|
|
self.model = PandasModel(df, db_file, table_name) # 모델 초기화에 db_file과 table_name 전달
|
|
self.dbviewer1.setModel(self.model)
|
|
|
|
delegate = EditDelegate()
|
|
self.dbviewer1.setItemDelegate(delegate)
|
|
|
|
# 셀 선택 변경 이벤트 연결
|
|
self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected)
|
|
self.update_match_count()
|
|
|
|
def loadTable(self):
|
|
table_name = self.comboBox.currentText()
|
|
logger.debug(f"loadTable_table_name : {table_name}")
|
|
self.current_table_name = table_name
|
|
self.loadDataFromDb(self.db_name, table_name)
|
|
self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected)
|
|
|
|
|
|
|
|
# searchbtn 클릭 이벤트 처리 함수
|
|
def on_searchbtn_clicked(self):
|
|
|
|
current_index = self.dbviewer1.currentIndex() # 현재 선택된 행의 인덱스
|
|
chinese_keyword = current_index.sibling(current_index.row(), 33).data() # 33번째 열에서 중국어 키워드 가져오기
|
|
|
|
if chinese_keyword:
|
|
# 타오바오 검색 URL 생성 및 로드 "https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://world.taobao.com"
|
|
search_url = f"https://main.m.taobao.com/search/index.html?pagetype=1&q={chinese_keyword}"
|
|
#search_url = f"https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://main.m.taobao.com/search/index.html?pagetype=1&q={chinese_keyword}"
|
|
self.webEngineView.load(QUrl(search_url))
|
|
|
|
def search_chinese(self):
|
|
#current_index = self.dbviewer1.currentIndex() # 현재 선택된 행의 인덱스
|
|
|
|
# 현재 선택된 행의 키워드 가져오기
|
|
#keyword = current_index.sibling(current_index.row(), 2).data()
|
|
|
|
search_keyword = self.search_keyword_box.text()
|
|
self.tabWidget.setCurrentIndex(0) # tab_1을 활성화
|
|
|
|
# 키워드 번역
|
|
translated_keyword = self.translate_with_papago(search_keyword)
|
|
if translated_keyword:
|
|
# 타오바오 검색 URL 생성 및 로드 https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=
|
|
search_url = f"https://main.m.taobao.com/search/index.html?pagetype=1&q={translated_keyword}"
|
|
#search_url = f"https://papago.naver.net/website?source=zh-CN&target=ko&locale=ko&url=https://main.m.taobao.com/search/index.html?pagetype=1&q={translated_keyword}"
|
|
self.webEngineView.load(QUrl(search_url))
|
|
|
|
# catcodebox 값을 확인하고 배경색을 설정하는 함수
|
|
def check_and_set_color(self):
|
|
if not self.catcodebox.text(): # catcodebox 값이 비어있는 경우
|
|
self.catcodebox.setStyleSheet("QLineEdit { background-color: red; }")
|
|
else:
|
|
self.catcodebox.setStyleSheet("") # 기본 스타일로 초기화
|
|
|
|
def on_table_clicked(self, index):
|
|
|
|
# 선택된 행의 키워드 가져오기
|
|
keyword = index.sibling(index.row(), 1).data()
|
|
|
|
try:
|
|
# 배송비 설정
|
|
delivery_fee = index.sibling(index.row(), 2).data()
|
|
logger.debug(f"delivery_fee : {delivery_fee}")
|
|
if delivery_fee is not None:
|
|
self.delivfee_spbox.setValue(int(delivery_fee)) # 올바른 방법으로 값을 설정
|
|
|
|
# 추가 포장비 설정
|
|
additional_packing_fee = index.sibling(index.row(), 3).data()
|
|
if additional_packing_fee is not None:
|
|
self.addpackingfee_spbox.setValue(int(additional_packing_fee)) # 올바른 방법으로 값을 설정
|
|
|
|
# 추가 마진 설정
|
|
additional_margin = index.sibling(index.row(), 4).data()
|
|
if additional_margin is not None:
|
|
self.addmargin_spbox.setValue(int(additional_margin)) # 올바른 방법으로 값을 설정
|
|
|
|
except Exception as e:
|
|
logger.debug(f"가격 오류 : {e}")
|
|
|
|
try:
|
|
cat1 = index.sibling(index.row(), 9).data()
|
|
logger.debug(f"카테고리1 = '{cat1}'선택됨.")
|
|
cat2 = index.sibling(index.row(), 10).data()
|
|
logger.debug(f"카테고리2 = '{cat2}'선택됨.")
|
|
cat3 = index.sibling(index.row(), 11).data()
|
|
logger.debug(f"카테고리3 = '{cat3}'선택됨.")
|
|
cat4 = index.sibling(index.row(), 12).data()
|
|
logger.debug(f"카테고리4 = '{cat4}'선택됨.")
|
|
cat_code = index.sibling(index.row(), 15).data()
|
|
logger.debug(f"카테고리 코드 = '{cat_code}'선택됨.")
|
|
# 카테고리 값들을 합치기
|
|
categories = [cat1, cat2, cat3]
|
|
if cat4: # category4Name이 비어있지 않은 경우에만 추가
|
|
categories.append(cat4)
|
|
# '-'를 사용하여 카테고리 이름들을 연결
|
|
combined_category = '-'.join(categories)
|
|
final_category = cat_code + " " + combined_category
|
|
logger.debug(f"최종 카테고리 = '{final_category}' ")
|
|
|
|
# 카테고리 박스에 선택된 사항 표시
|
|
self.catbox1.setText(cat1)
|
|
self.catbox2.setText(cat2)
|
|
self.catbox3.setText(cat3)
|
|
self.catbox4.setText(cat4)
|
|
self.catcodebox.setText(cat_code)
|
|
except Exception as e:
|
|
logger.debug(f"카테고리 오류 : {e}")
|
|
|
|
# 카테고리 코드 박스가 비어있을 경우 붉은색으로 경고표시
|
|
self.check_and_set_color()
|
|
|
|
# 키워드박스에 선택된 키워드 표시
|
|
self.selkeywordbox.setText(keyword)
|
|
self.search_keyword_box.setText(keyword)
|
|
|
|
# 선택된 행에서 이미지 경로 가져오기
|
|
localImagePath = index.sibling(index.row(), 13).data()
|
|
tao_image = index.sibling(index.row(), 15).data()
|
|
self.selected_image_path = localImagePath
|
|
|
|
logger.debug(f"이미지 경로 '{localImagePath}'선택됨.")
|
|
|
|
# 클립보드에 이미지 복사
|
|
try:
|
|
if localImagePath:
|
|
clipboard = QtGui.QGuiApplication.clipboard()
|
|
image = QtGui.QImage(localImagePath)
|
|
if not image.isNull():
|
|
clipboard.setImage(image)
|
|
logger.debug(f"이미지 '{localImagePath}'가 클립보드에 복사되었습니다.")
|
|
self.load_image_from_clipboard(localImagePath) # 이미지선택칸에 현재 이미지표시
|
|
# self.tabWidget.setCurrentIndex(1) # tab_2를 활성화
|
|
|
|
else:
|
|
logger.debug(f"이미지 로드 실패: {localImagePath}")
|
|
|
|
except Exception as e:
|
|
logger.debug(f"이미지 클립보드 복사 오류: {e}")
|
|
|
|
# 선택된 행의 ID 가져오기 (ID가 첫 번째 열에 있다고 가정)
|
|
self.selected_row_id = index.sibling(index.row(), 14).data()
|
|
|
|
# 선택된 행의 keyword_id 가져오기
|
|
keyword_id = index.sibling(index.row(), 0).data()
|
|
self.load_images_by_keyword_id(keyword_id)
|
|
logger.debug(f"선택된 keyword_id: {keyword_id}")
|
|
|
|
tao_records = None
|
|
|
|
# keyword_id에 해당하는 레코드들을 찾아서 처리
|
|
try:
|
|
logger.debug(f"DB 연결 시도: {self.db_name}")
|
|
conn = sqlite3.connect(self.db_name)
|
|
cursor = conn.cursor()
|
|
|
|
query = f"SELECT productTitle, manuTag, reviewCount, keepCnt, dlvryLowPrice, purchaseCnt FROM {self.current_table_name} WHERE keyword_id = ? ORDER BY id LIMIT 5"
|
|
#logger.debug(f"실행할 쿼리: {query}")
|
|
#cursor.execute(f"SELECT * FROM {self.current_table_name} WHERE keyword_id = ? ORDER BY some_ordering_column", (keyword_id,))
|
|
cursor.execute(query, (keyword_id,))
|
|
records = cursor.fetchall()
|
|
#logger.debug(f"가져온 레코드: {records}")
|
|
# 연관 태그를 가져오는 쿼리
|
|
#cursor.execute(f"SELECT relatedTags FROM {self.current_table_name} WHERE keyword_id = ? LIMIT 1", (keyword_id,))
|
|
cursor.execute(f"SELECT relatedTags FROM {self.current_table_name} WHERE keyword_id = ?", (keyword_id,))
|
|
relatedTags_records = cursor.fetchall()
|
|
|
|
# 연관 태그 처리
|
|
relatedTags_list = [tag for record in relatedTags_records for tag in record if tag]
|
|
relatedTags = ', '.join(relatedTags_list) if relatedTags_list else "없음"
|
|
|
|
logger.debug(f"가져온 연관검색어 레코드: {relatedTags_records}")
|
|
|
|
# sel_relatedTagbox에 연관 태그 설정
|
|
self.sel_relatedTagbox.setText(relatedTags)
|
|
self.sel_relatedTagbox.setCursorPosition(0)
|
|
|
|
tao_query = """
|
|
SELECT id, item_name, price, sales_volume, imageUrl, itemUrl, itemID, tao_localimage
|
|
FROM Taobao
|
|
WHERE keyword_id = ?
|
|
LIMIT 5
|
|
"""
|
|
# 최대 10개의 레코드를 가져옵니다.
|
|
cursor.execute(tao_query, (keyword_id,))
|
|
tao_records = cursor.fetchall()
|
|
le = len(tao_records)
|
|
logger.debug(f"설정된 tao_records 길이 : [{le}]")
|
|
logger.debug(f"설정된 tao_records \n [{tao_records}]")
|
|
# first_tao = tao_records[0]
|
|
# itemUrl = first_tao[5]
|
|
# logger.debug(f"설정된 itemUrl : [{itemUrl}]")
|
|
# self.taocard.update_item_url(itemUrl)
|
|
# itemID = first_tao[6]
|
|
# logger.debug(f"설정된 itemID : [{itemID}]")
|
|
# self.taocard.update_item_ID(itemID)
|
|
|
|
logger.debug("==========update_ui==========")
|
|
# tao_records 내의 모든 레코드를 반복 처리
|
|
# for record in tao_records:
|
|
# item_name, price, sales_volume, imageUrl, itemUrl, itemID = record[1], record[2], record[3], record[4], record[5], record[6]
|
|
# logger.debug(f"레코드 정보: 이름={item_name}, 가격={price}, 판매량={sales_volume}, 이미지URL={imageUrl}, itemUrl={itemUrl}, itemID={itemID}")
|
|
|
|
# # TaoCard 클래스에 각 레코드의 정보를 전달하여 UI 업데이트
|
|
# # self.taocard.load_images_by_keyword_id(record)
|
|
# # self.taocard.update_ui(item_name, price, sales_volume, imageUrl, itemUrl, itemID)
|
|
|
|
conn.close()
|
|
|
|
|
|
# 상품별 필드값 설정
|
|
manuTags = ["" for _ in range(5)] # 5개 상품의 manuTag 초기화
|
|
logger.debug(f"manuTags : {manuTags}")
|
|
|
|
if not records:
|
|
logger.debug("해당 keyword_id에 대한 레코드가 없습니다.")
|
|
return
|
|
# 레코드 처리
|
|
|
|
# 모든 관련 QLineEdit 위젯 클리어
|
|
#for k in range(1, 6):
|
|
# for l in range(1, 6):
|
|
# getattr(self, f'sel_itembox{i+1}{j}').clear()
|
|
|
|
for i, record in enumerate(records):
|
|
|
|
# 모든 관련 QLineEdit 위젯 클리어
|
|
for j in range(1, 7):
|
|
getattr(self, f'sel_itembox{i+1}{j}').clear()
|
|
|
|
# 레코드의 각 요소를 변수에 할당, 값이 없는 경우 "0" 또는 ""으로 설정
|
|
productTitle = str(record[0]) if len(record) > 0 else "0"
|
|
manuTag = str(record[1]) if len(record) > 1 else ""
|
|
reviewCount = str(record[2]) if len(record) > 2 else "0"
|
|
keepCnt = str(record[3]) if len(record) > 3 else "0"
|
|
dlvryLowPrice = "{:,}".format(int(record[4])) if len(record) > 4 and record[4] is not None else "0"
|
|
purchaseCnt = str(record[5]) if len(record) > 5 else "0"
|
|
|
|
# 위젯에 값 설정
|
|
getattr(self, f'sel_itembox{i+1}1').setText(productTitle)
|
|
getattr(self, f'sel_itembox{i+1}2').setText(manuTag)
|
|
getattr(self, f'sel_itembox{i+1}3').setText(reviewCount)
|
|
getattr(self, f'sel_itembox{i+1}4').setText(keepCnt)
|
|
getattr(self, f'sel_itembox{i+1}5').setText(dlvryLowPrice)
|
|
getattr(self, f'sel_itembox{i+1}6').setText(purchaseCnt)
|
|
|
|
# 모든 QLineEdit의 커서를 맨 왼쪽으로 설정
|
|
for j in range(1, 7):
|
|
getattr(self, f'sel_itembox{i+1}{j}').setCursorPosition(0)
|
|
|
|
# manuTag 누적
|
|
#manuTags[i] = manuTag
|
|
# manuTag 처리: 튜플이면 문자열로 변환
|
|
#if isinstance(manuTag, tuple):
|
|
# manuTag = ' '.join(map(str, manuTag)) # 튜플을 공백으로 구분된 문자열로 변환
|
|
#elif manuTag is None:
|
|
# manuTag = ""
|
|
|
|
#manuTags[i] += manuTag
|
|
|
|
# 각 상품 정보 설정
|
|
#getattr(self, f'sel_itembox{i+1}1').setText(productTitle)
|
|
#getattr(self, f'sel_itembox{i+1}3').setText(str(reviewCount))
|
|
#getattr(self, f'sel_itembox{i+1}4').setText(str(keepCnt))
|
|
#getattr(self, f'sel_itembox{i+1}5').setText(str(dlvryLowPrice))
|
|
#getattr(self, f'sel_itembox{i+1}6').setText(str(purchaseCnt))
|
|
|
|
# 모든 상품의 manuTag 설정
|
|
#for i in enumerate(records):
|
|
#getattr(self, f'sel_itembox{i+1}2').setText(manuTags[i])
|
|
|
|
except sqlite3.OperationalError as e:
|
|
logger.debug(f"SQL 오류: {e}")
|
|
QtWidgets.QMessageBox.warning(self, "오류", "DB에서 데이터를 가져오는 중 오류가 발생했습니다.")
|
|
except Exception as e:
|
|
logger.debug(f"일반 오류: {e}")
|
|
QtWidgets.QMessageBox.warning(self, "오류", "알 수 없는 오류가 발생했습니다.")
|
|
|
|
self.update_match_count()
|
|
|
|
logger.debug("==========load_images_by_keyword_id==========")
|
|
# logger.debug(f"tao_records 길이 : {len(tao_records)}")
|
|
if tao_records:
|
|
self.taocard.load_images_by_keyword_ids(tao_records)
|
|
|
|
def tao_card_match(self, itemID):
|
|
logger.debug("-tao_card_match-")
|
|
|
|
# 현재 활성화된 탭의 인덱스 가져오기
|
|
current_tab_index = self.tabWidget.currentIndex()
|
|
|
|
# # 현재 활성화된 탭에 따라 URL 가져오기
|
|
# if current_tab_index == 0: # 첫 번째 탭 (tab_1)이 활성화된 경우
|
|
# matching_url = self.webEngineView.url().toString()
|
|
# elif current_tab_index == 1: # 두 번째 탭 (tab_2)이 활성화된 경우
|
|
# matching_url = self.webEngineView_2.url().toString()
|
|
# elif current_tab_index == 2: # 두 번째 탭 (tab_2)이 활성화된 경우
|
|
# matching_url = self.webEngineView_2.url().toString()
|
|
logger.debug(f"current_tab_index : {current_tab_index}")
|
|
logger.debug(f"Tao_Item_ID_for_matching_url : {itemID}")
|
|
|
|
#matchbtn 버튼이 클릭되면, 입력한 값들을 DB에서 현재 선택된 키워드에 해당하는 필드에 기록
|
|
packing_fee = int(self.addpackingfee_spbox.text())
|
|
deliv_fee = int(self.delivfee_spbox.text())
|
|
add_margin = int(self.addmargin_spbox.text())
|
|
table_name = self.comboBox.currentText()
|
|
|
|
#현재는 QLineEdit
|
|
cat1 = self.catbox1.text()
|
|
cat2 = self.catbox2.text()
|
|
cat3 = self.catbox3.text()
|
|
cat4 = self.catbox4.text()
|
|
cat_code = self.catcodebox.text()
|
|
|
|
categories = [cat1, cat2, cat3]
|
|
if cat4: # cat4가 비어있지 않은 경우에만 추가
|
|
categories.append(cat4)
|
|
# '-'를 사용하여 카테고리 이름들을 연결
|
|
combined_category = '-'.join(categories)
|
|
logger.debug(f"최종 카테고리 = '{combined_category}' ")
|
|
|
|
# cat_code와 combined_category를 " "로 연결
|
|
final_category = cat_code + " " + combined_category
|
|
|
|
MatchingCat = final_category
|
|
|
|
# matching_url = self.update_url_for_match(matching_url)
|
|
pc_url = f"https://item.taobao.com/item.htm?id={itemID}"
|
|
matching_url = pc_url
|
|
logger.debug(f"matching_url : {matching_url}")
|
|
|
|
itemtag = self.sel_itemtagbox.text()
|
|
keyword = self.selkeywordbox.text()
|
|
#relatedTag = sel_relatedTagbox.text()
|
|
|
|
conn = sqlite3.connect(self.db_name) # DB 연결
|
|
cursor = conn.cursor()
|
|
|
|
#tablevier 변경1
|
|
df = pd.read_sql(f"SELECT * FROM {table_name}", conn)
|
|
logger.debug(f"선택된 행 : {self.selected_row_id}")
|
|
|
|
#self.selected_row_id = index.sibling(index.row(), 15).data()
|
|
|
|
|
|
# 업데이트 쿼리에서 선택된 행의 ID를 사용
|
|
if self.selected_row_id is not None:
|
|
cursor.execute("UPDATE NaverShopping SET MatchingCat =?, delvFee = ?, packingFee = ?, plusFee = ?, MatchingUrl = ?, manuTag = ?, keyword = ? WHERE id = ?",
|
|
(MatchingCat, deliv_fee, packing_fee, add_margin, matching_url, itemtag, keyword, self.selected_row_id))
|
|
conn.commit()
|
|
conn.close() # DB 연결 종료
|
|
|
|
# # self.model = PandasModel(df)
|
|
# self.model = PandasModel(df, self.db_name, table_name) # 모델 초기화에 db_file과 table_name 전달
|
|
|
|
# self.dbviewer1.setModel(self.model)
|
|
|
|
self.load_data_into_table_NaverShopping()
|
|
self.update_match_count()
|
|
self.loadTable()
|
|
|
|
# 셀 선택 변경 이벤트 처리
|
|
self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected)
|
|
|
|
|
|
|
|
|
|
|
|
def download_and_save_image(self, url, save_dir, image_name):
|
|
# 파일 이름에 타임스탬프 추가하여 고유한 이름 생성
|
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
unique_image_name = f"{timestamp}_{image_name}"
|
|
|
|
try:
|
|
response = requests.get(url)
|
|
if response.status_code == 200:
|
|
# 이미지 데이터를 BytesIO 객체로 변환
|
|
image_data = BytesIO(response.content)
|
|
image = Image.open(image_data)
|
|
|
|
# 이미지 크기 조정
|
|
#image = image.resize((150, 150), Image.ANTIALIAS)
|
|
image = image.resize((150, 150), Image.Resampling.LANCZOS)
|
|
|
|
|
|
# 이미지 저장 디렉토리 확인 및 생성
|
|
if not os.path.exists(save_dir):
|
|
os.makedirs(save_dir)
|
|
|
|
# 이미지 저장 경로 설정
|
|
file_path = os.path.join(save_dir, unique_image_name)
|
|
|
|
# Convert the image to RGB
|
|
rgb_image = image.convert('RGB')
|
|
# 이미지 저장
|
|
rgb_image.save(file_path, format='JPEG')
|
|
|
|
return file_path
|
|
else:
|
|
logger.debug(f"Failed to download image: {url}")
|
|
return None
|
|
except Exception as e:
|
|
logger.debug(f"Error downloading image {url}: {e}")
|
|
return None
|
|
|
|
def load_images_by_keyword_id(self, keyword_id):
|
|
logger.debug(keyword_id)
|
|
|
|
try:
|
|
conn = sqlite3.connect(self.db_name)
|
|
cursor = conn.cursor()
|
|
|
|
# 이미지 URL 대신 로컬 이미지 경로를 불러옵니다.
|
|
cursor.execute(f"SELECT localImagePath FROM {self.current_table_name} WHERE keyword_id = ? LIMIT 5", (keyword_id,))
|
|
image_paths = cursor.fetchall()
|
|
conn.close()
|
|
|
|
logger.debug("로컬 이미지 경로 로드완료")
|
|
logger.debug(image_paths)
|
|
|
|
self.load_images([path[0] for path in image_paths if path[0]])
|
|
|
|
except sqlite3.OperationalError as e:
|
|
logger.debug(e)
|
|
QtWidgets.QMessageBox.warning(self, "오류", "DB의 유효한 테이블을 선택해주세요.")
|
|
|
|
|
|
|
|
def load_images(self, image_paths):
|
|
self.scene.clear()
|
|
y_position = 0
|
|
for path in image_paths:
|
|
pixmap = QPixmap(path)
|
|
resized_pixmap = pixmap.scaled(150, 150, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
|
pixmap_item = QGraphicsPixmapItem(resized_pixmap)
|
|
pixmap_item.setPos(0, y_position)
|
|
self.scene.addItem(pixmap_item)
|
|
|
|
y_position += resized_pixmap.height() + 10
|
|
|
|
def load_image_from_clipboard(self,img_path):
|
|
self.scene_current_img.clear()
|
|
pixmap = QPixmap(img_path)
|
|
if not pixmap.isNull():
|
|
view_size = self.current_img.viewport().size()
|
|
scaled_pixmap = pixmap.scaled(view_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
|
pixmap_item = QGraphicsPixmapItem(scaled_pixmap)
|
|
self.scene_current_img.addItem(pixmap_item)
|
|
else:
|
|
self.scene_current_img.clear()
|
|
logger.debug("클립보드에 이미지가 없습니다.")
|
|
|
|
|
|
|
|
def update_url_for_match(self, q):
|
|
url_str = q
|
|
|
|
# 정규 표현식을 사용하여 12자리 숫자 찾기
|
|
match = re.search(r'id=(\d{10,12})', url_str)
|
|
if match:
|
|
# 정규 표현식으로 찾은 ID를 사용하여 PC 주소 생성
|
|
pc_url = f"https://item.taobao.com/item.htm?id={match.group(1)}"
|
|
else:
|
|
# id 매개변수가 없는 경우 원래 URL을 사용
|
|
pc_url = url_str
|
|
# 사용자에게 메시지 띄우기
|
|
QMessageBox.information(self, "알림", "상품URL을 확인하세요!")
|
|
|
|
return pc_url
|
|
|
|
def unmatchbtn_clicked(self):
|
|
conn = sqlite3.connect(self.db_name) # DB 연결
|
|
cursor = conn.cursor()
|
|
table_name = self.comboBox.currentText()
|
|
|
|
#tablevier 변경1
|
|
df = pd.read_sql(f"SELECT * FROM {table_name}", conn)
|
|
logger.debug(f"선택된 행 : {self.selected_row_id}")
|
|
|
|
#self.selected_row_id = index.sibling(index.row(), 15).data()
|
|
matching_url = None
|
|
|
|
# 업데이트 쿼리에서 선택된 행의 ID를 사용
|
|
if self.selected_row_id is not None:
|
|
cursor.execute("UPDATE NaverShopping SET MatchingUrl = ?WHERE id = ?",
|
|
(matching_url, self.selected_row_id))
|
|
conn.commit()
|
|
conn.close() # DB 연결 종료
|
|
|
|
# self.model = PandasModel(df)
|
|
# self.dbviewer1.setModel(self.model)
|
|
|
|
self.load_data_into_table_NaverShopping()
|
|
self.update_match_count()
|
|
self.loadTable()
|
|
|
|
# 셀 선택 변경 이벤트 처리
|
|
self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected)
|
|
|
|
|
|
|
|
|
|
def matchbtn_clicked(self):
|
|
# 현재 활성화된 탭의 인덱스 가져오기
|
|
current_tab_index = self.tabWidget.currentIndex()
|
|
|
|
# 현재 활성화된 탭에 따라 URL 가져오기
|
|
if current_tab_index == 0: # 첫 번째 탭 (tab_1)이 활성화된 경우
|
|
matching_url = self.webEngineView.url().toString()
|
|
elif current_tab_index == 1: # 두 번째 탭 (tab_2)이 활성화된 경우
|
|
matching_url = self.webEngineView_2.url().toString()
|
|
elif current_tab_index == 2: # 두 번째 탭 (tab_2)이 활성화된 경우
|
|
matching_url = self.webEngineView_2.url().toString()
|
|
logger.debug(f"current_tab_index : {current_tab_index}")
|
|
logger.debug(f"matching_url : {matching_url}")
|
|
|
|
#matchbtn 버튼이 클릭되면, 입력한 값들을 DB에서 현재 선택된 키워드에 해당하는 필드에 기록
|
|
packing_fee = self.addpackingfee_spbox.value()
|
|
# packing_fee = int(self.addpackingfee_spbox.text())
|
|
deliv_fee = int(self.delivfee_spbox.text())
|
|
add_margin = int(self.addmargin_spbox.text())
|
|
table_name = self.comboBox.currentText()
|
|
|
|
#현재는 QLineEdit
|
|
cat1 = self.catbox1.text()
|
|
cat2 = self.catbox2.text()
|
|
cat3 = self.catbox3.text()
|
|
cat4 = self.catbox4.text()
|
|
cat_code = self.catcodebox.text()
|
|
|
|
|
|
|
|
#콤보박스로 바꾸면 쓸 코드
|
|
#cat1 = self.cat1combo.currentText()
|
|
#cat2 = self.cat2combo.currentText()
|
|
#cat3 = self.cat3combo.currentText()
|
|
#cat4 = self.cat4combo.currentText()
|
|
#cat_code = self.catcodecombo.currentText()
|
|
|
|
|
|
categories = [cat1, cat2, cat3]
|
|
if cat4: # cat4가 비어있지 않은 경우에만 추가
|
|
categories.append(cat4)
|
|
# '-'를 사용하여 카테고리 이름들을 연결
|
|
combined_category = '-'.join(categories)
|
|
logger.debug(f"최종 카테고리 = '{combined_category}' ")
|
|
|
|
# cat_code와 combined_category를 " "로 연결
|
|
final_category = cat_code + " " + combined_category
|
|
|
|
MatchingCat = final_category
|
|
|
|
matching_url = self.update_url_for_match(matching_url)
|
|
|
|
itemtag = self.sel_itemtagbox.text()
|
|
keyword = self.selkeywordbox.text()
|
|
#relatedTag = sel_relatedTagbox.text()
|
|
|
|
conn = sqlite3.connect(self.db_name) # DB 연결
|
|
cursor = conn.cursor()
|
|
|
|
#tablevier 변경1
|
|
df = pd.read_sql(f"SELECT * FROM {table_name}", conn)
|
|
logger.debug(f"선택된 행 : {self.selected_row_id}")
|
|
|
|
#self.selected_row_id = index.sibling(index.row(), 15).data()
|
|
|
|
|
|
# 업데이트 쿼리에서 선택된 행의 ID를 사용
|
|
if self.selected_row_id is not None:
|
|
cursor.execute("UPDATE NaverShopping SET MatchingCat =?, delvFee = ?, packingFee = ?, plusFee = ?, MatchingUrl = ?, manuTag = ?, keyword = ? WHERE id = ?",
|
|
(MatchingCat, deliv_fee, packing_fee, add_margin, matching_url, itemtag, keyword, self.selected_row_id))
|
|
conn.commit()
|
|
conn.close() # DB 연결 종료
|
|
|
|
# self.model = PandasModel(df)
|
|
# self.dbviewer1.setModel(self.model)
|
|
|
|
self.load_data_into_table_NaverShopping()
|
|
self.update_match_count()
|
|
self.loadTable()
|
|
|
|
# 셀 선택 변경 이벤트 처리
|
|
self.dbviewer1.selectionModel().selectionChanged.connect(self.cell_selected)
|
|
|
|
|
|
def update_match_count(self):
|
|
# 데이터베이스 연결
|
|
conn = sqlite3.connect(self.db_name)
|
|
current_table = self.comboBox.currentText()
|
|
|
|
if current_table: # current_table이 비어 있지 않은 경우에만 쿼리 실행
|
|
# MatchingUrl이 있는 행의 수를 카운트
|
|
query = f"SELECT COUNT(*) FROM {current_table} WHERE MatchingUrl IS NOT NULL"
|
|
cursor = conn.cursor()
|
|
cursor.execute(query)
|
|
match_count = cursor.fetchone()[0]
|
|
conn.close()
|
|
# QLabel에 표시
|
|
self.matchCountbox.setText(str(match_count))
|
|
else:
|
|
# current_table이 비어 있는 경우
|
|
self.matchCountbox.setText("0") # 또는 적절한 기본값으로 설정
|
|
|
|
|
|
|
|
def find_naver_code(self, base_categories):
|
|
Percenty_SS_code_PATH = os.path.join(BASE_PATH, 'Percenty_SS_code.json')
|
|
|
|
for base_category in base_categories:
|
|
category_parts = re.split(r'>|-', base_category)
|
|
base_category1Name, base_category2Name, base_category3Name, base_category4Name = (category_parts + [None]*4)[:4]
|
|
|
|
with open(Percenty_SS_code_PATH, "r", encoding='utf-8') as file:
|
|
for line in file:
|
|
try:
|
|
item = json.loads(line)
|
|
if (item.get('category1Name') == base_category1Name and
|
|
item.get('category2Name') == base_category2Name and
|
|
item.get('category3Name') == base_category3Name and
|
|
item.get('category4Name') == base_category4Name):
|
|
return item['Naver_code'] # 조건에 맞는 첫 번째 코드를 반환하고 함수 종료
|
|
except json.JSONDecodeError as e:
|
|
logger.debug(f"Error decoding JSON: {e}")
|
|
continue
|
|
return None # 조건에 맞는 코드를 찾지 못한 경우
|
|
|
|
|
|
|
|
def find_naver_code_set(self, base_categories):
|
|
Percenty_SS_code_PATH = os.path.join(BASE_PATH, 'Percenty_SS_code.json')
|
|
|
|
naver_code_set = []
|
|
for base_category in base_categories:
|
|
# base_category가 None이거나 빈 문자열이면 건너뛴다
|
|
if not base_category:
|
|
continue
|
|
|
|
# ">" 기준으로 분류하여 변수 할당
|
|
# logger.debug(f"{base_category}")
|
|
# category_parts = base_category.split('>')
|
|
category_parts = re.split(r'>|-', base_category)
|
|
base_category1Name, base_category2Name, base_category3Name, base_category4Name = (category_parts + [None]*4)[:4]
|
|
|
|
# # JSON 파일 로드
|
|
# with open("Percenty_SS_code.json", "r", encoding='utf-8') as file:
|
|
# data = json.load(file)
|
|
# # 조건에 맞는 Naver_code 찾기
|
|
# for item in data:
|
|
# if (item.get('category1Name') == base_category1Name and
|
|
# item.get('category2Name') == base_category2Name and
|
|
# item.get('category3Name') == base_category3Name and
|
|
# item.get('category4Name') == base_category4Name):
|
|
# naver_code_set.append(item['Naver_code'])
|
|
|
|
# JSON 파일 로드, 파일의 각 줄을 개별 JSON 객체로 처리
|
|
with open(Percenty_SS_code_PATH, "r", encoding='utf-8') as file:
|
|
for line in file:
|
|
try:
|
|
item = json.loads(line)
|
|
# 조건에 맞는 Naver_code 찾기
|
|
if (item.get('category1Name') == base_category1Name and
|
|
item.get('category2Name') == base_category2Name and
|
|
item.get('category3Name') == base_category3Name and
|
|
item.get('category4Name') == base_category4Name):
|
|
naver_code_set.append(item['Naver_code'])
|
|
except json.JSONDecodeError as e:
|
|
logger.debug(f"Error decoding JSON: {e}")
|
|
continue
|
|
|
|
|
|
logger.debug("================naver_code_set==================")
|
|
|
|
logger.debug(naver_code_set)
|
|
return naver_code_set
|
|
|
|
def find_codes(self, base_categories):
|
|
results = [] # 각 카테고리에 대한 결과를 저장할 리스트
|
|
for base_category in base_categories:
|
|
# base_category가 None이거나 빈 문자열이면 건너뛴다
|
|
if not base_category:
|
|
continue
|
|
|
|
category_parts = re.split(r'>|-', base_category)
|
|
base_category1Name, base_category2Name, base_category3Name, base_category4Name = (category_parts + [""] * 4)[:4]
|
|
|
|
for item in self.json_data:
|
|
if (item.get('category1Name') == base_category1Name and
|
|
item.get('category2Name') == base_category2Name and
|
|
(item.get('category3Name') == base_category3Name or item.get('category3Name') == "") and
|
|
(item.get('category4Name') == base_category4Name or item.get('category4Name') == "")):
|
|
results.append((item['Naver_code'], item.get('cat_code'))) # 조건에 맞는 항목을 결과에 추가
|
|
break # 해당 카테고리에 대한 첫 번째 매칭 항목을 찾은 후 다음 카테고리로 이동
|
|
|
|
return results # 모든 검색 결과 반환
|
|
|
|
def loadJsonData(self):
|
|
Percenty_SS_code_PATH = os.path.join(BASE_PATH, 'Percenty_SS_code.json')
|
|
|
|
try:
|
|
with open(Percenty_SS_code_PATH, "r", encoding='utf-8') as file:
|
|
for line in file:
|
|
item = json.loads(line)
|
|
# 'category3Name'과 'category4Name'의 '$numberDouble':"NaN"을 처리
|
|
if isinstance(item.get("category3Name"), dict) and "$numberDouble" in item["category3Name"]:
|
|
item["category3Name"] = "" # 또는 None 사용
|
|
if isinstance(item.get("category4Name"), dict) and "$numberDouble" in item["category4Name"]:
|
|
item["category4Name"] = "" # 또는 None 사용
|
|
self.json_data.append(item)
|
|
|
|
except FileNotFoundError:
|
|
logger.debug("Percenty_SS_Code.json 파일을 찾을 수 없습니다.")
|
|
self.json_data = [] # 파일이 없을 경우 빈 리스트 할당
|
|
|
|
|
|
def on_tao_image_save_btn_clicked(self):
|
|
# 이미지 저장 폴더 설정 ('DB이름_save_images')
|
|
self.conndb = sqlite3.connect(self.db_name, check_same_thread=False)
|
|
|
|
ori_image_save_folder = f"{self.db_name}_tao_save_images"
|
|
image_save_folder = os.path.join(BASE_PATH, ori_image_save_folder)
|
|
|
|
# 폴더가 없으면 생성
|
|
if not os.path.exists(image_save_folder):
|
|
os.makedirs(image_save_folder)
|
|
# 이미지 저장 스레드 실행
|
|
self.tao_image_save_thread = TaobaoImageSaveThread(self.conndb, image_save_folder)
|
|
self.tao_image_save_thread.progress_updated.connect(self.update_image_progress_bar)
|
|
self.tao_image_save_thread.finished.connect(self.on_tao_image_save_finished) # 이미지 저장 완료 후 처리
|
|
self.tao_image_save_thread.start()
|
|
|
|
def on_tao_image_save_finished(self):
|
|
QMessageBox.information(self, "알림", "타오바오 이미지 저장 완료!")
|
|
self.conndb.close() # 모든 백그라운드 작업이 끝난 후 데이터베이스 연결 종료
|
|
|
|
|
|
|
|
def on_ns_scraping_clicked(self):
|
|
try:
|
|
isBranch = self.branch_checkBox.isChecked()
|
|
if isBranch:
|
|
branchCount = self.branchCount.text()
|
|
else:
|
|
branchCount = 0
|
|
logger.debug(f"가지치기 설정 : {isBranch}")
|
|
#conn = sqlite3.connect(self.db_name)
|
|
self.conndb = sqlite3.connect(self.db_name, check_same_thread=False)
|
|
logger.debug(f"선택된 DB: {self.db_name}")
|
|
|
|
cursor = self.conndb.cursor()
|
|
# cursor.execute("SELECT id, keyword FROM Keywords")
|
|
cursor.execute("SELECT id, keyword, base_category FROM Keywords")
|
|
|
|
rows = cursor.fetchall()
|
|
keywords = [row[1] for row in rows]
|
|
logger.debug("==========키워드===========")
|
|
logger.debug(f"{keywords}")
|
|
# base_category = [row[2] for row in rows]
|
|
base_categories = [row[2] for row in rows]
|
|
codes = self.find_codes(base_categories)
|
|
logger.debug("==========base_category===========")
|
|
logger.debug(f"{base_categories}")
|
|
# naver_code = self.find_naver_code_set(base_categories)
|
|
# logger.debug("==========naver_code===========")
|
|
# logger.debug(f"naver_code : {naver_code}")
|
|
QMessageBox.information(self, "알림", "네이버쇼핑 스크래핑 시작!")
|
|
|
|
self.scraping_thread = ScrapingThread(self.conndb, keywords, isBranch, branchCount, self.json_data, self.overPrice.text(), self.sortcount.text())
|
|
self.scraping_thread.progress_updated.connect(self.update_progress_bar)
|
|
# self.scraping_thread.finished.connect(self.on_scraping_finished) # 스크래핑 완료 후 처리
|
|
self.scraping_thread.finished.connect(self.start_image_save_thread) # 스크래핑 완료 후 이미지 저장 스레드 시작
|
|
self.scraping_thread.start()
|
|
except Exception as e:
|
|
logger.debug(f"에러발생 : {e}")
|
|
|
|
def click_image_save_btn(self):
|
|
# QMessageBox.information(self, "알림", "네이버 이미지 저장 시작!")
|
|
|
|
self.conndb = sqlite3.connect(self.db_name, check_same_thread=False)
|
|
logger.debug(f"선택된 DB: {self.db_name}")
|
|
|
|
self.conndb = sqlite3.connect(self.db_name, check_same_thread=False)
|
|
|
|
# 이미지 저장 폴더 설정 ('DB이름_save_images')
|
|
ori_image_save_folder = f"{self.db_name}_save_images"
|
|
image_save_folder = os.path.join(BASE_PATH, ori_image_save_folder)
|
|
|
|
# 폴더가 없으면 생성
|
|
if not os.path.exists(image_save_folder):
|
|
os.makedirs(image_save_folder)
|
|
|
|
# 이미지 저장 스레드 실행
|
|
self.image_save_thread = ImageSaveThread(self.conndb, image_save_folder)
|
|
self.image_save_thread.progress_updated.connect(self.update_image_progress_bar)
|
|
self.image_save_thread.finished.connect(self.on_image_save_finished) # 이미지 저장 완료 후 처리
|
|
self.image_save_thread.start()
|
|
|
|
|
|
def start_image_save_thread(self):
|
|
# 이미지 저장 폴더 설정 ('DB이름_save_images')
|
|
|
|
ori_image_save_folder = f"{self.db_name}_save_images"
|
|
image_save_folder = os.path.join(BASE_PATH, ori_image_save_folder)
|
|
|
|
# 폴더가 없으면 생성
|
|
if not os.path.exists(image_save_folder):
|
|
os.makedirs(image_save_folder)
|
|
# 이미지 저장 스레드 실행
|
|
self.image_save_thread = ImageSaveThread(self.conndb, image_save_folder)
|
|
self.image_save_thread.progress_updated.connect(self.update_image_progress_bar)
|
|
self.image_save_thread.finished.connect(self.on_image_save_finished) # 이미지 저장 완료 후 처리
|
|
self.image_save_thread.start()
|
|
|
|
def on_image_save_finished(self):
|
|
QMessageBox.information(self, "알림", "스크래핑 완료!")
|
|
self.conndb.close() # 모든 백그라운드 작업이 끝난 후 데이터베이스 연결 종료
|
|
|
|
#try:
|
|
# self.automatch_clicked()
|
|
#except Exception as e:
|
|
# logger.debug(f"automatch_clicked 실행 중 예외 발생: {e}")
|
|
|
|
|
|
|
|
def update_progress_bar(self, total, current):
|
|
self.sc_progressBar.setMaximum(total)
|
|
self.sc_progressBar.setValue(current)
|
|
|
|
def update_image_progress_bar(self, total, current):
|
|
self.img_progressBar.setMaximum(total)
|
|
self.img_progressBar.setValue(current)
|
|
|
|
|
|
|
|
|
|
def trans_chinese(self):
|
|
# 네이버 API 클라이언트 ID 및 시크릿
|
|
client_id = "V1UyIry1TNhzj4ln1UJ7"
|
|
client_secret = "YV3EsIWlTH"
|
|
|
|
logger.debug("중국어번역")
|
|
# DB 연결 및 cursor 생성
|
|
conn = sqlite3.connect(self.db_name) # 이 부분은 self.db_name이 정의되어 있어야 합니다.
|
|
cursor = conn.cursor()
|
|
|
|
# DB 연결 종료
|
|
conn.commit() # Commit the transaction
|
|
conn.close()
|
|
|
|
|
|
|
|
def update_url(self, q):
|
|
url_str = q.toString() if hasattr(q, 'toString') else q
|
|
self.urlbox.setText(url_str)
|
|
|
|
|
|
# 정규 표현식을 사용하여 11자리 또는 12자리 숫자 찾기
|
|
match = re.search(r'id=(\d{10,12})', url_str)
|
|
|
|
if match:
|
|
# 정규 표현식으로 찾은 ID를 사용하여 PC 주소 생성
|
|
pc_url = f"https://item.taobao.com/item.htm?id={match.group(1)}"
|
|
else:
|
|
pc_url = url_str # ID가 없는 경우 원래 URL을 사용
|
|
# 사용자에게 메시지 띄우기
|
|
# QMessageBox.information(self, "알림", "상품URL을 확인하세요!")
|
|
|
|
|
|
# 결과 URL 업데이트
|
|
self.taourlbox.setText(pc_url)
|
|
|
|
def openxls_copyman(self):
|
|
options = QFileDialog.Options()
|
|
file_name, _ = QFileDialog.getOpenFileName(None, "QFileDialog.getOpenFileName()", "", "Excel Files (*.xlsx)", options=options)
|
|
|
|
if file_name:
|
|
# DB 파일 이름 설정
|
|
now = datetime.now()
|
|
self.db_name = now.strftime('%Y-%m-%d-%H%M') + '_copyman' + '.db'
|
|
conn = sqlite3.connect(self.db_name)
|
|
|
|
# DB 생성 함수 호출 예시 (실제 구현 필요)
|
|
create_db(self.db_name)
|
|
logger.debug("카피맨 DB생성 완료")
|
|
|
|
# 엑셀 파일에서 데이터 로드
|
|
logger.debug("카피맨 데이터 : 카테고리', '가격', '상품명', '태그', '이미지URL' 읽기")
|
|
df = pd.read_excel(file_name, usecols=['카테고리', '가격', '상품명', '태그', '이미지URL'])
|
|
logger.debug("카피맨 엑셀파일 읽기 완료")
|
|
|
|
# '카테고리' 데이터를 분리하고 필요한 만큼의 컬럼만 선택
|
|
categories = df['카테고리'].str.split(r'>|-', expand=True).fillna('')
|
|
logger.debug("카피맨 카테고리 '-' 기준으로 분리")
|
|
|
|
if categories.shape[1] < 4:
|
|
# 필요한 열 개수만큼 빈 열 추가
|
|
for i in range(4 - categories.shape[1]):
|
|
categories[f'category{i+categories.shape[1]+1}Name'] = ''
|
|
# '카테고리' 데이터를 분리하고 필요한 만큼의 컬럼만 선택
|
|
logger.debug(f"카피맨 카테고리 {i}번째 빈열 추가")
|
|
|
|
|
|
categories = df['카테고리'].str.split(r'>|-', expand=True).fillna('')
|
|
if categories.shape[1] > 4:
|
|
categories = categories.iloc[:, :4] # 첫 4개의 컬럼만 유지
|
|
categories.columns = ['category1Name', 'category2Name', 'category3Name', 'category4Name']
|
|
logger.debug("카피맨 categories.columns을 4개의 catName으로 분리")
|
|
|
|
cat_codes = self.find_code_for_copyman(df['카테고리'].tolist())
|
|
logger.debug("카피맨 find_code_for_copyman로 카테고리 코드 리스트로 만들기")
|
|
|
|
df['cat_code'] = cat_codes
|
|
logger.debug("카피맨 df에 cat_code 넣기")
|
|
|
|
# 원본 df와 categories 데이터프레임을 결합
|
|
df_combined = pd.concat([df, categories], axis=1)
|
|
logger.debug("카피맨 원본 df와 categories 데이터프레임을 결합")
|
|
|
|
# keyword_id 열 추가: 1부터 시작하는 연속된 숫자 할당
|
|
df_combined['keyword_id'] = range(1, len(df_combined) + 1)
|
|
logger.debug("카피맨 키워드ID 임의 할당")
|
|
|
|
# NaverShopping 테이블 데이터 프레임 조정 및 저장
|
|
naver_shopping_df = df_combined[['상품명', 'keyword_id', '상품명', '가격', '태그', '이미지URL', 'cat_code', 'category1Name', 'category2Name', 'category3Name', 'category4Name']]
|
|
naver_shopping_df.columns = ['keyword', 'keyword_id', 'productTitle', 'price', 'manuTag', 'imageUrl', 'cat_code', 'category1Name', 'category2Name', 'category3Name', 'category4Name']
|
|
naver_shopping_df.to_sql('NaverShopping', conn, if_exists='append', index=False)
|
|
logger.debug("카피맨 naver_shopping_df 조정 및 저장")
|
|
|
|
# Keywords 테이블에 저장할 데이터 프레임 생성 및 저장
|
|
keywords_df = df[['상품명', '카테고리']]
|
|
keywords_df.columns = ['keyword', 'base_category']
|
|
keywords_df.to_sql('Keywords', conn, if_exists='append', index=False)
|
|
logger.debug("카피맨 keywords_df 조정 및 저장")
|
|
|
|
conn.close()
|
|
read_excel_category_data(self.db_name, BASE_PATH)
|
|
logger.debug("카피맨 read_excel_category_data")
|
|
|
|
# DB 로드 후 처리
|
|
logger.debug("카피맨 loadTableNames 시작")
|
|
self.loadTableNames()
|
|
|
|
def openxls_sourcingman(self):
|
|
options = QFileDialog.Options()
|
|
file_name, _ = QFileDialog.getOpenFileName(None, "QFileDialog.getOpenFileName()", "", "Excel Files (*.xlsx)", options=options)
|
|
|
|
if file_name:
|
|
# DB 파일 이름 설정
|
|
now = datetime.now()
|
|
self.db_name = now.strftime('%Y-%m-%d-%H%M') + '_sourcingman' + '.db'
|
|
conn = sqlite3.connect(self.db_name)
|
|
|
|
# DB 생성 함수 호출 예시 (실제 구현 필요)
|
|
create_db(self.db_name)
|
|
|
|
# 엑셀 파일에서 데이터 로드
|
|
df = pd.read_excel(file_name, sheet_name='추출마켓', usecols=['키워드','카테고리', '가격', '상품명', '이미지URL'])
|
|
|
|
# '카테고리' 데이터를 분리하고 필요한 만큼의 컬럼만 선택
|
|
categories = df['카테고리'].str.split(r'>|-', expand=True).fillna('')
|
|
|
|
if categories.shape[1] < 4:
|
|
# 필요한 열 개수만큼 빈 열 추가
|
|
for i in range(4 - categories.shape[1]):
|
|
categories[f'category{i+categories.shape[1]+1}Name'] = ' '
|
|
|
|
if categories.shape[1] > 4:
|
|
categories = categories.iloc[:, :4] # 첫 4개의 컬럼만 유지
|
|
categories.columns = ['category1Name', 'category2Name', 'category3Name', 'category4Name']
|
|
cat_codes = self.find_code_for_sourcingman(df['카테고리'].tolist())
|
|
df['cat_code'] = cat_codes
|
|
|
|
# 원본 df와 categories 데이터프레임을 결합
|
|
df_combined = pd.concat([df, categories], axis=1)
|
|
|
|
# keyword_id 열 추가: 1부터 시작하는 연속된 숫자 할당
|
|
df_combined['keyword_id'] = range(1, len(df_combined) + 1)
|
|
|
|
# NaverShopping 테이블 데이터 프레임 조정 및 저장
|
|
naver_shopping_df = df_combined[['키워드', 'keyword_id', '상품명', '가격', '이미지URL', 'cat_code', 'category1Name', 'category2Name', 'category3Name', 'category4Name']]
|
|
naver_shopping_df.columns = ['keyword', 'keyword_id', 'productTitle', 'price', 'imageUrl', 'cat_code', 'category1Name', 'category2Name', 'category3Name', 'category4Name']
|
|
naver_shopping_df.to_sql('NaverShopping', conn, if_exists='append', index=False)
|
|
|
|
# Keywords 테이블에 저장할 데이터 프레임 생성 및 저장
|
|
keywords_df = df[['키워드', '카테고리']]
|
|
keywords_df.columns = ['keyword', 'base_category']
|
|
keywords_df.to_sql('Keywords', conn, if_exists='append', index=False)
|
|
|
|
conn.close()
|
|
read_excel_category_data(self.db_name, BASE_PATH)
|
|
|
|
# DB 로드 후 처리
|
|
self.loadTableNames()
|
|
|
|
def find_code_for_copyman(self, base_categories):
|
|
results = [] # 각 카테고리에 대한 결과를 저장할 리스트
|
|
for base_category in base_categories:
|
|
category_parts = re.split(r'>|-', base_category)
|
|
base_category1Name, base_category2Name, base_category3Name, base_category4Name = (category_parts + [""] * 4)[:4]
|
|
matched = False # 매칭되는 항목이 있는지 추적하는 플래그
|
|
|
|
for item in self.json_data:
|
|
if (item.get('category1Name') == base_category1Name and
|
|
item.get('category2Name') == base_category2Name and
|
|
(item.get('category3Name') == base_category3Name or item.get('category3Name') == "") and
|
|
(item.get('category4Name') == base_category4Name or item.get('category4Name') == "")):
|
|
results.append(item.get('cat_code')) # 조건에 맞는 항목의 cat_code를 결과에 추가
|
|
matched = True
|
|
break # 해당 카테고리에 대한 첫 번째 매칭 항목을 찾은 후 다음 카테고리로 이동
|
|
|
|
if not matched:
|
|
results.append("") # 매칭되는 항목이 없는 경우 기본값(예: 빈 문자열) 추가
|
|
|
|
return results
|
|
|
|
|
|
def find_code_for_sourcingman(self, base_categories):
|
|
logger.debug(f"base_categories : {base_categories}")
|
|
if not base_categories: # base_categories가 None이거나 비어있는 경우 처리
|
|
return []
|
|
|
|
results = [] # 각 카테고리에 대한 결과를 저장할 리스트
|
|
for base_category in base_categories:
|
|
# base_category가 빈 문자열이거나 None이거나 NaN인 경우 처리
|
|
if not base_category or (isinstance(base_category, float) and math.isnan(base_category)):
|
|
results.append("") # 해당 경우에 대한 기본값(예: 빈 문자열) 추가
|
|
continue # 다음 base_category로 이동
|
|
|
|
category_parts = re.split(r'>|-', base_category)
|
|
base_category1Name, base_category2Name, base_category3Name, base_category4Name = (category_parts + [""] * 4)[:4]
|
|
matched = False # 매칭되는 항목이 있는지 추적하는 플래그
|
|
|
|
for item in self.json_data:
|
|
if (item.get('category1Name') == base_category1Name and
|
|
item.get('category2Name') == base_category2Name and
|
|
(item.get('category3Name') == base_category3Name or item.get('category3Name') == "") and
|
|
(item.get('category4Name') == base_category4Name or item.get('category4Name') == "")):
|
|
results.append(item.get('cat_code')) # 조건에 맞는 항목의 cat_code를 결과에 추가
|
|
matched = True
|
|
break # 해당 카테고리에 대한 첫 번째 매칭 항목을 찾은 후 다음 카테고리로 이동
|
|
|
|
if not matched:
|
|
results.append("") # 매칭되는 항목이 없는 경우 기본값(예: 빈 문자열) 추가
|
|
|
|
return results
|
|
|
|
def openxls(self):
|
|
options = QFileDialog.Options()
|
|
file_name, _ = QFileDialog.getOpenFileName(None,"QFileDialog.getOpenFileName()", "","Excel Files (*.xlsx)", options=options)
|
|
|
|
if file_name:
|
|
# DB 파일 이름 설정
|
|
now = datetime.now()
|
|
self.db_name = now.strftime('%Y-%m-%d-%H%M') + '.db'
|
|
conn = sqlite3.connect(self.db_name)
|
|
|
|
# DB 생성 및 데이터 로드
|
|
create_db(self.db_name)
|
|
df = pd.read_excel(file_name, usecols=['키워드', '카테고리'])
|
|
df.columns = ['keyword', 'base_category']
|
|
df.to_sql('Keywords', conn, if_exists='append', index=False)
|
|
conn.close()
|
|
|
|
read_excel_category_data(self.db_name, BASE_PATH)
|
|
|
|
# DB 로드 후 테이블 이름을 콤보박스에 로드하고 첫 번째 테이블 선택
|
|
self.loadTableNames()
|
|
|
|
def load_data_into_table_keyword(self):
|
|
conn = sqlite3.connect(self.db_name) # DB 연결
|
|
|
|
query = "SELECT * FROM Keywords" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문
|
|
result = pd.read_sql_query(query, conn) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환
|
|
|
|
# 결과를 QTableView에 표시
|
|
model = QStandardItemModel()
|
|
|
|
for row in range(result.shape[0]): # 각 행에 대해
|
|
for col in range(result.shape[1]): # 각 열에 대해
|
|
item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환
|
|
model.setItem(row, col, item) # 모델에 아이템 설정
|
|
|
|
model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정
|
|
|
|
self.dbviewer1.setModel(model) # QTableView에 모델 설정
|
|
# self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김
|
|
|
|
conn.close() # DB 연결 종료
|
|
self.model = QStandardItemModel() # model 속성 설정
|
|
self.update_match_count()
|
|
|
|
def load_data_into_table_NaverShopping(self):
|
|
conn = sqlite3.connect(self.db_name) # DB 연결
|
|
|
|
query = "SELECT * FROM NaverShopping" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문
|
|
result = pd.read_sql_query(query, conn) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환
|
|
|
|
# 결과를 QTableView에 표시
|
|
model = QStandardItemModel()
|
|
|
|
for row in range(result.shape[0]): # 각 행에 대해
|
|
for col in range(result.shape[1]): # 각 열에 대해
|
|
item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환
|
|
model.setItem(row, col, item) # 모델에 아이템 설정
|
|
|
|
model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정
|
|
|
|
self.dbviewer1.setModel(model) # QTableView에 모델 설정
|
|
self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김
|
|
|
|
conn.close() # DB 연결 종료
|
|
self.update_match_count()
|
|
|
|
def load_data_into_table_NS(self):
|
|
conn = sqlite3.connect(self.db_name) # DB 연결
|
|
|
|
query = "SELECT * FROM Navershooping" # Keywords 테이블에서 모든 데이터를 선택하는 쿼리문
|
|
result = pd.read_sql_query(query, conn) # 쿼리문 실행하고 결과를 pandas DataFrame으로 반환
|
|
|
|
# 결과를 QTableView에 표시
|
|
model = QStandardItemModel()
|
|
|
|
for row in range(result.shape[0]): # 각 행에 대해
|
|
for col in range(result.shape[1]): # 각 열에 대해
|
|
item = QStandardItem(str(result.iloc[row, col])) # 데이터를 QStandardItem로 변환
|
|
model.setItem(row, col, item) # 모델에 아이템 설정
|
|
|
|
model.setHorizontalHeaderLabels(result.columns.values.tolist()) # 열 이름 설정
|
|
|
|
self.dbviewer1.setModel(model) # QTableView에 모델 설정
|
|
self.dbviewer1.hideColumn(0) # 첫 번째 열(ID 열)을 숨김
|
|
|
|
conn.close() # DB 연결 종료
|
|
self.model = QStandardItemModel() # model 속성 설정
|
|
self.update_match_count()
|
|
|
|
#if __name__ == "__main__":
|
|
# app = QApplication([])
|
|
# window = Ui_Dialog()
|
|
# window.show()
|
|
# app.exec_()
|
|
|
|
def initialize_resource_paths():
|
|
global BASE_PATH
|
|
# 패키징된 실행 파일에서 실행되는 경우
|
|
if getattr(sys, 'frozen', False):
|
|
BASE_PATH = sys._MEIPASS
|
|
else:
|
|
# 스크립트가 직접 실행되는 경우
|
|
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
# Windows SetThreadExecutionState API를 사용하여 절전 모드 방지
|
|
def prevent_sleep_mode():
|
|
"""
|
|
이 함수는 시스템이 자동으로 화면을 끄거나 절전 모드로 들어가는 것을 방지합니다.
|
|
"""
|
|
# 실행 상태를 설정하는 데 사용되는 플래그
|
|
ES_CONTINUOUS = 0x80000000
|
|
ES_SYSTEM_REQUIRED = 0x00000001
|
|
|
|
# SetThreadExecutionState 함수 호출
|
|
ctypes.windll.kernel32.SetThreadExecutionState(
|
|
ES_CONTINUOUS |
|
|
ES_SYSTEM_REQUIRED
|
|
)
|
|
|
|
# 시스템 절전 모드 방지 해제
|
|
def restore_sleep_mode():
|
|
ES_CONTINUOUS = 0x80000000
|
|
ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS)
|
|
logger.info("시스템의 절전 모드 설정이 원래대로 복원되었습니다.")
|
|
|
|
# 사용자 로그아웃 시간 기록
|
|
def record_logout_time():
|
|
# 이 함수에서는 사용자 로그아웃 시간을 데이터베이스에 기록하는 로직을 구현해야 합니다.
|
|
# 여기서는 예시로 현재 시간을 로깅합니다.
|
|
logout_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
logger.info(f"사용자의 로그아웃 시간을 기록했습니다: {logout_time}")
|
|
|
|
# 프로그램 종료 메시지 보내기
|
|
def send_exit_message():
|
|
# 이 함수에서는 사용자에게 프로그램 종료 메시지를 보내는 로직을 구현해야 합니다.
|
|
# 여기서는 예시로 로깅만 수행합니다.
|
|
logger.info("프로그램이 종료되었습니다. 안녕히 가세요.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
|
|
app = QApplication(sys.argv)
|
|
|
|
# 로그 레벨을 DEBUG로 설정하여 로거를 초기화합니다.
|
|
logger = setup_logger('default_logger', 'application.log', level=logging.DEBUG)
|
|
|
|
logger.debug("로거 모듈 로드")
|
|
|
|
# 리소스 경로 초기화 함수 호출
|
|
initialize_resource_paths()
|
|
|
|
# 프로파일 및 페이지 생성
|
|
#profile = QWebEngineProfile("CustomProfile")
|
|
#mobile_user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1"
|
|
#profile.setHttpUserAgent(mobile_user_agent)
|
|
|
|
window = Ui_Dialog(logger)
|
|
window.show()
|
|
sys.exit(app.exec_()) |