qrpa 1.0.100__py3-none-any.whl → 1.1.2__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/feishu_client.py +410 -410
- qrpa/feishu_logic.py +1443 -1443
- qrpa/fun_excel.py +28 -0
- qrpa/shein_excel.py +72 -0
- qrpa/shein_lib.py +58 -4
- qrpa/shein_mysql.py +62 -62
- qrpa/wxwork.py +318 -318
- {qrpa-1.0.100.dist-info → qrpa-1.1.2.dist-info}/METADATA +1 -1
- {qrpa-1.0.100.dist-info → qrpa-1.1.2.dist-info}/RECORD +11 -11
- {qrpa-1.0.100.dist-info → qrpa-1.1.2.dist-info}/WHEEL +0 -0
- {qrpa-1.0.100.dist-info → qrpa-1.1.2.dist-info}/top_level.txt +0 -0
qrpa/fun_excel.py
CHANGED
|
@@ -151,6 +151,34 @@ def wrap_column(sheet, columns=None, WrapText=True):
|
|
|
151
151
|
log(f'设置[{c}] 换行 {WrapText}')
|
|
152
152
|
sheet.range(f'{col_name}:{col_name}').api.WrapText = WrapText
|
|
153
153
|
|
|
154
|
+
def sort_by_column_excel(sheet, sort_col: str, has_header=True, order="desc"):
|
|
155
|
+
"""
|
|
156
|
+
对整个表格按照某一列排序
|
|
157
|
+
|
|
158
|
+
:param sheet: xlwings 的 sheet 对象
|
|
159
|
+
:param sort_col: 排序依据的列(如 'D')
|
|
160
|
+
:param has_header: 是否有表头(默认 True)
|
|
161
|
+
:param order: 'asc' 升序,'desc' 降序
|
|
162
|
+
"""
|
|
163
|
+
# 找到表格的最后一行和列
|
|
164
|
+
last_cell = sheet.used_range.last_cell
|
|
165
|
+
rng = sheet.range((1, 1), (last_cell.row, last_cell.column))
|
|
166
|
+
|
|
167
|
+
# 排序依据列
|
|
168
|
+
col_index = ord(sort_col.upper()) - ord('A') + 1
|
|
169
|
+
key = sheet.range((2 if has_header else 1, col_index)).api
|
|
170
|
+
|
|
171
|
+
# 排序顺序
|
|
172
|
+
order_val = 1 if order == "asc" else 2
|
|
173
|
+
|
|
174
|
+
# 调用 Excel 的 Sort 方法
|
|
175
|
+
rng.api.Sort(
|
|
176
|
+
Key1=key,
|
|
177
|
+
Order1=order_val,
|
|
178
|
+
Orientation=1,
|
|
179
|
+
Header=1 if has_header else 0
|
|
180
|
+
)
|
|
181
|
+
|
|
154
182
|
def sort_by_column(data, col_index, header_rows=2, reverse=True):
|
|
155
183
|
if not data or header_rows >= len(data):
|
|
156
184
|
return data
|
qrpa/shein_excel.py
CHANGED
|
@@ -15,6 +15,78 @@ class SheinExcel:
|
|
|
15
15
|
self.bridge = bridge
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
|
+
def write_product(self):
|
|
19
|
+
erp = self.config.erp_source
|
|
20
|
+
excel_path = create_file_path(self.config.excel_shein_skc_profit)
|
|
21
|
+
cache_file = f'{self.config.auto_dir}/shein/product/product_{TimeUtils.today_date()}.json'
|
|
22
|
+
dict_product = read_dict_from_file(cache_file)
|
|
23
|
+
|
|
24
|
+
skc_header = ['SKC', '商家SKC', 'SKC图片', '近7天利润', '近30天利润']
|
|
25
|
+
skc_excel_data = []
|
|
26
|
+
dict_skc = []
|
|
27
|
+
|
|
28
|
+
summary_excel_data = []
|
|
29
|
+
header = []
|
|
30
|
+
for store_username, excel_data in dict_product.items():
|
|
31
|
+
header = excel_data[0]
|
|
32
|
+
new_data = []
|
|
33
|
+
for row_item in excel_data[1:]:
|
|
34
|
+
supplier_sku = row_item[5]
|
|
35
|
+
row_item[10] = self.bridge.get_sku_cost(supplier_sku, erp)
|
|
36
|
+
new_data.append(row_item)
|
|
37
|
+
|
|
38
|
+
if row_item[2] not in dict_skc:
|
|
39
|
+
dict_skc.append(row_item[2])
|
|
40
|
+
stat_data = []
|
|
41
|
+
stat_data.append(row_item[2])
|
|
42
|
+
stat_data.append(row_item[3])
|
|
43
|
+
stat_data.append(row_item[4])
|
|
44
|
+
stat_data.append('')
|
|
45
|
+
stat_data.append('')
|
|
46
|
+
skc_excel_data.append(stat_data)
|
|
47
|
+
|
|
48
|
+
summary_excel_data += new_data
|
|
49
|
+
|
|
50
|
+
sheet_name = '商品库'
|
|
51
|
+
|
|
52
|
+
batch_excel_operations(excel_path, [
|
|
53
|
+
(sheet_name, 'write', [header] + summary_excel_data),
|
|
54
|
+
(sheet_name, 'format', self.format_product),
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
sheet_name = 'Sheet1'
|
|
58
|
+
profit_data = [skc_header] + skc_excel_data
|
|
59
|
+
batch_excel_operations(excel_path, [
|
|
60
|
+
(sheet_name, 'write', sort_by_column(profit_data, 4, 1)),
|
|
61
|
+
(sheet_name, 'format', self.format_profit),
|
|
62
|
+
(sheet_name, 'format', sort_by_column_excel, 'E'),
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
def format_profit(self, sheet):
|
|
66
|
+
beautify_title(sheet)
|
|
67
|
+
add_borders(sheet)
|
|
68
|
+
format_to_money(sheet, ['成本价', '核价', '利润'])
|
|
69
|
+
column_to_right(sheet, ['成本价', '核价', '利润'])
|
|
70
|
+
add_formula_for_column(sheet, '近7天利润', '=SUMIFS(商品库!L:L,商品库!P:P,A2)')
|
|
71
|
+
add_formula_for_column(sheet, '近30天利润', '=SUMIFS(商品库!M:M,商品库!P:P,A2)')
|
|
72
|
+
InsertImageV2(sheet, ['SKC图片'], 'shein', 90)
|
|
73
|
+
|
|
74
|
+
def format_product(self, sheet):
|
|
75
|
+
merge_by_column_v2(sheet, 'SPU', ['店铺信息', '产品信息'])
|
|
76
|
+
merge_by_column_v2(sheet, 'SKC', ['SKC图片', '商家SKC'])
|
|
77
|
+
beautify_title(sheet)
|
|
78
|
+
add_borders(sheet)
|
|
79
|
+
format_to_datetime(sheet, ['时间'])
|
|
80
|
+
format_to_money(sheet, ['成本价', '核价', '利润'])
|
|
81
|
+
column_to_right(sheet, ['成本价', '核价', '利润'])
|
|
82
|
+
autofit_column(sheet, ['产品信息'])
|
|
83
|
+
column_to_left(sheet, ['产品信息', '商家SKU', '商家SKC', '属性集'])
|
|
84
|
+
specify_column_width(sheet, ['店铺信息', '产品信息', '属性集'], 160 / 6)
|
|
85
|
+
specify_column_width(sheet, ['商家SKU', '商家SKC'], 220 / 6)
|
|
86
|
+
add_formula_for_column(sheet, '近7天利润', '=IF(ISNUMBER(K2), H2*(J2-K2),0)')
|
|
87
|
+
add_formula_for_column(sheet, '近30天利润', '=IF(ISNUMBER(K2), I2*(J2-K2),0)')
|
|
88
|
+
InsertImageV2(sheet, ['SKC图片'], 'shein', 150, '商家SKC', 'shein_skc_img')
|
|
89
|
+
|
|
18
90
|
def write_week_ntb(self):
|
|
19
91
|
excel_path = create_file_path(self.config.excel_week_report)
|
|
20
92
|
|
qrpa/shein_lib.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from docker.rq.config import today_date
|
|
1
2
|
from .fun_file import read_dict_from_file, write_dict_to_file, read_dict_from_file_ex, write_dict_to_file_ex
|
|
2
3
|
from .fun_base import log, send_exception, md5_string, get_safe_value, NetWorkIdleTimeout
|
|
3
4
|
from .fun_web import fetch, fetch_get, full_screen_shot, safe_goto
|
|
@@ -80,11 +81,9 @@ class SheinLib:
|
|
|
80
81
|
|
|
81
82
|
while retries < MAX_RETRIES:
|
|
82
83
|
try:
|
|
83
|
-
|
|
84
84
|
retries += 1
|
|
85
85
|
|
|
86
86
|
while not web_page.locator('//div[contains(text(),"商家后台")]').nth(1).is_visible():
|
|
87
|
-
|
|
88
87
|
try:
|
|
89
88
|
current_url = web_page.url
|
|
90
89
|
current_title = web_page.title()
|
|
@@ -170,7 +169,6 @@ class SheinLib:
|
|
|
170
169
|
web_page.wait_for_timeout(3000)
|
|
171
170
|
|
|
172
171
|
if 'https://sso.geiwohuo.com/#/home' in web_page.url:
|
|
173
|
-
|
|
174
172
|
if 'SHEIN全球商家中心' in web_page.title() or '后台首页' in web_page.title() or '商家后台' in web_page.title():
|
|
175
173
|
log(web_page.title(), '中断循环', self.store_username, self.store_name)
|
|
176
174
|
web_page.wait_for_timeout(5000)
|
|
@@ -354,7 +352,6 @@ class SheinLib:
|
|
|
354
352
|
return list_item
|
|
355
353
|
|
|
356
354
|
def get_return_order_list(self, start_date, end_date, only_yesterday=1):
|
|
357
|
-
|
|
358
355
|
log(f'获取退货列表: {self.store_username} {self.store_name} {start_date} {end_date}')
|
|
359
356
|
|
|
360
357
|
page_num = 1
|
|
@@ -865,6 +862,63 @@ class SheinLib:
|
|
|
865
862
|
write_dict_to_file(cache_file, info)
|
|
866
863
|
return info
|
|
867
864
|
|
|
865
|
+
def get_product(self):
|
|
866
|
+
excel_data = [
|
|
867
|
+
['店铺信息', '产品信息', 'SKC', '商家SKC', 'SKC图片', '商家SKU', '属性集', '近7天销量', '近30天销量', '核价', 'ERP成本价', '近7天利润', '近30天利润', '导出时间', 'SPU', 'SKC_FOR_STAT']
|
|
868
|
+
]
|
|
869
|
+
skc_list = self.get_bak_base_info()
|
|
870
|
+
cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
|
|
871
|
+
dict_sku = read_dict_from_file(cache_file)
|
|
872
|
+
for skc_item in skc_list:
|
|
873
|
+
categoryName = skc_item['categoryName']
|
|
874
|
+
spu = skc_item['spu']
|
|
875
|
+
skc = skc_item['skc']
|
|
876
|
+
supplierCode = skc_item['supplierCode']
|
|
877
|
+
skc_img = skc_item['picUrl']
|
|
878
|
+
shelfDate = skc_item['shelfDate']
|
|
879
|
+
shelfDays = skc_item['shelfDays']
|
|
880
|
+
shelfStatusName = skc_item['shelfStatus']['name']
|
|
881
|
+
# if shelfStatusName != '已下架':
|
|
882
|
+
# continue
|
|
883
|
+
for sku_item in skc_item['skuList']:
|
|
884
|
+
supplierSku = sku_item['supplierSku']
|
|
885
|
+
attr = sku_item['attr']
|
|
886
|
+
sku = sku_item['skuCode']
|
|
887
|
+
c7dSaleCnt = sku_item['c7dSaleCnt']
|
|
888
|
+
c30dSaleCnt = sku_item['c30dSaleCnt']
|
|
889
|
+
if attr == '合计' or int(c30dSaleCnt) == 0:
|
|
890
|
+
log(f'跳过: {supplierSku},近30天销量: {c30dSaleCnt}')
|
|
891
|
+
continue
|
|
892
|
+
|
|
893
|
+
price = dict_sku[sku]
|
|
894
|
+
|
|
895
|
+
product_info = f'SPU: {spu}\n商品分类: {categoryName}\n上架日期: {shelfDate}\n上架天数: {shelfDays}\n上架状态: {shelfStatusName}'
|
|
896
|
+
|
|
897
|
+
store_info = f'{self.store_username}\n{self.store_name}\n{self.config.shein_store_manager.get(self.store_username)}'
|
|
898
|
+
|
|
899
|
+
row_item = []
|
|
900
|
+
row_item.append(store_info)
|
|
901
|
+
row_item.append(product_info)
|
|
902
|
+
row_item.append(skc)
|
|
903
|
+
row_item.append(supplierCode)
|
|
904
|
+
row_item.append(skc_img)
|
|
905
|
+
row_item.append(supplierSku)
|
|
906
|
+
row_item.append(attr)
|
|
907
|
+
row_item.append(c7dSaleCnt)
|
|
908
|
+
row_item.append(c30dSaleCnt)
|
|
909
|
+
row_item.append(price)
|
|
910
|
+
row_item.append('')
|
|
911
|
+
row_item.append('')
|
|
912
|
+
row_item.append('')
|
|
913
|
+
row_item.append(TimeUtils.current_datetime())
|
|
914
|
+
row_item.append(spu)
|
|
915
|
+
row_item.append(skc)
|
|
916
|
+
excel_data.append(row_item)
|
|
917
|
+
|
|
918
|
+
cache_file = f'{self.config.auto_dir}/shein/product/product_{TimeUtils.today_date()}.json'
|
|
919
|
+
write_dict_to_file_ex(cache_file, {self.store_username: excel_data}, [self.store_username])
|
|
920
|
+
return excel_data
|
|
921
|
+
|
|
868
922
|
# 存储商品库
|
|
869
923
|
def store_product_info(self):
|
|
870
924
|
# todo 商品详情 属性 规格 图片 重量 与 尺寸
|
qrpa/shein_mysql.py
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
from .mysql_module.shein_return_order_model import SheinReturnOrderManager
|
|
4
|
-
from .mysql_module.shein_product_model import SheinProductManager
|
|
5
|
-
from .fun_base import log
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
class SheinMysql:
|
|
10
|
-
def __init__(self, config):
|
|
11
|
-
self.config = config
|
|
12
|
-
|
|
13
|
-
def upsert_shein_return_order(self, json_file):
|
|
14
|
-
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
15
|
-
# 创建管理器实例
|
|
16
|
-
manager = SheinReturnOrderManager(self.config.db.database_url)
|
|
17
|
-
# 创建数据表
|
|
18
|
-
manager.create_tables()
|
|
19
|
-
# 读取JSON文件
|
|
20
|
-
with open(json_file, 'r', encoding='utf-8') as f:
|
|
21
|
-
dict = json.load(f)
|
|
22
|
-
for store_username, data_list in dict.items():
|
|
23
|
-
manager.upsert_return_order_data(store_username, data_list)
|
|
24
|
-
|
|
25
|
-
def upsert_shein_product(self, json_file):
|
|
26
|
-
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
27
|
-
# 创建管理器实例
|
|
28
|
-
manager = SheinProductManager(self.config.db.database_url)
|
|
29
|
-
# 创建数据表
|
|
30
|
-
manager.create_tables()
|
|
31
|
-
with open(json_file, 'r', encoding='utf-8') as f:
|
|
32
|
-
file_list = json.load(f)
|
|
33
|
-
for store_username, store_skc_list_file in file_list.items():
|
|
34
|
-
with open(store_skc_list_file, 'r', encoding='utf-8') as f:
|
|
35
|
-
dict_store_skc_list = json.load(f)
|
|
36
|
-
for store_username, data_list in dict_store_skc_list.items():
|
|
37
|
-
manager.upsert_product_data(data_list)
|
|
38
|
-
|
|
39
|
-
def upsert_shein_product_info(self, json_file):
|
|
40
|
-
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
41
|
-
# 创建管理器实例
|
|
42
|
-
manager = SheinProductManager(self.config.db.database_url)
|
|
43
|
-
# 创建数据表
|
|
44
|
-
manager.create_tables()
|
|
45
|
-
with open(json_file, 'r', encoding='utf-8') as f:
|
|
46
|
-
file_list = json.load(f)
|
|
47
|
-
for store_username, store_spu_list in file_list.items():
|
|
48
|
-
for spu in store_spu_list:
|
|
49
|
-
product_detail_file = f'{self.config.auto_dir}/shein/product_detail/product_detail_{spu}.json'
|
|
50
|
-
attribute_file = f'{self.config.auto_dir}/shein/attribute/attribute_template_{spu}.json'
|
|
51
|
-
if os.path.exists(product_detail_file):
|
|
52
|
-
with open(product_detail_file, 'r', encoding='utf-8') as f:
|
|
53
|
-
data_list = json.load(f)
|
|
54
|
-
manager.upsert_product_detail(spu, 'product_detail', data_list)
|
|
55
|
-
else:
|
|
56
|
-
log(f'文件不存在: {product_detail_file}')
|
|
57
|
-
if os.path.exists(attribute_file):
|
|
58
|
-
with open(attribute_file, 'r', encoding='utf-8') as f:
|
|
59
|
-
data_list = json.load(f)
|
|
60
|
-
manager.upsert_product_detail(spu, 'attribute_template', data_list)
|
|
61
|
-
else:
|
|
62
|
-
log(f'文件不存在: {attribute_file}')
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from .mysql_module.shein_return_order_model import SheinReturnOrderManager
|
|
4
|
+
from .mysql_module.shein_product_model import SheinProductManager
|
|
5
|
+
from .fun_base import log
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
class SheinMysql:
|
|
10
|
+
def __init__(self, config):
|
|
11
|
+
self.config = config
|
|
12
|
+
|
|
13
|
+
def upsert_shein_return_order(self, json_file):
|
|
14
|
+
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
15
|
+
# 创建管理器实例
|
|
16
|
+
manager = SheinReturnOrderManager(self.config.db.database_url)
|
|
17
|
+
# 创建数据表
|
|
18
|
+
manager.create_tables()
|
|
19
|
+
# 读取JSON文件
|
|
20
|
+
with open(json_file, 'r', encoding='utf-8') as f:
|
|
21
|
+
dict = json.load(f)
|
|
22
|
+
for store_username, data_list in dict.items():
|
|
23
|
+
manager.upsert_return_order_data(store_username, data_list)
|
|
24
|
+
|
|
25
|
+
def upsert_shein_product(self, json_file):
|
|
26
|
+
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
27
|
+
# 创建管理器实例
|
|
28
|
+
manager = SheinProductManager(self.config.db.database_url)
|
|
29
|
+
# 创建数据表
|
|
30
|
+
manager.create_tables()
|
|
31
|
+
with open(json_file, 'r', encoding='utf-8') as f:
|
|
32
|
+
file_list = json.load(f)
|
|
33
|
+
for store_username, store_skc_list_file in file_list.items():
|
|
34
|
+
with open(store_skc_list_file, 'r', encoding='utf-8') as f:
|
|
35
|
+
dict_store_skc_list = json.load(f)
|
|
36
|
+
for store_username, data_list in dict_store_skc_list.items():
|
|
37
|
+
manager.upsert_product_data(data_list)
|
|
38
|
+
|
|
39
|
+
def upsert_shein_product_info(self, json_file):
|
|
40
|
+
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
41
|
+
# 创建管理器实例
|
|
42
|
+
manager = SheinProductManager(self.config.db.database_url)
|
|
43
|
+
# 创建数据表
|
|
44
|
+
manager.create_tables()
|
|
45
|
+
with open(json_file, 'r', encoding='utf-8') as f:
|
|
46
|
+
file_list = json.load(f)
|
|
47
|
+
for store_username, store_spu_list in file_list.items():
|
|
48
|
+
for spu in store_spu_list:
|
|
49
|
+
product_detail_file = f'{self.config.auto_dir}/shein/product_detail/product_detail_{spu}.json'
|
|
50
|
+
attribute_file = f'{self.config.auto_dir}/shein/attribute/attribute_template_{spu}.json'
|
|
51
|
+
if os.path.exists(product_detail_file):
|
|
52
|
+
with open(product_detail_file, 'r', encoding='utf-8') as f:
|
|
53
|
+
data_list = json.load(f)
|
|
54
|
+
manager.upsert_product_detail(spu, 'product_detail', data_list)
|
|
55
|
+
else:
|
|
56
|
+
log(f'文件不存在: {product_detail_file}')
|
|
57
|
+
if os.path.exists(attribute_file):
|
|
58
|
+
with open(attribute_file, 'r', encoding='utf-8') as f:
|
|
59
|
+
data_list = json.load(f)
|
|
60
|
+
manager.upsert_product_detail(spu, 'attribute_template', data_list)
|
|
61
|
+
else:
|
|
62
|
+
log(f'文件不存在: {attribute_file}')
|