업데이트

This commit is contained in:
9700X_PC 2025-03-29 17:30:14 +09:00
parent 3059ba375c
commit e806dc0538
7 changed files with 116 additions and 23 deletions

131
main.py
View File

@ -3,6 +3,8 @@ import subprocess
import json import json
from datetime import datetime, time from datetime import datetime, time
from src.version import __version__ from src.version import __version__
import xlrd
from PySide6.QtGui import QIcon
import logging import logging
import traceback import traceback
@ -295,6 +297,11 @@ class MainWindow(QMainWindow):
self.db_input_button.setToolTip("엑셀 파일을 선택하여 DB에 저장합니다. 기존 DB를 제거하거나 추가로 데이터를 입력할 수 있습니다.") self.db_input_button.setToolTip("엑셀 파일을 선택하여 DB에 저장합니다. 기존 DB를 제거하거나 추가로 데이터를 입력할 수 있습니다.")
self.db_input_button.clicked.connect(self.load_excel) self.db_input_button.clicked.connect(self.load_excel)
# DB 양식보기 버튼
self.sample_excel_button = QPushButton("추가양식 보기")
self.sample_excel_button.setToolTip("추가 양식을 확인합니다.")
self.sample_excel_button.clicked.connect(self.load_sample_excel)
# 기존 DB 제거 체크박스 # 기존 DB 제거 체크박스
self.remove_db_checkbox = QCheckBox("기존 DB 제거") self.remove_db_checkbox = QCheckBox("기존 DB 제거")
self.remove_db_checkbox.setToolTip("체크하면 기존 DB를 삭제하고 새롭게 데이터를 입력합니다.") self.remove_db_checkbox.setToolTip("체크하면 기존 DB를 삭제하고 새롭게 데이터를 입력합니다.")
@ -413,6 +420,7 @@ class MainWindow(QMainWindow):
self.browser_selection_layout.addWidget(self.whale_path_button) self.browser_selection_layout.addWidget(self.whale_path_button)
self.buttons_layout.addWidget(self.db_input_button) self.buttons_layout.addWidget(self.db_input_button)
self.buttons_layout.addWidget(self.sample_excel_button) # 추가 양식 버튼 추가
self.buttons_layout.addWidget(self.remove_db_checkbox) self.buttons_layout.addWidget(self.remove_db_checkbox)
self.buttons_layout.addWidget(self.view_data_button) self.buttons_layout.addWidget(self.view_data_button)
self.buttons_layout.addWidget(self.run_button) self.buttons_layout.addWidget(self.run_button)
@ -475,11 +483,21 @@ class MainWindow(QMainWindow):
self.log(f"크롬 프로파일 경로가 설정되었습니다: {self.chrome_bookmarks_path}") self.log(f"크롬 프로파일 경로가 설정되었습니다: {self.chrome_bookmarks_path}")
self.log(f"웨일 프로파일 경로가 설정되었습니다: {self.whale_bookmarks_path}") self.log(f"웨일 프로파일 경로가 설정되었습니다: {self.whale_bookmarks_path}")
# 아이콘 설정 (추가된 부분)
self.set_app_icon()
def update_chunk_size(self): def update_chunk_size(self):
"""폴더당 북마크 갯수 변경 시 chunk_size 업데이트""" """폴더당 북마크 갯수 변경 시 chunk_size 업데이트"""
# self.log(f"폴더당 북마크 갯수가 변경되었습니다: {self.worker.chunk_size}") # self.log(f"폴더당 북마크 갯수가 변경되었습니다: {self.worker.chunk_size}")
def set_app_icon(self):
"""애플리케이션 아이콘을 설정합니다."""
base_dir = self.get_base_dir()
icon_path = os.path.join(base_dir, "bookmaker.ico")
if os.path.exists(icon_path):
self.setWindowIcon(QIcon(icon_path))
else:
pass
# 브라우저 실행 파일 경로 가져오기 # 브라우저 실행 파일 경로 가져오기
def get_browser_path(self, browser_name): def get_browser_path(self, browser_name):
@ -623,6 +641,38 @@ class MainWindow(QMainWindow):
self.log("DB 파일이 로드되었습니다.") self.log("DB 파일이 로드되었습니다.")
self.view_data_button.setEnabled(True) self.view_data_button.setEnabled(True)
def get_base_dir(self):
"""
실행 환경에 따라 base_dir을 설정하는 메서드.
cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정.
"""
if getattr(sys, 'frozen', False): # 패키징된 경우
base_dir = os.path.dirname(sys.executable)
internal_dir = os.path.join(base_dir, 'lib', 'src') # lib 디렉토리 포함
if os.path.exists(internal_dir): # lib 디렉토리가 존재하면 base_dir로 설정
return internal_dir
else: # 일반 Python 실행 환경
base_dir = os.path.dirname(os.path.abspath(__file__))
debug_dir = os.path.join(base_dir, 'src') # lib 디렉토리 포함
return debug_dir
def load_sample_excel(self):
"""'추가양식.xlsx' 파일을 엽니다."""
try:
base_dir = self.get_base_dir()
file_path = os.path.join(base_dir, "추가양식.xlsx")
if os.path.exists(file_path):
os.startfile(file_path)
self.log(f"추가 양식 파일 열기: {file_path}")
else:
self.log(f"추가 양식 파일을 찾을 수 없습니다: {file_path}")
QMessageBox.warning(self, "파일 없음", "추가 양식 파일을 찾을 수 없습니다.")
except Exception as e:
self.log(f"추가 양식 파일 열기 중 오류 발생: {str(e)}", exc_info=True)
QMessageBox.critical(self, "오류", f"추가 양식 파일을 열 수 없습니다: {str(e)}")
def load_excel(self): def load_excel(self):
file_path, _ = QFileDialog.getOpenFileName(self, "엑셀 파일 선택", "", "Excel Files (*.xlsx *.xls)") file_path, _ = QFileDialog.getOpenFileName(self, "엑셀 파일 선택", "", "Excel Files (*.xlsx *.xls)")
if not file_path: if not file_path:
@ -634,8 +684,16 @@ class MainWindow(QMainWindow):
# 파일 확장자 확인 # 파일 확장자 확인
ext = os.path.splitext(file_path)[-1].lower() ext = os.path.splitext(file_path)[-1].lower()
# 필수 열 이름 정의 # 열 이름 매핑 정의
required_columns = ['country', 'mall_grade', 'mall_name', 'mall_url'] column_name_mapping = {
'mall_url': ['mall_url(필수)', 'mall_url', '마켓링크', 'URL'],
'mall_name': ['mall_name', '셀러명', '판매자명'],
'mall_grade': ['mall_grade', '등급', '판매자등급'],
'country': ['country', '국가', '지역']
}
required_columns = ['mall_url']
all_expected_columns = list(column_name_mapping.keys())
# 엑셀 파일 읽기 # 엑셀 파일 읽기
if ext == ".xls": if ext == ".xls":
@ -658,22 +716,34 @@ class MainWindow(QMainWindow):
self.log("지원되지 않는 파일 형식입니다. .xls 또는 .xlsx 파일을 선택하세요.") self.log("지원되지 않는 파일 형식입니다. .xls 또는 .xlsx 파일을 선택하세요.")
return return
# 엑셀에서 가져온 열과 필수 열 비교 # 엑셀 열 이름과 정의된 이름 매핑
available_columns = [col for col in required_columns if col in df.columns] rename_dict = {}
if not available_columns: excel_columns = df.columns.tolist()
self.log("엑셀 파일에 필수 열이 없습니다. 필요한 열: " + ", ".join(required_columns)) for internal_name, possible_names in column_name_mapping.items():
for excel_col in excel_columns:
if excel_col in possible_names:
rename_dict[excel_col] = internal_name
break # 하나라도 매칭되면 다음 내부 이름으로 이동
# 데이터프레임 열 이름 변경
df.rename(columns=rename_dict, inplace=True)
# 필수 열이 있는지 확인
if 'mall_url' not in df.columns:
self.log(f"엑셀 파일에 필수 열({column_name_mapping['mall_url']}) 중 하나라도 있어야 합니다.")
return return
# 필요한 열만 가져오기 # 필요한 열만 선택 (매핑된 내부 이름 사용)
df = df[available_columns] available_columns = [col for col in all_expected_columns if col in df.columns]
df = df[available_columns].copy()
# 누락된 열은 빈 값으로 추가 # 누락된 열은 빈 값으로 채우기
for col in required_columns: for col in all_expected_columns:
if col not in df.columns: if col not in df.columns:
df[col] = "" # 누락된 열은 빈 값으로 채움 df[col] = ""
# 열 이름을 정렬하여 설정 # 열 순서 설정 (데이터베이스 테이블 순서와 맞춤)
df = df[required_columns] df = df[['country', 'mall_grade', 'mall_name', 'mall_url']]
# datetime.time 타입 데이터를 문자열로 변환 # datetime.time 타입 데이터를 문자열로 변환
def convert_time_to_string(x): def convert_time_to_string(x):
@ -691,6 +761,7 @@ class MainWindow(QMainWindow):
df['mall_url'] = df['mall_url'].apply(convert_time_to_string) df['mall_url'] = df['mall_url'].apply(convert_time_to_string)
conn = sqlite3.connect(self.db_path) conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if self.remove_db_checkbox.isChecked(): if self.remove_db_checkbox.isChecked():
# 기존 DB 제거 및 새 테이블 생성 # 기존 DB 제거 및 새 테이블 생성
@ -714,22 +785,42 @@ class MainWindow(QMainWindow):
# 컬럼이 이미 있는 경우 무시 # 컬럼이 이미 있는 경우 무시
pass pass
# 데이터 저장, 중복 방지 added_count = 0
df.drop_duplicates(subset=['mall_url'], inplace=True) skipped_count = 0
# id 열을 자동 생성하지 않으므로, 기존 테이블 스키마를 유지하면서 데이터를 삽입 # 데이터 저장 및 중복 방지
for _, row in df.iterrows(): for _, row in df.iterrows():
conn.execute(""" mall_url = row['mall_url']
INSERT INTO markets (country, mall_grade, mall_name, mall_url) cursor.execute("SELECT mall_url FROM markets WHERE mall_url=?", (mall_url,))
VALUES (?, ?, ?, ?) existing_mall = cursor.fetchone()
""", (row['country'], row['mall_grade'], row['mall_name'], row['mall_url'])) if not existing_mall:
cursor.execute("""
INSERT INTO markets (country, mall_grade, mall_name, mall_url)
VALUES (?, ?, ?, ?)
""", (row['country'], row['mall_grade'], row['mall_name'], mall_url))
self.log(f"새로운 몰 추가: {mall_url}")
added_count += 1
else:
self.log(f"중복된 몰 건너뛰기: {mall_url}")
skipped_count += 1
conn.commit() conn.commit()
conn.close() conn.close()
self.log("DB 저장 완료") self.log("DB 저장 완료")
self.show_load_excel_result_message(added_count, skipped_count)
except Exception as e: except Exception as e:
self.log(f"엑셀 파일을 불러오는 중 에러 발생: {str(e)}", exc_info=True) self.log(f"엑셀 파일을 불러오는 중 에러 발생: {str(e)}", exc_info=True)
def show_load_excel_result_message(self, added_count, skipped_count):
"""엑셀 로드 완료 후 결과를 메시지 박스로 보여줍니다."""
QMessageBox.information(
self,
"엑셀 처리 완료",
f"{added_count}개의 몰이 추가되었고, {skipped_count}개의 중복된 몰은 제외되었습니다."
)
def view_data(self): def view_data(self):
try: try:
conn = sqlite3.connect(self.db_path) conn = sqlite3.connect(self.db_path)

