qrpa 1.0.19__py3-none-any.whl → 1.0.21__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.

@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 店铺销售明细数据模型
5
+ 使用SQLAlchemy定义store_sales_detail表结构
6
+ """
7
+
8
+ from sqlalchemy import and_
9
+ from sqlalchemy import create_engine, Column, Integer, String, Date, DECIMAL, UniqueConstraint
10
+ # from sqlalchemy.ext.declarative import declarative_base
11
+ from sqlalchemy.orm import declarative_base
12
+ from sqlalchemy.orm import sessionmaker
13
+
14
+ from datetime import datetime, timedelta
15
+
16
+ from sqlalchemy import distinct
17
+
18
+ # 创建基础类
19
+ Base = declarative_base()
20
+
21
+ # 定义模型类
22
+ class SheinStoreSalesDetail(Base):
23
+ __tablename__ = 'store_sales_detail'
24
+
25
+ id = Column(Integer, primary_key=True, autoincrement=True)
26
+ store_username = Column(String(255), nullable=True)
27
+ store_name = Column(String(255), nullable=True)
28
+ day = Column(Date, nullable=True)
29
+ sales_num = Column(Integer, nullable=True)
30
+ sales_num_inc = Column(Integer, nullable=True)
31
+ sales_amount = Column(DECIMAL(10, 2), nullable=True)
32
+ sales_amount_inc = Column(DECIMAL(10, 2), nullable=True)
33
+ visitor_num = Column(Integer, nullable=True)
34
+ visitor_num_inc = Column(Integer, nullable=True)
35
+ bak_A_num = Column(Integer, nullable=True)
36
+ bak_A_num_inc = Column(Integer, nullable=True)
37
+ new_A_num = Column(Integer, nullable=True)
38
+ new_A_num_inc = Column(Integer, nullable=True)
39
+ on_sales_product_num = Column(Integer, nullable=True)
40
+ on_sales_product_num_inc = Column(Integer, nullable=True)
41
+ wait_shelf_product_num = Column(Integer, nullable=True)
42
+ wait_shelf_product_num_inc = Column(Integer, nullable=True)
43
+ upload_product_num = Column(Integer, nullable=True)
44
+ upload_product_num_inc = Column(Integer, nullable=True)
45
+ sold_out_product_num = Column(Integer, nullable=True)
46
+ shelf_off_product_num = Column(Integer, nullable=True)
47
+ remark = Column(String(255), nullable=True)
48
+
49
+ __table_args__ = (
50
+ UniqueConstraint('store_username', 'day', name='uq_store_day'),
51
+ )
52
+
53
+ def __repr__(self):
54
+ return f"<SheinStoreSalesDetail(store_username={self.store_username}, store_name={self.store_name}, day={self.day})>"
55
+
56
+ class SheinStoreSalesDetailManager:
57
+ """
58
+ 店铺销售明细数据管理器
59
+ 提供数据库操作相关方法
60
+ """
61
+
62
+ # database_url = f"mysql+pymysql://{config.mysql_username}:{config.mysql_password}@{config.mysql_host}:{config.mysql_port}/{config.mysql_database}"
63
+
64
+ def __init__(self, database_url=None):
65
+ """
66
+ 初始化数据库连接
67
+
68
+ Args:
69
+ database_url (str): 数据库连接URL,例如:
70
+ mysql+pymysql://username:password@localhost:3306/database_name
71
+ """
72
+ if database_url is None:
73
+ database_url = self.database_url
74
+
75
+ self.engine = create_engine(database_url, echo=False)
76
+ self.Session = sessionmaker(bind=self.engine)
77
+
78
+ def create_tables(self):
79
+ """
80
+ 创建数据表
81
+ """
82
+ Base.metadata.create_all(self.engine)
83
+ print("✅ 数据表检查并创建完毕(如不存在则自动创建)")
84
+
85
+ def drop_tables(self):
86
+ """
87
+ 删除数据表
88
+ """
89
+ Base.metadata.drop_all(self.engine)
90
+ print("数据表删除成功!")
91
+
92
+ def get_session(self):
93
+ """
94
+ 创建会话
95
+
96
+ Returns:
97
+ session: SQLAlchemy会话对象
98
+ """
99
+ return self.Session()
100
+
101
+ def get_all_records(self):
102
+ """
103
+ 查询所有记录
104
+
105
+ Returns:
106
+ list: 所有销售明细记录列表
107
+ """
108
+ session = self.get_session()
109
+ try:
110
+ records = session.query(SheinStoreSalesDetail).all()
111
+ return records
112
+ finally:
113
+ session.close()
114
+
115
+ def get_records_by_username_and_day(self, username, query_day):
116
+ """
117
+ 查询特定用户名和日期的记录
118
+
119
+ Args:
120
+ username (str): 店铺用户名
121
+ query_day (date): 查询日期
122
+
123
+ Returns:
124
+ list: 符合条件的记录列表
125
+ """
126
+ session = self.get_session()
127
+ try:
128
+ records = session.query(SheinStoreSalesDetail).filter(
129
+ SheinStoreSalesDetail.store_username == username,
130
+ SheinStoreSalesDetail.day == query_day
131
+ ).all()
132
+ return records
133
+ finally:
134
+ session.close()
135
+
136
+ def get_records_sorted_by_sales_amount(self):
137
+ """
138
+ 查询记录并按销售金额排序
139
+
140
+ Returns:
141
+ list: 按销售金额倒序排列的记录列表
142
+ """
143
+ session = self.get_session()
144
+ try:
145
+ records = session.query(SheinStoreSalesDetail).order_by(SheinStoreSalesDetail.sales_amount.desc()).all()
146
+ return records
147
+ finally:
148
+ session.close()
149
+
150
+ def get_limited_records(self, limit):
151
+ """
152
+ 查询并限制结果数量
153
+
154
+ Args:
155
+ limit (int): 限制的记录数量
156
+
157
+ Returns:
158
+ list: 限制数量的记录列表
159
+ """
160
+ session = self.get_session()
161
+ try:
162
+ records = session.query(SheinStoreSalesDetail).limit(limit).all()
163
+ return records
164
+ finally:
165
+ session.close()
166
+
167
+ def get_records_by_usernames(self, usernames):
168
+ """
169
+ 查询多个店铺用户名的记录
170
+
171
+ Args:
172
+ usernames (list): 店铺用户名列表
173
+
174
+ Returns:
175
+ list: 符合条件的记录列表
176
+ """
177
+ session = self.get_session()
178
+ try:
179
+ records = session.query(SheinStoreSalesDetail).filter(
180
+ SheinStoreSalesDetail.store_username.in_(usernames)
181
+ ).all()
182
+ return records
183
+ finally:
184
+ session.close()
185
+
186
+ def record_to_dict(self, record):
187
+ """
188
+ 将记录转换为字典
189
+
190
+ Args:
191
+ record: SQLAlchemy记录对象
192
+
193
+ Returns:
194
+ dict: 记录字典
195
+ """
196
+ return {column.name: getattr(record, column.name) for column in record.__table__.columns}
197
+
198
+ def get_records_by_condition(self, filter_condition, order_by=None):
199
+ """
200
+ 公共查询函数(包含排序功能)
201
+
202
+ Args:
203
+ filter_condition: SQLAlchemy过滤条件
204
+ order_by: 排序条件
205
+
206
+ Returns:
207
+ list: 符合条件的记录列表
208
+ """
209
+ session = self.get_session()
210
+ try:
211
+ # 执行查询,添加排序功能
212
+ query = session.query(SheinStoreSalesDetail).filter(filter_condition)
213
+
214
+ # 如果传入了排序条件,则加入排序
215
+ if order_by is not None:
216
+ query = query.order_by(order_by)
217
+
218
+ # 获取查询结果
219
+ records = query.all()
220
+ return records
221
+
222
+ except Exception as e:
223
+ import traceback
224
+ print("数据库错误:", e)
225
+ traceback.print_exc()
226
+ return []
227
+
228
+ finally:
229
+ # 确保会话关闭
230
+ session.close()
231
+
232
+ def get_distinct_store_sales_list(self):
233
+ """
234
+ 获取 distinct 的 store_username 和 store_name
235
+
236
+ Returns:
237
+ list: 唯一店铺用户名列表
238
+ """
239
+ session = self.get_session()
240
+ try:
241
+ # 执行 distinct 查询,选择唯一的 store_username 和 store_name
242
+ records = session.query(
243
+ distinct(SheinStoreSalesDetail.store_username)
244
+ ).all()
245
+
246
+ # 转换查询结果为二维列表格式
247
+ result = [[record[0], ''] for record in records]
248
+ return result
249
+
250
+ finally:
251
+ # 确保会话关闭
252
+ session.close()
253
+
254
+ def get_one_day_records(self, yesterday, order_by=None):
255
+ """
256
+ 获取单日数据
257
+
258
+ Args:
259
+ yesterday (str): 日期字符串,格式:'YYYY-MM-DD'
260
+ order_by: 排序条件,默认按销售数量倒序
261
+
262
+ Returns:
263
+ list: 指定日期的记录列表
264
+ """
265
+ if order_by is None:
266
+ order_by = SheinStoreSalesDetail.sales_num.desc()
267
+
268
+ # 将字符串日期转换为 datetime.date 对象
269
+ yesterday_date = datetime.strptime(yesterday, '%Y-%m-%d').date()
270
+
271
+ # 定义查询条件
272
+ filter_condition = SheinStoreSalesDetail.day == yesterday_date
273
+
274
+ # 使用公共查询函数,传入排序条件
275
+ return self.get_records_by_condition(filter_condition, order_by)
276
+
277
+ def get_one_month_records(self, year, month, username, order_by=None):
278
+ """
279
+ 获取某年某月的数据
280
+
281
+ Args:
282
+ year (int): 年份
283
+ month (int): 月份
284
+ username (str): 店铺用户名
285
+ order_by: 排序条件,默认按日期升序
286
+
287
+ Returns:
288
+ list: 指定月份的记录列表
289
+ """
290
+ if order_by is None:
291
+ order_by = SheinStoreSalesDetail.day.asc()
292
+
293
+ # 将年份和月份格式化为日期(构造该月的第一天)
294
+ first_day_of_month = datetime(year, month, 1).date()
295
+
296
+ # 获取该月的最后一天
297
+ last_day_of_month = (first_day_of_month.replace(day=28) + timedelta(days=4)).replace(day=1) - timedelta(days=1)
298
+
299
+ # 定义查询条件:查询指定年份和月份的记录
300
+ filter_condition = and_(
301
+ SheinStoreSalesDetail.day >= first_day_of_month,
302
+ SheinStoreSalesDetail.day <= last_day_of_month,
303
+ SheinStoreSalesDetail.store_username == username,
304
+ )
305
+
306
+ # 使用公共查询函数,传入排序条件
307
+ return self.get_records_by_condition(filter_condition, order_by)
308
+
309
+ def get_records_as_dict(self, username, query_day_str):
310
+ """
311
+ 获取指定用户名和日期的记录并转换为字典
312
+
313
+ Args:
314
+ username (str): 店铺用户名
315
+ query_day_str (str): 查询日期字符串,格式:'YYYY-MM-DD'
316
+
317
+ Returns:
318
+ dict: 记录字典,如果没有记录则返回空字典
319
+ """
320
+ # 将字符串日期转换为 datetime.date 对象
321
+ query_day = datetime.strptime(query_day_str, '%Y-%m-%d').date()
322
+ # 获取数据库会话
323
+ session = self.get_session()
324
+ try:
325
+ # 查询特定条件的记录
326
+ records = session.query(SheinStoreSalesDetail).filter(
327
+ SheinStoreSalesDetail.store_username == username,
328
+ SheinStoreSalesDetail.day == query_day
329
+ ).all()
330
+ # 如果没有记录,返回一个空字典
331
+ if not records:
332
+ return {}
333
+ # 将记录转换为字典
334
+ records_dict = [
335
+ {column.name: getattr(record, column.name) for column in SheinStoreSalesDetail.__table__.columns}
336
+ for record in records
337
+ ]
338
+
339
+ return records_dict[0]
340
+
341
+ finally:
342
+ session.close()
343
+
344
+ def insert_data(self, data_list):
345
+ """
346
+ 插入多条数据
347
+
348
+ Args:
349
+ data_list (list): 要插入的数据列表,每个元素为字典格式
350
+ """
351
+ # 获取会话
352
+ session = self.get_session()
353
+ try:
354
+ # 将字典转换为模型对象并插入
355
+ records = []
356
+ for data in data_list:
357
+ record = SheinStoreSalesDetail(**data) # 将字典解包为关键字参数
358
+ records.append(record)
359
+ # 将所有记录添加到会话
360
+ session.add_all(records)
361
+ # 提交事务
362
+ session.commit()
363
+ print(f"成功插入 {len(records)} 条数据")
364
+ except Exception as e:
365
+ # 如果发生错误,回滚事务
366
+ session.rollback()
367
+ print(f"插入数据时发生错误: {e}")
368
+ raise
369
+ finally:
370
+ # 关闭会话
371
+ session.close()
372
+
373
+ if __name__ == '__main__':
374
+ # 新的管理器方式示例(取消注释以运行测试)
375
+ pass
qrpa/shein_excel.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from .fun_excel import *
2
2
  from .fun_base import log
3
- from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex
3
+ from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex, delete_file
4
4
  from .time_utils import TimeUtils
5
+ from .wxwork import WxWorkBot
6
+ from .shein_daily_report_model import SheinStoreSalesDetailManager, SheinStoreSalesDetail
5
7
 
6
8
  class SheinExcel:
7
9
 
@@ -9,6 +11,296 @@ class SheinExcel:
9
11
  self.config = config
10
12
  pass
11
13
 
14
+ def write_sales_data(self):
15
+ yesterday = TimeUtils.get_yesterday()
16
+ model = SheinStoreSalesDetailManager(self.config.database_url)
17
+ records = model.get_one_day_records(yesterday, SheinStoreSalesDetail.sales_amount.desc())
18
+ data_day = []
19
+ dict_store_manager_shein = self.config.shein_store_manager
20
+ dict_store_name = read_dict_from_file(self.config.shein_store_alias)
21
+ for record in records:
22
+ store_data = []
23
+ # store_data.append(record.store_name)
24
+ store_data.append(dict_store_name.get(record.store_username))
25
+ store_data.append(dict_store_manager_shein.get(str(record.store_username).lower(), '-'))
26
+ # log(dict_store_manager_shein.get(str(record.store_username).lower(),'-'))
27
+ store_data.append(record.sales_num)
28
+ store_data.append(record.sales_num_inc)
29
+ store_data.append(record.sales_amount)
30
+ store_data.append(record.sales_amount_inc)
31
+ store_data.append(record.visitor_num)
32
+ store_data.append(record.visitor_num_inc)
33
+ store_data.append(record.bak_A_num)
34
+ store_data.append(record.bak_A_num_inc)
35
+ store_data.append(record.new_A_num)
36
+ store_data.append(record.new_A_num_inc)
37
+ store_data.append(record.on_sales_product_num)
38
+ store_data.append(record.on_sales_product_num_inc)
39
+ store_data.append(record.wait_shelf_product_num)
40
+ store_data.append(record.wait_shelf_product_num_inc)
41
+ store_data.append(record.upload_product_num)
42
+ store_data.append(record.upload_product_num_inc)
43
+ store_data.append(record.sold_out_product_num)
44
+ store_data.append(record.shelf_off_product_num)
45
+ # store_data.append(record.remark) # 不要了
46
+ data_day.append(store_data)
47
+
48
+ excel_path = create_file_path(self.config.excel_daily_report)
49
+ delete_file(excel_path)
50
+ sheet_name_first = 'SHEIN销售部每日店铺情况'
51
+ # write_json_to_excel('excel_daily.json',excel_path,sheet_name_first)
52
+ app, wb, sheet = open_excel(excel_path, sheet_name_first)
53
+ delete_sheet_if_exists(wb, 'Sheet1')
54
+
55
+ las_row = len(data_day) + 4
56
+ log('len(las_row)', las_row)
57
+ sheet.range('B5').value = data_day
58
+ # sheet.range('3:3').api.WrapText = True
59
+ sheet.range(f'A5:U{las_row}').api.Font.Color = 0x000000 # 红色
60
+ sheet.range(f'A5:U{las_row}').api.Font.Bold = False
61
+ sheet.range('A1').value = f'销售部SHEIN{TimeUtils.get_current_month()}月店铺数据'
62
+ sheet.range('A4').value = f'{TimeUtils.format_date_cross_platform(yesterday)}\n({TimeUtils.get_chinese_weekday(yesterday)})'
63
+ sheet.range('A4').column_width = 16
64
+ sheet.range('A4').api.VerticalAlignment = -4160 # 垂直顶部对齐
65
+ sheet.range(f'A4:A{las_row}').merge()
66
+
67
+ # 获取列数据范围
68
+ column_range = sheet.range(f'E5:E{las_row}')
69
+ # 遍历列中的每个单元格
70
+ for cell in column_range:
71
+ # 检查单元格是否有值且为负数
72
+ if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
73
+ # 设置字体颜色为红色
74
+ cell.font.color = (255, 0, 0) # RGB值表示红色
75
+
76
+ # 获取列数据范围
77
+ column_range = sheet.range(f'G5:G{las_row}')
78
+ # 遍历列中的每个单元格
79
+ for cell in column_range:
80
+ # 检查单元格是否有值且为负数
81
+ if cell.value is not None and cell.value < 0:
82
+ # 设置字体颜色为红色
83
+ cell.font.color = (255, 0, 0) # RGB值表示红色
84
+
85
+ # 获取列数据范围
86
+ column_range = sheet.range(f'I5:I{las_row}')
87
+ # 遍历列中的每个单元格
88
+ for cell in column_range:
89
+ # 检查单元格是否有值且为负数
90
+ if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
91
+ # 设置字体颜色为红色
92
+ cell.font.color = (255, 0, 0) # RGB值表示红色
93
+
94
+ # 获取列数据范围
95
+ column_range = sheet.range(f'K5:K{las_row}')
96
+ # 遍历列中的每个单元格
97
+ for cell in column_range:
98
+ # 检查单元格是否有值且为负数
99
+ if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
100
+ # 设置字体颜色为红色
101
+ cell.font.color = (255, 0, 0) # RGB值表示红色
102
+
103
+ # 处理表头
104
+ # 处理第一行
105
+ rangeOne = f'A1:U1'
106
+ sheet.range(rangeOne).merge()
107
+ sheet.range(rangeOne).api.Font.Size = 24
108
+ sheet.range(rangeOne).api.Font.Bold = True
109
+ sheet.range(rangeOne).api.HorizontalAlignment = -4108
110
+ sheet.range(rangeOne).api.VerticalAlignment = -4108
111
+
112
+ # 处理第二行
113
+ rangeTwo_part_1 = f'A2:C2'
114
+ rangeTwo_part_2 = f'D2:O2'
115
+ rangeTwo_part_3 = f'P2:U2'
116
+ sheet.range(rangeTwo_part_1).merge()
117
+ sheet.range(rangeTwo_part_2).merge()
118
+ sheet.range(rangeTwo_part_3).merge()
119
+
120
+ sheet.range(f'A2:C3').color = 0x47a100
121
+
122
+ sheet.range('D2').value = f'店铺的结果和稳定性'
123
+ sheet.range(rangeTwo_part_2).api.Font.Size = 16
124
+ sheet.range(rangeTwo_part_2).api.Font.Color = 0xFFFFFF
125
+ sheet.range(rangeTwo_part_2).api.Font.Bold = True
126
+ sheet.range(rangeTwo_part_2).api.HorizontalAlignment = -4108
127
+ sheet.range(rangeTwo_part_2).api.VerticalAlignment = -4108
128
+ sheet.range(f'D2:O3').color = 0x0000FF
129
+
130
+ sheet.range('P2').value = f'上新的质量和数量'
131
+ sheet.range(rangeTwo_part_3).api.Font.Size = 16
132
+ sheet.range(rangeTwo_part_3).api.Font.Color = 0xFFFFFF
133
+ sheet.range(rangeTwo_part_3).api.Font.Bold = True
134
+ sheet.range(rangeTwo_part_3).api.HorizontalAlignment = -4108
135
+ sheet.range(rangeTwo_part_3).api.VerticalAlignment = -4108
136
+ sheet.range(f'P2:U3').color = 0x47a100
137
+
138
+ # 处理第三行
139
+ rangeThree = f'A3:U3'
140
+ sheet.range('A3').value = ['日期', '店铺', '店长', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客',
141
+ '对比前天', '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
142
+ '对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
143
+ sheet.range(rangeThree).api.Font.Size = 11
144
+ sheet.range(rangeThree).api.Font.Color = 0xFFFFFF
145
+ sheet.range(rangeThree).api.Font.Bold = True
146
+ sheet.range(rangeThree).api.HorizontalAlignment = -4108
147
+ sheet.range(rangeThree).api.VerticalAlignment = -4108
148
+
149
+ # 处理第4行
150
+ rangeFour = f'B4:U4'
151
+ sheet.range('B4').value = '汇总'
152
+ sheet.range('C4').value = '-'
153
+ sheet.range(rangeFour).api.Font.Size = 11
154
+ sheet.range(rangeThree).api.HorizontalAlignment = -4108
155
+ sheet.range(rangeThree).api.VerticalAlignment = -4108
156
+ sheet.range(f'B4:U4').color = 0x50d092
157
+
158
+ for col in range(2, 22): # 对应 C 列到 J 列
159
+ col_letter = xw.utils.col_name(col)
160
+ if col_letter not in ['A', 'B', 'C']:
161
+ sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}{las_row})'
162
+ # 所有列水平居中和垂直居中
163
+ sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
164
+ sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
165
+ sheet.autofit()
166
+
167
+ range_to_border = sheet.range(f'A2:U{las_row}') # 定义范围
168
+ # 设置外部边框(所有边都为实线)
169
+ range_to_border.api.Borders(7).LineStyle = 1 # 上边框
170
+ range_to_border.api.Borders(8).LineStyle = 1 # 下边框
171
+ range_to_border.api.Borders(9).LineStyle = 1 # 左边框
172
+ range_to_border.api.Borders(10).LineStyle = 1 # 右边框
173
+ # 设置内部边框
174
+ range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
175
+ range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
176
+ range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
177
+ range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
178
+
179
+ wb.save()
180
+ close_excel(app, wb)
181
+
182
+ store_list = model.get_distinct_store_sales_list()
183
+ for store in store_list:
184
+ store_username = store[0]
185
+ store_name = dict_store_name.get(store_username)
186
+ records = model.get_one_month_records(TimeUtils.get_current_year(), TimeUtils.get_current_month(), store_username)
187
+ data_month = []
188
+ for record in records:
189
+ store_data = []
190
+ store_data.append(record.day)
191
+ store_data.append(record.sales_num)
192
+ store_data.append(record.sales_num_inc)
193
+ store_data.append(record.sales_amount)
194
+ store_data.append(record.sales_amount_inc)
195
+ store_data.append(record.visitor_num)
196
+ store_data.append(record.visitor_num_inc)
197
+ store_data.append(record.bak_A_num)
198
+ store_data.append(record.bak_A_num_inc)
199
+ store_data.append(record.new_A_num)
200
+ store_data.append(record.new_A_num_inc)
201
+ store_data.append(record.on_sales_product_num)
202
+ store_data.append(record.on_sales_product_num_inc)
203
+ store_data.append(record.wait_shelf_product_num)
204
+ store_data.append(record.wait_shelf_product_num_inc)
205
+ store_data.append(record.upload_product_num)
206
+ store_data.append(record.upload_product_num_inc)
207
+ store_data.append(record.sold_out_product_num)
208
+ store_data.append(record.shelf_off_product_num)
209
+ store_data.append(record.remark)
210
+ data_month.append(store_data)
211
+
212
+ excel_path = create_file_path(self.config.excel_daily_report)
213
+ sheet_name = store_name
214
+ # write_json_to_excel('excel_month.json',excel_path,sheet_name)
215
+ las_row = len(data_month) + 4
216
+
217
+ app, wb, sheet = open_excel(excel_path, sheet_name)
218
+
219
+ # 处理表头
220
+ # 处理第一行
221
+ rangeOne = f'A1:S1'
222
+ sheet.range(rangeOne).merge()
223
+ sheet.range(rangeOne).api.Font.Size = 24
224
+ sheet.range(rangeOne).api.Font.Bold = True
225
+ sheet.range(rangeOne).api.HorizontalAlignment = -4108
226
+ sheet.range(rangeOne).api.VerticalAlignment = -4108
227
+
228
+ # 处理第二行
229
+ rangeTwo_part_1 = f'A2'
230
+ rangeTwo_part_2 = f'B2:M2'
231
+ rangeTwo_part_3 = f'N2:S2'
232
+ sheet.range(rangeTwo_part_2).merge()
233
+ sheet.range(rangeTwo_part_3).merge()
234
+
235
+ sheet.range(f'A2:A3').color = 0x47a100
236
+
237
+ sheet.range('B2').value = f'店铺的结果和稳定性'
238
+ sheet.range(rangeTwo_part_2).api.Font.Size = 16
239
+ sheet.range(rangeTwo_part_2).api.Font.Color = 0xFFFFFF
240
+ sheet.range(rangeTwo_part_2).api.Font.Bold = True
241
+ sheet.range(rangeTwo_part_2).api.HorizontalAlignment = -4108
242
+ sheet.range(rangeTwo_part_2).api.VerticalAlignment = -4108
243
+ sheet.range(f'B2:M3').color = 0x0000FF
244
+
245
+ sheet.range('N2').value = f'上新的质量和数量'
246
+ sheet.range(rangeTwo_part_3).api.Font.Size = 16
247
+ sheet.range(rangeTwo_part_3).api.Font.Color = 0xFFFFFF
248
+ sheet.range(rangeTwo_part_3).api.Font.Bold = True
249
+ sheet.range(rangeTwo_part_3).api.HorizontalAlignment = -4108
250
+ sheet.range(rangeTwo_part_3).api.VerticalAlignment = -4108
251
+ sheet.range(f'N2:S3').color = 0x47a100
252
+
253
+ # 处理第三行
254
+ rangeThree = f'A3:S3'
255
+ sheet.range('A3').value = ['日期', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客', '对比前天',
256
+ '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
257
+ '对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
258
+ sheet.range(rangeThree).api.Font.Size = 11
259
+ sheet.range(rangeThree).api.Font.Color = 0xFFFFFF
260
+ sheet.range(rangeThree).api.Font.Bold = True
261
+ sheet.range(rangeThree).api.HorizontalAlignment = -4108
262
+ sheet.range(rangeThree).api.VerticalAlignment = -4108
263
+
264
+ # 处理第4行
265
+ rangeFour = f'A4:S4'
266
+ sheet.range('A4').value = '汇总'
267
+ sheet.range(rangeFour).api.Font.Size = 11
268
+ sheet.range(rangeThree).api.HorizontalAlignment = -4108
269
+ sheet.range(rangeThree).api.VerticalAlignment = -4108
270
+ sheet.range(f'A4:S4').color = 0x50d092
271
+
272
+ # 原逻辑
273
+ sheet.range('A5').value = data_month
274
+ # sheet.range('3:3').api.WrapText = True
275
+ sheet.range('A4:T35').api.Font.Color = 0x000000
276
+ sheet.range('A4:T35').api.Font.Bold = False
277
+ sheet.range('A1').value = f'{store_name}SHEIN{TimeUtils.get_current_month()}月店铺数据'
278
+
279
+ range_to_border = sheet.range(f'A2:S{las_row}') # 定义范围
280
+ # 设置外部边框(所有边都为实线)
281
+ range_to_border.api.Borders(7).LineStyle = 1 # 上边框
282
+ range_to_border.api.Borders(8).LineStyle = 1 # 下边框
283
+ range_to_border.api.Borders(9).LineStyle = 1 # 左边框
284
+ range_to_border.api.Borders(10).LineStyle = 1 # 右边框
285
+ # 设置内部边框
286
+ range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
287
+ range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
288
+ range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
289
+ range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
290
+ wb.save()
291
+
292
+ for col in range(2, 20): # 对应 C 列到 J 列
293
+ col_letter = xw.utils.col_name(col)
294
+ # 所有列水平居中和垂直居中
295
+ sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
296
+ sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
297
+ sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}36)'
298
+
299
+ if sheet_name_first in [sheet.name for sheet in wb.sheets]:
300
+ wb.sheets[sheet_name_first].activate()
301
+ wb.save()
302
+ close_excel(app, wb)
303
+
12
304
  def format_bak_advice(self, excel_path, sheet_name, mode):
13
305
  app, wb, sheet = open_excel(excel_path, sheet_name)
14
306
  beautify_title(sheet)
@@ -30,6 +322,8 @@ class SheinExcel:
30
322
  InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
31
323
  wb.save()
32
324
  close_excel(app, wb)
325
+ if mode == 4:
326
+ WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
33
327
 
34
328
  def write_bak_advice(self, mode_list):
35
329
  excel_path_list = [
@@ -159,6 +453,7 @@ class SheinExcel:
159
453
  InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
160
454
  wb.save()
161
455
  close_excel(app, wb)
456
+ WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
162
457
 
163
458
  def write_week_report(self):
164
459
  excel_path = create_file_path(self.config.excel_week_sales_report)
@@ -188,7 +483,7 @@ class SheinExcel:
188
483
  format_to_percent(sheet, ['支付率', '点击率', '毛利率'])
189
484
  self.dealFormula(sheet) # 有空再封装优化
190
485
  colorize_by_field(app, wb, sheet, 'SPU')
191
- autofit_column(sheet, ['商品信息','店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
486
+ autofit_column(sheet, ['商品信息', '店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
192
487
  column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
193
488
  specify_column_width(sheet, ['商品标题'], 150 / 6)
194
489
  add_borders(sheet)
qrpa/shein_lib.py CHANGED
@@ -11,6 +11,8 @@ import json
11
11
  from datetime import datetime
12
12
  from playwright.sync_api import Page
13
13
 
14
+ from .shein_daily_report_model import SheinStoreSalesDetailManager
15
+
14
16
  class SheinLib:
15
17
 
16
18
  def __init__(self, config, bridge, web_page: Page, store_username, store_name):
@@ -80,6 +82,318 @@ class SheinLib:
80
82
  # web_page.goto('https://sso.geiwohuo.com')
81
83
  log('鉴权处理结束')
82
84
 
85
+ # 已上架备货款A数量
86
+ def get_product_bak_A_count(self):
87
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
88
+ payload = {
89
+ "pageNumber" : 1,
90
+ "pageSize" : 10,
91
+ "supplierCodes" : "",
92
+ "skcs" : "",
93
+ "spu" : "",
94
+ "c7dSaleCntBegin" : "",
95
+ "c7dSaleCntEnd" : "",
96
+ "goodsLevelIdList" : [
97
+ 61,
98
+ 90
99
+ ],
100
+ "supplyStatus" : "",
101
+ "shelfStatus" : 1,
102
+ "categoryIdList" : [],
103
+ "skcStockBegin" : "",
104
+ "skcStockEnd" : "",
105
+ "skuStockBegin" : "",
106
+ "skuStockEnd" : "",
107
+ "skcSaleDaysBegin" : "",
108
+ "skcSaleDaysEnd" : "",
109
+ "skuSaleDaysBegin" : "",
110
+ "skuSaleDaysEnd" : "",
111
+ "planUrgentCountBegin" : "",
112
+ "planUrgentCountEnd" : "",
113
+ "skcAvailableOrderBegin": "",
114
+ "skcAvailableOrderEnd" : "",
115
+ "skuAvailableOrderBegin": "",
116
+ "skuAvailableOrderEnd" : "",
117
+ "shelfDateBegin" : "",
118
+ "shelfDateEnd" : "",
119
+ "stockWarnStatusList" : [],
120
+ "labelFakeIdList" : [],
121
+ "sheinSaleByInventory" : "",
122
+ "tspIdList" : [],
123
+ "adviceStatus" : [],
124
+ "sortBy7dSaleCnt" : 2,
125
+ "goodsLevelFakeIdList" : [
126
+ 3
127
+ ]
128
+ }
129
+ response_text = fetch(self.web_page, url, payload)
130
+ error_code = response_text.get('code')
131
+ if str(error_code) != '0':
132
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
133
+ info = response_text.get('info')
134
+ count = info.get('count', 0)
135
+ log('获取已上架备货款A数量', count)
136
+ return count
137
+
138
+ # 已上架备货款B数量
139
+ def get_product_bak_B_count(self):
140
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
141
+ payload = {
142
+ "pageNumber" : 1,
143
+ "pageSize" : 10,
144
+ "supplierCodes" : "",
145
+ "skcs" : "",
146
+ "spu" : "",
147
+ "c7dSaleCntBegin" : "",
148
+ "c7dSaleCntEnd" : "",
149
+ "goodsLevelIdList" : [
150
+ 62,
151
+ 227,
152
+ 12,
153
+ 230,
154
+ 80,
155
+ 58,
156
+ 224
157
+ ],
158
+ "supplyStatus" : "",
159
+ "shelfStatus" : 1,
160
+ "categoryIdList" : [],
161
+ "skcStockBegin" : "",
162
+ "skcStockEnd" : "",
163
+ "skuStockBegin" : "",
164
+ "skuStockEnd" : "",
165
+ "skcSaleDaysBegin" : None,
166
+ "skcSaleDaysEnd" : "",
167
+ "skuSaleDaysBegin" : "",
168
+ "skuSaleDaysEnd" : "",
169
+ "planUrgentCountBegin" : "",
170
+ "planUrgentCountEnd" : "",
171
+ "skcAvailableOrderBegin": "",
172
+ "skcAvailableOrderEnd" : "",
173
+ "skuAvailableOrderBegin": None,
174
+ "skuAvailableOrderEnd" : "",
175
+ "shelfDateBegin" : "",
176
+ "shelfDateEnd" : "",
177
+ "stockWarnStatusList" : [],
178
+ "labelFakeIdList" : [],
179
+ "sheinSaleByInventory" : "",
180
+ "tspIdList" : [],
181
+ "adviceStatus" : [],
182
+ "sortBy7dSaleCnt" : 2,
183
+ "goodsLevelFakeIdList" : [
184
+ 4
185
+ ]
186
+ }
187
+ response_text = fetch(self.web_page, url, payload)
188
+ error_code = response_text.get('code')
189
+ if str(error_code) != '0':
190
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
191
+ info = response_text.get('info')
192
+ count = info.get('count', 0)
193
+ log('获取已上架备货款B数量', count)
194
+ return count
195
+
196
+ # 已上架新款A数量
197
+ def get_product_A_count(self):
198
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
199
+ payload = {
200
+ "pageNumber" : 1,
201
+ "pageSize" : 10,
202
+ "supplierCodes" : "",
203
+ "skcs" : "",
204
+ "spu" : "",
205
+ "c7dSaleCntBegin" : "",
206
+ "c7dSaleCntEnd" : "",
207
+ "goodsLevelIdList" : [
208
+ 107
209
+ ],
210
+ "supplyStatus" : "",
211
+ "shelfStatus" : 1,
212
+ "categoryIdList" : [],
213
+ "skcStockBegin" : "",
214
+ "skcStockEnd" : "",
215
+ "skuStockBegin" : "",
216
+ "skuStockEnd" : "",
217
+ "skcSaleDaysBegin" : None,
218
+ "skcSaleDaysEnd" : "",
219
+ "skuSaleDaysBegin" : "",
220
+ "skuSaleDaysEnd" : "",
221
+ "planUrgentCountBegin" : "",
222
+ "planUrgentCountEnd" : "",
223
+ "skcAvailableOrderBegin": "",
224
+ "skcAvailableOrderEnd" : "",
225
+ "skuAvailableOrderBegin": None,
226
+ "skuAvailableOrderEnd" : "",
227
+ "shelfDateBegin" : "",
228
+ "shelfDateEnd" : "",
229
+ "stockWarnStatusList" : [],
230
+ "labelFakeIdList" : [],
231
+ "sheinSaleByInventory" : "",
232
+ "tspIdList" : [],
233
+ "adviceStatus" : [],
234
+ "sortBy7dSaleCnt" : 2,
235
+ "goodsLevelFakeIdList" : [
236
+ 2
237
+ ]
238
+ }
239
+ response_text = fetch(self.web_page, url, payload)
240
+ error_code = response_text.get('code')
241
+ if str(error_code) != '0':
242
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
243
+ info = response_text.get('info')
244
+ count = info.get('count', 0)
245
+ log('获取已上架新款A数量', count)
246
+ return count
247
+
248
+ # 本周已上架数量
249
+ def get_week_shelf_product_count(self, start_date, end_date):
250
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
251
+ payload = {
252
+ "pageNumber" : 1,
253
+ "pageSize" : 10,
254
+ "supplierCodes" : "",
255
+ "skcs" : "",
256
+ "spu" : "",
257
+ "c7dSaleCntBegin" : "",
258
+ "c7dSaleCntEnd" : "",
259
+ "goodsLevelIdList" : [],
260
+ "supplyStatus" : "",
261
+ "shelfStatus" : 1,
262
+ "categoryIdList" : [],
263
+ "skcStockBegin" : "",
264
+ "skcStockEnd" : "",
265
+ "skuStockBegin" : "",
266
+ "skuStockEnd" : "",
267
+ "skcSaleDaysBegin" : "",
268
+ "skcSaleDaysEnd" : "",
269
+ "skuSaleDaysBegin" : "",
270
+ "skuSaleDaysEnd" : "",
271
+ "planUrgentCountBegin" : "",
272
+ "planUrgentCountEnd" : "",
273
+ "skcAvailableOrderBegin": "",
274
+ "skcAvailableOrderEnd" : "",
275
+ "skuAvailableOrderBegin": "",
276
+ "skuAvailableOrderEnd" : "",
277
+ "shelfDateBegin" : start_date,
278
+ "shelfDateEnd" : end_date,
279
+ "stockWarnStatusList" : [],
280
+ "labelFakeIdList" : [],
281
+ "sheinSaleByInventory" : "",
282
+ "tspIdList" : [],
283
+ "adviceStatus" : [],
284
+ "sortBy7dSaleCnt" : 2
285
+ }
286
+ response_text = fetch(self.web_page, url, payload)
287
+ error_code = response_text.get('code')
288
+ if str(error_code) != '0':
289
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
290
+ info = response_text.get('info')
291
+ count = info.get('count', 0)
292
+ log('获取本周上架数量')
293
+ return count
294
+
295
+ # 已上架数量
296
+ def get_shelf_product_count(self):
297
+ log('获取所有已上架数量')
298
+ url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num=1&page_size=10"
299
+ payload = {
300
+ "language" : "zh-cn",
301
+ "only_recommend_resell" : False,
302
+ "only_spmb_copy_product": False,
303
+ "search_abandon_product": False,
304
+ "shelf_type" : "ON_SHELF",
305
+ "sort_type" : 1
306
+ }
307
+ response_text = fetch(self.web_page, url, payload)
308
+ error_code = response_text.get('code')
309
+ if str(error_code) != '0':
310
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
311
+
312
+ info = response_text.get('info')
313
+ customObj = info.get('meta').get('customObj')
314
+ # 将数据转换成字典
315
+ result = {item["shelf_status"]: item["count"] for item in customObj}
316
+ return result
317
+
318
+ def get_yesterday_upload_product_count(self, dt=None):
319
+ url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/publish/record/page_list?page_num=1&page_size=100"
320
+ payload = {
321
+ "edit_type" : 0,
322
+ "language" : "zh-cn",
323
+ "only_current_month_recommend": False,
324
+ "only_spmb_copy_product" : False,
325
+ "query_time_out" : False,
326
+ "search_diy_custom" : False
327
+ }
328
+ response_text = fetch(self.web_page, url, payload)
329
+ error_code = response_text.get('code')
330
+ if str(error_code) != '0':
331
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
332
+ info = response_text.get('info')
333
+ count = 0
334
+ for item in info.get('data', {}):
335
+ if TimeUtils.is_yesterday(item['create_time'], dt):
336
+ count += 1
337
+ log('获取昨日已上传数量', count)
338
+ return count
339
+
340
+ def get_week_sales_stat_detail(self):
341
+ global ListNotify, NotifyItem
342
+ dt = self.get_dt_time()
343
+ yesterday = TimeUtils.get_yesterday(dt)
344
+ date_7_days_ago = TimeUtils.get_past_nth_day(6, None, '%Y-%m-%d')
345
+ log('-7', date_7_days_ago)
346
+ date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y-%m-%d')
347
+ log('-1', date_1_days_ago)
348
+
349
+ url = "https://sso.geiwohuo.com/sbn/index/get_critical_indicator_curve_chart"
350
+ payload = {
351
+ "areaCd" : "cn",
352
+ "dt" : dt,
353
+ "countrySite": [
354
+ "shein-all"
355
+ ],
356
+ "startDate" : date_7_days_ago,
357
+ "endDate" : date_1_days_ago,
358
+ "queryType" : 1,
359
+ "pageNum" : 1,
360
+ "pageSize" : 100
361
+ }
362
+ response_text = fetch(self.web_page, url, payload)
363
+ error_code = response_text.get('code')
364
+ if str(error_code) != '0':
365
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
366
+ info = response_text.get('info', {})
367
+
368
+ last_item = SheinStoreSalesDetailManager(self.config.database_url).get_records_as_dict(self.store_username, yesterday)
369
+ log('last_item', last_item)
370
+ day_item = info[-1]
371
+ item = {}
372
+ item["store_username"] = self.store_username
373
+ item["store_name"] = self.store_name
374
+ item["day"] = day_item["dataDate"]
375
+ item["sales_num"] = day_item["saleCnt1d"]
376
+ item['sales_num_inc'] = item['sales_num'] - last_item.get('sales_num', 0)
377
+ item['sales_amount'] = day_item['dealAmt1d']
378
+ item['sales_amount_inc'] = item['sales_amount'] - float(last_item.get('sales_amount', 0))
379
+ item['visitor_num'] = day_item['idxShopGoodsUv1d']
380
+ item['visitor_num_inc'] = item['visitor_num'] - last_item.get('visitor_num', 0)
381
+ item['bak_A_num'] = self.get_product_bak_A_count()
382
+ item['bak_A_num_inc'] = item['bak_A_num'] - last_item.get('bak_A_num', 0)
383
+ item['new_A_num'] = self.get_product_A_count()
384
+ item['new_A_num_inc'] = item['new_A_num'] - last_item.get('new_A_num', 0)
385
+ dictProduct = self.get_shelf_product_count()
386
+ item['on_sales_product_num'] = dictProduct.get('ON_SHELF')
387
+ item['on_sales_product_num_inc'] = item['on_sales_product_num'] - last_item.get('on_sales_product_num', 0)
388
+ item['wait_shelf_product_num'] = dictProduct.get('WAIT_SHELF')
389
+ item['wait_shelf_product_num_inc'] = item['wait_shelf_product_num'] - last_item.get('wait_shelf_product_num', 0)
390
+ item['upload_product_num'] = self.get_yesterday_upload_product_count()
391
+ item['upload_product_num_inc'] = item['upload_product_num'] - last_item.get('upload_product_num', 0)
392
+ item['sold_out_product_num'] = dictProduct.get('SOLD_OUT')
393
+ item['shelf_off_product_num'] = dictProduct.get('OUT_SHELF')
394
+
395
+ SheinStoreSalesDetailManager(self.config.database_url).insert_data([item])
396
+
83
397
  def get_delivery_order_list(self, orderType=2):
84
398
  page_num = 1
85
399
  page_size = 200
qrpa/wxwork.py CHANGED
@@ -51,6 +51,9 @@ class WxWorkBot:
51
51
  raise Exception("upload_media error", err)
52
52
 
53
53
  def send_file(self, file_path):
54
+ if not os.path.exists(file_path):
55
+ print('文件不存在: ', file_path)
56
+ return
54
57
  """
55
58
  发送文件到群里
56
59
  :param file_path:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qrpa
3
- Version: 1.0.19
3
+ Version: 1.0.21
4
4
  Summary: qsir's rpa library
5
5
  Author: QSir
6
6
  Author-email: QSir <1171725650@qq.com>
@@ -6,14 +6,15 @@ qrpa/fun_excel.py,sha256=uaSNvJGA0vopBn0N__tVYpdeX4WlliOAQWP4sHanFUw,104030
6
6
  qrpa/fun_file.py,sha256=yzjDV16WL5vRys7J4uQcNzIFkX4D5MAlSCwxcD-mwQo,11966
7
7
  qrpa/fun_web.py,sha256=5QLQorAhRzMOGMRh4eCJ2UH8ZhVHvxkHwobWhmgU5qM,6286
8
8
  qrpa/fun_win.py,sha256=-LnTeocdTt72NVH6VgLdpAT9_C5oV9okeudXG6CftMA,8034
9
- qrpa/shein_excel.py,sha256=DWyZscdNpzfCFrHWHImMllHxonyW6cno7aQ0qxc3A6g,13646
10
- qrpa/shein_lib.py,sha256=zHT7gSaw1Qg6uLAbcK4addr6Xr65aU6fzn4qTLBklYg,65547
9
+ qrpa/shein_daily_report_model.py,sha256=C2ZE3XhLEZdV4Imdz7gE6756apSWyVku-hVKy4BOAwg,12588
10
+ qrpa/shein_excel.py,sha256=e0P6OABDEycwwCgpRLRHvsC1M1mh502sC1cArh9bfQc,29106
11
+ qrpa/shein_lib.py,sha256=IurTKa-K5Ay6DCOSv-cxiz1N6uvFKrxpxvREkua78FE,78696
11
12
  qrpa/shein_sqlite.py,sha256=ZQwD0Gz81q9WY7tY2HMEYvSF9r3N_G_Aur3bYfST9WY,5707
