#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""诊断原料销售部 飞书活动 → 客户需求趋势（近1个月 vs 前1个月，等长30天窗口）"""
import re, json, collections, sys

BASE = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/数据/'
items = json.load(open(BASE + 'daily_activities_mcp.json'))['items']
roles = json.load(open(BASE + 'user_roles.json'))

DA = sorted({u['name'] for u in roles if u.get('line') == 'da'})
BOTH = sorted({u['name'] for u in roles if u.get('line') == 'both'})
SCOPE = set(DA) | set(BOTH)

# 窗口：本期 2026-09-01~09-30（30天） vs 上期 2026-08-02~08-31（30天）
CUR = ('2026-09-01', '2026-09-30', '2026-09-01~2026-09-30')
PRE = ('2026-08-02', '2026-08-31', '2026-08-02~2026-08-31')

def clean(t):
    t = re.sub(r'!\[.*?\]\(.*?\)', '', t or '')
    t = re.sub(r'https?://\S+', '', t)
    return re.sub(r'<[a-zA-Z/][^>]{0,300}>', '', t)

def window(a, b):
    return [i for i in items
            if (i.get('owner') or {}).get('name_cn') in SCOPE
            and a <= (i.get('start_time') or '') <= b]

BEHAV = {
 '测试/验证': r'测试|验证|复现|上机|跑样|试用|试测|对比实验|评估|测评|初测|复测',
 '采购/下单': r'采购|下单|订单|复购|批量订购|已形成.*订单|备货|发货|付款|结算|货款',
 '报价/价格': r'报价|价格|单价|折扣|优惠|促销|费用|成本|预算|降价|价格表|梯度',
 '送样/寄样': r'送样|寄样|寄送|发样|寄一|发出.*样|小样|给客户发|寄了|样品',
 '客诉/质量': r'客诉|投诉|批间差|非特异|起线|质量问题|不合格|换货|退货|异常|污染|不稳定|复现.*问题',
 '竞品/替代': r'竞品|竞对|国产替代|替换|换品牌|比对.*品牌|诺唯赞|诺维赞|诺威赞|Vazyme|近岸|菲鹏|翌圣|翊圣|天根|康为|全式金|爱博泰克|ABclonal|诺禾|宝日医|Takara',
 '新项目/开发': r'新项目|新客户|开发|立项|询价|新需求|新体系|项目开始|导入|首次',
 '技术/优化': r'优化|调整|改进|配方|浓度|体系.*改|调试|提高|提升|灵敏度|扩增效率|抑制|Ct值|CT值|Rn',
 '回款/账期': r'回款|账期|逾期|应收账款|付款计划|发票|开票',
 '学术/会议': r'会议|展会|学术|讲座|培训|报告|研讨会|走访|来访|参观',
}
THEME = {
 '冻干': r'冻干|lyophil|冻干粉|冻干球',
 '直扩/免提取': r'直扩|直接扩增|免提取|粗提|裂解液|直提',
 'NA/低背景': r'NA版本|NA版|背景菌|背景控制|低背景|核酸残留|宿主残留|大肠.*残留|残留',
 '多重/超多重': r'多重|超多重|multiplex|联检',
 'POCT/快检': r'POCT|快检|即时检测|现场检测|手持|便携',
 'RNA/逆转录': r'逆转录|RT酶|RTase|RNA|cDNA|一步法',
 'DNA聚合酶/Taq': r'Taq|聚合酶|DNA酶|高保真|热启动|Hotstart',
 'LAMP/等温': r'LAMP|等温|RPA|恒温',
 '磁珠/提取': r'磁珠|提取试剂|核酸提取|柱提',
 '酶原料/裸酶': r'裸酶|单酶|酶原料|BST|连接酶|T4',
}
SIGNAL = {
 '转化成交信号': r'已形成.*订单|已形成采购|已下采购|形成采购订单|转采购|下单了|已下单|成交|签约|敲定',
 '项目暂停/放缓': r'暂停|搁置|推迟|延期|放缓|资金.*(未|没)|预算.*(不足|没有)|暂时不|先不|等.*再决定|观望',
 '降本/替代压价': r'降本|价格高|成本压力|便宜|降价|价格优|替代|国产化|集采|砍价|比价',
 '定制/联合开发': r'定制|联合开发|共同开发|白盒|配方调整|按客户要求|专属',
 '交付/物流': r'交期|到货|签收|物流|发货时间|断货|缺货|库存',
 '多项目并行/OEM': r'OEM|代工|贴牌|多项目|同时推进|平台化',
}
BRANDS = {
 '诺唯赞': r'诺唯赞|诺维赞|诺威赞|Vazyme',
 '近岸蛋白': r'近岸', '菲鹏': r'菲鹏', '翌圣': r'翌圣|翊圣',
 '天根': r'天根', '康为世纪': r'康为', '爱博泰克': r'爱博泰克|ABclonal',
 'Takara': r'Takara|宝日医', '迈迪安': r'迈迪安',
 '金诺美': r'金诺美', '博奥': r'博奥', '圣湘': r'圣湘',
}

