76 lines
7.6 KiB
Python
76 lines
7.6 KiB
Python
"""Reconcile existing named gaps with authenticated QA proof; retain earlier names/counts."""
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
|
|
root = Path(__file__).resolve().parent.parent
|
|
private = root / 'storage/app/private/investmoz-v2/portal-audit'
|
|
docs = root / 'docs/investmoz/v2/auditoria-portal'
|
|
proof = json.loads((private / 'geometry-crosswalk-http-qa.json').read_text(encoding='utf-8-sig'))
|
|
preservation = json.loads((private / 'geometry-crosswalk-preservation-qa.json').read_text(encoding='utf-8-sig'))
|
|
consumer_http = json.loads((private / 'geometry-crosswalk-consumers-http-qa.json').read_text(encoding='utf-8-sig'))
|
|
assert proof['applied'] and not proof['published'] and not proof['interface_observed'] and proof['active_writes'] == 0
|
|
assert preservation['target'] == proof['target'] and preservation['catalogue_preserved'] and preservation['original_geometries_preserved'] == 361
|
|
assert consumer_http['target'] == proof['target'] and not consumer_http['applied'] and not consumer_http['interface_observed']
|
|
assert len(consumer_http['consumer_recovery']) == len(consumer_http['public_recovery']) == 32
|
|
plans = {p['level']: p for p in proof['plans']}
|
|
names = {1: 'Províncias', 2: 'Distritos', 3: 'Postos administrativos'}
|
|
gaps, explicit = [], []
|
|
for level in [1, 2, 3]:
|
|
path = private / f'moz-adm{level}-ine-20190607.geojson'
|
|
assert hashlib.sha256(path.read_bytes()).hexdigest() == plans[level]['source_sha256']
|
|
source = json.loads(path.read_text(encoding='utf-8-sig'))
|
|
rows = {r['external_id'].rsplit(':', 1)[1]: r for r in plans[level]['rows']}
|
|
for feature in source['features']:
|
|
p = feature['properties']
|
|
r = rows[p[f'ADM{level}_PCODE']]
|
|
if r.get('correspondence') and r['status'] in ['insert', 'reuse']:
|
|
explicit.append({'pcode': p[f'ADM{level}_PCODE'], 'source_name': r['name'], 'id': r['location_id'], **r['correspondence']})
|
|
if r['status'] in ['insert', 'reuse']:
|
|
continue
|
|
reason = {'parent_unresolved': 'Pai do exemplar sem correspondência; não associa por nome isolado',
|
|
'name_unresolved': 'Nome sem correspondência única sob o pai resolvido',
|
|
'ambiguous_name': 'Mais de um cadastro com mesmo nome/pai; ambos preservados',
|
|
'invalid_geometry': 'Geometria inválida; original sem reparação silenciosa'}.get(r['status'], r['status'])
|
|
gaps.append({'level': level, 'name': r['name'], 'pcode': p[f'ADM{level}_PCODE'], 'parent_name': p.get(f'ADM{level-1}_PT'),
|
|
'parent_pcode': p.get(f'ADM{level-1}_PCODE'), 'province': p['ADM1_PT'], 'reason': reason, 'status': r['status'], 'candidates': r['candidates']})
|
|
resolved = sum(p['resolved'] for p in plans.values())
|
|
expected = sum(p['expected'] for p in plans.values())
|
|
assert expected - resolved == len(gaps) and preservation['total_active'] == resolved
|
|
text = ['# Correspondências cartográficas — estado atual e histórico', '',
|
|
'10/10/2026. Complemento de AUD-GEO-02/03 na mesma matriz. QA18097/desk_restore_20261009_174726_test:55441. Esperado refere o exemplar INE/UNOCHA de07/06/2019 aceite, não uma divisão administrativa atual certificada.', '',
|
|
'| Nível | Esperado | Resolvido QA | Pendente | Incremento sobre09/10 |', '| --- | ---: | ---: | ---: | ---: |']
|
|
for level in [1, 2, 3]:
|
|
p = plans[level]
|
|
text.append(f"| {names[level]} | {p['expected']} | {p['resolved']} | {p['pending']} | +{p['resolved'] - {1:9,2:120,3:232}[level]} |")
|
|
text += ['', f'**{resolved} resolvidos/{len(gaps)} pendentes, de {expected} elementos.** As 32 novas geometrias são 2+8+22; as 361 anteriores e os 1990 registos territoriais foram preservados no retrato. O seletor recupera 1920 referências de quatro níveis:11+148+437+1324; os 70 países do cadastro não integram esse denominador. Sem geometrias de localidades neste exemplar. Cadastro, geometria autorizada e apresentação funcional são critérios independentes.', '',
|
|
'## Correspondências explícitas', '', '| INE | Nome do exemplar | Cadastro/código | ID existente | Fundamento |', '| --- | --- | --- | --- | --- |']
|
|
for r in explicit:
|
|
text.append(f"| {r['pcode']} | {r['source_name']} | {r['target_name']}/{r['target_source_code']} | {r['id']} | {r['basis']} |")
|
|
text += ['', 'Configuração versionada config/geometry-correspondences.php; fonte/hash/edição/nome/código/pai entram na prévia assinada. CódigosINE e workbook não são convertidos aritmeticamente. Zero renomeações, novos territórios, mudanças de pai ou publicação. Cidade De Maputo/MZ0601 é agregado de2019: não se associa a um dos distritos urbanos cadastrados.', '',
|
|
'## Mandie — ambiguidade preservada', '',
|
|
'MZ040402/Mandie sob Guro tem candidatos4944a651-23ec-5d51-a5ff-14b0b53ed672 (source_code02) e ea7be18f-2a93-5416-a07a-c207a700e070 (03). Workbook aceite:MANDIE nas linhas11117 e11138, códigos diferentes. Sufixo do P-code não prova equivalência de códigos. Conferir correspondência documental específica; não fundir cadastros. Não bloqueia correspondentes únicos independentes.', '']
|
|
for level in [1, 2, 3]:
|
|
items = [r for r in gaps if r['level'] == level]
|
|
text += [f'## {names[level]} — {len(items)} pendentes atuais', '', '| Código | Nome exato | Pai | Causa |', '| --- | --- | --- | --- |']
|
|
for r in items:
|
|
text.append(f"| {r['pcode']} | {r['name']} | {r['parent_name'] or '—'} | {r['reason']} |")
|
|
text.append('')
|
|
text += ['## Provas e critérios abertos', '',
|
|
'- geometry-crosswalk-http-qa.json: aplicação autenticada/MFA, replay e API do mapa para22postos novos. A tentativa anterior aplicou2províncias+8distritos antes de parar na ambiguidadeMandie; recibos/eventos originais estão na prova de preservação. O tratamento foi corrigido para manter ambiguidades pendentes e importar as correspondências únicas.',
|
|
'- geometry-crosswalk-preservation-qa.json: alvo guardado,393 internos,32novos,361anteriores/1990cadastros preservados. geometry-consumers-path-qa.json recupera393/393 por serviço READ ONLY e0limites privados públicos. Não é prova de publicação ou desenho.',
|
|
'- geometry-crosswalk-consumers-http-qa.json: nova consulta dos 32 limites inseridos, pelo endpoint usado pelo componente, recupera 32 para gestor/MFA e nenhum para público; as duas províncias Maputo têm correspondência única por nome exato. Não aplicou outro lote e não demonstra renderização.',
|
|
'- TerritoryFields e ProvinceSelect partilhados usam os mesmos IDs/endpoints/preview. ProvinceSelect exige nome exato/único; conserva texto histórico e explica ausência. TypeScript/build passam; seleção/gravação/recuperação e Leaflet por consumidor exigem interface.',
|
|
'- Reversão recusa alteração/publicação/FK e descendentes ativos da edição. Ordem:postos→distritos→províncias, só lotes novos; originais/cadastros/histórico preservados. Identidades revertidas não são ressuscitadas por replay.', '']
|
|
destination = docs / 'correspondencias-cartograficas.md'
|
|
old = destination.read_text(encoding='utf-8-sig')
|
|
marker = '## Histórico09/10 — nomes e contagens anteriores'
|
|
history = old.split(marker, 1)[1].strip() if marker in old else old.strip()
|
|
destination.write_text('\n'.join(text) + '\n' + marker + '\n\n' + history + '\n', encoding='utf-8')
|
|
named = private / 'geometry-named-gaps.json'
|
|
archive = private / 'geometry-named-gaps-20261009.json'
|
|
if named.exists() and not archive.exists():
|
|
archive.write_bytes(named.read_bytes())
|
|
named.write_text(json.dumps(gaps, ensure_ascii=False, indent=2), encoding='utf-8')
|
|
print(json.dumps({'expected': expected, 'resolved': resolved, 'pending': len(gaps), 'named_gaps': 'correspondencias-cartograficas.md', 'active_writes': 0}))
|