from pathlib import Path
from datetime import date, datetime
import json,csv,re,statistics,hashlib
import xlsxwriter
ROOT=Path(__file__).resolve().parent
REPO=ROOT.parent.parent
ITEMS=REPO/'outputs/2026-10-08-team-work-log-analysis/source/items.json'
LEDGER=REPO/'work_company_knowledge/online-documents/2026-08-08-deployment-intake/imports/WEDOC-SMARTSHEET-20260808-CURRENT-DELIVERY-DEPLOYMENT/sheets/2-项目管理总表.csv'
items=json.loads(ITEMS.read_text()); bykey={x['key']:x for x in items}
def safe(t):
 t=re.sub(r'https?://\S+','[链接已隐藏]',t)
 t=re.sub(r'\b(?:\d{1,3}\.){3}\d{1,3}(?::\d+)?','[地址已隐藏]',t)
 t=re.sub(r'(?<!\d)1[3-9]\d{9}(?!\d)','[手机号已隐藏]',t)
 t=re.sub(r'(?:密码|口令|token|secret|api_key)\s*[:：=]\s*[^\s，。；;]+','[敏感值已隐藏]',t,flags=re.I)
 return t
# Endpoint pairs are manually reviewed. These are stage spans, not labor estimates.
cases=[
 ['央美','筹备→主流程测试可用','2026-08-17','2026-09-03',15,25,'阶段性可用；缺最终签字验收','W00069,W01758,W04826','8/29餐台进场；9月底仍有餐盘扫码整改。17天不能代表完整交付。','中等集成项目的上线阶段；含常规准备，不含未确认外部等待'],
 ['滨州','已运行后的保障→日志称收尾','2026-08-17','2026-09-23',7,14,'保障与追加设备混合；终验待证','W00106,W01452,W03326,W03974,W04154','第三批设备9/22安装；长期跟餐混有缺陷、使用指导和新增批次，不全是首期实施。','稳定上线后的保障窗口；新增批次另算'],
 ['四方达','备案完成记录→余额切换/开餐处理记录','2026-08-26','2026-09-30',25,35,'阶段跨度；缺真实首日及完整验收','W00870,W03850,W04458,W04682,W04735,W04737','9/22仍试运行；9/28原系统余额有差异；9/30仍导入余额并处理开餐。','单点位带补贴规则与余额迁移的项目；外部等待未量化'],
 ['哈尔滨膳质舍','项目启动会→现场培训记录','2026-08-20','2026-09-20',25,35,'阶段跨度；无完整验收回读','W00364,W01762,W02680,W02912,W03507,W03510','机器人接口与文档不一致；9/20才完成客户布线。培训不代表合同全量完成。','餐厅+体检设备+机器人接口的集成阶段；外部等待未量化'],
 ['哈尔滨膳质舍','明确进场→设备及平台培训','2026-09-15','2026-09-20',5,7,'现场阶段；同类控制值内','W02912,W03223,W03507,W03510','现场段5天，不能据此忽略前期开发、联调和客户网电准备。','设备/版本/人员就绪后的现场段'],
 ['保康中学','服务器阻塞明确→仍90%及云部署要求','2026-09-03','2026-09-29',25,35,'阶段未闭环；完成状态存在冲突','W01819,W02005,W02008,W03047,W03986,W04703,W04705','9/4声称整体完成，9/29仍90%；服务器、虚拟化、网络施工与云/本地范围反复。','多模块多设备集成项目；须先冻结部署方式'],
 ['三全','主数据开发记录→内网部署仍未贯通','2026-09-14','2026-09-30',20,30,'接口阶段仍未闭环；不能算完成周期','W02807,W03184,W03185,W04139,W04683,W04811,W04813,W04814','客户成本中心接口未可用、回调未贯通；6基地与其他需求不包含在16天内。','一组主数据/成本中心/刷脸回调集成；技术协议与环境就绪'],
 ['张家港','方案沟通→部署包完成','2026-08-19','2026-09-17',15,25,'研发交接阶段；不是客户验收','W00246,W00341,W03126,W03205','日志跨度29天；无完整投入及第三方等待天数，不能认定研发多耗29人天。','支付接口开发、联调与打包阶段；接口可用'],
 ['张家港','部署包完成→后续现场安装记录','2026-09-17','2026-10-07',5,7,'阶段间隔；首进场日期不完整','W03205,W03474,W04840,W04846,W04848','10/6记录现场交接，不能当成项目第一次进场；20天不等于纯安装时间。','到货、样本、网电齐套后的现场段；当前起点不同，不直接判超期'],
 ['国康医院','补单需求确认→测试上线和客户培训','2026-08-20','2026-08-26',5,7,'小功能按日志完成；客户验收待证','W00340,W00418,W00774,W00913,W00935','6天为需求至现场功能交付跨度，8/20承诺下周四，8/26已有上线培训记录。','已明确的设备端补单小改动，含研发、测试及现场升级'],
 ['三全','食安部署方案→日志称部署验收','2026-09-18','2026-09-21',2,4,'单模块部署；日志自述验收','W03359,W03524,W03740,W03747','3天仅食安模块部署；不能代表整个三全项目完成。','版本/网络/服务器齐套后部署已有模块'],
 ['四方达','端口修改记录→日志称部署完成','2026-08-20','2026-08-21',1,3,'基础部署；业务闭环尚未完成','W00370,W00478','1天只是软件部署阶段，不含补贴、余额迁移和现场正式切换。','基础软件安装配置阶段']
]
for c in cases:
 c.insert(4,(date.fromisoformat(c[3])-date.fromisoformat(c[2])).days)
 for k in c[8].split(','): assert k in bykey,k
