TaoSourcerer/taoseller.py

2527 lines
114 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}", exc_info=True)
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}", exc_info=True)
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}", exc_info=True)
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}", exc_info=True)
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}", exc_info=True)
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):
try:
# 로그 다이얼로그 생성 및 표시
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"
query2 = "SELECT MatchingUrl, keyword, MatchingCat, delvFee, packingFee, plusFee, manuTag, price FROM NaverShopping WHERE MatchingUrl IS NOT NULL"
try:
df = pd.read_sql_query(query, conn)
except:
df = pd.read_sql_query(query2, 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 시작")
except Exception as e:
logger.debug(e)
# 예외를 로그에 기록
logging.error(f"파일 저장 준비 중 예외 발생: {str(e)}", exc_info=True)
message = f"파일 저장 준비 중 예외 발생: {str(e)}"
log_dialog.add_log_message(message) # 로그 다이얼로그에 메시지 추가
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
# 데이터 삽입
for index, row in df_subset.iterrows():
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']
if not row['murged_price']:
final_price = row['plusFee']
final_price = row['murged_price']
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)}", exc_info=True)
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}'에 로그데이터가 추가되었습니다.")
except Exception as e:
logger.debug(e)
# 예외를 로그에 기록
logging.error(f"파일 저장 중 예외 발생: {str(e)}", exc_info=True)
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, merged_price 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}", exc_info=True)
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}", exc_info=True)
# 카테고리 코드 박스가 비어있을 경우 붉은색으로 경고표시
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}", exc_info=True)
# 선택된 행의 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}", exc_info=True)
QtWidgets.QMessageBox.warning(self, "오류", "DB에서 데이터를 가져오는 중 오류가 발생했습니다.")
except Exception as e:
logger.debug(f"일반 오류: {e}", exc_info=True)
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}", exc_info=True)
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}", exc_info=True)
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("카피맨 엑셀파일 읽기 완료")
# '카테고리' 컬럼이 없거나 NaN인 경우 '카테고리1>카테고리2>카테고리3>카테고리4'로 대체
if '카테고리' not in df.columns or df['카테고리'].isnull().all():
df['카테고리'] = '카테고리1>카테고리2>카테고리3>카테고리4'
else:
df['카테고리'] = df['카테고리'].fillna('카테고리1>카테고리2>카테고리3>카테고리4')
# '카테고리' 데이터를 분리하고 필요한 만큼의 컬럼만 선택
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__":
# 프로그램 종료 시 호출될 함수 등록
atexit.register(restore_sleep_mode)
atexit.register(record_logout_time)
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_())