first commit
This commit is contained in:
commit
97dd188666
|
|
@ -0,0 +1,10 @@
|
|||
Lib/
|
||||
libs/
|
||||
Scripts/
|
||||
build/
|
||||
dist/
|
||||
Include/
|
||||
__pycache__/
|
||||
*.log
|
||||
*.log.*
|
||||
pyvenv.cfg
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import shutil
|
||||
import os
|
||||
from subprocess import call
|
||||
|
||||
# 삭제할 디렉토리 목록
|
||||
folders_to_delete = ['build', 'dist']
|
||||
|
||||
# 현재 디렉토리 내의 폴더들을 순회하며 삭제
|
||||
for folder in folders_to_delete:
|
||||
if os.path.exists(folder):
|
||||
shutil.rmtree(folder)
|
||||
print(f"{folder} 폴더를 삭제했습니다.")
|
||||
|
||||
# PyInstaller 명령 실행
|
||||
# 여기에서 'your_script.py'를 실제 빌드하고자 하는 스크립트의 이름으로 바꿔주세요.
|
||||
# 추가 PyInstaller 옵션이 필요한 경우 이 부분에 추가합니다.
|
||||
call(['pyinstaller', 'taomanXLS.spec'])
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[DEFAULT]
|
||||
excludewords = 오늘출발, 오늘발송
|
||||
banwords = 샤오미, 나이키, 크록스
|
||||
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
import sys
|
||||
import os
|
||||
import pandas as pd
|
||||
import random
|
||||
import configparser
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,
|
||||
QFileDialog, QRadioButton, QTextEdit, QMessageBox, QLineEdit, QCheckBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
||||
import xlwings as xw
|
||||
import traceback
|
||||
|
||||
class ExportThread(QThread):
|
||||
log_signal = pyqtSignal(str)
|
||||
finished_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, data, export_format, output_folder, original_file_name, exclude_words, ban_words, shuffle_title):
|
||||
super().__init__()
|
||||
self.data = data
|
||||
self.export_format = export_format
|
||||
self.output_folder = output_folder
|
||||
self.original_file_name = original_file_name
|
||||
self.exclude_words = exclude_words
|
||||
self.ban_words = ban_words
|
||||
self.shuffle_title = shuffle_title
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
chunk_size = 50
|
||||
for i in range(0, len(self.data), chunk_size):
|
||||
chunk = self.data.iloc[i:i+chunk_size].copy()
|
||||
|
||||
# Exclude words from tags
|
||||
if self.exclude_words:
|
||||
for word in self.exclude_words:
|
||||
chunk['태그'] = chunk['태그'].str.replace(f'\\b{word}\\b', '', regex=True).str.strip()
|
||||
|
||||
# Exclude words from titles
|
||||
if self.ban_words:
|
||||
for word in self.ban_words:
|
||||
chunk['상품명'] = chunk['상품명'].apply(lambda x: x.replace(word, '').strip())
|
||||
|
||||
# Shuffle title words if needed
|
||||
if self.shuffle_title:
|
||||
chunk['상품명'] = chunk['상품명'].apply(self.shuffle_title_words)
|
||||
|
||||
output_file = os.path.join(self.output_folder, f"percenty-{i//chunk_size + 1:02d}_{self.original_file_name}")
|
||||
|
||||
# Create a DataFrame with exactly 50 rows, filling with empty values if necessary
|
||||
if len(chunk) < chunk_size:
|
||||
empty_rows = pd.DataFrame("", index=range(chunk_size - len(chunk)), columns=chunk.columns)
|
||||
chunk = pd.concat([chunk, empty_rows], ignore_index=True)
|
||||
|
||||
if self.export_format == "퍼센티":
|
||||
template_file = "퍼센티양식.xlsx"
|
||||
with xw.App(visible=False) as app:
|
||||
template_wb = app.books.open(template_file)
|
||||
template_ws = template_wb.sheets[0]
|
||||
|
||||
new_wb = xw.Book()
|
||||
new_ws = new_wb.sheets[0]
|
||||
|
||||
# Copy the template sheet to the new workbook
|
||||
template_ws.api.Copy(Before=new_ws.api)
|
||||
new_ws = new_wb.sheets[0]
|
||||
|
||||
# Write data to the new workbook
|
||||
new_ws.range('B4:B53').value = [[v] for v in chunk['타오바오URL']]
|
||||
new_ws.range('C4:C53').value = [[v] for v in chunk['상품명']]
|
||||
new_ws.range('D4:D53').value = [[v] for v in chunk['가격']]
|
||||
new_ws.range('F4:F53').value = [[v] for v in chunk['태그']]
|
||||
new_ws.range('G4:G53').value = [[v] for v in chunk['퍼센티카테고리']]
|
||||
|
||||
new_wb.save(output_file)
|
||||
new_wb.close()
|
||||
template_wb.close()
|
||||
|
||||
elif self.export_format == "헤이셀러":
|
||||
# Implement 헤이셀러 export format if provided
|
||||
pass
|
||||
|
||||
self.log_signal.emit(f"Exported {output_file}")
|
||||
|
||||
os.startfile(self.output_folder)
|
||||
self.log_signal.emit("Export completed and folder opened.")
|
||||
except Exception as e:
|
||||
self.log_signal.emit(f"Error during export: {str(e)}")
|
||||
self.log_signal.emit(traceback.format_exc())
|
||||
|
||||
self.finished_signal.emit()
|
||||
|
||||
def shuffle_title_words(self, title):
|
||||
words = title.split()
|
||||
if len(words) > 4:
|
||||
prefix = words[:4]
|
||||
suffix = words[4:]
|
||||
random.shuffle(suffix)
|
||||
return ' '.join(prefix + suffix)
|
||||
return title
|
||||
|
||||
class ExcelProcessorApp(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.initUI()
|
||||
self.load_config()
|
||||
self.copy_data = None
|
||||
self.source_data = None
|
||||
self.copy_file_path = None
|
||||
self.source_file_path = None
|
||||
|
||||
def initUI(self):
|
||||
self.setWindowTitle('WRMC TaomanXLS')
|
||||
self.setGeometry(100, 100, 450, 600)
|
||||
self.setFixedSize(450, 600)
|
||||
|
||||
main_layout = QVBoxLayout()
|
||||
|
||||
# First layout with buttons
|
||||
button_layout = QHBoxLayout()
|
||||
self.copy_button = QPushButton('카피맨 읽기')
|
||||
self.copy_button.setMinimumHeight(70)
|
||||
self.copy_button.clicked.connect(self.read_copy)
|
||||
button_layout.addWidget(self.copy_button)
|
||||
|
||||
self.source_button = QPushButton('소싱맨 읽기')
|
||||
self.source_button.setMinimumHeight(70)
|
||||
self.source_button.setEnabled(False)
|
||||
self.source_button.clicked.connect(self.read_source)
|
||||
button_layout.addWidget(self.source_button)
|
||||
|
||||
self.export_button = QPushButton('출력')
|
||||
self.export_button.setMinimumHeight(70)
|
||||
self.export_button.clicked.connect(self.export_data)
|
||||
button_layout.addWidget(self.export_button)
|
||||
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
# New layout with exclude words
|
||||
exclude_layout = QHBoxLayout()
|
||||
self.exclude_label = QLabel('제외단어:')
|
||||
exclude_layout.addWidget(self.exclude_label)
|
||||
self.exclude_input = QLineEdit()
|
||||
self.exclude_input.setPlaceholderText('오늘출발, 오늘발송')
|
||||
exclude_layout.addWidget(self.exclude_input)
|
||||
|
||||
main_layout.addLayout(exclude_layout)
|
||||
|
||||
# New layout with shuffle title and ban words
|
||||
shuffle_layout = QVBoxLayout()
|
||||
shuffle_title_layout = QHBoxLayout()
|
||||
self.shuffle_checkbox = QCheckBox('상품명섞기')
|
||||
shuffle_title_layout.addWidget(self.shuffle_checkbox)
|
||||
shuffle_layout.addLayout(shuffle_title_layout)
|
||||
|
||||
ban_layout = QHBoxLayout()
|
||||
self.ban_label = QLabel('상품명금지단어:')
|
||||
ban_layout.addWidget(self.ban_label)
|
||||
self.ban_input = QLineEdit()
|
||||
self.ban_input.setPlaceholderText('샤오미, 나이키')
|
||||
ban_layout.addWidget(self.ban_input)
|
||||
shuffle_layout.addLayout(ban_layout)
|
||||
|
||||
main_layout.addLayout(shuffle_layout)
|
||||
|
||||
# Second layout with radio buttons
|
||||
radio_layout = QHBoxLayout()
|
||||
self.percent_radio = QRadioButton('퍼센티')
|
||||
self.percent_radio.setChecked(True)
|
||||
radio_layout.addWidget(self.percent_radio)
|
||||
|
||||
self.hayseller_radio = QRadioButton('헤이셀러')
|
||||
self.hayseller_radio.setEnabled(False)
|
||||
radio_layout.addWidget(self.hayseller_radio)
|
||||
|
||||
main_layout.addLayout(radio_layout)
|
||||
|
||||
# Third layout with log text
|
||||
self.log_text = QTextEdit()
|
||||
self.log_text.setReadOnly(True)
|
||||
log_layout = QVBoxLayout()
|
||||
log_layout.addWidget(self.log_text)
|
||||
main_layout.addLayout(log_layout)
|
||||
|
||||
# Footer layout with developer label
|
||||
self.developer_label = QLabel('by 내차는 언제타냐')
|
||||
main_layout.addWidget(self.developer_label, alignment=Qt.AlignCenter)
|
||||
|
||||
self.setLayout(main_layout)
|
||||
self.center()
|
||||
|
||||
def center(self):
|
||||
qr = self.frameGeometry()
|
||||
cp = QApplication.desktop().screen().rect().center()
|
||||
qr.moveCenter(cp)
|
||||
self.move(qr.topLeft())
|
||||
|
||||
def log(self, message):
|
||||
self.log_text.append(message)
|
||||
self.log_text.ensureCursorVisible()
|
||||
print(message) # 콘솔에도 로그 출력
|
||||
|
||||
def load_config(self):
|
||||
self.config = configparser.ConfigParser()
|
||||
if os.path.exists('config.ini'):
|
||||
self.config.read('config.ini')
|
||||
else:
|
||||
self.config['DEFAULT'] = {
|
||||
'ExcludeWords': '오늘출발, 오늘발송',
|
||||
'BanWords': '샤오미, 나이키'
|
||||
}
|
||||
with open('config.ini', 'w') as configfile:
|
||||
self.config.write(configfile)
|
||||
self.exclude_input.setText(self.config['DEFAULT'].get('ExcludeWords', '오늘출발, 오늘발송'))
|
||||
self.ban_input.setText(self.config['DEFAULT'].get('BanWords', '샤오미, 나이키'))
|
||||
|
||||
def save_config(self):
|
||||
self.config['DEFAULT']['ExcludeWords'] = self.exclude_input.text()
|
||||
self.config['DEFAULT']['BanWords'] = self.ban_input.text()
|
||||
with open('config.ini', 'w') as configfile:
|
||||
self.config.write(configfile)
|
||||
|
||||
def read_excel(self, title):
|
||||
options = QFileDialog.Options()
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, title, "", "Excel Files (*.xlsx)", options=options)
|
||||
if file_path:
|
||||
self.log(f"Reading file: {file_path}")
|
||||
try:
|
||||
df = pd.read_excel(file_path, sheet_name='추출상점')
|
||||
required_columns = ["퍼센티카테고리", "가격", "상품명", "태그", "타오바오URL"]
|
||||
if all(col in df.columns for col in required_columns):
|
||||
self.log(f"Successfully read data from {file_path}")
|
||||
return df[required_columns], file_path
|
||||
else:
|
||||
self.log(f"Required columns are missing in the file: {file_path}")
|
||||
QMessageBox.critical(self, "Error", "The selected file does not contain all required columns.")
|
||||
except Exception as e:
|
||||
self.log(f"Error reading file: {str(e)}")
|
||||
QMessageBox.critical(self, "Error", "An error occurred while reading the file.")
|
||||
return None, None
|
||||
|
||||
def read_copy(self):
|
||||
self.copy_data, self.copy_file_path = self.read_excel("카피맨 파일 선택")
|
||||
if self.copy_data is not None:
|
||||
self.log("카피맨 데이터 읽기 완료")
|
||||
|
||||
def read_source(self):
|
||||
self.source_data, self.source_file_path = self.read_excel("소싱맨 파일 선택")
|
||||
if self.source_data is not None:
|
||||
self.log("소싱맨 데이터 읽기 완료")
|
||||
|
||||
def export_data(self):
|
||||
data = self.copy_data if self.copy_data is not None else self.source_data
|
||||
if data is None:
|
||||
self.log("데이터가 없습니다.")
|
||||
QMessageBox.warning(self, "Warning", "No data to export.")
|
||||
return
|
||||
|
||||
exclude_words = self.exclude_input.text().split(',')
|
||||
exclude_words = [word.strip() for word in exclude_words if word.strip()]
|
||||
|
||||
ban_words = self.ban_input.text().split(',')
|
||||
ban_words = [word.strip() for word in ban_words if word.strip()]
|
||||
|
||||
shuffle_title = self.shuffle_checkbox.isChecked()
|
||||
|
||||
export_format = "퍼센티" if self.percent_radio.isChecked() else "헤이셀러"
|
||||
self.log(f"Exporting data in {export_format} format")
|
||||
|
||||
# Set default export folder based on the source of data
|
||||
if self.copy_data is not None:
|
||||
default_folder = os.path.dirname(self.copy_file_path)
|
||||
original_file_name = os.path.basename(self.copy_file_path)
|
||||
elif self.source_data is not None:
|
||||
default_folder = os.path.dirname(self.source_file_path)
|
||||
original_file_name = os.path.basename(self.source_file_path)
|
||||
else:
|
||||
default_folder = ""
|
||||
original_file_name = "output.xlsx"
|
||||
|
||||
output_folder = QFileDialog.getExistingDirectory(self, "Export Folder", default_folder)
|
||||
if not output_folder:
|
||||
self.log("출력 폴더를 선택하지 않았습니다.")
|
||||
return
|
||||
|
||||
self.export_thread = ExportThread(data, export_format, output_folder, original_file_name, exclude_words, ban_words, shuffle_title)
|
||||
self.export_thread.log_signal.connect(self.log)
|
||||
self.export_thread.finished_signal.connect(self.on_export_finished)
|
||||
self.export_button.setEnabled(False)
|
||||
self.export_thread.start()
|
||||
|
||||
def on_export_finished(self):
|
||||
self.export_button.setEnabled(True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
ex = ExcelProcessorApp()
|
||||
|
||||
# Add signal to save config on exit
|
||||
app.aboutToQuit.connect(ex.save_config)
|
||||
|
||||
ex.show()
|
||||
sys.exit(app.exec_())
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['main.py'], # main.py 대신 여러분의 메인 파일 이름을 입력하세요.
|
||||
pathex=['.'],
|
||||
binaries=[],
|
||||
datas=[
|
||||
('퍼센티양식.xlsx', '.'), # 퍼센티양식.xlsx 파일을 포함합니다.
|
||||
('config.ini', '.'), # config.ini 파일을 포함합니다.
|
||||
],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='ExcelProcessorApp',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False # GUI 프로그램이므로 console을 False로 설정합니다.
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='ExcelProcessorApp'
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue