mdbq 3.4.2__py3-none-any.whl → 3.4.4__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.
@@ -4,20 +4,36 @@ import os
4
4
  import re
5
5
  import socket
6
6
  import platform
7
+ import getpass
7
8
  import datetime
8
9
  import time
9
10
  from mdbq.config import myconfig
10
11
  from mdbq.mysql import mysql
11
12
  from mdbq.mysql import s_query
13
+ from mdbq.other import ua_sj
12
14
  import pandas as pd
13
15
  import numpy as np
14
16
  import plotly.express as px
15
17
  import plotly.graph_objects as go
16
18
  from plotly.subplots import make_subplots
17
19
  import tkinter as tk
20
+ import requests
21
+ from io import BytesIO
22
+ from PIL import Image
23
+ import base64
24
+ import matplotlib.pyplot as plt
18
25
 
19
26
  from sqlalchemy.sql.functions import count
20
27
 
28
+ if platform.system() == 'Windows':
29
+ D_PATH = os.path.join(f'C:\\Users\\{getpass.getuser()}\\Downloads')
30
+ elif platform.system() == 'Linux':
31
+ D_PATH = 'Downloads'
32
+ if not os.path.exists(D_PATH):
33
+ os.makedirs(D_PATH)
34
+ else:
35
+ D_PATH = os.path.join(f'/Users/{getpass.getuser()}/Downloads')
36
+
21
37
  m_engine = mysql.MysqlUpload(username='', password='', host='', port=0, charset='utf8mb4')
22
38
  company_engine = mysql.MysqlUpload(username='', password='', host='', port=0, charset='utf8mb4')
23
39
 
@@ -92,7 +108,7 @@ else:
92
108
 
93
109
  class DataShow:
94
110
  def __init__(self):
95
- self.path = '/Users/xigua/Downloads/html文件'
111
+ self.path = os.path.join(D_PATH, 'http_server')
96
112
  if not os.path.isdir(self.path):
97
113
  os.makedirs(self.path)
98
114
  root = tk.Tk()
@@ -105,8 +121,10 @@ class DataShow:
105
121
 
106
122
  def getdata(self, db_name, table_name, pro_list, start_date=None, end_date=None):
107
123
  download = s_query.QueryDatas(username=username, password=password, host=host, port=port)
108
- if not start_date or not end_date:
109
- start_date, end_date = '2000-01-01', '2099-12-31' # 从数据库提取数据,不能是 self.start_date
124
+ if not start_date:
125
+ start_date = '2000-01-01' # 从数据库提取数据,不能是 self.start_date
126
+ if not end_date:
127
+ end_date = self.today.strftime('%Y-%m-%d')
110
128
  projection = {}
111
129
  [projection.update({k: 1}) for k in pro_list]
112
130
  __res = []
@@ -122,14 +140,14 @@ class DataShow:
122
140
  df = pd.concat(__res, ignore_index=True)
123
141
  return df
124
142
 
125
- def pov_city(self, db_name='生意经3', filename='销售地域分布', start_date=None, end_date=None, percentage=None):
143
+ def pov_city(self, db_name='生意经3', filename='销售地域分布', start_date=None, end_date=None, percent=None):
126
144
  """
127
145
  生意经 省份城市销售分析
128
146
  """
129
147
  if not start_date:
130
148
  start_date = self.start_date
131
149
  if not end_date:
132
- end_date = self.end_date
150
+ end_date = self.today.strftime('%Y-%m-%d')
133
151
  pov_set = self.getdata(
134
152
  db_name='属性设置3',
135
153
  table_name=f'城市等级',
@@ -168,11 +186,27 @@ class DataShow:
168
186
  df_pov = df_pov.groupby(['店铺名称', '省份'], as_index=False).agg(
169
187
  **{'销售额': ('销售额', np.sum), '退款额': ('退款额', np.sum)})
170
188
  df_pov.drop_duplicates(subset='省份', keep='last', inplace=True, ignore_index=True)
189
+
190
+ # df_pov2: gmv 的饼图
191
+ df_pov['gmv销售'] = df_pov.apply(lambda x: x['销售额'] + x['退款额'], axis=1)
192
+ df_pov.sort_values(['gmv销售'], ascending=[False], ignore_index=True, inplace=True)
193
+ df_pov2 = df_pov.copy()
194
+ sales_sum = df_pov2['gmv销售'].sum()
195
+ df_pov2['省份'] = df_pov2.apply(lambda x: '其他' if (x['gmv销售'] / sales_sum) < percent else x['省份'], axis=1)
196
+
197
+ # df_pov3: 销售额的饼图
171
198
  df_pov.sort_values(['销售额'], ascending=[False], ignore_index=True, inplace=True)
172
- df_pov = df_pov[df_pov['省份'] != '其他']
173
- percentages = df_pov['销售额'] / df_pov['销售额'].sum() * 100
174
- df_pov1 = df_pov.head(10)
175
- data_list = [('销售 top省份', df_pov1['省份'].tolist(), df_pov1['销售额'].tolist(), percentages)]
199
+ df_pov3 = df_pov.copy()
200
+ sales_sum = df_pov3['销售额'].sum()
201
+ df_pov3['省份'] = df_pov3.apply(lambda x: '其他' if (x['销售额'] / sales_sum) < 0.016 else x['省份'], axis=1)
202
+
203
+ # df_pov1: 省份 销售额 堆叠柱形图
204
+ df_pov1 = df_pov.copy()
205
+ df_pov1 = df_pov1.head(15)
206
+ pov_sales_sum = df_pov1['销售额'].tolist()
207
+ pov_refunds = df_pov1['退款额'].tolist()
208
+ percentages = df_pov1['gmv销售'] / df_pov1['gmv销售'].sum() * 100
209
+ bar_list = [('省份销售/退款', df_pov1['省份'].tolist(), pov_sales_sum, percentages, pov_refunds)]
176
210
 
177
211
  # 将城市等级添加到df
178
212
  pov_set = pov_set[['城市等级', '城市']]
@@ -180,72 +214,93 @@ class DataShow:
180
214
  df_city = pd.merge(df_city, pov_set, left_on=['城市'], right_on=['城市'], how='left')
181
215
  df_level = df_city.groupby(['店铺名称', '城市等级'], as_index=False).agg(
182
216
  **{'销售额': ('销售额', np.sum), '退款额': ('退款额', np.sum)})
183
- city_level_list = [('按城市等级', df_level['城市等级'].tolist(), df_level['销售额'].tolist())]
217
+ pie_list = [
218
+ ('按城市等级', df_level['城市等级'].tolist(), df_level['销售额'].tolist()),
219
+ ('净销售 top省份', df_pov3['省份'].tolist(), df_pov3['销售额'].tolist()),
220
+ ('GMV top省份', df_pov2['省份'].tolist(), df_pov2['gmv销售'].tolist())
221
+ ]
222
+
223
+ # df_city1: 城市 销售额 堆叠柱形图
184
224
  df_city.drop_duplicates(subset='城市', keep='last', inplace=True, ignore_index=True)
225
+ df_city['gmv销售'] = df_city.apply(lambda x: x['销售额'] + x['退款额'], axis=1)
185
226
  df_city.sort_values(['销售额'], ascending=[False], ignore_index=True, inplace=True)
186
227
  df_city = df_city[df_city['城市'] != '其他']
187
- percentages = df_city['销售额'] / df_city['销售额'].sum() * 100
188
- df_city1 = df_city.head(10)
189
- data_list += [('销售 top城市', df_city1['城市'].tolist(), df_city1['销售额'].tolist(), percentages)]
190
-
191
- # 退款 top 城市
192
- df_city.sort_values(['退款额'], ascending=[False], ignore_index=True, inplace=True)
193
- percentages = df_city['退款额'] / df_city['退款额'].sum() * 100
194
- df_city2 = df_city.head(10)
195
- data_list += [('退款 top城市', df_city2['城市'].tolist(), df_city2['退款额'].tolist(), percentages)]
196
-
197
- t_p1 = [{"type": "pie"}]
198
- for i in range(2):
199
- t_p1.extend([{"type": "bar"}]) # 折线图类型
228
+ percentages = df_city['gmv销售'] / df_city['gmv销售'].sum() * 100
229
+ df_city1 = df_city.head(15)
230
+ city_sales_sum = df_city1['销售额'].tolist()
231
+ city_refunds = df_city1['退款额'].tolist()
232
+ bar_list += [('城市销售/退款', df_city1['城市'].tolist(), city_sales_sum, percentages, city_refunds)]
233
+
234
+ t_p1 = []
235
+ for i in range(3):
236
+ t_p1.extend([{"type": "pie"}])
200
237
  t_p2 = []
201
238
  for i in range(3):
202
- t_p2.extend([{"type": "bar"}]) # 饼图类型
239
+ t_p2.extend([{"type": "bar"}])
203
240
  specs = [t_p1, t_p2]
204
241
  fig = make_subplots(rows=2, cols=3, specs=specs)
205
- title, labels, values = city_level_list[0]
206
- # 添加饼图
207
- fig.add_trace(
208
- go.Pie(
209
- labels=labels,
210
- values=values,
211
- name=title,
212
- textinfo='label+percent'
213
- ),
214
- row=1,
215
- col=1,
216
- )
217
- x = 0.14
218
- y = 1
219
- fig.add_annotation(
220
- text=title,
221
- x=x,
222
- y=y,
223
- xref='paper', # # 相对于整个图表区域
224
- yref='paper',
225
- showarrow=True, # 显示箭头
226
- align="left", # 文本对齐方式
227
- font=dict(size=14),
228
- )
229
- row = 1
230
- col = 1
231
- for item in data_list:
232
- title, labels, values, percentages = item
233
- bar = go.Bar(
234
- x=labels,
235
- y=values,
236
- name=title,
237
- orientation='v', # 垂直柱形图
238
- text=percentages.map('{:.2f}%'.format), # 设置要显示的文本(百分比)
239
- textposition = 'outside', # 设置文本位置在柱形图外部
240
- width=0.55 # 调整柱子最大宽度
241
- )
242
- fig.add_trace(
243
- bar,
244
- row=row // 3 + 1,
245
- col=col % 3 + 1,
246
- )
242
+
243
+ row = 0
244
+ col = 0
245
+ for i in range(6):
246
+ if row // 3 == 0:
247
+ try:
248
+ title, labels, values = pie_list[col % 3]
249
+ except:
250
+ row += 1
251
+ col += 1
252
+ continue
253
+ # 添加饼图
254
+ fig.add_trace(
255
+ go.Pie(
256
+ labels=labels,
257
+ values=values,
258
+ name=title,
259
+ textinfo='label+percent'
260
+ ),
261
+ row=row//3 + 1,
262
+ col=col % 3 + 1,
263
+ )
264
+ else:
265
+ try:
266
+ title, labels, values, percentages, refunds = bar_list[col % 3]
267
+ except:
268
+ row += 1
269
+ col += 1
270
+ continue
271
+ bar = go.Bar(
272
+ x=labels,
273
+ y=values,
274
+ name='销售额',
275
+ orientation='v', # 垂直柱形图
276
+ # text=percentages.map('{:.1f}%'.format), # 设置要显示的文本(百分比)
277
+ # textposition = 'outside', # 设置文本位置在柱形图外部
278
+ width=0.55, # 调整柱子最大宽度
279
+ # marker_color='blue',
280
+ )
281
+ fig.add_trace(
282
+ bar,
283
+ row=row // 3 + 1,
284
+ col=col % 3 + 1,
285
+ )
286
+ bar = go.Bar(
287
+ x=labels,
288
+ y=refunds,
289
+ name='退款额',
290
+ orientation='v', # 垂直柱形图
291
+ text=percentages.map('{:.1f}%'.format), # 设置要显示的文本(百分比)
292
+ textposition='outside', # 设置文本位置在柱形图外部
293
+ width=0.55, # 调整柱子最大宽度
294
+ # marker_color = 'red',
295
+ )
296
+ fig.add_trace(
297
+ bar,
298
+ row=row // 3 + 1,
299
+ col=col % 3 + 1,
300
+ )
301
+
247
302
  x = 0.14 + 0.355 * (row % 3)
248
- y = 1 - 0.575 * (row // 3)
303
+ y = 0.99 - 0.58 * (row // 3)
249
304
  fig.add_annotation(
250
305
  text=title,
251
306
  x=x,
@@ -254,7 +309,7 @@ class DataShow:
254
309
  yref='paper',
255
310
  showarrow=True, # 显示箭头
256
311
  align="left", # 文本对齐方式
257
- font=dict(size=14),
312
+ font=dict(size=14)
258
313
  )
259
314
  row += 1
260
315
  col += 1
@@ -264,20 +319,21 @@ class DataShow:
264
319
  margin=dict(
265
320
  l=100, # 左边距
266
321
  r=100,
267
- t=100, # 上边距
268
- b=100,
322
+ t=80, # 上边距
323
+ b=80,
269
324
  ),
270
325
  legend=dict(
271
326
  orientation='v', # 图例方向('h' 表示水平,'v' 表示垂直)
272
327
  font=dict(
273
328
  size=12 # 图例字体大小
274
329
  )
275
- )
330
+ ),
331
+ barmode='stack', # stack(堆叠)、group(并列)、overlay(覆盖)、relative(相对)
276
332
  )
277
333
  fig.add_annotation(
278
- text=f'统计时间周期: {start_date}~{end_date}',
334
+ text=f'统计时间周期: {start_date}~{end_date} tips: 饼图剔除了销售<{f"{percent * 100}%"}的数据',
279
335
  x=0.5,
280
- y=-0.1,
336
+ y=-0.09,
281
337
  xref='paper', # # 相对于整个图表区域
282
338
  yref='paper',
283
339
  showarrow=False, # 显示箭头
@@ -621,8 +677,8 @@ class DataShow:
621
677
  y=values,
622
678
  name=table_name,
623
679
  orientation='v', # 垂直柱形图
624
- text=percentages.map('{:.2f}%'.format), # 设置要显示的文本(百分比)
625
- # textposition = 'outside', # 设置文本位置在柱形图外部
680
+ text=percentages.map('{:.1f}%'.format), # 设置要显示的文本(百分比)
681
+ textposition = 'outside', # 设置文本位置在柱形图外部
626
682
  width=0.55 # 调整柱子最大宽度
627
683
  )
628
684
  row = count // 3 + 1
@@ -684,7 +740,7 @@ class DataShow:
684
740
  align="left", # 文本对齐方式
685
741
  font=dict(size=12),
686
742
  )