12
13
  qrpa/shein_ziniao.py,sha256=nSqqcEPh4nVQtUxUnIRzeZfTLyXywGPjPZn5pP-w57U,18309
13
14
  qrpa/time_utils.py,sha256=ef0hhbN_6b-gcnz5ETIVOoxemIMvcxGVGGIRnHnGaBo,29564
14
15
  qrpa/time_utils_example.py,sha256=shHOXKKF3QSzb0SHsNc34M61wEkkLuM30U9X1THKNS8,8053
15
- qrpa/wxwork.py,sha256=8LzRmoHYo0KBfZ1cmkWPj824Zp52NAUYm3RXYzmIIi0,10507
16
- qrpa-1.0.19.dist-info/METADATA,sha256=Z8-dB4M6VeS9bNuvHD28HyuZOJQF-z0HSXcDpYVf3lg,231
17
- qrpa-1.0.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- qrpa-1.0.19.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
19
- qrpa-1.0.19.dist-info/RECORD,,
16
+ qrpa/wxwork.py,sha256=zu6eQZHJEpwe4Q6vvwmBJaU31qm0bixII_XC9uJIrJc,10618
17
+ qrpa-1.0.21.dist-info/METADATA,sha256=r-VvSiexCLJNsr-esjT0QeZEJCadEC9lrioY4imRNog,231
18
+ qrpa-1.0.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ qrpa-1.0.21.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
20
+ qrpa-1.0.21.dist-info/RECORD,,
File without changes