# Collect only historical canteen rows with complete dates and an explicit completed status.
hist=[]
for r in csv.DictReader(LEDGER.open()):
 if '团餐' not in r['业务组别'] or r['项目状态']!='已完成' or r['验收状态']!='终验完成':continue
 if not(r['开始时间'] and r['结束时间']):continue
 def dt(v):return datetime.fromtimestamp(int(v)/1000).astimezone().date()
 a,b=dt(r['开始时间']),dt(r['结束时间'])
 hist.append([r['项目名称'],a.isoformat(),b.isoformat(),(b-a).days,r['工期要求'],r['项目状态'],r['验收状态'],r['备注'],'2026-08-08台账快照；时间含义待负责人复核，无签字件回读'])
assert len(hist)==3
norms=[
 ['轻量配置/单模块','已有软件配置、少量标准设备；无新接口',7,14,5,1.5,2,'资料/网电/设备齐套；不含制造物流与审批等待'],
 ['标准智慧食堂','单点位结算+标准设备+数据初始化',15,25,10,3,5,'使用已验证版本；无重大定制或未知设备协议'],
 ['中等集成','一卡通/支付/补贴/迁移或多个标准模块',25,35,20,6,22,'接口、迁移口径和验收范围冻结；关键外部依赖可用'],
 ['复杂/多基地定制','多基地、多系统、多权限和复杂定制',60,90,50,20,80,'先试点再复制；模块数量与功能范围冻结；不得把所有定制打包成一个标准项目']
]
roles=[['交付',5,'赵野、张帅A、侯世伟、柴玉龙、王金洋'],['产品',3,'李潇、孙海琴、王朔A（按日志工作画像暂归类）'],['研发',6,'赖清涛、尹旭亮、兰海军、郭东伟、孙倩倩、田亚辉（按画像暂归类）']]
model={'workdays_month':20,'availability':.6,'mix_standard_quarter':6,'mix_medium_quarter':3,'mix_complex_quarter':1}
wb=xlsxwriter.Workbook(ROOT/'14人团队_实际与建议交付时间对比_20261008.xlsx')
base={'font_name':'微软雅黑','font_size':12,'valign':'top','text_wrap':True}
fmt=wb.add_format(base);head=wb.add_format({**base,'bold':True,'bg_color':'#173C4A','font_color':'white'});title=wb.add_format({**base,'bold':True,'font_size':18,'font_color':'#173C4A'});warn=wb.add_format({**base,'bg_color':'#FFF1CE'});num=wb.add_format({**base,'num_format':'0.0'});integer=wb.add_format({**base,'num_format':'0'});inp=wb.add_format({**base,'font_color':'#245BCD','bg_color':'#FFF1CE'});percent=wb.add_format({**base,'num_format':'0%','font_color':'#245BCD','bg_color':'#FFF1CE'})
def sheet(name,headers,widths):
 s=wb.add_worksheet(name);s.set_default_row(45,fmt);s.freeze_panes(1,1);s.write_row(0,0,headers,head);s.set_row(0,42)
 for i,w in enumerate(widths):s.set_column(i,i,w,fmt)
 return s
def table_rows(s,rows):
 for ri,row in enumerate(rows,1):
  s.write_row(ri,0,row,fmt);s.set_row(ri,110)
 if rows:s.autofilter(0,0,len(rows),len(rows[0])-1)
s=wb.add_worksheet('先看结论');s.set_column('A:A',25,fmt);s.set_column('B:F',17,fmt);s.set_default_row(44,fmt)
s.merge_range('A1:F2','14人团队：实际记录与建议交付时间',title)
notes=[
 ('人员边界','交付5人、产品3人、研发6人。产品/研发姓名依据日志画像暂归类，需组织名册确认。销售不纳入。'),
 ('来源','2026-08-17至10-07的463份日志；2026-08-08项目台账；10月8日提供的实施原表及分类实施计划。未刷新在线台账。'),
 ('真实平均的边界','当前项目起止与客户签字验收不完整，无法得出14人团队真实完整平均交付周期。3个历史完整台账样本只能作小样本参照。'),
 ('已知历史跨度','无锡48天、海开225天、中债7天；均值93.3天、中位48天。台账日期含义待复核；不是现场作业周期或当前14人的平均。'),
 ('当前阶段观察','国康补单6天；央美筹备至主链测试17天；哈尔滨现场至培训5天；三全食安单模块部署3天。不同阶段不得混算平均。'),
 ('重点冲突','保康9/4声称整体完成，9/29仍90%并重新明确云部署；滨州保障含第三批追加设备；张家港打包到后续安装20天，首次进场未知。'),
 ('建议控制值','标准完整项目15—25自然日；中等集成25—35；复杂多基地60—90。D级排期草案，条件齐套起算，不能作客户承诺或人员考核。'),
 ('14人产能情景','每月20工作日、60%时间可用于本轮项目。季度6标准+3集成+1复杂，共10个；约40个/年。含持续产品工作和售后预留的假设，不是实测产能。'),
 ('先补齐什么','每项目确认范围、真实启动、齐套、上线、签字验收日期；逐日角色工时、等待原因/起止、追加批次和返工。才能量化等待和实际人天。'),
 ('时间口径','表中日期跨度＝结束日期−开始日期，不含起始日；自然日含周末节假日。人天按每人实际工作日，不能与自然日直接相加。'),
]
for ri,(k,v) in enumerate(notes,3):
 s.write(ri,0,k,head);s.merge_range(ri,1,ri,5,v,warn if k in ['真实平均的边界','建议控制值','14人产能情景'] else fmt);s.set_row(ri,70)
s.set_landscape();s.fit_to_pages(1,1);s.print_area(0,0,12,5)
s=sheet('项目时间对比',['项目','核对阶段','记录起点','记录终点','观察跨度/自然日','建议下限/自然日','建议上限/自然日','实际状态与边界','证据编号','阻塞/异常/解释','建议适用边界','机械差值：跨度减上限','可否认定超期'],[20,29,15,15,15,15,15,35,27,55,45,17,32])
for ri,c in enumerate(cases,1):
 s.write_row(ri,0,c,fmt)
 s.write_formula(ri,4,f'=DATEVALUE(D{ri+1})-DATEVALUE(C{ri+1})',integer,c[4])
 # A mechanical difference is displayed for review, not interpreted as employee delay.
 s.write_formula(ri,11,f'=E{ri+1}-G{ri+1}',integer,c[4]-c[6]);s.write(ri,12,'仅机械差值；边界/等待/签字日期未齐，不作责任或完整超期判断',warn);s.set_row(ri,125)