687
- fig.write_html(os.path.join(self.path, f'{filename}_{item_id}.html'))
743
+ fig.write_html(os.path.join(self.path, f'{filename}.html'))
688
744
 
689
745
  def crowd(self, db_name='人群画像2', table_list=None, pro_list=None, filename='达摩盘人群画像', crowd_id=None, last_date=None):
690
746
  # item_ids = [696017020186, 714066010148, 830890472575]
@@ -753,8 +809,8 @@ class DataShow:
753
809
  y=values,
754
810
  name=table_name,
755
811
  orientation='v', # 垂直柱形图
756
- text=percentages.map('{:.2f}%'.format), # 设置要显示的文本(百分比)
757
- # textposition = 'outside', # 设置文本位置在柱形图外部
812
+ text=percentages.map('{:.1f}%'.format), # 设置要显示的文本(百分比)
813
+ textposition = 'outside', # 设置文本位置在柱形图外部
758
814
  width=0.55 # 调整柱子最大宽度
759
815
  )
760
816
  row = count // 3 + 1
@@ -823,12 +879,337 @@ class DataShow:
823
879
  align="left", # 文本对齐方式
824
880
  font=dict(size=12),
825
881
  )
826
- fig.write_html(os.path.join(self.path, f'{filename}_{crowd_name[:15]}.html'))
882
+ fig.write_html(os.path.join(self.path, f'{filename}.html'))
883
+
884
+ def item_show(self, db_name='聚合数据', table_list=None, pro_list=None, filename='商品数据', start_date=None, end_date=None):
885
+ if not pro_list:
886
+ pro_list = ['日期', '店铺名称', '营销场景', '商品id', '花费', '点击量', '加购量', '成交笔数', '成交金额']
887
+ table_name = '天猫_主体报表'
888
+ df = self.getdata(
889
+ db_name=db_name,
890
+ table_name=table_name,
891
+ pro_list=pro_list,
892
+ start_date=start_date,
893
+ end_date=end_date
894
+ )
895
+ df_set = self.getdata(
896
+ db_name='属性设置3',
897
+ table_name='商品sku属性',
898
+ pro_list=['商品id', '白底图'],
899
+ start_date='2020-01-01',
900
+ end_date=end_date
901
+ )
902
+ df_set = df_set[df_set['白底图'] != '0']
903
+ df_set.drop_duplicates(subset='商品id', keep='last', inplace=True, ignore_index=True)
904
+
905
+ if len(df) == 0:
906
+ print(f'数据不能为空: {table_name}')
907
+ return
908
+ df['日期'] = pd.to_datetime(df['日期'])
909
+ min_date = df['日期'].min().strftime('%Y-%m-%d')
910
+ max_date = df['日期'].max().strftime('%Y-%m-%d')
911
+
912
+ df = df.groupby(['店铺名称', '商品id'], as_index=False).agg(
913
+ **{
914
+ '花费': ('花费', np.sum),
915
+ '点击量': ('点击量', np.sum),
916
+ '加购量': ('加购量', np.sum),
917
+ '成交笔数': ('成交笔数', np.sum),
918
+ '成交金额': ('成交金额', np.sum),
919
+ })
920
+ cost_sum = df['花费'].sum()
921
+ df['花费占比'] = df.apply(lambda x: f'{round(x['花费']/cost_sum * 100, 1)}%', axis=1)
922
+ df['roi投产'] = df.apply(lambda x: f'{round(x['成交金额'] / x['花费'], 2)}' if x['花费'] > 0 else 0, axis=1)
923
+ df = pd.merge(df, df_set, left_on='商品id', right_on='商品id', how='left')
924
+ df.sort_values(['花费'], ascending=[False], ignore_index=True, inplace=True)
925
+ df = df.head(100)
926
+ df.reset_index(inplace=True)
927
+ df['index'] = df['index'] + 1
928
+ df.rename(columns={'index': '序号'}, inplace=True)
929
+
930
+ # 创建临时目录来存储图片
931
+ temp_dir = os.path.join(self.path, 'temp_images')
932
+ os.makedirs(temp_dir, exist_ok=True)
933
+
934
+ df_new = df.copy()
935
+ df_new = df_new.head(10)
936
+ pic_title1 = '商品花费占比'
937
+ img_file1 = os.path.join(temp_dir, f'{pic_title1}.png')
938
+ if not os.path.isfile(img_file1):
939
+ font_properties = {
940
+ 'family': 'PingFang HK', # 字体类型 PingFang HK, Hiragino Sans GB, Arial Unicode MS
941
+ 'size': 12, # 字体大小
942
+ 'weight': 'light', # 字体粗细('light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black')
943
+ 'style': 'italic' # 字体样式('normal', 'italic', 'oblique')
944
+ }
945
+ fig, ax = plt.subplots()
946
+ ax.pie(df_new['花费'], labels=df_new['商品id'], autopct='%1.1f%%', startangle=140)
947
+ ax.set_title(pic_title1, fontdict=font_properties) # 设置饼图的标题
948
+ ax.axis('equal') # 确保饼图是圆形的
949
+ plt.savefig(img_file1) # 保存饼图为PNG文件
950
+ plt.close()
951
+
952
+ # # 下载图片并保存到临时目录
953
+ # for i, url in enumerate(df['白底图']):
954
+ # item_id = df['商品id'].tolist()[i]
955
+ # img_path = os.path.join(temp_dir, f'image_{item_id}.jpg')
956
+ # if os.path.isfile(img_path):
957
+ # df.at[i, '白底图'] = img_path
958
+ # continue
959
+ # response = requests.get(url, headers={'User-Agent': ua_sj.get_ua()})
960
+ # if response.status_code == 200:
961
+ # with open(img_path, 'wb') as f:
962
+ # f.write(response.content)
963
+ # # 更新 DataFrame 中的图片地址列为本地路径
964
+ # df.at[i, '白底图'] = img_path
965
+ # else:
966
+ # print(f"Failed to download image at URL: {url}")
967
+
968
+ # 转换图片列
969
+ def convert_image_to_html(image_url_or_base64):
970
+ if os.path.isfile(image_url_or_base64):
971
+ # image_url_or_base64 是本地图片, 将图片路径转换为 Base64 编码的 <img> 标签
972
+ with open(image_url_or_base64, "rb") as image_file:
973
+ encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
974
+ img_tag = (f'<img class="img" src="data:image/jpeg;base64,{encoded_string}" alt="Image">')
975
+ return img_tag
976
+ else:
977
+ # image_url_or_base64 是在线 url 或者 Base64编码的图片
978
+ return f'<img class="img" src="{image_url_or_base64}" alt="Image">'
979
+
980
+ # 应用这个函数到图片列
981
+ df['Image_HTML'] = df['白底图'].apply(convert_image_to_html)
982
+
983
+ local_file1 = os.path.join(self.path, '多店推广场景.html')
984
+ local_file2 = os.path.join(self.path, '多店推广场景.html')
985
+ local_file3 = os.path.join(self.path, '多店推广场景.html')
986
+ local_file4 = os.path.join(self.path, '多店推广场景.html')
987
+ local_file5 = os.path.join(self.path, '多店推广场景.html')
988
+
989
+ # 创建 HTML
990
+ html_template = """
991
+ <head>
992
+ <meta charset="UTF-8">
993
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
994
+ <title>商品推广数据</title>
995
+ <style>
996
+ .centered-table {
997
+ margin-top: 20px;
998
+ margin-down: 100px;
999
+ margin-left: auto;
1000
+ margin-right: auto;
1001
+ border-collapse: collapse; /* 可选,用于合并表格边框 */
1002
+ width: 60%; /* 设置表格宽度为父容器宽度的50%,或者你可以使用固定宽度 */
1003
+ }
1004
+ thead th {
1005
+ background-color: #f2f2f2; /* 设置表头背景颜色 */
1006
+ font-size: 16px; /* 增大表头字体 */
1007
+ font-weight: bold; /* 加粗表头字体 */
1008
+ text-align: center; /* 设置表头文本居中 */
1009
+ }
1010
+ caption {
1011
+ caption-side: top; /* 标题显示在表格上方 */
1012
+ font-size: 24px; /* 设置标题字体大小 */
1013
+ font-weight: bold; /* 设置标题字体加粗 */
1014
+ text-align: center; /* 设置标题文本居中 */
1015
+ margin-bottom: 20px; /* 为标题和表格之间添加间距 */
1016
+ }
1017
+ td, th {
1018
+ border: 1px solid #ddd; /* 单元格边框 */
1019
+ line-height: 1em; /* 设置行高为2倍的当前字体大小 */
1020
+ padding: 5 5px; /* 设置左右边距,内边距增加单元格的整体高度 */
1021
+ text-align: center; /* 设置文本对齐方式 */
1022
+ }
1023
+ img {
1024
+ width: 80px; /* 设置图片宽度 */
1025
+ height: auto; /* 高度自动调整以保持宽高比 */
1026
+ /* 如果需要垂直居中且图片是块级元素,则可以使用以下样式(但通常不是必需的,因为图片默认是内联元素)
1027
+ text-align: center; /* 水平居中(适用于内联或块级子元素) */
1028
+ display: block;
1029
+ margin: 0 auto; */
1030
+ }
1031
+ button {
1032
+ border: none;
1033
+ padding: 8px 12px;
1034
+ font-size: 14px;
1035
+ cursor: pointer;
1036
+ }
1037
+ .centered-text {
1038
+ position: fixed; /* 固定定位 */
1039
+ bottom: 15px; /* 距离页面顶部10px(可根据需要调整) */
1040
+ right: calc(25vw - 420px); /* 距离页面右侧1/4宽度减去文本自身的宽度和可能的边距(这里假设文本宽度和边距共10px,实际情况需根据文本样式调整) */
1041
+ /* 如果文本宽度未知或可变,可以只使用25vw并接受可能的溢出 */
1042
+ /* right: 25vw; */ /* 直接使用25vw定位,不考虑文本宽度 */
1043
+ padding: 3px 10px; /* 可选的文本内边距 */
1044
+ background-color: rgba(255, 255, 255, 0.8); /* 可选的背景色和透明度 */
1045
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* 可选的阴影效果 */
1046
+ }
1047
+
1048
+ .image-container {
1049
+ position: absolute; /* 使用绝对定位 */
1050
+ width: 15%; /* 设置图片宽度 */
1051
+ left: 10px; /* 距离页面左侧20px */
1052
+ top: 50%; /* 距离页面顶部50% */
1053
+ transform: translateY(-50%); /* 向上移动自身高度的一半,以实现垂直居中 */
1054
+ }
1055
+ .image-container img {
1056
+ width: 20%; /* 设置图片宽度 */
1057
+ height: auto; /* 高度自动调整以保持宽高比 */
1058
+ /* 如果需要垂直居中且图片是块级元素,则可以使用以下样式(但通常不是必需的,因为图片默认是内联元素)*/
1059
+ display: flex;
1060
+ flex-direction: column;
1061
+ align-items: flex-start;
1062
+ }
1063
+ .button1 {
1064
+ border: none;
1065
+ padding: 8px 12px;
1066
+ font-size: 14px;
1067
+ cursor: pointer;
1068
+ position: absolute; /* 使用绝对定位 */
1069
+ left: 5%; /* 距离页面左侧20px */
1070
+ top: 10%; /* 距离页面顶部50% */
1071
+ }
1072
+ .button2 {
1073
+ border: none;
1074
+ padding: 8px 12px;
1075
+ font-size: 14px;
1076
+ cursor: pointer;
1077
+ position: absolute; /* 使用绝对定位 */
1078
+ left: 5%; /* 距离页面左侧20px */
1079
+ top: 17%; /* 距离页面顶部50% */
1080
+ }
1081
+ .button3 {
1082
+ border: none;
1083
+ padding: 8px 12px;
1084
+ font-size: 14px;
1085
+ cursor: pointer;
1086
+ position: absolute; /* 使用绝对定位 */
1087
+ left: 5%; /* 距离页面左侧20px */
1088
+ top: 24%; /* 距离页面顶部50% */
1089
+ }
1090
+ .button4 {
1091
+ border: none;
1092
+ padding: 8px 12px;
1093
+ font-size: 14px;
1094
+ cursor: pointer;
1095
+ position: absolute; /* 使用绝对定位 */
1096
+ left: 5%; /* 距离页面左侧20px */
1097
+ top: 31%; /* 距离页面顶部50% */
1098
+ }
1099
+ .button5 {
1100
+ border: none;
1101
+ padding: 8px 12px;
1102
+ font-size: 14px;
1103
+ cursor: pointer;
1104
+ position: absolute; /* 使用绝对定位 */
1105
+ left: 5%; /* 距离页面左侧20px */
1106
+ top: 38%; /* 距离页面顶部50% */
1107
+ }
1108
+
1109
+ </style>
1110
+ </head>
1111
+
1112
+ <div class="div-button">
1113
+ <!-- 创建一个按钮 -->
1114
+ <button id="button1" class="button1">多店推广场景</button>
1115
+ <button id="button2" class="button2">店铺流量来源</button>
1116
+ <button id="button3" class="button3">达摩盘人群画像</button>
1117
+ <button id="button4" class="button4">商品人群画像</button>
1118
+ <button id="button5" class="button5">销售地域分布</button>
1119
+ </div>
1120
+ <script>
1121
+ // 获取按钮元素
1122
+ var tg = document.getElementById('button1');
1123
+ var dpll = document.getElementById('button2');
1124
+ var dmp1 = document.getElementById('button3');
1125
+ var dmp2 = document.getElementById('button4');
1126
+ var syj = document.getElementById('button5');
1127
+ tg.addEventListener('click', function() {
1128
+ window.open('{local_file1}', '_blank');
1129
+ });
1130
+ dpll.addEventListener('click', function() {
1131
+ window.open('{local_file2}', '_blank');
1132
+ });
1133
+ dmp1.addEventListener('click', function() {
1134
+ window.open('{local_file3}', '_blank');
1135
+ });
1136
+ dmp2.addEventListener('click', function() {
1137
+ window.open('{local_file4}', '_blank');
1138
+ });
1139
+ syj.addEventListener('click', function() {
1140
+ window.open('{local_file5}', '_blank');
1141
+ });
1142
+ </script>
1143
+
1144
+ <p class="centered-text">统计周期</p>
1145
+ <!--
1146
+ <img class="image-container" src="{img_file1}" alt="图片">
1147
+ -->
1148
+ <table class="centered-table">
1149
+ <thead>
1150
+ <caption>天猫商品推广数据</caption>
1151
+ <div>
1152
+ <tr>
1153
+ <th>序号</th>
1154
+ <th>商品</th>
1155
+ <th>店铺名称</th>
1156
+ <th>商品id</th>
1157
+ <th>花费</th>
1158
+ <th>花费占比</th>
1159
+ <th>点击量</th>
1160
+ <th>加购量</th>
1161
+ <th>成交笔数</th>
1162
+ <th>成交金额</th>
1163
+ <th>roi投产</th>
1164
+ </tr>
1165
+ </div>
1166
+ </thead>
1167
+ <tbody>
1168
+ {rows}
1169
+ </tbody>
1170
+ </table>
1171
+ """
1172
+ rows = []
1173
+ for _, row in df.iterrows():
1174
+ row_html = (f'<tr>'
1175
+ f'<td>{row["序号"]}</td>'
1176
+ f'<td>{row["Image_HTML"]}</td>'
1177
+ f'<td>{row["店铺名称"]}</td>'
1178
+ f'<td>{row["商品id"]}</td>'
1179
+ f'<td>{row["花费"]}</td>'
1180
+ f'<td>{row["花费占比"]}</td>'
1181
+ f'<td>{row["点击量"]}</td>'
1182
+ f'<td>{row["加购量"]}</td>'
1183
+ f'<td>{row["成交笔数"]}</td>'
1184
+ f'<td>{row["成交金额"]}</td>'
1185
+ f'<td>{row["roi投产"]}</td>'
1186
+ f'</tr>'
1187
+ )
1188
+ rows.append(row_html)
1189
+
1190
+ final_html = html_template.replace('{rows}', ''.join(rows))
1191
+ final_html = final_html.replace('统计周期', f'统计周期: {min_date} ~ {max_date}')
1192
+ final_html = final_html.replace('{local_file1}', local_file1)
1193
+ final_html = final_html.replace('{local_file2}', local_file2)
1194
+ final_html = final_html.replace('{local_file3}', local_file3)
1195
+ final_html = final_html.replace('{local_file4}', local_file4)
1196
+ final_html = final_html.replace('{local_file5}', local_file5)
1197
+ file = os.path.join(self.path, f'{filename}.html')
1198
+ with open(file, 'w') as f:
1199
+ f.write(final_html)
827
1200
 
828
1201
 
829
1202
  def main():
830
1203
  ds = DataShow()
831
1204
 
1205
+ ds.item_show(
1206
+ db_name='聚合数据',
1207
+ table_list=None,
1208
+ pro_list=None,
1209
+ filename='天猫商品推广数据',
1210
+ start_date='2024-12-01',
1211
+ end_date=None,
1212
+ )
832
1213
  # 店铺流量来源
833
1214
  ds.dpll()
834
1215
  # 多店聚合推广数据
@@ -865,9 +1246,10 @@ def main():
865
1246
  db_name='生意经3',
866
1247
  filename='销售地域分布',
867
1248
  start_date='2024-12-01',
868
- end_date='2024-12-11',
869
- percentage=0.02,
1249
+ end_date=None,
1250
+ percent=0.015,
870
1251
  )
871
1252
 
1253
+
872
1254
  if __name__ == '__main__':
873
1255
  main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mdbq
3
- Version: 3.4.2
3
+ Version: 3.4.4
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -2,7 +2,7 @@ mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
2
2
  mdbq/__version__.py,sha256=y9Mp_8x0BCZSHsdLT_q5tX9wZwd5QgqrSIENLrb6vXA,62
3
3
  mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
4
4
  mdbq/aggregation/aggregation.py,sha256=-yzApnlqSN2L0E1YMu5ml-W827qpKQvWPCOI7jj2kzY,80264
5
- mdbq/aggregation/datashow.py,sha256=k4gUYldnmi_iZJrM7wNtjeenXJl82hUoYcPu6iIL3PU,35864
5
+ mdbq/aggregation/datashow.py,sha256=XTIhjlbC8MUDAq1XnWXco-IfL31FJO0Kgw9z3QoM2Jc,53219
6
6
  mdbq/aggregation/optimize_data.py,sha256=RXIv7cACCgYyehAxMjUYi_S7rVyjIwXKWMaM3nduGtA,3068
7
7
  mdbq/aggregation/query_data.py,sha256=9NALeHTP9tblOEPyntLBRtdroLG_qN9qWi34Hg4rXFM,178891
8
8
  mdbq/bdup/__init__.py,sha256=AkhsGk81SkG1c8FqDH5tRq-8MZmFobVbN60DTyukYTY,28
@@ -34,7 +34,7 @@ mdbq/pbix/refresh_all.py,sha256=OBT9EewSZ0aRS9vL_FflVn74d4l2G00wzHiikCC4TC0,5926
34
34
  mdbq/pbix/refresh_all_old.py,sha256=_pq3WSQ728GPtEG5pfsZI2uTJhU8D6ra-htIk1JXYzw,7192
35
35
  mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
36
36
  mdbq/spider/aikucun.py,sha256=v7VO5gtEXR6_4Q6ujbTyu1FHu7TXHcwSQ6hIO249YH0,22208
37
- mdbq-3.4.2.dist-info/METADATA,sha256=I2lVjMi-WsvegW9ZCQcR4UV8wg4g1A9-mzgVFQ_H7x4,243
38
- mdbq-3.4.2.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
39
- mdbq-3.4.2.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
40
- mdbq-3.4.2.dist-info/RECORD,,
37
+ mdbq-3.4.4.dist-info/METADATA,sha256=owU691dFqOy3Rv_GlFjck7sWdx5R8ZeFZkfkWtpJBcE,243
38
+ mdbq-3.4.4.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
39
+ mdbq-3.4.4.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
40
+ mdbq-3.4.4.dist-info/RECORD,,
File without changes