Binary file not shown.

BIN
requirements.txt Normal file

Binary file not shown.

View File

@ -17,12 +17,14 @@ main_file = "main.py"
# 필요한 추가 파일 설정 (예: 리소스 파일, 아이콘 등) # 필요한 추가 파일 설정 (예: 리소스 파일, 아이콘 등)
include_files = [ include_files = [
("markets.db", "markets.db"), # 데이터베이스 파일 (필요 시) ("markets.db", "markets.db"),
("추가양식.xlsx", "lib/src/추가양식.xlsx"),
("bookmaker.ico", "lib/src/bookmaker.ico"), # 아이콘 파일 포함 확인
] ]
# 빌드 옵션 # 빌드 옵션
build_options = { build_options = {
"packages": ["os", "sys", "sqlite3", "subprocess", "psutil", "pygetwindow", "glob","json", "pandas", "datetime", "PySide6", "openpyxl"], "packages": ["os", "sys", "sqlite3", "subprocess", "psutil", "pygetwindow", "glob","json", "pandas", "datetime", "PySide6", "openpyxl", "xlrd"],
"include_files": include_files, "include_files": include_files,
"excludes": ['PySide6.QtAsyncio.events'], # tkinter 미사용 시 제외 "excludes": ['PySide6.QtAsyncio.events'], # tkinter 미사용 시 제외
} }

View File

@ -1 +1 @@
__version__ = "1.3.3" __version__ = "1.3.4"

BIN
추가양식.xlsx Normal file

Binary file not shown.