Liga acervo documental e guias a jornada do investidor
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
const fs = require('fs');
|
||||
const d = 'storage/app/private/investmoz-v2/';
|
||||
const read = (f) =>
|
||||
JSON.parse(fs.readFileSync(d + f, 'utf8').replace(/^\uFEFF/, ''));
|
||||
const norm = (s) =>
|
||||
s
|
||||
.replace(/[\u200b\ufeff]/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
.replace(
|
||||
'2. Visão, objectivos e princípios',
|
||||
'2. Visão, objetivos e princípios',
|
||||
);
|
||||
const clean = (a) =>
|
||||
a
|
||||
.filter((p) => !/(ndice|toc)/i.test(p.style) && norm(p.text))
|
||||
.map((p) => ({ ...p, text: norm(p.text) }));
|
||||
const a = clean(read('master-paragraphs.json')),
|
||||
b = clean(read('master-paragraphs-reopened.json'));
|
||||
const pairs = [];
|
||||
function row(x0, x1, y0, y1, reverse) {
|
||||
const size = y1 - y0;
|
||||
let prev = new Uint32Array(size + 1),
|
||||
curr = new Uint32Array(size + 1);
|
||||
for (let i = 0; i < x1 - x0; i++) {
|
||||
curr[0] = 0;
|
||||
const text = a[reverse ? x1 - 1 - i : x0 + i].text;
|
||||
for (let j = 1; j <= size; j++)
|
||||
curr[j] =
|
||||
text === b[reverse ? y1 - j : y0 + j - 1].text
|
||||
? prev[j - 1] + 1
|
||||
: Math.max(prev[j], curr[j - 1]);
|
||||
[prev, curr] = [curr, prev];
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
function align(x0, x1, y0, y1) {
|
||||
while (x0 < x1 && y0 < y1 && a[x0].text === b[y0].text)
|
||||
pairs.push([x0++, y0++]);
|
||||
const tail = [];
|
||||
while (x0 < x1 && y0 < y1 && a[x1 - 1].text === b[y1 - 1].text)
|
||||
tail.push([--x1, --y1]);
|
||||
if (x0 < x1 && y0 < y1) {
|
||||
if (x1 - x0 === 1) {
|
||||
for (let j = y0; j < y1; j++)
|
||||
if (a[x0].text === b[j].text) {
|
||||
pairs.push([x0, j]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
let mid = (x0 + x1) >> 1;
|
||||
let f = row(x0, mid, y0, y1, false),
|
||||
r = row(mid, x1, y0, y1, true),
|
||||
split = 0,
|
||||
best = -1;
|
||||
for (let j = 0; j <= y1 - y0; j++)
|
||||
if (f[j] + r[y1 - y0 - j] > best) {
|
||||
best = f[j] + r[y1 - y0 - j];
|
||||
split = j;
|
||||
}
|
||||
align(x0, mid, y0, y0 + split);
|
||||
align(mid, x1, y0 + split, y1);
|
||||
}
|
||||
}
|
||||
pairs.push(...tail.reverse());
|
||||
}
|
||||
align(0, a.length, 0, b.length);
|
||||
const ma = new Set(pairs.map((p) => p[0])),
|
||||
mb = new Set(pairs.map((p) => p[1]));
|
||||
const removed = a.filter((p, i) => !ma.has(i)),
|
||||
added = b.filter((p, i) => !mb.has(i));
|
||||
const result = {
|
||||
method: 'LCS paragraph alignment; exclude TOC and empty paragraphs; normalize heading orthography only',
|
||||
matched: pairs.length,
|
||||
removed,
|
||||
added,
|
||||
mapping: pairs.map(([i, j]) => ({ old: a[i].p, current: b[j].p })),
|
||||
};
|
||||
fs.writeFileSync(
|
||||
d + 'master-aligned-changes.json',
|
||||
JSON.stringify(result, null, 2),
|
||||
);
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
matched: pairs.length,
|
||||
removed: removed.length,
|
||||
added: added.length,
|
||||
addedTexts: added.slice(0, 8),
|
||||
removedFirst: removed.slice(0, 4),
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,150 @@
|
||||
const fs = require('node:fs');
|
||||
const dir = 'storage/app/private/investmoz-v2/';
|
||||
const p = JSON.parse(fs.readFileSync(dir + 'master-paragraphs.json', 'utf8'));
|
||||
const diff = JSON.parse(
|
||||
fs.readFileSync('docs/investmoz/v2/repeticoes-reconciliacao.json', 'utf8'),
|
||||
).repeated.filter((x) => x.comparison === 'historical_content_differs');
|
||||
const proposals = [
|
||||
[
|
||||
'Índices e paginação',
|
||||
'Os intervalos abrangem sumários extensos; a diferença não equivale a regra funcional.',
|
||||
'Preservar ambos os índices e usar títulos/identificadores de parágrafo, sem páginas impressas como chave.',
|
||||
],
|
||||
[
|
||||
'Linhas vazias',
|
||||
'Não altera o fluxo BM/AT.',
|
||||
'Conservar o texto comum; interoperabilidade permanece fora da fase.',
|
||||
],
|
||||
[
|
||||
'Linhas vazias',
|
||||
'Não altera benefícios nem ativa cálculos.',
|
||||
'Unir o conteúdo comum, conservando referências de ambas as ocorrências.',
|
||||
],
|
||||
[
|
||||
'Fluxo deslocado e texto introdutório',
|
||||
'A primeira ocorrência termina com a introdução de outro documento; a segunda contém a tabela de 14 etapas, também repetida após §13.',
|
||||
'Separar introdução e tabela; reutilizar a tabela como guia de preparação, sem conceder benefícios nem executar atos externos.',
|
||||
],
|
||||
[
|
||||
'Comprimento dos espaços de preenchimento',
|
||||
'Não altera nomes dos campos.',
|
||||
'Reutilizar os mesmos campos; preservar os exemplares originais, sem escolher por número de ocorrência.',
|
||||
],
|
||||
[
|
||||
'Vazios/estrutura de apresentação',
|
||||
'Não prova alteração de condições jurídicas.',
|
||||
'Conservar todas as condições comuns e referências; nenhuma validação jurídica automática.',
|
||||
],
|
||||
[
|
||||
'Comprimento dos espaços de preenchimento',
|
||||
'Não altera a cobertura pedida.',
|
||||
'Consolidar os cabeçalhos/campos; preservar linhas e proveniências.',
|
||||
],
|
||||
[
|
||||
'Selecção / Seleção',
|
||||
'Diferença ortográfica, sem alteração da obrigatoriedade indicada na fonte.',
|
||||
'Usar Seleção na orientação; não impor requisito ao ato sem fundamento específico.',
|
||||
],
|
||||
[
|
||||
'Tabela de fluxo anexada a §13',
|
||||
'A tabela de 14 etapas foi incluída no intervalo da primeira ocorrência; as ações de governação comuns permanecem.',
|
||||
'Manter §13 com ações comuns; referenciar separadamente o guia do fluxo. Nomeações/acordos não são executados pelo portal.',
|
||||
],
|
||||
];
|
||||
const esc = (s) =>
|
||||
String(s)
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>');
|
||||
let html =
|
||||
'<!doctype html><html lang="pt"><meta charset="utf-8"><title>Nove diferenças do mestre</title><style>body{font:16px system-ui;margin:2rem}table{width:100%;table-layout:fixed;border-collapse:collapse}td,th{border:1px solid #ccc;padding:12px;vertical-align:top}p{white-space:pre-wrap;overflow-wrap:anywhere}.number{color:#666;font-size:12px}section{margin-bottom:3rem}</style><h1>Nove diferenças lado a lado</h1><p>Extração histórica identificada pelo hash do índice. Não certifica o original atualmente aberto. As ocorrências são A/B, não versões oficiais sucessivas. Sem seleção automática da última ocorrência.</p>';
|
||||
const summary = [];
|
||||
diff.forEach((g, i) => {
|
||||
const a = g.occurrences.map((o) =>
|
||||
p.filter((x) => x.p >= o.start && x.p <= o.end),
|
||||
);
|
||||
html += `<section><h2>${i + 1}. ${esc(g.title)}</h2><p><b>Natureza:</b> ${esc(proposals[i][0])}</p><p><b>Impacto:</b> ${esc(proposals[i][1])}</p><p><b>Proposta fundamentada:</b> ${esc(proposals[i][2])}</p><table><tr>${g.occurrences.map((o) => `<th>Ocorrência p.${o.start}–${o.end}</th>`).join('')}</tr><tr>${a.map((rows) => '<td>' + rows.map((x) => `<p><span class="number">p.${x.p}</span> ${esc(x.text) || '[parágrafo vazio]'}</p>`).join('') + '</td>').join('')}</tr></table></section>`;
|
||||
summary.push({
|
||||
section: g.title,
|
||||
occurrences: g.occurrences,
|
||||
kind: proposals[i][0],
|
||||
functionalImpact: proposals[i][1],
|
||||
proposal: proposals[i][2],
|
||||
decision:
|
||||
'compatible_content_preserved; no unresolved behavior contradiction identified in these nine spans',
|
||||
});
|
||||
});
|
||||
fs.writeFileSync(dir + 'nove-diferencas-lado-a-lado.html', html + '</html>');
|
||||
fs.writeFileSync(
|
||||
'docs/investmoz/v2/consolidacao-nove-diferencas.json',
|
||||
JSON.stringify(summary, null, 2),
|
||||
);
|
||||
const register = JSON.parse(
|
||||
fs.readFileSync(dir + 'document-reconciliation-register.json', 'utf8'),
|
||||
);
|
||||
const groups = new Map();
|
||||
for (const r of register.records) {
|
||||
if (!r.sha256) continue;
|
||||
const h = r.sha256.toLowerCase();
|
||||
groups.set(h, [...(groups.get(h) ?? []), r]);
|
||||
}
|
||||
const duplicateProposals = [...groups.entries()]
|
||||
.filter(([, g]) => g.length > 1)
|
||||
.map(([h, g]) => {
|
||||
const sorted = [...g].sort((a, b) => {
|
||||
const score = (x) =>
|
||||
x.registeredDocuments?.length
|
||||
? 0
|
||||
: x.identifier.includes('\\Relatorios\\')
|
||||
? 1
|
||||
: x.identifier.length;
|
||||
return (
|
||||
score(a) - score(b) || a.identifier.localeCompare(b.identifier)
|
||||
);
|
||||
});
|
||||
return {
|
||||
sha256: h,
|
||||
reference: sorted[0].identifier,
|
||||
allProvenances: g.map((x) => x.identifier),
|
||||
basis: 'Exact byte equality; prefer registered record, then report archive, then shortest stable identifier. No later-version selection.',
|
||||
scope: g[0].identifier.includes('::')
|
||||
? 'embedded_asset_only_not_parent_document'
|
||||
: 'whole_file',
|
||||
authorization: 'unchanged_not_inferred',
|
||||
action: 'proposal_only_no_delete_or_move',
|
||||
};
|
||||
});
|
||||
fs.writeFileSync(
|
||||
dir + 'duplicate-reference-proposals.json',
|
||||
JSON.stringify(duplicateProposals, null, 2),
|
||||
);
|
||||
const steps = [];
|
||||
for (let n = 1782; n <= 1847; n += 5) {
|
||||
const get = (k) => p.find((x) => x.p === k)?.text ?? '';
|
||||
steps.push({
|
||||
title: get(n),
|
||||
inputs: get(n + 2),
|
||||
sourceParagraph: n,
|
||||
href:
|
||||
n === 1797
|
||||
? '/simulador-fiscal'
|
||||
: n === 1802 || n === 1807
|
||||
? '/balcao-virtual/painel'
|
||||
: '/documentos',
|
||||
});
|
||||
}
|
||||
fs.writeFileSync(
|
||||
fs.existsSync(dir + 'docx-reopened.json')
|
||||
? dir + 'fiscal-preparation-guide-historical.ts'
|
||||
: 'resources/js/lib/fiscal-preparation-guide.ts',
|
||||
'// Historical source paragraphs; orientation only, never executable legal rules.\nexport const fiscalPreparationGuide = ' +
|
||||
JSON.stringify(steps, null, 2) +
|
||||
';\n',
|
||||
);
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
comparisons: diff.length,
|
||||
duplicates: duplicateProposals.length,
|
||||
guideSteps: steps.length,
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,170 @@
|
||||
const fs = require('node:fs');
|
||||
const crypto = require('node:crypto');
|
||||
const dir = 'storage/app/private/investmoz-v2/';
|
||||
const read = (p) =>
|
||||
JSON.parse(fs.readFileSync(dir + p, 'utf8').replace(/^\uFEFF/, ''));
|
||||
const inventory = read('legal-journey-inventory.json');
|
||||
const active = read('document-reconciliation-active.json');
|
||||
const documents = active.tables.documents;
|
||||
const resources = active.tables.portal_resources;
|
||||
const records = [];
|
||||
for (const item of inventory.entries) {
|
||||
records.push({
|
||||
identifier: item.path ?? item.root,
|
||||
origin: {
|
||||
inventoryPath: item.path,
|
||||
status: 'local_provenance_not_official_validation',
|
||||
},
|
||||
availability: item.status ?? 'inventoried',
|
||||
failure: item.reason ?? null,
|
||||
sha256: item.sha256 ?? null,
|
||||
version: 'not_identified_from_bytes',
|
||||
consultationAuthorization: 'not_determined_by_inventory',
|
||||
legalStatus: 'not_determined_by_inventory',
|
||||
calculationUse: 'not_activated_by_inventory',
|
||||
registeredDocuments: documents
|
||||
.filter(
|
||||
(d) => d.sha256?.toLowerCase() === item.sha256?.toLowerCase(),
|
||||
)
|
||||
.map((d) => ({
|
||||
metadata: d,
|
||||
resources: resources
|
||||
.filter((r) => r.document_id === d.id)
|
||||
.map((r) => ({
|
||||
...r,
|
||||
links: active.tables.portal_resource_links.filter(
|
||||
(l) => l.resource_id === r.id,
|
||||
),
|
||||
})),
|
||||
})),
|
||||
});
|
||||
for (const attachment of item.attachments ?? [])
|
||||
records.push({
|
||||
identifier: item.path + '::' + attachment.identifier,
|
||||
origin: {
|
||||
parent: item.path,
|
||||
embeddedIdentifier: attachment.identifier,
|
||||
},
|
||||
sha256: attachment.sha256,
|
||||
availability: 'inventoried_embedded',
|
||||
version: 'not_identified',
|
||||
consultationAuthorization: 'not_determined',
|
||||
legalStatus: 'not_determined',
|
||||
calculationUse: 'not_activated_by_inventory',
|
||||
});
|
||||
}
|
||||
const groups = new Map();
|
||||
for (const r of records) {
|
||||
if (!r.sha256) continue;
|
||||
const key = r.sha256.toLowerCase();
|
||||
groups.set(key, [...(groups.get(key) ?? []), r.identifier]);
|
||||
}
|
||||
for (const r of records)
|
||||
r.byteDuplicates = r.sha256
|
||||
? (groups.get(r.sha256.toLowerCase()) ?? []).filter(
|
||||
(p) => p !== r.identifier,
|
||||
)
|
||||
: [];
|
||||
fs.writeFileSync(
|
||||
dir + 'document-reconciliation-register.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
activeReadOnly: active.target,
|
||||
records,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
function csv(text) {
|
||||
let rows = [],
|
||||
row = [],
|
||||
field = '',
|
||||
quoted = false;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let c = text[i];
|
||||
if (c === '"') {
|
||||
if (quoted && text[i + 1] === '"') {
|
||||
field += '"';
|
||||
i++;
|
||||
} else quoted = !quoted;
|
||||
} else if (c === ',' && !quoted) {
|
||||
row.push(field);
|
||||
field = '';
|
||||
} else if (c === '\n' && !quoted) {
|
||||
row.push(field.replace(/\r$/, ''));
|
||||
rows.push(row);
|
||||
row = [];
|
||||
field = '';
|
||||
} else field += c;
|
||||
}
|
||||
if (field || row.length) {
|
||||
row.push(field);
|
||||
rows.push(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
const index = csv(
|
||||
fs.readFileSync('docs/investmoz/v2/indice-documento-mestre.csv', 'utf8'),
|
||||
);
|
||||
const paragraphs = read('master-paragraphs.json');
|
||||
const headings = new Map();
|
||||
for (const row of index.slice(1)) {
|
||||
const title = row[5];
|
||||
if (!title) continue;
|
||||
const start = Number(row[2]),
|
||||
end = Number(row[3]);
|
||||
const body = paragraphs
|
||||
.filter((p) => p.p >= start && p.p <= end)
|
||||
.map((p) => p.text.replace(/\s+/g, ' ').trim())
|
||||
.join('\n');
|
||||
const key = title.trim().toLowerCase();
|
||||
headings.set(key, [
|
||||
...(headings.get(key) ?? []),
|
||||
{
|
||||
title,
|
||||
start,
|
||||
end,
|
||||
paragraphs: end - start + 1,
|
||||
contentSha256: crypto
|
||||
.createHash('sha256')
|
||||
.update(body)
|
||||
.digest('hex'),
|
||||
},
|
||||
]);
|
||||
}
|
||||
const repeated = [...headings.values()]
|
||||
.filter((g) => g.length > 1)
|
||||
.map((g) => ({
|
||||
title: g[0].title,
|
||||
occurrences: g,
|
||||
comparison:
|
||||
new Set(g.map((r) => r.contentSha256)).size > 1
|
||||
? 'historical_content_differs'
|
||||
: 'historical_content_identical',
|
||||
decision: 'pending_human_reconciliation_no_requirement_discarded',
|
||||
}));
|
||||
fs.writeFileSync(
|
||||
'docs/investmoz/v2/repeticoes-reconciliacao.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
sourceSha256: index[1][1],
|
||||
basis: 'historical_saved_extraction_not_current_locked_original',
|
||||
repeated,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
records: records.length,
|
||||
duplicateGroups: [...groups.values()].filter((g) => g.length > 1)
|
||||
.length,
|
||||
repeatedGroups: repeated.length,
|
||||
differentGroups: repeated.filter(
|
||||
(g) => g.comparison === 'historical_content_differs',
|
||||
).length,
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,135 @@
|
||||
const {
|
||||
chromium,
|
||||
} = require('C:/Users/macbook/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
(async () => {
|
||||
const b = await chromium.launch({
|
||||
headless: true,
|
||||
executablePath:
|
||||
'C:/Users/macbook/AppData/Local/ms-playwright/chromium-1243/chrome-win64/chrome.exe',
|
||||
});
|
||||
const checks = [];
|
||||
try {
|
||||
for (const width of [1440, 390]) {
|
||||
const c = await b.newContext({ viewport: { width, height: 850 } });
|
||||
await c.route('**/*', (r) =>
|
||||
r.request().url().startsWith('http://127.0.0.1:18092')
|
||||
? r.continue()
|
||||
: r.abort(),
|
||||
);
|
||||
const p = await c.newPage();
|
||||
const errors = [];
|
||||
p.on('pageerror', (e) => errors.push(e.message));
|
||||
for (const path of [
|
||||
'/documentos',
|
||||
'/legislacao',
|
||||
'/como-investir',
|
||||
]) {
|
||||
const r = await p.goto('http://127.0.0.1:18092' + path);
|
||||
assert.equal(
|
||||
r.headers()['x-investmoz-qa-target'],
|
||||
'investmoz_v2_20261008_test:55441',
|
||||
);
|
||||
const section = p.getByRole('region', {
|
||||
name: 'Fontes e guias para a jornada',
|
||||
exact: true,
|
||||
});
|
||||
await section.waitFor();
|
||||
await section
|
||||
.getByText(
|
||||
'Guia de preparação fiscal — 14 etapas da fonte',
|
||||
{ exact: true },
|
||||
)
|
||||
.click();
|
||||
assert.equal(
|
||||
await section.locator('details ol > li').count(),
|
||||
14,
|
||||
);
|
||||
assert.match(
|
||||
await section.locator('details').innerText(),
|
||||
/p\.973–1042/,
|
||||
);
|
||||
assert.match(
|
||||
await section
|
||||
.locator('details ol > li')
|
||||
.first()
|
||||
.innerText(),
|
||||
/973 \(histórico: 1782\)/,
|
||||
);
|
||||
assert.match(
|
||||
await section.innerText(),
|
||||
/não concede benefícios/,
|
||||
);
|
||||
assert.match(
|
||||
await section.innerText(),
|
||||
/estados independentes/,
|
||||
);
|
||||
await section
|
||||
.getByRole('link', {
|
||||
name: 'Consultar exemplares de formulários',
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
await p.waitForURL(
|
||||
(u) =>
|
||||
u.pathname === '/documentos' &&
|
||||
u.searchParams.get('document_type') === 'official_form',
|
||||
);
|
||||
await p
|
||||
.getByRole('region', {
|
||||
name: 'Fontes e guias para a jornada',
|
||||
exact: true,
|
||||
})
|
||||
.getByRole('link', {
|
||||
name: 'Consultar guias disponíveis',
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
await p.waitForURL(
|
||||
(u) => u.searchParams.get('document_type') === 'guide',
|
||||
);
|
||||
await p
|
||||
.getByRole('region', {
|
||||
name: 'Fontes e guias para a jornada',
|
||||
exact: true,
|
||||
})
|
||||
.getByRole('link', {
|
||||
name: 'Explorar todo o acervo autorizado',
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
await p.waitForURL(
|
||||
(u) => u.pathname === '/documentos' && !u.search,
|
||||
);
|
||||
checks.push({
|
||||
width,
|
||||
path,
|
||||
formFilter: true,
|
||||
guideFilter: true,
|
||||
unrestrictedLibrary: true,
|
||||
});
|
||||
}
|
||||
assert.deepEqual(errors, []);
|
||||
await p.screenshot({
|
||||
path: `storage/app/private/investmoz-v2/legal-journey-${width}.png`,
|
||||
fullPage: true,
|
||||
});
|
||||
await c.close();
|
||||
}
|
||||
fs.writeFileSync(
|
||||
'storage/app/private/investmoz-v2/legal-journey-browser.json',
|
||||
JSON.stringify(
|
||||
{ qa: 'investmoz_v2_20261008_test:55441', checks },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
console.log('Legal journey QA navigation passed');
|
||||
} finally {
|
||||
await b.close();
|
||||
}
|
||||
})().catch((e) => {
|
||||
console.error(e);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -0,0 +1,127 @@
|
||||
const fs = require('fs'),
|
||||
crypto = require('crypto');
|
||||
const d = 'storage/app/private/investmoz-v2/';
|
||||
const r = (f) =>
|
||||
JSON.parse(fs.readFileSync(d + f, 'utf8').replace(/^\uFEFF/, ''));
|
||||
const p = r('master-paragraphs-reopened.json'),
|
||||
source = r('docx-reopened.json');
|
||||
const heads = p.filter((x) => /^(Ttulo|Title|Heading)/i.test(x.style));
|
||||
const rows = heads.map((x, i) => ({
|
||||
title: x.text,
|
||||
start: x.p,
|
||||
end: heads[i + 1] ? heads[i + 1].p - 1 : p.length,
|
||||
style: x.style,
|
||||
}));
|
||||
const q = (s) => '"' + String(s).replaceAll('"', '""') + '"';
|
||||
fs.writeFileSync(
|
||||
'docs/investmoz/v2/indice-documento-mestre-atual.csv',
|
||||
[
|
||||
'fonte,sha256,paragrafo_inicio,paragrafo_fim,estilo,secção,estado',
|
||||
...rows.map((x) =>
|
||||
[
|
||||
'melhorar e transformar o Portal do Investidor atual.docx',
|
||||
source.sha256,
|
||||
x.start,
|
||||
x.end,
|
||||
x.style,
|
||||
x.title,
|
||||
'Leitura atual; implementação individual não certificada',
|
||||
]
|
||||
.map(q)
|
||||
.join(','),
|
||||
),
|
||||
].join('\n') + '\n',
|
||||
);
|
||||
const groups = new Map();
|
||||
for (const x of rows) {
|
||||
const text = p
|
||||
.filter((v) => v.p >= x.start && v.p <= x.end)
|
||||
.map((v) => v.text.replace(/\s+/g, ' ').trim())
|
||||
.join('\n');
|
||||
const row = {
|
||||
...x,
|
||||
contentSha256: crypto.createHash('sha256').update(text).digest('hex'),
|
||||
};
|
||||
const key = x.title.trim().toLowerCase();
|
||||
groups.set(key, [...(groups.get(key) ?? []), row]);
|
||||
}
|
||||
const repeated = [...groups.values()]
|
||||
.filter((g) => g.length > 1)
|
||||
.map((g) => ({
|
||||
title: g[0].title,
|
||||
occurrences: g,
|
||||
comparison:
|
||||
new Set(g.map((v) => v.contentSha256)).size > 1
|
||||
? 'current_content_differs'
|
||||
: 'current_content_identical',
|
||||
}));
|
||||
fs.writeFileSync(
|
||||
'docs/investmoz/v2/repeticoes-reconciliacao-atual.json',
|
||||
JSON.stringify(
|
||||
{ sha256: source.sha256, checked_at: source.checked_at, repeated },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
const changes = r('master-aligned-changes.json');
|
||||
const map = new Map(changes.mapping.map((v) => [v.old, v.current]));
|
||||
const historic = r('master-paragraphs.json');
|
||||
const steps = [];
|
||||
for (let old = 1782; old <= 1847; old += 5) {
|
||||
const title = historic.find((x) => x.p === old).text;
|
||||
const inputs = historic.find((x) => x.p === old + 2).text;
|
||||
const current = map.get(old);
|
||||
if (
|
||||
!current ||
|
||||
p.find((v) => v.p === current).text !== title ||
|
||||
p.find((v) => v.p === map.get(old + 2)).text !== inputs
|
||||
)
|
||||
throw Error('Guide text changed ' + old);
|
||||
steps.push({
|
||||
title,
|
||||
inputs,
|
||||
sourceParagraph: current,
|
||||
historicalSourceParagraph: old,
|
||||
href:
|
||||
old === 1797
|
||||
? '/simulador-fiscal'
|
||||
: old === 1802 || old === 1807
|
||||
? '/balcao-virtual/painel'
|
||||
: '/documentos',
|
||||
});
|
||||
}
|
||||
if (!fs.existsSync(d + 'fiscal-preparation-guide-before-reread.ts'))
|
||||
fs.copyFileSync(
|
||||
'resources/js/lib/fiscal-preparation-guide.ts',
|
||||
d + 'fiscal-preparation-guide-before-reread.ts',
|
||||
);
|
||||
fs.writeFileSync(
|
||||
'resources/js/lib/fiscal-preparation-guide.ts',
|
||||
'// Current source verified against preserved historical paragraphs; orientation only.\nexport const fiscalPreparationSource = ' +
|
||||
JSON.stringify({ sha256: source.sha256, start: 973, end: 1042 }) +
|
||||
';\nexport const fiscalPreparationGuide = ' +
|
||||
JSON.stringify(steps, null, 2) +
|
||||
';\n',
|
||||
);
|
||||
fs.writeFileSync(
|
||||
d + 'master-reread-verification.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
sha256: source.sha256,
|
||||
paragraphs: p.length,
|
||||
headingCount: heads.length,
|
||||
repeatedGroups: repeated.length,
|
||||
differentGroups: repeated.filter(
|
||||
(g) => g.comparison === 'current_content_differs',
|
||||
).length,
|
||||
bodyAdded: changes.added.length,
|
||||
bodyRemoved: changes.removed.length,
|
||||
guideStepsVerified: steps.length,
|
||||
guideTextUnchanged: true,
|
||||
newFunctionalRequirementsIdentified: false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
console.log(fs.readFileSync(d + 'master-reread-verification.json', 'utf8'));
|
||||
Reference in New Issue
Block a user