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

46 lines
3.3 KiB
Python

"""Read inventories and byte hashes without exposing private paths or contents."""
from pathlib import Path
from collections import Counter
import json, hashlib
from datetime import datetime, timezone
R=Path(__file__).resolve().parent.parent
P=R/'storage/app/private/investmoz-v2/portal-audit'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
load=lambda p:json.loads(p.read_text(encoding='utf-8-sig'))
latest=load(P/'service-followup-backup-restoration-qa.json')
reports=[]
for name in ['workflow-upgrade-target-backup-20261011.json', 'workflow-upgrade-backup-restoration-qa-20261011.json',
'service-followup-backup-restoration-qa.json']:
proof=load(P/name)
files=proof.get('file_manifest',proof.get('active_files_manifest',{}))
counts=dict(Counter(k.split('/')[0] for k in files))
private={k:v for k,v in files.items() if k.startswith('private/')}
current_private={k:v for k,v in latest['file_manifest'].items() if k.startswith('private/')}
common={k:v for k,v in private.items() if k in current_private}
mismatched=[k for k,v in common.items() if current_private[k]!=v]
reports.append({'proof':name,'verified_at':proof.get('verified_at'), 'target':proof.get('target'),
'file_count':proof['file_count'], 'manifest_entry_count':len(files),'groups':counts,
'private_references':len(private),'same_private_entries_in_latest':len(common),
'private_entries_not_in_latest':len(private)-len(common), 'common_bytes_changed':len(mismatched),
'table_count':proof.get('table_count',len(proof.get('tables',{}))),
'sequence_count':proof.get('sequence_count',len(proof.get('sequences',[])))})
original=load(P/'workflow-upgrade-target-backup-20261011.json')
original_private={k:v for k,v in original.get('file_manifest',original.get('active_files_manifest',{})).items() if k.startswith('private/')}
latest_private={k:v for k,v in latest['file_manifest'].items() if k.startswith('private/')}
assert original_private and all(latest_private.get(k)==v for k,v in original_private.items())
fixed=R/'outputs/investmoz-workflow-upgrade-20261011'
delta=R/'outputs/investmoz-service-followup-20261011'
plan=R/'outputs/investmoz-workflow-service-plan-20261011'
before={'at':datetime.now(timezone.utc).isoformat(),
'fixed_candidate':{p.relative_to(fixed).as_posix():sha(p) for p in fixed.rglob('*') if p.is_file()},
'delta_reference':{p.relative_to(delta).as_posix():sha(p) for p in delta.rglob('*') if p.is_file()},
'service_plan':{p.relative_to(plan).as_posix():sha(p) for p in plan.rglob('*') if p.is_file()}}
(P/'consolidated-preservation-before.json').write_text(json.dumps(before,indent=2),encoding='utf-8')
result={'at':datetime.now(timezone.utc).isoformat(),'result':'passed','reports':reports,
'originals_from_actual_destination_in_latest_191_by_name_and_hash':len(original_private),
'latest_191_groups':dict(Counter(k.split('/')[0] for k in latest['file_manifest'])),
'all_original_destination_bytes_preserved':True, 'active_writes':0,
'scope':'Referenced private documents, merged candidate code, production bundle and private QA .env; not the whole historical acervo or live runtime'}
(P/'consolidated-191-reconciliation.json').write_text(json.dumps(result,indent=2),encoding='utf-8')
print(json.dumps(result,ensure_ascii=False))