Files
Investidor-V2/tools/portal-segmentation-report.py
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

145 lines
21 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Publish readable reconciliation, preserving the sealed workflow candidate."""
from pathlib import Path
from datetime import datetime, timezone
from zipfile import ZipFile
from collections import Counter
import hashlib, json, csv, shutil, re, xml.etree.ElementTree as ET
csv.field_size_limit(20_000_000)
root=Path(__file__).resolve().parent.parent
private=root/'storage/app/private/investmoz-v2/portal-audit'
directory=private/'segmentation-reconciliation-20261011'
docs=root/'docs/investmoz/v2/auditoria-portal'
read=lambda name:json.loads((directory/name).read_text(encoding='utf-8'))
provenance=read('source-provenance.json');counts=read('counts.json');correspondence=read('correspondence.json')
aliases=read('document-aliases.json');blocks=read('request-blocks.json');legal=read('legal-reference-links.json')
digest=provenance['sha256']
matrix=list(csv.DictReader((docs/'matriz-requisitos.csv').open(encoding='utf-8-sig',newline='')))
by_id={r['ID']:r for r in matrix}
assert len(matrix)==counts['requirements'] and len(by_id)==len(matrix)
assert sum(int(r['Quantidade de linhas de origem']) for r in matrix)==counts['source_occurrences']
assert dict(Counter(r['Estado'] for r in matrix))==counts['states']
packet=read('csv-edits.json')
before=packet['files'][0];original={r[0]:dict(zip(before['rows'][0],r)) for r in before['rows'][1:]}
for rid,row in original.items():
for field in ['ID','Pedido/trecho','Estado','Critério de aceitação','Disponibilidade ativa']:
assert row[field]==by_id[rid][field], 'Prior decision/state changed: '+rid+' '+field
previous=json.loads(row['Critérios e versões por origem (JSON)'] or '[]')
current=json.loads(by_id[rid]['Critérios e versões por origem (JSON)'] or '[]')
assert current[:len(previous)]==previous,'Original source history lost'
for name,h in json.loads((private/'functional-active-files-before-20261011.json').read_text()).items():
assert hashlib.sha256((root/name).read_bytes()).hexdigest()==h,'Active application changed'
# Add to the existing source inventory JSON, retaining its entire previous snapshot.
inventory_path=private/'inventario-fontes.json'
backup=directory/'source-registry-before.json'
if not backup.exists():shutil.copy2(inventory_path,backup)
inventory=json.loads(inventory_path.read_text(encoding='utf-8-sig'))
known={(x['path'],x['sha256']) for x in inventory['files']}
prior_inventory=json.loads(backup.read_text(encoding='utf-8-sig'))
prior_known={(x['path'],x['sha256']) for x in prior_inventory['files']}
added=[]
for item in provenance['source_directory_inventory']:
if (item['path'],item['sha256']) in known:continue
entry={**item,'consultation':'Inventariado; original selecionado privadamente apenas em QA. Não inferir divulgação.',
'rule_use':'Nenhuma regra/cálculo ativado pela incorporação.',
'legal_document_state':'Preservar origem; documento de programação não equivale a diploma oficial.'}
if item['sha256']==digest:entry.update({'retained':provenance['retained'],'QA_library_resource':29,'requested_filename_identity_pending':True,'version_properties':provenance['version_properties']})
inventory['files'].append(entry);added.append(item['path'])
inventory['segmentation_source_added']={'at':datetime.now(timezone.utc).isoformat(),'sha256':digest,
'observed_filename':provenance['observed_filename'],'requested_filename':provenance['expected_filename'],
'exact_requested_exemplar_confirmed':False,'added_files':sum((x['path'],x['sha256']) not in prior_known for x in provenance['source_directory_inventory']),'inventory_restart':False,'publication':False,'calculation_activation':False}
inventory_path.write_text(json.dumps(inventory,ensure_ascii=False,indent=2),encoding='utf-8')
new_ids=set(counts['new_IDs'])
def clean(s):return s.replace('|','/').replace('\n',' ')
section_table=[]
for item in correspondence:
ids=item['requirements'];states=sorted({by_id[rid]['Estado'] for rid in ids})
if item['section']=='3.3.1':action='Reutilizar workflow6categorias/contratos, evidências e segregação; ofertas/modulares permanecem separados.'
elif item['section'].startswith('1.8'):action='Conferir artigo/âmbito/período por regra; conservar acesso/alterações/redação separados, sem reabrir fonte aceite.'
elif any(rid in new_ids for rid in ids):action='Executar incremento específico abaixo; reutilizar cadastros/serviços e demonstrar consumidor próprio.'
else:action='Reutilizar as provas existentes; executar apenas a lacuna concreta da linha da matriz.'
section_table.append('| '+clean(item['heading'])+' / p'+str(item['paragraphs'][0])+'–'+str(item['paragraphs'][-1])+' | '+', '.join(ids)+' | '+', '.join(states)+' | '+action+' |')
alias_table=[]
for alias,ids in aliases.items():
alias_table.append('| '+alias+' | '+', '.join(ids)+' | '+('Acrescentado, sem correspondência direta' if all(x in new_ids for x in ids) else 'Reutilizado/complementado')+' |')
increments=[
('Workflow base', ['AUD-OP-03','AUD-PROFILE-28'], 'Candidata3d9d392e0a0f: seis ramos ensaiados, materialidade, condições, recursos, mapas/filtros e9rotas de acessibilidade. Sem code/bundle novo nesta incorporação.', '030000→040000/permissões/ClamD/processamento;190000 só para percurso integrado. Variantes semânticas/documentais/§27 e restauro continuam delimitadas no relatório da candidata.'),
('Fiscal/contributivo', ['AUD-SIM-01','AUD-SIM-02'], 'SimulationFormDrafts, SimulationTaxParameterResolver/RuleGovernance/Execution e resultados/fontes existentes; provasA/B reutilizadas.', 'Fechar/recolher/reabrir preservando cenários; comparar/exportar com versões e memória. INSS e cada imposto exigem parâmetros/âmbito/período identificados; indeterminado em vez de zero. Não ativar regra fiscal pela importação.'),
('Negócios similares/importação', ['AUD-MARKET-01','AUD-ESTABLISHMENT-01'], 'Cadastros e serviços ExistingBusiness/MNE, organizações e cartografia mestres. DoisXLSX já constam do inventário do diretório de origem.', 'Inspecionar folhas/colunas, IDs, cobertura, datas e conflitos; correspondência inequívoca, rejeições e cargaQA idempotente. Não converter estabelecimento observado em oportunidade. Pesquisa por raio exige geometria/coordenada real.'),
('Fundos', ['AUD-FUND-01'], 'Biblioteca e parceiros/entidades como fontes; RF-CLU-005 como requisito relacionado.', 'Catálogo com gestor/cobertura/datas/estado/elegibilidade e explicação de correspondência. Obter os conteúdos reais; não inventar fundos ou datas e não aprovar financiamento.'),
('Societário/modelos', ['AUD-COMPANY-01','AUD-COMPANY-02'], 'Organização mestre, perfil/dossier e biblioteca/revisions/links.', 'Perguntas e alternativas com fontes/limites; distinguir sociedade/empresário/representação/consórcio. Localizar exemplares/URLs de modelos antes de afirmar disponibilidade; conferir a redação específica, sem segunda validação oficial.'),
('Capitalização/Bolsa', ['AUD-CAPITAL-01','AUD-CAPITAL-02'], 'Biblioteca, conteúdo público, dossier e encaminhamento existentes.', 'Guias versionados com preparação/riscos/documentos; distinguir Bolsa/Banco de Moçambique/operador; segmentos e atos específicos. Ofertas e competências modulares não são aprovadas por esta incorporação.'),
('Checklists configuráveis', ['AUD-CHECKLIST-01'], 'Catálogo config/document-requirements e requisitos/formulários/tarefas/documentos do Balcão.', 'Editor/preview/publicação de versões; três camadas independentes(comercial/registo/licença). Manter versão da submissão. Entidade/ato/artigo/canal concreto, sem regra/SLA inventados.'),
('Indicadores de utilização', ['MASTER-e1169a77-0018'], 'Eventos/auditoria/filas/tarefas existentes; estatísticas de origem são outro consumidor.', 'Definir denominador/eventos/período/dimensões para abandono/conclusão/tempo; não inferir investidor único de solicitações nem taxa de sucesso de investimento. Metas/calendário/responsáveis permanecem decisão operacional.'),
]
increment_table=['| '+name+' | '+', '.join(ids)+' | '+reuse+' | '+next_action+' |' for name,ids,reuse,next_action in increments]
report='''# Incorporação e reconciliação de segmentar processos
O exemplar acessível **segmentar processos v1.docx** foi conservado e importado privadamente na bibliotecaQA existente. **Ainda não foi localizado nem confirmado o exemplar solicitado segmentar processos v1(3).docx.** Não se declara identidade entre esses ficheiros; a pergunta sobre o caminho exato permanece pendente. O sufixo de nome(3) não é tratado como versão funcional3.
Esta reconciliação usa o original disponível e os pedidos expressos desta sessão, preservando o histórico. A confirmação do exemplar solicitado é o critério documental restante; se os bytes forem diferentes, comparar as secções antes de alterar os vínculos. Não repetir provas funcionais por uma simples cópia de nome diferente.
'''
report+=f"Original: `{provenance['source']}`. SHA-256 **{digest}**, {provenance['bytes']}bytes. PropriedadesOOXML: criador{provenance['version_properties']['creator']}, revision{provenance['version_properties']['revision']}, criação{provenance['version_properties']['created']}, alteração{provenance['version_properties']['modified']}. São propriedades do exemplar, sem inferir aprovação, autoria certificada ou vigência jurídica.\n\n"
report+='907parágrafos,14tabelas,74secções;0objetos/imagens incorporados,0alterações acompanhadas e notas/cabeçalhos/rodapés verificados estruturalmente. Referências usam `word/document.xml#pN` e tabela/linha/célula na extração; não foram inventados números de página. Não foi produzida nem editada uma versãoDOCX.\n\n'
report+=f"**Mesma matriz:** {counts['old_requirements']}→{counts['requirements']}requisitos/linhas;39IDs anteriores complementados,8IDs novos apenas para pedidos sem correspondência direta. {counts['source_request_blocks']}blocos de pedido deste exemplar, {counts['added_source_occurrences']}ligações acrescentadas, {counts['source_occurrences']}ocorrências totais. Estados: "+'; '.join(f'{n} {s}' for s,n in counts['states'].items())+'. Nenhum estado/critério de aceitação anterior foi alterado.327blocos não significam327novos requisitos; relações de um bloco a vários requisitos são contadas separadamente.\n\n'
report+='## Evidência do acervoQA\n\n'
report+='QA: `https://portal-investidor.test:18445`, base `stability_consumers_20261011_084534_test:55441`, guarda antes de providers e antes de qualquer escrita. Prévia sem mutação→importação existente por gestorQA17 autorizado→ClamDreal/mesmohash→repetição sem duplicação→recuperação dos mesmosbytes. Recurso29/documento589ed9ac-967e-439e-9dde-b0fb3996e546, revisão/auditoria de criação1, classificação interna, published=false. Nenhuma conta, organização, representação, oportunidade, tabela ou regra criada por este lote.\n\n'
report+='Chromium/HTTPS reconhecido: gestão mostra o exemplar, clique no download devolve o hash original, nova sessão recupera; PromotorQA21 não autorizado recebe403, download público29 recebe404. Não houve publicação ou cálculo. Recibos `segmentation-reconciliation-20261011/library-preview/apply/replay-qa.json` e `segmentation-library-chromium-20261011.json`. Estes ensaios comprovam o consumidor documental, não funcionalidades futuras descritas noDOCX.\n\n'
report+='## Aliases documentais para IDs existentes\n\n| Referência noDOCX | ID na mesma matriz | Tratamento |\n| --- | --- | --- |\n'+'\n'.join(alias_table)+'\n\n'
report+='## Secção para requisito, estado e próxima ação\n\nA extração completa conserva cada pedido e o seu locator. O ficheiroJSON de correspondência é uma projeção das referências acrescentadas à matriz, sem backlog independente. Estados abaixo são os do requisito completo; provas pontuaisQA não são fechos globais.\n\n| Secção/parágrafos do exemplar | Requisitos | Estado na matriz | Próxima ação |\n| --- | --- | --- | --- |\n'+'\n'.join(section_table)+'\n\n'
report+='## Incrementos executáveis\n\n| Incremento | Requisitos | Reutilização/progresso | Critério restante |\n| --- | --- | --- | --- |\n'+'\n'.join(increment_table)+'\n\n'
report+='## Divergências preservadas\n\n'
report+='- Versão anterior `segmentar processos .docx`, SHA256 `fef1f3b0835ce5e2a47ee4e7127b665c741f63d4f73a3a819b88dce2a5801b54`: §1.3 dizia compra/venda de terra; §3 listava formas/relações societárias e interpretações de responsabilidade/quotas. O v1§1.4/3.3.1/6.3/9 restringe terra às modalidades admitidas, separa empresário/sociedades/representações/consórcio e pede redação pertinente. Originais e variantes preservados; nenhuma conversão de cadastros ou adoção dessas frases como regra.\n'
report+='- §2 organiza releasesF0–F4; não substitui as cinco fases/19 etapas nem renumeraE0… existentes. §3.2.1 não unifica estados de domínios.\n'
report+='- §3.1 usa perfis genéricos; seguemRBAC/ABAC/perfisv1 existentes. Entidade externa não recebe papel ou poder por figurar no documento. Coedição organizacional previamente aprovada e demonstrada preservada.\n'
report+='- §3.3.1 usa agrupamentos funcionais; investimento/imobiliário/fundiário/venda/parceria/franquia mantêm códigos e contratos. Concessão/fornecimento/procura ou oferta de capital não se tornam uma categoria base nova. As ofertas e competências modulares permanecem decisões separadas.\n'
report+='- §6 afirma gestão básica/modelos ativos: contraposição com leitura ativa atual e exemplares/URLs concretos requerida. Ativo temCOUNTRY02 e020/050, sem030/040/190,0organizações/representações e0geometrias; workflow ampliado deste lote éQA. “Sem demonstração suficiente” não é prova de ausência de código.\n'
report+='- §7p811 pede validação de conteúdo institucional antes de publicação: a aceitação oficial de08/10 tem precedência sobre credibilidade/proveniência já aceites. Conferir artigo, âmbito, período, retificação, alteração ou revogação concreta continua necessário à aplicação; não exigir nova certificação genérica.\n'
report+='- §§2.2/4.1.1/7.3/8.4–8.5 propõem metas/calendário/responsáveis/SLA; não são compromissos aprovados nem prazos de entidade externa. Nenhuma nomeação realizada.\n\n'
report+='## Referências legais e regras\n\n'
report+='Referências dos§§1.0.2/1.8 estão ligadas aos requisitos de checklists, perfil, comparador, fiscalidade, capitalização e admissão pertinentes. O registo `legal-reference-links.json` separa acesso declarado, alterações/revogações declaradas e redação proposta pelo exemplar. Não atribui a esta execução a pesquisa que o documento relata; nem atribui artigo/competência a regra inexistente.\n\n'
report+='Regra fiscal/contributiva: usarSimulationTaxRuleGovernance/ParameterResolver existentes somente com fonte/artigo/período/parâmetros concretos; o vínculo documental não grava nem ativa uma versão no motor. Checklist: Código Comercial/obrigações gerais, registo/publicidade e regime setorial têm efeitos independentes. BVM: admissão, supervisão e patrocínio são competências distintas. Relações ainda sem artigo específico ficam pendentes e visíveis.\n\n'
report+='Verificação técnica adicional: o [PDF do Código do Mercado de Valores Mobiliários no Banco de Moçambique](https://www.bancomoc.mz/media/bvhi3j4e/decreto-lei-4_2009_24-de-julho_aprova-o-codigo-do-mercado-de-valores-mobiliarios.pdf) foi recuperado pela ferramentaweb. OsURLs doBR229 noINTIC e do regulamento de admissão naBVM não foram recuperados nessa tentativa. Isso não revoga a aceitação das fontes nem demonstra revogação/vigência. Nenhum artigo novo foi adotado no motor.\n\n'
report+='## Candidata e condições de disponibilização\n\n'
report+='Candidata de workflow **3d9d392e0a0f**, código **df6be252f1a0**, ZIP SHA256 **6366c49d44cdc4242f4f93da5f5c1a53f2292d9eb485f70ef2b4e9a9f88589e9** preservados: nenhuma alteração de código/bundle pelas referências deste documento. Relatório, manifesto e procedimento selados não foram sobrescritos. A importação documental é um incremento de dados privadoQA posterior ao backup dessa candidata e fica fora doZIP e do ensaio de restauro de12:10:22UTC. Renovar snapshot/restauro se este acervo for incluído num pacote futuro.\n\n'
report+='Dependências ativas continuam concretas: atualizar/manter assinaturasClamD3310, demonstrarworker supervisionado, designações/perfis/representações legítimos e backup/restauro/prévias correspondentes. Para o workflow:020→030→040 somente se ausentes por ação autenticada;190 só quando se disponibilizar o percurso integrado. Mapas ativos exigem ativação cartográfica separada. Não foram ativados030/040/190, importações, legado, ofertas, módulos ouSLA.\n\n'
report+='COUNTRY02 ativo e fechosA/B/C37 conservados. Contenção futura do workflow desabilita apenas as suasflags, preservandoHTTPS/cookie/cifra/COUNTRY02/schema/documentos/histórico. O original desta incorporação não é publicado; eventual retirada do recurso será lógica/auditada, sem apagar bytes. Restauro integral ou eliminação de dados ativos exige decisão própria. R-ARCHIVE/R-DRAFT continuam propostas; a incorporação não decide restauração de oportunidades.\n'
# Plain prose spacing; identifiers and hashes retain their exact bytes.
report=re.sub(r'(\d+)(bytes|parágrafos|tabelas|secções|objetos|alterações|requisitos|linhas|blocos|ligações|ocorrências|IDs|rotas|categorias|contas|organizações|geometrias)(?=\b)',r'\1 \2',report)
report=report.replace('v1.docx**','v1.docx**').replace('COUNTRY02ON','COUNTRY02 ON')
(docs/'segmentacao-processos-reconciliacao-20261011.md').write_text(report,encoding='utf-8')
out=root/'outputs/investmoz-segmentation-reconciliation-20261011';out.mkdir(exist_ok=True)
(out/'relatorio.md').write_text(report,encoding='utf-8')
for name in ['correspondence.json','document-aliases.json','legal-reference-links.json','counts.json']:
shutil.copy2(directory/name,out/name)
summary='''<!-- segmentation-reconciliation-20261011:begin -->
## Segmentação de processos incorporada em QA com confirmação de exemplar pendente
[Correspondência completa por secção, requisito, estado e próxima ação](segmentacao-processos-reconciliacao-20261011.md). Exemplar local`segmentar processos v1.docx`,SHA256`bf26caeee0437a8d292bf29be59247e2862c14e79b5ae465c00322747af72af5`,OOXMLrevision15/11-10. O pedido identifica`v1(3).docx`,ainda não localizado/confirmado; não declarar os exemplares equivalentes.
Mesma matriz3401requisitos/linhas,7739ocorrências;39IDs anteriores complementados,8IDs novos por ausência de correspondência direta.327blocos/74secções. Estados:7concluídos,3236parciais,152não implementados,1implementado/não ativo,5dependentes de decisão. Nenhum fecho anterior reaberto.
AcervoQA:prévia/ClamDreal/mesmoSHA/repetição sem duplicação/recuperação, recurso29privado. Chromium gestão/download/nova sessão passaram; Promotor403/público404. Não publicou, não ativou regra/cálculo, não criou identidade/tabela. Workflow6categorias/candidata3d9d392e0a0f reutilizados, sem alterarZIP. COUNTRY02 ativo,A/B/C37 fechados preservados. Dados desta incorporação são posteriores ao backupQA da candidata; incluir num pacote futuro exige renovar o snapshot correspondente.
Próximos incrementos: fiscal/contributivo(AUD-SIM-01/02), negócios similares/importação(AUD-MARKET-01/ESTABLISHMENT-01), fundos(AUD-FUND-01), comparador/modelos(AUD-COMPANY-01/02), capitalização/BVM(AUD-CAPITAL-01/02), editor de checklists(AUD-CHECKLIST-01), utilização(MASTER-e1169a77-0018). Ofertas/competências modulares/SLA e restauro proposto continuam decisões separadas. Nenhuma ativação adicional autorizada.
<!-- segmentation-reconciliation-20261011:end -->
'''
for filename in ['backlog.md','relatorio-executivo.md']:
file=docs/filename;current=file.read_text(encoding='utf-8-sig');assert 'segmentation-reconciliation-20261011:begin' not in current
file.write_text(summary+current,encoding='utf-8')
receipt={'result':'passed','at':datetime.now(timezone.utc).isoformat(),'counts':counts,
'preserved_original_requirements':len(original),'protected_fields_and_old_source_history_preserved':True,
'active_critical_files_preserved':True,'actual_requested_exemplar_pending':True,'active_writes':0,
'sealed_workflow_candidate_not_rewritten':True,'files':{f.name:hashlib.sha256(f.read_bytes()).hexdigest() for f in out.iterdir() if f.is_file()}}
(directory/'reconciliation-integrity.json').write_text(json.dumps(receipt,ensure_ascii=False,indent=2),encoding='utf-8')
(out/'verificacao.json').write_text(json.dumps(receipt,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps({'result':'passed','requirements':len(matrix),'old_requirements_preserved':len(original),'added_source_files':len(added),'active_writes':0},ensure_ascii=False))