s.autofilter(0,0,len(cases),12)
s=sheet('历史台账周期',['项目','台账开始','台账结束','日期跨度/自然日','表内工期要求','项目状态','验收状态','备注','证据边界'],[40,15,15,18,20,16,18,32,50]);table_rows(s,hist)
for ri,h in enumerate(hist,1):s.write_formula(ri,3,f'=DATEVALUE(C{ri+1})-DATEVALUE(B{ri+1})',integer,h[3])
s.write(5,0,'3样本均值（不是团队平均）',warn);s.write_formula(5,3,'=AVERAGE(D2:D4)',num,statistics.mean(x[3] for x in hist));s.write(6,0,'3样本中位',warn);s.write_formula(6,3,'=MEDIAN(D2:D4)',num,statistics.median(x[3] for x in hist))
s=sheet('建议周期与人天',['类型','范围','周期下限/自然日','周期上限/自然日','交付人天/包','产品人天/包','研发人天/包','前置条件'],[24,45,18,18,18,18,18,60]);table_rows(s,norms)
s.write(7,0,'注意',warn);s.merge_range(7,1,8,7,'全部为D级建议，不是实测平均。交付人天不包含产品/研发人天，角色之间可并行；周期从已冻结范围和齐套条件开始。复杂项目必须按现场点位、接口数和批次重估。',warn)
s=sheet('14人人员产能',['角色','人数','人员映射待确认','月工作日假设','项目可用率假设','月可用项目人天','季度可用项目人天'],[15,12,62,20,20,23,25])
for ri,r in enumerate(roles,1):
 s.write_row(ri,0,r,fmt);s.write(ri,3,20,inp);s.write(ri,4,.6,percent)
 s.write_formula(ri,5,f'=B{ri+1}*D{ri+1}*E{ri+1}',num,r[1]*20*.6)
 s.write_formula(ri,6,f'=F{ri+1}*3',num,r[1]*20*.6*3);s.set_row(ri,95)
s.write_row(6,0,['季度组合','项目包数','交付消耗人天','产品消耗人天','研发消耗人天'],head)
for ri,(label,q,nd) in enumerate([('标准项目',6,1),('中等集成',3,2),('复杂项目',1,3)],7):
 s.write(ri,0,label);s.write(ri,1,q,inp)
 for col,idx,letter in [(2,4,'E'),(3,5,'F'),(4,6,'G')]:
  s.write_formula(ri,col,f"=B{ri+1}*'建议周期与人天'!{letter}{nd+2}",num,q*norms[nd][idx])
s.write(10,0,'季度合计',head)
for col in range(1,5):
 letter=xlsxwriter.utility.xl_col_to_name(col)
 vals=[10,170,56,176]
 s.write_formula(10,col,f'=SUM({letter}8:{letter}10)',num,vals[col-1])
s.write(12,0,'年度项目包情景');s.write_formula(12,1,'=B11*4',integer,40)
s.write_row(14,0,['角色','季度可用人天','组合所需人天','剩余人天','解释'],head)
for ri,role,cap,cost,src1,src2 in [(15,'交付',180,170,'G2','C11'),(16,'产品',108,56,'G3','D11'),(17,'研发',216,176,'G4','E11')]:
 s.write(ri,0,role);s.write_formula(ri,1,f'={src1}',num,cap);s.write_formula(ri,2,f'={src2}',num,cost);s.write_formula(ri,3,f'=B{ri+1}-C{ri+1}',num,cap-cost);s.write(ri,4,'60%可用率是假设；共享平台、售后、其他业务占用需用工时表校准',warn);s.set_row(ri,90)
s.merge_range('A20:G22','这组项目组合先由交付人天约束。对比此前120包/年的销售倒排，现有5名交付在此假设下仅有720项目人天/年：120个标准包需要1200交付人天，缺口480人天。可通过扩大外部安装、降低单位人天或增加交付能力解决；不能用日志片段数当工时。',warn)
s=sheet('证据明细',['证据编号','汇报日期','人员','归属依据','项目标签','安全摘录','源文件','源单元格','日志编号','证据等级与限制'],[15,15,14,30,35,90,45,15,25,45])
keys=list(dict.fromkeys(k for c in cases for k in c[8].split(',')))
for ri,k in enumerate(keys,1):
 x=bykey[k];s.write_row(ri,0,[k,x['date'],x['name'],x['basis'],x['projects'],safe(x['text']),x['source'],x['cell'],x['log'],'B：工作自述；日期按汇报日，不自动等于工作发生日；无客户签字回读'],fmt);s.set_row(ri,110)
