ai_member_xiaoxi/scripts/excel_v4.py
2026-05-15 08:00:01 +08:00

130 lines
6.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Excel v4: L1只看L1课程, L2只看L2课程"""
import json, openpyxl
from datetime import date
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from openpyxl.chart import LineChart, BarChart, Reference
from openpyxl.utils import get_column_letter
with open('/root/.openclaw/workspace/output/course_data_v4.json') as f:
raw = json.load(f)
results = raw['results']
for r in results:
r['ws'] = date.fromisoformat(r['ws'])
r['we'] = date.fromisoformat(r['we'])
wb = openpyxl.Workbook()
wb.remove(wb.active)
hfont = Font(name='微软雅黑', bold=True, size=9, color='FFFFFF')
hfill = PatternFill(start_color='002F5496', end_color='002F5496', fill_type='solid')
dfont = Font(name='微软雅黑', size=9)
tfont = Font(name='微软雅黑', bold=True, size=14, color='002F5496')
sfont = Font(name='微软雅黑', bold=True, size=11, color='002F5496')
bd = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
ctr = Alignment(horizontal='center', vertical='center')
def ac(ws, r, c, v, font=dfont, fill=None, align=ctr):
cl = ws.cell(row=r, column=c, value=v)
cl.font, cl.border, cl.alignment = font, bd, align
if fill: cl.fill = fill
def ah(ws, r, c, v):
cl = ws.cell(row=r, column=c, value=v)
cl.font, cl.fill, cl.border, cl.alignment = hfont, hfill, bd, ctr
# Sheet 1
ws1 = wb.create_sheet("概览")
ws1.merge_cells('A1:H1')
ac(ws1,1,1,"付费用户课消分析 v4只看对应级别课程剔除U0",font=tfont,align=Alignment(horizontal='left'))
notes = [
"口径L1付费群 = 买过L1商品的付费用户, 只看L1课程课消 | L2付费群 = 买过L2商品的付费用户, 只看L2课程课消",
"L1+L2用户在L1视角只统计L1课程课消, L2视角只统计L2课程课消",
"课消用户首次完成某一课时剔除U0序章",
"付费用户status=1 + 未删除 + 有未退款订单",
]
for i,n in enumerate(notes):
ws1.merge_cells(f'A{3+i}:H{3+i}')
ac(ws1,3+i,1,n,font=Font(name='微软雅黑',size=9,color='666666'),align=Alignment(horizontal='left'))
row=9
ws1.merge_cells(f'A{row}:H{row}')
ac(ws1,row,1,"汇总(截至最后一周)",font=sfont,align=Alignment(horizontal='left'))
row+=1
for j,h in enumerate(['分类','付费用户','有课消','无课消','无课消率','人均课消','有消人均'],1):
ah(ws1,row,j,h)
row+=1
last=results[-1]
skus = [
('L1付费群(只看L1课程)', last['L1_paid'],last['L1_cons_users'],last['L1_no_cons'],last['L1_avg_all'],last['L1_avg_cons'], '00A8CFF1'),
('L2付费群(只看L2课程)', last['L2_paid'],last['L2_cons_users'],last['L2_no_cons'],last['L2_avg_all'],last['L2_avg_cons'], '00F4A9A0'),
('合计(去重)', last['total_paid'],last['total_cons_users'],last['total_no_cons'],last['total_avg_all'],last['total_avg_cons'], '00C8E6C9'),
]
for name,p,cu,nc,aa,ac_,clr in skus:
no_rate=f"{nc/p*100:.0f}%" if p else "0%"
fl=PatternFill(start_color=clr,end_color=clr,fill_type='solid')
for j,v in enumerate([name,p,cu,nc,no_rate,aa,ac_],1):
ac(ws1,row,j,v,font=Font(name='微软雅黑',bold=(j==1),size=10),fill=fl)
row+=1
# Sheet 2
ws2=wb.create_sheet("每周明细")
headers=['','周一起','周日']
for pfx in ['合计','L1付费群','L2付费群']:
for m in ['付费','有消','无消','课消','人均','有消人均']:
headers.append(f'{pfx}{m}')
for j,h in enumerate(headers,1): ah(ws2,1,j,h)
for ri,r in enumerate(results):
rw=ri+2
ac(ws2,rw,1,r['ws'].strftime('%m/%d'))
ac(ws2,rw,2,r['ws'].strftime('%Y-%m-%d'))
ac(ws2,rw,3,r['we'].strftime('%Y-%m-%d'))
col=4
for prefix in ['total','L1','L2']:
for k in ['paid','cons_users','no_cons','cons','avg_all','avg_cons']:
ac(ws2,rw,col,r[f'{prefix}_{k}'])
col+=1
for ci in range(1,len(headers)+1):
ws2.column_dimensions[get_column_letter(ci)].width=11 if ci<=3 else 10
ws2.freeze_panes='D2'
# Sheet 3+4: charts
for lvl, pf, clr in [('L1','L1','4A90D9'),('L2','L2','E85D47')]:
ws=wb.create_sheet(f"{pf}图表")
lh=['','付费用户','有课消用户','无课消用户','课消总数','人均课消','有消人均']
first=next(i for i,r in enumerate(results) if r[f'{pf}_paid']>0)
ld=results[first:]
for j,h in enumerate(lh,1): ah(ws,1,j,h)
for ri,r in enumerate(ld):
rw=ri+2
ac(ws,rw,1,r['ws'].strftime('%m/%d'))
for j,k in enumerate([f'{pf}_paid',f'{pf}_cons_users',f'{pf}_no_cons',f'{pf}_cons',f'{pf}_avg_all',f'{pf}_avg_cons'],2):
ac(ws,rw,j,r[k])
n=len(ld)
cr=Reference(ws,min_col=1,min_row=2,max_row=n+1)
ch1=BarChart(); ch1.type="col"; ch1.grouping="stacked"
ch1.title=f"{pf}付费用户周课消分布(只看{pf}课程)"; ch1.style=10; ch1.width=24; ch1.height=13
ch1.add_data(Reference(ws,min_col=3,min_row=1,max_row=n+1),titles_from_data=True)
ch1.add_data(Reference(ws,min_col=4,min_row=1,max_row=n+1),titles_from_data=True)
ch1.set_categories(cr)
ch1.series[0].graphicalProperties.solidFill='A8CFF1' if pf=='L1' else 'F4A9A0'
ch1.series[1].graphicalProperties.solidFill='D9D9D9'
ch1.y_axis.title='用户数'; ch1.legend.position='b'
ws.add_chart(ch1,"A9")
ch2=LineChart(); ch2.title=f"{pf}付费用户周人均课消趋势(只看{pf}课程)"; ch2.style=10; ch2.width=24; ch2.height=13
ch2.add_data(Reference(ws,min_col=6,min_row=1,max_row=n+1),titles_from_data=True)
ch2.add_data(Reference(ws,min_col=7,min_row=1,max_row=n+1),titles_from_data=True)
ch2.set_categories(cr)
ch2.series[0].graphicalProperties.line.solidFill='999999'; ch2.series[0].graphicalProperties.line.width=20000
ch2.series[1].graphicalProperties.line.solidFill=clr; ch2.series[1].graphicalProperties.line.width=28000
ch2.y_axis.scaling.min=0; ch2.y_axis.title='课消数(节/周)'; ch2.legend.position='b'
ws.add_chart(ch2,"A27")
for ci in range(1,8): ws.column_dimensions[get_column_letter(ci)].width=12
path='/root/.openclaw/workspace/output/course_consumption_by_level_v4.xlsx'
wb.save(path)
print(f'{path}')