qrpa 1.0.22__py3-none-any.whl → 1.0.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qrpa might be problematic. Click here for more details.
- qrpa/__init__.py +5 -1
- qrpa/fun_base.py +38 -0
- qrpa/fun_excel.py +50 -10
- qrpa/shein_daily_report_model.py +1 -1
- qrpa/shein_excel.py +444 -243
- qrpa/shein_lib.py +155 -0
- qrpa/temu_chrome.py +56 -0
- qrpa/temu_excel.py +109 -0
- qrpa/temu_lib.py +154 -0
- qrpa/wxwork.py +6 -0
- {qrpa-1.0.22.dist-info → qrpa-1.0.24.dist-info}/METADATA +1 -1
- qrpa-1.0.24.dist-info/RECORD +23 -0
- qrpa-1.0.22.dist-info/RECORD +0 -20
- {qrpa-1.0.22.dist-info → qrpa-1.0.24.dist-info}/WHEEL +0 -0
- {qrpa-1.0.22.dist-info → qrpa-1.0.24.dist-info}/top_level.txt +0 -0
qrpa/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@ from .db_migrator import DatabaseMigrator, DatabaseConfig, RemoteConfig, create_
|
|
|
3
3
|
|
|
4
4
|
from .shein_ziniao import ZiniaoRunner
|
|
5
5
|
|
|
6
|
-
from .fun_base import log, send_exception, md5_string, hostname, get_safe_value, sanitize_filename
|
|
6
|
+
from .fun_base import log, send_exception, md5_string, hostname, get_safe_value, sanitize_filename, get_file_size, calculate_star_symbols
|
|
7
7
|
|
|
8
8
|
from .time_utils import TimeUtils
|
|
9
9
|
|
|
@@ -18,3 +18,7 @@ from .shein_excel import SheinExcel
|
|
|
18
18
|
from .shein_lib import SheinLib
|
|
19
19
|
|
|
20
20
|
from .fun_excel import InsertImageV2
|
|
21
|
+
|
|
22
|
+
from .temu_lib import TemuLib
|
|
23
|
+
from .temu_excel import TemuExcel
|
|
24
|
+
from .temu_chrome import temu_chrome_excute
|
qrpa/fun_base.py
CHANGED
|
@@ -105,3 +105,41 @@ def copy_file(source, destination):
|
|
|
105
105
|
print(f"错误:没有权限复制到 '{destination}'")
|
|
106
106
|
except Exception as e:
|
|
107
107
|
print(f"错误:发生未知错误 - {e}")
|
|
108
|
+
|
|
109
|
+
def get_file_size(file_path, human_readable=False):
|
|
110
|
+
"""
|
|
111
|
+
获取文件大小
|
|
112
|
+
|
|
113
|
+
:param file_path: 文件路径
|
|
114
|
+
:param human_readable: 是否返回可读格式(KB, MB, GB)
|
|
115
|
+
:return: 文件大小(字节数或可读格式)
|
|
116
|
+
"""
|
|
117
|
+
if not os.path.isfile(file_path):
|
|
118
|
+
raise FileNotFoundError(f"文件不存在: {file_path}")
|
|
119
|
+
|
|
120
|
+
size_bytes = os.path.getsize(file_path)
|
|
121
|
+
|
|
122
|
+
if not human_readable:
|
|
123
|
+
return size_bytes
|
|
124
|
+
|
|
125
|
+
# 转换为可读单位
|
|
126
|
+
units = ["B", "KB", "MB", "GB", "TB"]
|
|
127
|
+
size = float(size_bytes)
|
|
128
|
+
for unit in units:
|
|
129
|
+
if size < 1024:
|
|
130
|
+
return f"{size:.2f} {unit}"
|
|
131
|
+
size /= 1024
|
|
132
|
+
|
|
133
|
+
def calculate_star_symbols(rating):
|
|
134
|
+
"""
|
|
135
|
+
计算星级对应的符号组合(独立评分逻辑函数)
|
|
136
|
+
参数:
|
|
137
|
+
rating (int): 标准化评分(0-5)
|
|
138
|
+
返回:
|
|
139
|
+
str: 星级符号字符串(如★★★⭐☆)
|
|
140
|
+
"""
|
|
141
|
+
full_stars = int(rating)
|
|
142
|
+
empty_stars = 5 - full_stars
|
|
143
|
+
star_string = '★' * full_stars
|
|
144
|
+
star_string += '☆' * empty_stars
|
|
145
|
+
return star_string
|
qrpa/fun_excel.py
CHANGED
|
@@ -12,6 +12,7 @@ import concurrent.futures
|
|
|
12
12
|
from collections import defaultdict
|
|
13
13
|
import threading
|
|
14
14
|
from playwright.sync_api import sync_playwright
|
|
15
|
+
import psutil
|
|
15
16
|
|
|
16
17
|
from .fun_base import log, sanitize_filename, create_file_path, copy_file, add_https, send_exception
|
|
17
18
|
|
|
@@ -89,13 +90,13 @@ def set_cell_prefix_red(cell, n, color_name):
|
|
|
89
90
|
except Exception as e:
|
|
90
91
|
print(f"设置字体颜色失败: {e}")
|
|
91
92
|
|
|
92
|
-
def sort_by_column(data, col_index,
|
|
93
|
-
if not data or
|
|
93
|
+
def sort_by_column(data, col_index, header_rows=2, reverse=True):
|
|
94
|
+
if not data or header_rows >= len(data):
|
|
94
95
|
return data
|
|
95
96
|
|
|
96
97
|
try:
|
|
97
|
-
header = data[:
|
|
98
|
-
new_data_sorted = data[
|
|
98
|
+
header = data[:header_rows]
|
|
99
|
+
new_data_sorted = data[header_rows:]
|
|
99
100
|
|
|
100
101
|
def get_key(row):
|
|
101
102
|
value = row[col_index]
|
|
@@ -541,11 +542,11 @@ def insert_fixed_scale_image(sheet, cell, image_path, scale=1.0):
|
|
|
541
542
|
|
|
542
543
|
return None
|
|
543
544
|
|
|
544
|
-
def InsertImageV2(
|
|
545
|
+
def InsertImageV2(sheet, columns=None, platform='shein', img_width=150, img_save_key=None, dir_name=None, cell_height_with_img=False):
|
|
545
546
|
if not columns:
|
|
546
547
|
return
|
|
547
548
|
|
|
548
|
-
minimize(app)
|
|
549
|
+
minimize(sheet.book.app)
|
|
549
550
|
|
|
550
551
|
# 清空所有图片
|
|
551
552
|
clear_all_pictures(sheet)
|
|
@@ -1162,6 +1163,9 @@ def check_data(data):
|
|
|
1162
1163
|
log(len(row), row)
|
|
1163
1164
|
|
|
1164
1165
|
def write_data(excel_path, sheet_name, data, format_to_text_colunm=None):
|
|
1166
|
+
log('write_data入参:', excel_path, sheet_name, 'data', format_to_text_colunm)
|
|
1167
|
+
close_excel_file(excel_path)
|
|
1168
|
+
|
|
1165
1169
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1166
1170
|
# 清空工作表中的所有数据
|
|
1167
1171
|
sheet.clear()
|
|
@@ -1174,8 +1178,8 @@ def write_data(excel_path, sheet_name, data, format_to_text_colunm=None):
|
|
|
1174
1178
|
wb.save()
|
|
1175
1179
|
close_excel(app, wb)
|
|
1176
1180
|
|
|
1177
|
-
def colorize_by_field(
|
|
1178
|
-
minimize(app)
|
|
1181
|
+
def colorize_by_field(sheet, field):
|
|
1182
|
+
minimize(sheet.book.app)
|
|
1179
1183
|
# 读取数据
|
|
1180
1184
|
field_column = find_column_by_data(sheet, 1, field) # 假设 SPU 在 C 列
|
|
1181
1185
|
if field_column is None:
|
|
@@ -2405,6 +2409,7 @@ def format_excel_with_lock(excel_path, sheet_name, format_func, *args, **kwargs)
|
|
|
2405
2409
|
log(f"格式化失败: {e}")
|
|
2406
2410
|
return False
|
|
2407
2411
|
|
|
2412
|
+
# 经过观察 fortmat时 传入函数需要为类函数且第二个参数必须是 sheet
|
|
2408
2413
|
def batch_excel_operations(excel_path, operations):
|
|
2409
2414
|
"""
|
|
2410
2415
|
批量 Excel 操作函数,一次性打开 Excel 执行多个操作
|
|
@@ -2439,21 +2444,36 @@ def batch_excel_operations(excel_path, operations):
|
|
|
2439
2444
|
sheet.activate()
|
|
2440
2445
|
|
|
2441
2446
|
if operation_type == 'write':
|
|
2442
|
-
data, format_to_text_colunm = args[:
|
|
2447
|
+
data, format_to_text_colunm = args[0], args[1:] if len(args) > 1 else None
|
|
2443
2448
|
# 清空工作表
|
|
2444
2449
|
sheet.clear()
|
|
2445
2450
|
# 格式化文本列
|
|
2446
|
-
|
|
2451
|
+
if format_to_text_colunm:
|
|
2452
|
+
format_to_text_v2(sheet, format_to_text_colunm)
|
|
2447
2453
|
# 写入数据
|
|
2448
2454
|
sheet.range('A1').value = data
|
|
2449
2455
|
log(f"批量操作:写入数据到 {sheet_name}")
|
|
2450
2456
|
|
|
2451
2457
|
elif operation_type == 'format':
|
|
2452
2458
|
format_func, format_args = args[0], args[1:] if len(args) > 1 else ()
|
|
2459
|
+
log('格式化入参', *format_args)
|
|
2453
2460
|
# 执行格式化
|
|
2454
2461
|
format_func(sheet, *format_args)
|
|
2455
2462
|
log(f"批量操作:格式化工作表 {sheet_name}")
|
|
2456
2463
|
|
|
2464
|
+
elif operation_type == 'delete':
|
|
2465
|
+
pass
|
|
2466
|
+
delete_sheet_if_exists(wb, sheet_name)
|
|
2467
|
+
|
|
2468
|
+
elif operation_type == 'move':
|
|
2469
|
+
pass
|
|
2470
|
+
position = args[0]
|
|
2471
|
+
move_sheet_to_position(wb, sheet_name, position)
|
|
2472
|
+
|
|
2473
|
+
elif operation_type == 'active':
|
|
2474
|
+
pass
|
|
2475
|
+
sheet.activate()
|
|
2476
|
+
|
|
2457
2477
|
# 保存所有更改
|
|
2458
2478
|
wb.save()
|
|
2459
2479
|
log(f"批量操作完成: {excel_path}")
|
|
@@ -2465,6 +2485,26 @@ def batch_excel_operations(excel_path, operations):
|
|
|
2465
2485
|
finally:
|
|
2466
2486
|
# 释放锁但不关闭 Excel(保持复用)
|
|
2467
2487
|
excel_lock_manager.release_excel_lock(excel_path)
|
|
2488
|
+
close_excel_with_lock(excel_path, app, wb, True)
|
|
2489
|
+
|
|
2490
|
+
def close_excel_file(file_path):
|
|
2491
|
+
file_path = os.path.abspath(file_path).lower()
|
|
2492
|
+
|
|
2493
|
+
for proc in psutil.process_iter(['pid', 'name']):
|
|
2494
|
+
if proc.info['name'] and proc.info['name'].lower() in ['excel.exe', 'wps.exe']: # 只找 Excel
|
|
2495
|
+
try:
|
|
2496
|
+
for f in proc.open_files():
|
|
2497
|
+
if os.path.abspath(f.path).lower() == file_path:
|
|
2498
|
+
print(f"文件被 Excel 占用 (PID: {proc.pid}),正在关闭进程...")
|
|
2499
|
+
proc.terminate()
|
|
2500
|
+
proc.wait(timeout=3)
|
|
2501
|
+
print("已关闭。")
|
|
2502
|
+
return True
|
|
2503
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
2504
|
+
continue
|
|
2505
|
+
|
|
2506
|
+
print("文件没有被 Excel 占用。")
|
|
2507
|
+
return False
|
|
2468
2508
|
|
|
2469
2509
|
def force_close_excel_file(excel_path):
|
|
2470
2510
|
"""
|