def stat(rows, kws):
    c = collections.Counter()
    for i in rows:
        t = clean(i.get('field_b99055'))
        for k, p in kws.items():
            if re.search(p, t, re.I):
                c[k] += 1
    return c

def cust(rows):
    s = set()
    for i in rows:
        lbl = (i.get('field_5f20fc') or {}).get('label')
        if lbl:
            s.add(lbl)
    return s

cur, pre = window(*CUR[:2]), window(*PRE[:2])
print('=== 口径 ===')
print('DA(在职名册 line=da):', len(DA), DA)
print('BOTH(管理层):', BOTH)
print('SCOPE 人数:', len(SCOPE))
print()
for tag, rows in (('本期 09-01~09-30', cur), ('上期 08-02~08-31', pre)):
    ppl = collections.Counter((i.get('owner') or {}).get('name_cn') for i in rows)
    cs = cust(rows)
    print(f'--- {tag} ---')
    print(f'活动条数 {len(rows)} | 覆盖客户 {len(cs)} | 活跃人员 {len(ppl)} | 人均 {len(rows)/max(len(ppl),1):.1f} | 客户均活动 {len(rows)/max(len(cs),1):.2f}')
    print('TOP10活跃:', ppl.most_common(10))
    print()

def block(title, kw):
    sc, sp = stat(cur, kw), stat(pre, kw)
    print(f'=== {title} ===')
    print('标签\t本期\t占比\t上期\t占比\t变化pp')
    for k in kw:
        r1 = sc[k]/max(len(cur),1)*100
        r2 = sp[k]/max(len(pre),1)*100
        print(f'{k}\t{sc[k]}\t{r1:.1f}%\t{sp[k]}\t{r2:.1f}%\t{r1-r2:+.1f}pp')
    print()

block('行为结构', BEHAV)
block('需求主题', THEME)
block('转化与阻力', SIGNAL)

print('=== 竞品提及（本期 / 上期）===')
bc, bp = stat(cur, BRANDS), stat(pre, BRANDS)
for k in BRANDS:
    if bc[k] or bp[k]:
        print(f'{k}\t{bc[k]}\t{bp[k]}')
print()

# 客户排名：本期活跃度 TOP20 客户（企业 vs 学术）
ACAD = re.compile(r'大学|学院|研究所|科学院|医院|疾控|海关|检验检疫|动物疫控|兽医|研究所')
cnt = collections.Counter((i.get('field_5f20fc') or {}).get('label') for i in cur)
print('=== 本期活跃客户 TOP20 ===')
for n, c in cnt.most_common(20):
    print(f'{c}\t{n}\t{"学术/院所" if ACAD.search(n or "") else "企业"}')
print()
acad = sum(1 for n in cust(cur) if ACAD.search(n or ''))
print(f'本期覆盖客户 {len(cust(cur))} 家，其中学术/院所/医院类 {acad} 家（{acad/max(len(cust(cur)),1)*100:.0f}%）')
acad_p = sum(1 for n in cust(pre) if ACAD.search(n or ''))
print(f'上期覆盖客户 {len(cust(pre))} 家，其中学术/院所/医院类 {acad_p} 家（{acad_p/max(len(cust(pre)),1)*100:.0f}%）')

json.dump({'cur': [i['work_item_id'] for i in cur], 'pre': [i['work_item_id'] for i in pre]},
          open('/Users/liuxinyuan/Desktop/Hermes输出-工作类/数据/_trend_tmp_ids.json', 'w'))
