업데이트
This commit is contained in:
parent
3059ba375c
commit
e806dc0538
131
main.py
131
main.py
|
|
@ -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)
|
||||||
|
|
|
||||||
BIN
markets.db
BIN
markets.db
Binary file not shown.
Binary file not shown.
6
setup.py
6
setup.py
|
|
@ -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 미사용 시 제외
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
__version__ = "1.3.3"
|
__version__ = "1.3.4"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue