--- content_quality_audit.orig.py 2026-11-02 00:10:14 +++ content_quality_audit.py 2026-11-02 00:10:33 @@ -1,8 +1,9 @@ -# Read-only audit; run from any directory. Detailed metrics go to /tmp/adelante-content-metrics.json. -import json,re,unicodedata,collections,hashlib,random,itertools,math +# Read-only audit (Adelante docs/research/content_quality_audit.py at af1aea2), changed ONLY so the +# input library and the output file are arguments instead of the repo asset and /tmp. +# Usage: python3 -I content_quality_audit.py +import json,re,unicodedata,collections,hashlib,random,itertools,math,sys from pathlib import Path -root=Path(__file__).resolve().parents[2] -data=(root/'assets/content/library.json').read_bytes();r=json.loads(data) +data=Path(sys.argv[1]).read_bytes();r=json.loads(data) def tokens(s):return re.findall(r'\w+',unicodedata.normalize('NFKC',s).casefold()) norm=lambda s:' '.join(tokens(s)) sets=[set(tokens(x['text'])) for x in r] @@ -54,7 +55,7 @@ for j in range(max(0,len(words)-6+1)):phrases[' '.join(words[j:j+6])].add(i) repeated=sorted(((p,idx) for p,idx in phrases.items() if len(idx)>=15),key=lambda z:(-len(z[1]),z[0])) metric={'sha256':hashlib.sha256(data).hexdigest(),'count':len(r),'generated':sum(x['id'].startswith('gen-') for x in r),'categories':examples,'faith':faith,'contentTypes':dict(collections.Counter(x['contentType'] for x in r)),'missing_url_external_ids':[x['id'] for x in r if x['author']!='Adelante' and not x['sourceUrl']],'missing_translation_external_ids':[x['id'] for x in r if x['author']!='Adelante' and not x['translation']],'short_differs':sum(norm(x['shortText'])!=norm(x['text']) for x in r),'short_not_substring':[x['id'] for x in r if norm(x['shortText']) not in norm(x['text'])],'near':{'candidate_pairs':len(candidates),'verified_pairs':len(pairs),'affected_records':sum(len(g) for g in groups.values()),'connected_groups':len(groups),'cross_primary_category_pairs':sum(r[a]['categories'][0]!=r[b]['categories'][0] for a,b,s in pairs),'pairs':[{'score':round(s,4),'a':r[a]['id'],'b':r[b]['id'],'text_a':r[a]['text'],'text_b':r[b]['text']} for a,b,s in sorted(pairs,key=lambda v:-v[2])[:50]],'largest_groups':[[r[i]['id'] for i in sorted(g)] for g in sorted(groups.values(),key=len,reverse=True)[:10]]},'repeated_six_word_phrases':[{'phrase':p,'records':len(idx),'categories':sorted(set(c for i in idx for c in r[i]['categories']))} for p,idx in repeated[:20]]} -Path('/tmp/adelante-content-metrics.json').write_text(json.dumps(metric,indent=2)) +Path(sys.argv[2]).write_text(json.dumps(metric,indent=2)) print(json.dumps({k:v for k,v in metric.items() if k not in ['categories','faith','missing_url_external_ids','missing_translation_external_ids','short_not_substring','near']},indent=2)) print('NEAR',json.dumps({k:v for k,v in metric['near'].items() if k not in ['pairs','largest_groups']},indent=2)) for p in metric['near']['pairs'][:20]: print(p)