36 lines
3.5 KiB
Python
36 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
||
"""Join content inventory and versioned validation evidence without upgrading partial coverage."""
|
||
import argparse,collections,json,pathlib,re
|
||
from inventory_corpus import atomic
|
||
|
||
def release(name):
|
||
match=re.fullmatch(r'(.+)-(\d+(?:_\d+)+)(?:_spike([123]))?\.spk(?:\.zip)?',name,re.I)
|
||
if not match:raise ValueError(f'unrecognized package label: {name}')
|
||
slug,version,generation=match.groups();slug=slug.lower()
|
||
edition='unspecified'
|
||
for suffix,label in [('_pro','Pro'),('_le','Premium/LE'),('_elg','ELG'),('_the_pin','The Pin')]:
|
||
if slug.endswith(suffix):slug=slug[:-len(suffix)];edition=label;break
|
||
names={'got':'Game of Thrones','wn':'Whoa Nellie','wwe':'WrestleMania','elvira3':'Elvira’s House of Horrors','mando':'The Mandalorian','turtles':'Teenage Mutant Ninja Turtles','sword_of_rage':'Black Knight: Sword of Rage','guardians':'Guardians of the Galaxy','avengers_infinity':'Avengers: Infinity Quest','metallica_spike':'Metallica Remastered','star_wars_elg':'Star Wars ELG'}
|
||
return {'game':names.get(slug,slug.replace('_',' ').title()),'game_slug':slug,'edition':edition,'version':version.replace('_','.'),'generation':f'SPIKE {generation}' if generation else 'unconfirmed','label_provenance':'filename; edition grouping and display labels are inferred, not a manifest declaration'}
|
||
|
||
def matrix(inventory,evidence):
|
||
rows=[]
|
||
for package in inventory['packages']:
|
||
receipt=evidence/(package['name']+'.json');media=evidence/(package['name']+'.media.json')
|
||
extraction=json.loads(receipt.read_text()) if receipt.exists() else {}
|
||
counts=collections.Counter();families=collections.defaultdict(collections.Counter)
|
||
if media.exists():
|
||
for asset in json.loads(media.read_text())['assets']:
|
||
status=asset['status'];counts[status]+=1
|
||
family=asset.get('profile') or ('radium-pcm' if asset.get('section')==8 else 'radium-bitmap' if asset.get('section')==3 else pathlib.PurePosixPath(asset.get('source','')).suffix or 'unclassified')
|
||
families[family][status]+=1
|
||
wrapper=extraction.get('wrapper',{})
|
||
if wrapper and wrapper.get('source_sha256')!=package['sha256']:raise ValueError('receipt source identity differs from content inventory')
|
||
rows.append({**package,**release(package['name']),'processing':extraction.get('processing'),'extraction_status':extraction.get('status','not_processed'),'extracted_files':len(extraction.get('inventory',[])),'asset_status_counts':dict(counts),'families':dict(families),'acceptance':'blocked','blockers':['Full family coverage, readback, playback and edition comparison have not all been certified.']})
|
||
return {'schema':1,'expected':inventory['expected'],'packages':rows,'accepted_packages':0}
|
||
if __name__=='__main__':
|
||
p=argparse.ArgumentParser();p.add_argument('inventory',type=pathlib.Path);p.add_argument('evidence',type=pathlib.Path);p.add_argument('output',type=pathlib.Path);a=p.parse_args();m=matrix(json.loads(a.inventory.read_text()),a.evidence);atomic(a.output,m)
|
||
lines=['# SPIKE corpus coverage','',f"{len(m['packages'])} packages inventoried; full acceptance remains blocked.",'','| Package | Game | Edition | Generation | Wrapper | Extraction | Files |','| --- | --- | --- | --- | --- | --- | --- |']
|
||
for r in m['packages']:lines.append(f"| {r['name']} | {r['game']} | {r['edition']} | {r['generation']} | {r['wrapper']} {r.get('payload_wrapper','')} | {r['extraction_status']} | {r['extracted_files']} |")
|
||
a.output.with_suffix('.md').write_text('\n'.join(lines)+'\n')
|