s.autofilter(0,0,len(keys),9)
s=sheet('口径与来源',['来源','快照/覆盖时间','绝对路径','用途','不能证明'],[25,25,85,40,55])
table_rows(s,[['团队工作日志','2026-08-17至2026-10-07',str(ITEMS),'阶段事实、角色工作内容与报告日期','实际人天、全天投入、正式验收、未报日期的活动'],['项目管理总表','2026-08-08快照',str(LEDGER),'历史起止/表内计划/完成状态','当前在线状态、签字验收真实性、当前团队工时'],['交付实施原表','2026-10-08来源快照',str(REPO/'work/delivery-assetization/2026-10-08-classified-implementation-plans/source/original-rows.json'),'准备/部署/设备/试运行依赖及范围核对','其3—4月日期全部真实完成；项目归属尚未确认'],['人数','2026-10-08用户提供','本聊天：交付5、产品3、研发6','产能人数边界','9名产研具体归属及全部投入智慧食堂'],['目标周期/人天/可用率','2026-10-08分析假设',str(ROOT/'build_comparison.py'),'可修改的排期和产能情景','实测效率、员工绩效、已批准客户承诺']])
wb.close()
# A concise, safe report and a manifest make the result recoverable.
report='''# 14人团队实际记录与建议交付时间对比

人员边界：交付5、产品3、研发6。销售不计。执行模式：常规Codex。

## 结论

不能用前文50人销售收入模型代替14人交付产能。当前完整启动/齐套/上线/签字验收日期及角色工时不足，无法计算当前14人真实完整平均周期。

3个历史台账记为终验完成且起止完整：无锡48天、海开225天、中债7天，均值93.3天、中位48天；样本小、范围不同且不属于当前14人实测，不能作团队平均或人员绩效。

当前阶段：国康补单6自然日，央美筹备至主流程测试17自然日，哈尔滨现场至培训5自然日，三全食安部署3自然日。不同阶段不混算平均。保康9/4声称整体完成与9/29仍90%及云端要求冲突。滨州保障跨度37天包含第三批追加设备；张家港打包至后续安装记录20天，首次进场未知。

## 建议控制值（D级）

轻量配置7—14自然日；标准15—25；中等集成25—35；复杂多基地60—90。起点为范围冻结且条件齐套；审批、物流和客户等待另列；并行角色人天不能相加为周期。建议需负责人评审，不是客户承诺。

标准/中等/复杂包的人天假设分别为交付10/20/50、产品3/6/20、研发5/22/80。月20工作日、60%可用于本批项目，季度交付180、产品108、研发216人天。季度6标准+3中等+1复杂需交付170、产品56、研发176人天，共10包，年40包情景。此可用率非日志实测；交付目前为该组合先遇到的约束。120标准包需1200交付人天，5名交付该假设年可用720，差480。

## 资料边界

463份日志截至10月7日；项目台账是8月8日历史快照，未宣称在线新鲜。日志按汇报日期定位；源明示工作日期应继续核对。源表安排不自动等于实绩；计划、试运行、模块部署、整体交付、签字终验分开。

## 交付物

- Excel：14人团队_实际与建议交付时间对比_20261008.xlsx
- 构建与公式回读：build_comparison.py、validation.json
- 来源为其他任务已有只读资料，未修改源文件。

## 待补

每项目真实启动/齐套/上线/签字验收日期；逐角色工时；等待起止、返工、变更与追加批次。产研姓名按日志工作画像归类，待组织名册确认。目标需产品/研发/交付负责人确认后才可派工。
'''
(ROOT/'summary.md').write_text(report)
(ROOT/'analysis-data.json').write_text(json.dumps({'cases':cases,'historical':hist,'norms':norms,'roles':roles,'model':model,'evidence_keys':keys,'source_sha256':{str(p):hashlib.sha256(p.read_bytes()).hexdigest() for p in [ITEMS,LEDGER]}},ensure_ascii=False,indent=2))
print('Created workbook; cases=',len(cases),'evidence=',len(keys),'historical=',hist)
