| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- #!/usr/bin/env node
- const fs = require('fs');
- const os = require('os');
- const path = require('path');
- const { spawnSync } = require('child_process');
- const { buildRecords, readCsv } = require('./history-dataset-from-csv');
- const { auditRecords, loadRecords } = require('./historical-dataset-audit');
- function main() {
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-history-from-csv-'));
- const input = path.join(dir, 'history.csv');
- const output = path.join(dir, 'history-dataset');
- fs.writeFileSync(input, withBom([
- 'brief编号,项目名称,类目,客户原始Brief,历史人工补号量基线,参考账号或视频,平台,博主名称,主页链接,人工复核标签,客户选择,拒绝原因',
- 'dha-2026,DHA历史Brief,母婴,母婴DHA真实历史Brief,含预算和参考账号,6,https://www.xiaohongshu.com/user/profile/ref-dha,小红书,营养师妈妈,https://example.com/dha/a,可直接发客户,客户选中,',
- 'dha-2026,DHA历史Brief,母婴,母婴DHA真实历史Brief,含预算和参考账号,6,https://www.xiaohongshu.com/user/profile/ref-dha,小红书,泛生活号,https://example.com/dha/b,跑偏,客户拒绝,内容方向偏离DHA',
- 'skin-2026,修护历史Brief,护肤,敏感肌修护真实历史Brief,含禁投项,4,https://www.xiaohongshu.com/explore/ref-skin,小红书,敏感肌成分党,https://example.com/skin/a,商务复核,客户选中,',
- 'skin-2026,修护历史Brief,护肤,敏感肌修护真实历史Brief,含禁投项,4,https://www.xiaohongshu.com/explore/ref-skin,小红书,硬广导购号,https://example.com/skin/b,调性不符,客户拒绝,硬广感太强',
- 'home-2026,家清历史Brief,家清,家清清洁真实历史Brief,含人群和场景,5,https://www.douyin.com/user/ref-home,抖音,家居清洁实验室,https://example.com/home/a,可直接发客户,客户选中,',
- 'home-2026,家清历史Brief,家清,家清清洁真实历史Brief,含人群和场景,5,https://www.douyin.com/user/ref-home,抖音,装修设计号,https://example.com/home/b,跑偏,客户拒绝,内容重装修设计'
- ].join('\n')), 'utf8');
- const records = buildRecords(readCsv(input));
- assert(records.length === 3, 'should group rows into three history briefs');
- assert(records[0].manualFinalList.length === 2, 'should keep two manual list rows per brief');
- assert(records[0].customerFeedback.length === 2, 'should keep customer feedback rows');
- assert(records[0].referenceLinks.length === 1, 'should dedupe reference links per brief');
- const result = spawnSync(process.execPath, [
- path.join(__dirname, 'history-dataset-from-csv.js'),
- '--input',
- input,
- '--output',
- output
- ], { cwd: path.resolve(__dirname, '..'), encoding: 'utf8' });
- if (result.status !== 0) {
- process.stderr.write(result.stderr || result.stdout || '');
- throw new Error('history-dataset-from-csv failed');
- }
- assert(fs.existsSync(path.join(output, 'dha-2026.json')), 'should write per-brief json file');
- const summary = auditRecords({ inputPath: output, records: loadRecords(output), minBriefs: 3 });
- assert(summary.acceptance.readyForLongRun, 'converted dataset should be ready for long run');
- assert(summary.acceptance.readyForCustomerEffectProof, 'converted dataset should be ready for customer effect proof');
- assert(summary.withManualSupplementBaseline === 3, 'converted dataset should include manual supplement baselines');
- const missingInput = path.join(dir, 'history-missing.csv');
- fs.writeFileSync(missingInput, withBom([
- 'brief编号,项目名称,类目,客户原始Brief,参考账号或视频,平台,博主名称,主页链接,人工复核标签,客户选择,拒绝原因',
- 'missing-2026,缺基线Brief,护肤,缺人工补号量基线,https://example.com/ref,小红书,敏感肌记录者,https://example.com/missing/a,可直接发客户,客户选中,'
- ].join('\n')), 'utf8');
- const missingRecords = buildRecords(readCsv(missingInput));
- const missingSummary = auditRecords({
- inputPath: missingInput,
- records: missingRecords.map((record, index) => ({ file: `csv-row-${index}.json`, record })),
- minBriefs: 1
- });
- assert(missingSummary.acceptance.readyForLongRun, 'missing baseline should still allow basic long-run readiness');
- assert(!missingSummary.acceptance.readyForCustomerEffectProof, 'missing baseline should block customer effect proof');
- console.log(JSON.stringify({
- ok: true,
- input,
- output,
- briefCount: records.length,
- readyForCustomerEffectProof: summary.acceptance.readyForCustomerEffectProof
- }, null, 2));
- }
- function withBom(text) {
- return `\uFEFF${text}`;
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- if (require.main === module) main();
|