import sqlite3,json,hashlib,time,datetime,subprocess
from pathlib import Path
ROOT=Path('/opt/struktur/mas-visual-library'); DB=ROOT/'database/visual_library.db'; ORIG=ROOT/'originals'
c=sqlite3.connect(DB); c.row_factory=sqlite3.Row
count=dict(c.execute('select analysis_status,count(*) from visuals group by analysis_status').fetchall())
analysis=c.execute('select count(*),count(distinct visual_id) from analyses').fetchone()
# Verify every stored original hash against its filename stem.
files=list(ORIG.iterdir()); hashes=[]; mism=[]
for f in files:
if not f.is_file(): continue
h=hashlib.sha256(f.read_bytes()).hexdigest(); hashes.append(h)
if f.stem.lower()!=h: mism.append(f.name)
sha_db={r[0] for r in c.execute('select sha256 from visuals')}
db_integrity=c.execute('pragma integrity_check').fetchone()[0]=='ok'
# Broad keyword coverage for requested spot-check domains, based only on stored curator JSON.
terms=['Lüftung','Feuchte','Schimmel','technische','Foto','portrait','landscape','portrait','square','quadratisch']
coverage={t: c.execute("select count(*) from analyses where lower(coalesce(theme,'')||' '||coalesce(technical_topic,'')||' '||coalesce(content_type,'')||' '||coalesce(visual_subject,'')) like ?",('%'+t.lower()+'%',)).fetchone()[0] for t in terms}
report={'report_type':'MAS Visual Library / Visual-Kurator Pipeline','report_timestamp_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'visual_kurator_found':'JA','profile':'visual-kurator','gateway_host':'ubuntu (VPS)','profile_path':'/home/hermes/.hermes/profiles/visual-kurator','provider':'openai-codex','model':'gpt-5.6-luna','call_path':'Hermes CLI: hermes -p visual-kurator chat --oneshot --image PATH -q PROMPT; executed as user hermes','vision_input':'native --image attachment; confirmed by CLI vision tool event','single_vision_test':'ERFOLGREICH','analysis_pending_before':348,'successful_analyses':count.get('ANALYZED',0),'analysis_failed':count.get('ANALYSIS_FAILED',0),'analysis_pending_after':count.get('ANALYSIS_PENDING',0),'analysis_rows':analysis[0],'analysis_distinct_visual_ids':analysis[1],'asset_groups':c.execute('select count(*) from asset_groups').fetchone()[0],'imported_variants':0,'variant_candidates_open':c.execute('select count(*) from variant_candidates').fetchone()[0],'unique_assets':count.get('ANALYZED',0),'approved_automatic':c.execute('select count(*) from visuals where approved!=0').fetchone()[0],'original_files':len(files),'original_hash_filename_mismatches':len(mism),'duplicate_events':c.execute('select count(*) from duplicate_events').fetchone()[0],'database_integrity':db_integrity,'sha256_originals':len(set(hashes)),'sha256_visuals':len(sha_db),'sha_sets_match':set(hashes)==sha_db,'spotcheck_keyword_coverage':coverage,'originals_path':str(ORIG),'database_path':str(DB),'status':'VOLLSTÄNDIG KATALOGISIERT' if count.get('ANALYZED',0)==348 and count.get('ANALYSIS_PENDING',0)==0 and count.get('ANALYSIS_FAILED',0)==0 and db_integrity and set(hashes)==sha_db else 'TEILWEISE KATALOGISIERT'}
out=ROOT/'imports'/'MAS-VISUAL-KURATOR-20260904.report.json'; out.write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n'); print(json.dumps(report,ensure_ascii=False,indent=2))