| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- """Deterministic fictional industry data; no real users, scraped quotes or production IO."""
- import hashlib
- import json
- from pathlib import Path
- ROOT = Path(__file__).resolve().parents[1]
- seed = json.loads((ROOT / 'data/preview-seed.json').read_text(encoding='utf-8'))
- DEMO = seed['groups'][0]['demoId']
- COMMON = dict(demoId=DEMO, dataMode='sample', isSynthetic=True, provenanceType='synthetic_demo')
- ext = dict(version='ksf.synthetic-extension.20261103.v2', demoId=DEMO, isSynthetic=True,
- note='全部为规则生成的虚构观察;不是实际访谈、商品报价、销量或营养检测。',
- voices=[], observations=[], prices=[], citations=[], researchTasks=[], collaborationRecords=[])
- contexts = [
- ['工位午餐', '加班后在茶水间', '通勤前的早餐', '午休只剩十五分钟', '下午临时加餐', '晚归后独自吃饭'],
- ['宿舍晚自习后', '考试周的午饭', '同寝分享夜宵', '周末留校晚饭', '图书馆回宿舍', '月末控制餐费'],
- ['有炉具的露营', '只有保温水壶的徒步', '长途高铁上', '服务区停留', '雨天帐篷里', '出差住酒店'],
- ['运动后便餐', '午间轻食', '晚间控制总能量', '轮班后的早餐', '通勤时补一餐', '周末在家吃面'],
- ['两人晚餐', '独自加班后回家', '孩子睡后的夜宵', '家庭周末午饭', '冰箱剩菜搭配', '临时替代外卖'],
- ['第一次尝红酸汤', '连续第二次吃地域风味', '朋友推荐的椒麻面', '周末尝新', '节前囤几种口味', '与室友分吃新口味'],
- ]
- purposes = ['这顿当正餐', '只是两餐之间垫一下', '想和同伴分着吃', '主要想喝热汤', '只是嘴馋试味道', '晚饭之后又有点饿']
- tools = ['手边只有热水,没有锅', '可以用电煮锅,愿意多等三分钟', '微波炉要排队,暂时不考虑', '需要倒水的做法不方便', '有锅但不想洗太多餐具', '有小炉子但用水有限']
- tastes = ['喜欢葱香但不想味道留在房间', '想要番茄酸味,不喜欢甜腻', '希望辣油单独放,可以自己决定', '更在意面条有嚼劲', '喜欢浓酱,清淡不是我的首选', '汤鲜就行,不追求地名听起来正宗']
- behaviors = ['我平时就加鸡蛋,不代表这份面不够', '这次吃一份刚好,不打算再加主食', '如果是加餐,我可能只吃半份', '愿意配蔬菜是为了换口感', '偶尔会再买饭团,要先区分是不是没吃正餐', '包装里有没有碗,比多一包酱更影响我选择']
- budgets = ['每份五元以内才会常买', '七元上下可以接受,要把运费算进去', '愿意多花两元换方便的制备', '组合装先算清楚每顿多少钱', '促销会囤,平时按需要买', '价格不是第一位,吃得方便更重要']
- frequencies = ['每周大约一次', '一个月两三次', '只在赶时间时', '最近想换口味时', '出差或值班才', '和朋友一起时']
- channels = ['模拟社媒分享', '模拟电商评价', '模拟追问记录', '模拟使用日记']
- for group_index, group in enumerate(seed['groups']):
- group_projects = [p for p in seed['projects'] if p['groupId'] == group['id']]
- for i in range(2000):
- # A coprime permutation spreads all dimensions rather than repeating six stock quotes.
- code = (i * 7919 + group_index * 137) % (6 ** 7)
- digits = []
- for _ in range(7):
- digits.append(code % 6); code //= 6
- a, b, c, d, e, f, g = digits
- raw = f'{contexts[group_index][a]},{purposes[b]}。{tools[c]};{tastes[d]}。{behaviors[e]}。{budgets[f]},我{frequencies[g]}会考虑方便面。'
- project = group_projects[i % 2]
- ext['voices'].append(dict(COMMON, id=f'SIM-VOICE-{group_index+1}-{i+1:04d}', groupId=group['id'], projectId=project['id'],
- title=f'{contexts[group_index][a]}|{purposes[b]}', rawText=raw, channel=channels[i % 4],
- authorLabel=f'模拟观察{group_index+1}-{i+1:04d}', verificationState='synthetic_not_real_interview',
- mealPurpose=purposes[b], preparation=tools[c], budget=budgets[f], behavior=behaviors[e],
- contentState=['相关分享', '相关分享', '广告待排除', '内容不全', '相关分享'][i % 5]))
- packaging = ['袋装', '碗装', '杯装', '双份组合', '家庭六连包']
- flavors = ['番茄鸡蛋', '葱油酱香', '菌菇清汤', '轻辣芝士', '红酸汤', '川香椒麻']
- for i in range(720):
- p = seed['projects'][i % 12]
- pack_count = [1, 1, 2, 6, None][i % 5]
- weight = [85, 95, 110, 125, None][(i // 5) % 5]
- obs = dict(COMMON, id=f'SIM-OBS-{i+1:04d}', groupId=p['groupId'], projectId=p['id'],
- title=f'模拟系列{(i//30)+1:02d} · {flavors[(i//5)%6]} · {packaging[i%5]}',
- productName=f'模拟系列{(i//30)+1:02d}', flavor=flavors[(i//5)%6], packaging=packaging[i%5],
- netWeightG=weight, packCount=pack_count, preparation=['热水复水候选', '需煮制候选', '条件待核'][i % 3],
- nutritionClaim=None, actualSku=None, note='虚构商品观察,未关联真实在售商品;营养与工艺未知。')
- ext['observations'].append(obs)
- for j, day in enumerate(['2026-10-12', '2026-10-19', '2026-10-26', '2026-11-02']):
- count = pack_count
- paid = round((4.5 + (i % 17) * .37 + j * .25) * (count or 1), 2)
- comparable = bool(count and weight and j != 2)
- ext['prices'].append(dict(COMMON, id=f'SIM-PRICE-{i+1:04d}-{j+1}', observationId=obs['id'], projectId=p['id'], groupId=p['groupId'],
- date=day, channel=['模拟电商自营', '模拟商超', '模拟促销组合', '模拟便利店'][j], currency='CNY',
- pricePaid=paid, includesShipping=j == 0, packCount=count, netWeightG=weight,
- unitPricePer100G=round(paid / (count * weight) * 100, 2) if comparable else None,
- comparisonState='可按演示规格比较' if comparable else '口径待核,不参与单位价比较'))
- for i in range(354):
- source = ext['voices'][(i * 31) % len(ext['voices'])]
- quote = source['rawText'].split(';')[0] + ';'
- ext['citations'].append(dict(COMMON, id=f'SIM-EVIDENCE-{i+1:03d}', groupId=source['groupId'], projectId=source['projectId'],
- sourceId=source['id'], quote=quote, locator=dict(start=0, end=len(quote)), reviewState='demo_only',
- stance=['support', 'contradict', 'unresolved'][i % 3], claim='用于演示不同场景与其他解释,并非已验证用户需求。'))
- for i in range(180):
- p = seed['projects'][i % 12]
- state = ['done', 'in_progress', 'pending', 'failed', 'reused'][i % 5]
- ext['researchTasks'].append(dict(COMMON, id=f'SIM-RESEARCH-{i+1:03d}', groupId=p['groupId'], projectId=p['id'],
- ownerMemberId=p['memberIds'][(i//12) % 6], scope=['核对面体制备说明', '区分正餐与加餐', '检查商品组合规格', '核对相反材料', '追问搭配原因'][i % 5],
- state=state, requiredMaterial=['包装制备原文', '带情境的追问记录', '规格与价格截图'][i % 3],
- failureReason='演示来源不可用,保留失败并待补其他来源' if state == 'failed' else None,
- note='模拟任务回放;没有执行真实采集。'))
- for i in range(312):
- p = seed['projects'][i % 12]
- owner = seed['members'][(i * 7) % 36]
- body = ['这条声音是否可能来自加餐?建议先问清楚当餐目的。', '已在模拟资料中补上制备条件,原方案版本保留。',
- '支持继续研究,但不能把搭配习惯直接当成份量不足。', '价格缺少包数,请先核规格,暂不参与单位价比较。',
- '工艺条件与目标场景冲突,建议保留暂停理由并分配补证任务。', '课堂方案可展示,实际成本和营养参数仍待核实。'][i % 6]
- ext['collaborationRecords'].append(dict(COMMON, id=f'SIM-COLLAB-{i+1:03d}', groupId=p['groupId'], projectId=p['id'],
- ownerMemberId=owner['id'], user=owner['displayName'], kind=['质疑', '答复', '补证', '版本说明', '取舍', '复核'][i % 6], body=body,
- at=f'2026-11-0{1+i%3}T{9+(i%8):02d}:{i%60:02d}:00+08:00', note='模拟协作记录'))
- counts = {key: len(ext[key]) for key in ['voices', 'observations', 'prices', 'citations', 'researchTasks', 'collaborationRecords']}
- assert len({v['rawText'] for v in ext['voices']}) == 12000
- assert sum(counts.values()) == 16446
- ext['counts'] = counts
- target = ROOT / 'src/assets/showcase/demo-extension.json'
- target.write_text(json.dumps(ext, ensure_ascii=False, separators=(',', ':')) + '\n', encoding='utf-8')
- print(json.dumps(dict(file=str(target), bytes=target.stat().st_size, counts=counts, uniqueVoices=12000,
- totalEvidence=len(seed['evidence'])+counts['citations'], sha256=hashlib.sha256(target.read_bytes()).hexdigest()), ensure_ascii=False))
|