Compare commits

...

3 Commits

Author SHA1 Message Date
9700X_PC e806dc0538 업데이트 2025-03-29 17:30:14 +09:00
Envy_PC 3059ba375c . 2025-03-29 16:00:18 +09:00
9700X_PC 49b7172187 1.3.3 2025-03-28 09:11:01 +09:00
9 changed files with 150 additions and 27 deletions

167
main.py
View File

@ -3,6 +3,8 @@ import subprocess
import json
from datetime import datetime, time
from src.version import __version__
import xlrd
from PySide6.QtGui import QIcon
import logging
import traceback
@ -33,6 +35,7 @@ class BookmarkWorker(QThread):
self.browser_path = browser_path
self.selected_browser = selected_browser
self.remove_existing = remove_existing
self.chunk_size = 1000
def run(self):
try:
@ -76,7 +79,7 @@ class BookmarkWorker(QThread):
}
# 하위 폴더 생성
chunk_size = 100 # 하위 폴더에 넣을 북마크 수
chunk_size = self.chunk_size # 하위 폴더에 넣을 북마크 수
for idx, chunk_start in enumerate(range(0, total_bookmarks, chunk_size), start=1):
# 하위 폴더 이름 생성
folder_name = f"거상북마크-{self.folder_name}-{idx}" # 하위 폴더 이름 (예: 거상북마크-중국-1)
@ -294,6 +297,11 @@ class MainWindow(QMainWindow):
self.db_input_button.setToolTip("엑셀 파일을 선택하여 DB에 저장합니다. 기존 DB를 제거하거나 추가로 데이터를 입력할 수 있습니다.")
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 제거 체크박스
self.remove_db_checkbox = QCheckBox("기존 DB 제거")
self.remove_db_checkbox.setToolTip("체크하면 기존 DB를 삭제하고 새롭게 데이터를 입력합니다.")
@ -332,14 +340,23 @@ class MainWindow(QMainWindow):
self.grade_dropdown.addItems(["일반", "파워", "빅파워", "랜덤"])
self.grade_dropdown.setCurrentText("랜덤")
# 스핀박스: 갯수
self.count_label = QLabel("갯수")
# 스핀박스: 추출 몰 갯수
self.count_label = QLabel("추출 몰 갯수")
self.count_spinbox = QSpinBox()
self.count_spinbox.setMinimum(1000)
self.count_spinbox.setMaximum(50000)
self.count_spinbox.setSingleStep(1000)
self.count_spinbox.setValue(1000)
# 스핀박스: 폴더당 북마크 갯수
self.fcount_label = QLabel("폴더당 북마크 갯수")
self.fcount_spinbox = QSpinBox()
self.fcount_spinbox.setMinimum(1000)
self.fcount_spinbox.setMaximum(50000)
self.fcount_spinbox.setSingleStep(100)
self.fcount_spinbox.setValue(100)
self.fcount_spinbox.valueChanged.connect(self.update_chunk_size)
# 기존 북마크 제거 체크박스
self.remove_existing_checkbox = QCheckBox("기존 북마크 제거")
self.remove_existing_checkbox.setToolTip("체크하면 '거상북마크'로 시작하는 모든 북마크를 제거합니다.")
@ -384,6 +401,9 @@ class MainWindow(QMainWindow):
self.filter_layout.addWidget(self.count_label)
self.filter_layout.addWidget(self.count_spinbox)
self.filter_layout.addWidget(QLabel(" "))
self.filter_layout.addWidget(self.fcount_label)
self.filter_layout.addWidget(self.fcount_spinbox)
self.filter_layout.addWidget(QLabel(" "))
self.filter_layout.addWidget(self.remove_existing_checkbox)
@ -400,6 +420,7 @@ class MainWindow(QMainWindow):
self.browser_selection_layout.addWidget(self.whale_path_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.view_data_button)
self.buttons_layout.addWidget(self.run_button)
@ -421,6 +442,16 @@ class MainWindow(QMainWindow):
self.main_widget.setLayout(self.layout)
self.setCentralWidget(self.main_widget)
# 모던한 디자인을 위한 스타일시트 적용
self.setStyleSheet("""
QMainWindow { background-color: #f9f9f9; }
QPushButton { background-color: #4CAF50; color: white; padding: 6px 12px; border-radius: 4px; }
QPushButton:hover { background-color: #45a049; }
QLineEdit, QComboBox, QSpinBox, QCheckBox, QTextEdit { font-size: 14px; }
QLabel { font-size: 14px; }
QProgressBar { height: 20px; }
""")
# 상태 변수
self.db_path = "markets.db"
@ -452,6 +483,21 @@ class MainWindow(QMainWindow):
self.log(f"크롬 프로파일 경로가 설정되었습니다: {self.chrome_bookmarks_path}")
self.log(f"웨일 프로파일 경로가 설정되었습니다: {self.whale_bookmarks_path}")
# 아이콘 설정 (추가된 부분)
self.set_app_icon()
def update_chunk_size(self):
"""폴더당 북마크 갯수 변경 시 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):
@ -595,6 +641,38 @@ class MainWindow(QMainWindow):
self.log("DB 파일이 로드되었습니다.")
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):
file_path, _ = QFileDialog.getOpenFileName(self, "엑셀 파일 선택", "", "Excel Files (*.xlsx *.xls)")
if not file_path:
@ -606,8 +684,16 @@ class MainWindow(QMainWindow):
# 파일 확장자 확인
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":
@ -630,22 +716,34 @@ class MainWindow(QMainWindow):
self.log("지원되지 않는 파일 형식입니다. .xls 또는 .xlsx 파일을 선택하세요.")
return
# 엑셀에서 가져온 열과 필수 열 비교
available_columns = [col for col in required_columns if col in df.columns]
if not available_columns:
self.log("엑셀 파일에 필수 열이 없습니다. 필요한 열: " + ", ".join(required_columns))
# 엑셀 열 이름과 정의된 이름 매핑
rename_dict = {}
excel_columns = df.columns.tolist()
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
# 필요한 열만 가져오기
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:
df[col] = "" # 누락된 열은 빈 값으로 채움
df[col] = ""
# 열 이름을 정렬하여 설정
df = df[required_columns]
# 열 순서 설정 (데이터베이스 테이블 순서와 맞춤)
df = df[['country', 'mall_grade', 'mall_name', 'mall_url']]
# datetime.time 타입 데이터를 문자열로 변환
def convert_time_to_string(x):
@ -663,6 +761,7 @@ class MainWindow(QMainWindow):
df['mall_url'] = df['mall_url'].apply(convert_time_to_string)
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if self.remove_db_checkbox.isChecked():
# 기존 DB 제거 및 새 테이블 생성
@ -686,22 +785,42 @@ class MainWindow(QMainWindow):
# 컬럼이 이미 있는 경우 무시
pass
# 데이터 저장, 중복 방지
df.drop_duplicates(subset=['country', 'mall_grade', 'mall_name', 'mall_url'], inplace=True)
added_count = 0
skipped_count = 0
# id 열을 자동 생성하지 않으므로, 기존 테이블 스키마를 유지하면서 데이터를 삽입
# 데이터 저장 및 중복 방지
for _, row in df.iterrows():
conn.execute("""
INSERT INTO markets (country, mall_grade, mall_name, mall_url)
VALUES (?, ?, ?, ?)
""", (row['country'], row['mall_grade'], row['mall_name'], row['mall_url']))
mall_url = row['mall_url']
cursor.execute("SELECT mall_url FROM markets WHERE mall_url=?", (mall_url,))
existing_mall = cursor.fetchone()
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.close()
self.log("DB 저장 완료")
self.show_load_excel_result_message(added_count, skipped_count)
except Exception as e:
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):
try:
conn = sqlite3.connect(self.db_path)
@ -904,6 +1023,8 @@ class MainWindow(QMainWindow):
self.worker = BookmarkWorker(self.bookmarks, folder_name, selected_bookmarks_path, selected_browser_path, selected_browser, remove_existing)
self.worker.chunk_size = self.fcount_spinbox.value()
self.worker.progress.connect(self.progress_bar.setValue)
self.worker.log.connect(self.log)
self.worker.completed.connect(self.task_completed)

Binary file not shown.

BIN
requirements.txt Normal file

Binary file not shown.

View File

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

Binary file not shown.

Binary file not shown.

View File

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

BIN
추가양식.xlsx Normal file

Binary file not shown.