build_report_preview.py 17 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. """Create a local report form using existing case material, without new research claims."""
  2. from pathlib import Path
  3. import hashlib
  4. import html
  5. import json
  6. OUT = Path(__file__).resolve().parent
  7. ROOT = OUT.parents[1]
  8. def esc(value):
  9. return html.escape(value, quote=True)
  10. def main():
  11. source = ROOT / 'docs/workshop-live-case-r5-20261109/rounds/R05/local-review-sources/samyang-cn-seq152.html'
  12. assert '油炸面' in source.read_text(encoding='utf-8')
  13. rows = [
  14. ('康师傅 BIG 香辣牛肉', '144g 盒装', '泡 → 沥水 → 拌',
  15. '前轮转述;本款包装说明和时间尚未取得',
  16. '观察盒体如何承担制备、排水与食用;不先认定少洗器具就是购买理由。', '转述待核'),
  17. ('三养 经典原味火鸡', '140g 袋装', '煮 → 留部分水 → 加酱翻炒',
  18. '中国官网明确写油炸面[S1];步骤来自前轮美国说明,包装版本对应待核[S2]',
  19. '将工艺与食用方式分开;正式使用对照先固定本次包装版本。', '官方声明 / 版本待核'),
  20. ('有你一面 葱油', '118g 袋装', '煮 → 沥水 → 拌',
  21. '刀削宽面和煮制步骤来自零售/社区转述;未见本款完整包装',
  22. '保留煮制方案作为参照,查什么体验可能值得额外步骤;不预设面体更好。', '转述待核'),
  23. ('阿宽 土豆泥泥拌面烧烤', '84g 杯装', '注水 → 等待 → 搅拌',
  24. '免滤水为前轮品牌/系列口径转述;本款配料和注水量待核',
  25. '研究计量和稠度控制;酱体结合水的解释不能替代面体复水验证。', '转述待核'),
  26. ]
  27. table = ''.join('<tr><th scope="row">' + esc(name) + '<small>' + esc(spec) + '</small></th><td><b>' + esc(steps) + '</b><span class="state">' + esc(state) + '</span><small>' + esc(basis) + '</small></td><td>' + esc(meaning) + '</td></tr>' for name, spec, steps, basis, meaning, state in rows)
  28. tasks = [
  29. ('器具是否构成限制', '找真实购买或制作经历:可用什么器具,是否因此放弃某款或换一种吃法。',
  30. '也查愿意煮制、把额外步骤视为值得的人。', '若器具不可得确实改变选择,再优先研究仅热水可完成的路线。'),
  31. ('排水还是配比更费事', '分别看倒水、残水、加水计量、搅拌与实际应对,保留失败及成功的上下文。',
  32. '也查不觉得排水麻烦,或比起省一步更在意干湿口感的表达。', '若困难集中在排水,比较排水结构;若在稠度与计量,比较注水与混合控制。'),
  33. ('少清理要付出什么', '查实际清洗动作、现有替代,以及为了口感、分量或风味接受的额外准备。',
  34. '没有价格和选择依据时,支付意愿保持未知。', '判断是减少器具、改变容器,还是保留现有制作方式;不把便利直接排在风味之前。'),
  35. ]
  36. task_cards = ''.join('<article class="task"><span class="pill">待验证任务</span><h3>' + esc(title) + '</h3><p>' + esc(question) + '</p><p class="muted"><b>反向材料:</b>' + esc(counter) + '</p><p class="implication">' + esc(use) + '</p></article>' for title, question, counter, use in tasks)
  37. document = '''<!doctype html>
  38. <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
  39. <title>拌面准备过程:哪一步值得简化?</title><style>
  40. :root{--ink:#16352e;--sub:#52635b;--paper:#f4f5ef;--white:#fffefa;--line:#d7dfd6;--green:#245646;--soft:#e8eee4;--amber:#895d26;--sand:#f4ebda}*{box-sizing:border-box}html{scroll-behavior:smooth;scroll-padding-top:92px}body{margin:0;background:var(--paper);color:var(--ink);font:16px/1.8 "Microsoft YaHei","PingFang SC",sans-serif}a{color:#1c6352;text-underline-offset:3px}button{font:inherit}header{background:var(--green);color:#f9faef;padding:48px max(22px,calc((100vw - 1160px)/2)) 42px}.eyebrow{font-size:14px;letter-spacing:.1em;color:#d9e3ce}h1{font-size:clamp(29px,4vw,46px);line-height:1.35;margin:12px 0 18px;max-width:860px}header p{max-width:850px;margin:0;color:#e5ecdc}.scope{display:flex;gap:9px;flex-wrap:wrap;margin-top:22px}.scope span{font-size:14px;border:1px solid #96b39f;border-radius:20px;padding:3px 11px}.local-note{max-width:1204px;margin:20px auto 0;padding:0 22px;font-size:14px;color:var(--sub)}nav{position:sticky;top:0;z-index:3;background:#f4f5eff5;border-bottom:1px solid var(--line);backdrop-filter:blur(8px)}.nav-inner{max-width:1204px;margin:auto;padding:12px 22px;display:flex;gap:10px;align-items:center;justify-content:space-between}.anchors{display:flex;gap:18px;overflow-x:auto;white-space:nowrap}.anchors a{font-size:15px;text-decoration:none;padding:5px 0}.modes{display:flex;flex-shrink:0;gap:4px;border:1px solid var(--line);background:var(--white);padding:3px;border-radius:9px}.modes button{border:0;border-radius:6px;background:transparent;padding:5px 10px;font-size:14px;color:var(--green);cursor:pointer}.modes button[aria-pressed=true]{background:var(--green);color:white}button:focus-visible,a:focus-visible{outline:3px solid #bf974c;outline-offset:3px}main{max-width:1204px;margin:auto;padding:18px 22px 60px}section{margin:28px 0 38px;scroll-margin-top:90px}.section-title{display:flex;gap:13px;align-items:baseline;border-bottom:1px solid var(--line);padding-bottom:12px;margin-bottom:22px}.section-title>span{font-size:14px;color:#617760}h2{font-size:26px;line-height:1.5;margin:0}h3{font-size:19px;line-height:1.5;margin:9px 0 10px}p{margin:0 0 14px}.lead{font-size:18px;line-height:1.9;max-width:1020px}.muted{color:var(--sub)}.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.finding,.task{background:var(--white);border:1px solid var(--line);border-radius:13px;padding:23px;min-width:0;overflow-wrap:anywhere}.pill{font-size:13px;background:var(--soft);color:var(--green);padding:3px 8px;border-radius:5px;display:inline-block}.pill.pending{background:var(--sand);color:var(--amber)}.finding p:last-child,.task p:last-child{margin-bottom:0}.finding small{display:block;color:var(--sub);font-size:14px;margin-top:12px}.callout{background:var(--sand);border-left:4px solid #b68d46;padding:17px 20px;margin:20px 0;border-radius:0 9px 9px 0}.callout p:last-child{margin:0}.table-scroll{max-width:100%;overflow-x:auto;border:1px solid var(--line);border-radius:12px;background:var(--white)}table{width:100%;min-width:820px;border-collapse:collapse;font-size:15px}caption{text-align:left;padding:15px 18px;color:var(--sub);font-size:14px}th,td{text-align:left;vertical-align:top;padding:17px;border-bottom:1px solid var(--line)}thead th{background:var(--soft)}tbody th{width:23%;font-weight:600}td:nth-child(2){width:40%}tr:last-child th,tr:last-child td{border-bottom:0}small{display:block;font-size:14px;font-weight:400;color:var(--sub);line-height:1.7;margin-top:6px}.state{display:inline-block;margin:8px 0 2px;border-radius:5px;background:#eef1eb;color:var(--sub);padding:2px 7px;font-size:13px}.implication{border-top:1px solid var(--line);padding-top:12px}.split{display:grid;grid-template-columns:1fr 1fr;gap:18px}.box{border:1px solid var(--line);border-radius:12px;padding:23px;background:var(--white);min-width:0}.box ul{padding-left:21px;margin:0}.box li{margin:8px 0}.source{padding:15px 0;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.source b{margin-right:9px}.source p{margin:5px 0 0;color:var(--sub);font-size:14px}.sources-title{font-size:19px}.footer{border-top:1px solid var(--line);padding-top:18px;color:var(--sub);font-size:14px}.body-note{font-size:14px;color:var(--sub)}body[data-mode=brief] [data-detail]{display:none}
  41. body[data-mode=brief] .anchors a[href="#products"]{display:none}
  42. @media(max-width:850px){.grid{grid-template-columns:minmax(0,1fr)}.split{grid-template-columns:minmax(0,1fr)}.nav-inner{align-items:stretch;flex-direction:column;gap:9px}.anchors{gap:19px}.modes{align-self:flex-start}section{scroll-margin-top:130px}.finding,.task,.box{padding:20px}header{padding-top:34px;padding-bottom:32px}h2{font-size:23px}}
  43. @media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
  44. @media print{body{background:white;font-size:11pt}header{background:white;color:#16352e;padding:10mm 0 4mm}header p,.eyebrow{color:#52635b}.scope span{border-color:#aab7aa}nav,.local-note{position:static}.modes{display:none}main{padding:0}body[data-mode=brief] [data-detail]{display:block!important}.grid{display:block}.finding,.task{margin-bottom:12px;break-inside:avoid}.split{display:block}.box{margin-bottom:14px}section{break-inside:auto}.table-scroll{overflow:visible}table{min-width:0;font-size:10pt}th,td{padding:8px}.source a{overflow-wrap:anywhere}.footer{font-size:10pt}}
  45. </style></head><body data-mode="full">
  46. <header><div class="eyebrow">康师傅 · 拌面产品研究</div><h1>准备一份拌面,<br>哪一步值得简化?</h1><p>从四款产品的制备方案出发,把器具限制、排水和清理分开,再判断哪些改变值得研发投入。</p><div class="scope"><span>产品对照与研究问题</span><span>开放探索 · 尚未选定方向</span></div></header>
  47. <p class="local-note">本地报告形式样稿 · 使用已收到的案例材料,未新增消费者研究、购样或试验。本文未替代真实对话与原报告。</p>
  48. <nav aria-label="报告目录"><div class="nav-inner"><div class="anchors"><a href="#answer">当前判断</a><a href="#products">产品对照</a><a href="#needs">需求与取舍</a><a href="#research">研发衔接</a><a href="#sources">依据</a></div><div class="modes" role="group" aria-label="阅读方式"><button data-set-mode="full" aria-pressed="true">完整报告</button><button data-set-mode="brief" aria-pressed="false">汇报提要</button></div></div></nav>
  49. <main><section id="answer"><div class="section-title"><span>01</span><h2>先看使用条件,再比较“省事”</h2></div><p class="lead">现有材料已能提出三种不同问题:能不能使用锅灶,是否需要排水,以及最后要清洗什么。它们可以同时发生,不能合并成一个“便利性”排名。下一步应查哪些条件真的改变选择,再讨论产品改动。</p>
  50. <div class="grid"><article class="finding"><span class="pill">可核声明</span><h3>工艺标签与吃法分开看</h3><p>三养中国官网的140g火鸡面明确写“食品的类型:油炸面”。不能因产品需要煮制,就把它归为非油炸。</p><small>依据:官方中国版产品页[S1]。工艺声明不证明具体食用性能。</small></article>
  51. <article class="finding"><span class="pill pending">研究解释</span><h3>容器可能承担不同任务</h3><p>前轮材料分别提及滤水结构与注水线。两者提示的是排水和配比控制,值得分开研究;本款完整包装依据仍待补齐。</p><small>依据:前轮材料转述[S2];不据此证明实际省时、少洗或复购。</small></article>
  52. <article class="finding"><span class="pill pending">决定性未知</span><h3>还不能判断哪一步最值钱</h3><p>本轮未取得可独立核验的四款对应消费者证言。尚不能把商品用不用锅,转成消费者偏好或愿付溢价。</p><small>下一步寻找实际购买、制作、调整和放弃的经历,同时保留反例。</small></article></div>
  53. <aside class="callout"><b>这份报告支持的下一项讨论</b><p>先比较“器具不可得”与“步骤麻烦”分别会怎样改变选择;保留口感与风味的对照。当前不选定免沥水、面体工艺或某种容器为最终路线。</p></aside></section>
  54. <section id="products" data-detail><div class="section-title"><span>02</span><h2 id="products-title">同为拌面,准备过程怎样不同</h2></div><p>四款用于理解不同方案。风味、规格和版本并不相同,不能把它们当作控制了其他变量的性能试验。</p><p class="body-note" id="products-note">产品身份、步骤依据与研究含义分开记录;转述内容尚未完成同款包装核准。窄屏可左右滑动查看各列。</p><div class="table-scroll" tabindex="0" role="region" aria-label="四款产品对照,可横向滚动"><table aria-labelledby="products-title" aria-describedby="products-note"><thead><tr><th scope="col">产品参照</th><th scope="col">准备过程与依据</th><th scope="col">对研发比较的意义</th></tr></thead><tbody>__ROWS__</tbody></table></div><p class="body-note">没有可比成交价、实测复水表现或成本数据,本表不比较贵便宜,也不排序品牌体验。</p></section>
  55. <section id="needs"><div class="section-title"><span>03</span><h2>需要解释的需求,是怎样的取舍</h2></div><p>以下是研究任务,尚不是消费者洞察结论。满意和不满都要看:有人希望少一步,也可能愿意多做一步换取特定口感。</p><div class="grid">__TASKS__</div></section>
  56. <section id="research"><div class="section-title"><span>04</span><h2>把下一轮研究接到研发选择</h2></div><div class="split"><article class="box"><span class="pill">消费者研究 · 拟开展</span><h3>找会改变选择的具体经历</h3><ul><li>先恢复已有材料,再按任务缺口使用实际接入的VOC取得公开评价或讨论。</li><li>区分购买动机、使用感受、实际调整与放弃;不把点赞、产品宣称或未提及当需求强度。</li><li>同款证言与泛品类任务材料分列,原话带可点击来源与对应对象。</li><li>找到支持与反例后,判断是否足以区分研究路线;渠道不可用或仍无关键依据时交有限判断,避免无限扩搜。</li></ul></article>
  57. <article class="box"><span class="pill pending">产品验证 · 尚未执行</span><h3>把性能与机理分层验证</h3><ul><li>先固定产品、包装版本与说明,建立各款正常制备基准。</li><li>成品使用对照可观察实际动作、排水或计量失误、清理负担与感官变化。</li><li>若要解释工艺原因,再核面体厚度、预熟化等参数并设计相应对照;不因缺参数就宣称任何测试都不能做。</li><li>目标来自实际基准、任务或适用标准;当前不设置任意省时、硬度或支付意愿门槛。</li></ul></article></div><p class="body-note">上述是建议的研究与验证安排,未开展访谈、问卷、购样或实验。真实资源、产品选择与企业批准仍待业务方确定。</p></section>
  58. <section id="sources"><div class="section-title"><span>05</span><h2>依据与适用范围</h2></div><div class="source"><b>[S1]</b><a href="https://www.samyangfoods.com/chn/brand/view.do?seq=152" target="_blank" rel="noopener noreferrer">三养中国官网 · 三养火鸡面140克</a><p>2026-11-09本地核读页面,列“食品的类型:油炸面”。来源支持该页面版本的品牌声明;美国包装及当前购买版本的对应尚未核实。</p></div>
  59. <div class="source"><b>[S2]</b><a href="https://s3.fmode.cn/user/DpPsnlBm0K/report/ksf-four-products-r1-20261109.html" target="_blank" rel="noopener noreferrer">前轮四款拌面产品对照 · 收到的报告原件</a><p>本地已读取该文件及两张内嵌图片;其他产品属性仍是前轮资料的转述。原件中的推断、来源类型和“核准”措辞不替代同款核验或实测。</p></div>
  60. <div class="source"><b>本报告新增内容</b><span>对准备过程、研究取舍与下一项判别的组织和研究建议。</span><p>未增加市场规模、消费者原话、需求频次或工厂能力事实;候选关系均保持研究状态。</p></div></section>
  61. <footer class="footer">本轮能用于讨论研究重点,尚不能决定哪个方案更受欢迎、愿付多少或哪项工艺性能更好。后续按实际证据更新受影响判断。</footer></main>
  62. <script>for(const b of document.querySelectorAll('[data-set-mode]'))b.addEventListener('click',()=>{document.body.dataset.mode=b.dataset.setMode;for(const x of document.querySelectorAll('[data-set-mode]'))x.setAttribute('aria-pressed',String(x===b))});</script>
  63. </body></html>'''.replace('__ROWS__', table).replace('__TASKS__', task_cards)
  64. target = OUT / '拌面研究报告形式样稿.html'
  65. target.write_text(document, encoding='utf-8', newline='\n')
  66. (OUT / 'sample-scope.json').write_text(json.dumps({
  67. 'kind': 'local-report-form-preview-based-on-received-material',
  68. 'path': target.name, 'sha256': hashlib.sha256(target.read_bytes()).hexdigest(),
  69. 'newConsumerResearch': False, 'physicalTesting': False,
  70. 'businessSaved': False, 'newDigitalHumanReply': False,
  71. 'directlyCheckedProductStatement': 'CN official 140g page lists fried noodles',
  72. 'otherProductFacts': 'relayed and version-limited; not independently verified',
  73. 'borrowedReferenceIndustryData': False, 'remoteInstalled': False,
  74. }, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
  75. print(json.dumps({'preview': str(target), 'productReferences': len(rows),
  76. 'researchQuestions': len(tasks), 'newBusinessEvidence': False}, ensure_ascii=False))
  77. if __name__ == '__main__':
  78. main()