"""Original denominator audit. Source aggregates and constructed models stay separate."""
from pathlib import Path
import csv,json
from fractions import Fraction
ROOT=Path(__file__).resolve().parent
I=json.loads((ROOT/'google-risk-source-values.json').read_text(encoding='utf-8'))
URL='https://reviewremoval.ca/blog/google-review-risk-score-denominator-study.html'
LICENCE='https://creativecommons.org/licenses/by/4.0/'
velocity=[dict(record_id=f'V{i+1:02}',record_type='source_aggregate',velocity_label=r[0],profiles=r[1],median_lifespan_days=r[2],average_deletions_per_profile=r[3],five_star_deleted_share_percent=r[4],share_denominator='profile_deleted_review_set_not_full_profile',source_url=I['source_url']) for i,r in enumerate(I['velocity_rows'])]
quintiles=[dict(record_id=f'Q{i+1:02}',record_type='source_aggregate_with_derived_ratio',metric=r[0],unit=r[1],top_quintile_display=r[2],bottom_quintile_display=r[3],nominal_ratio=r[2]/r[3],source_url=I['source_url'],notes='Ratio of displayed source summaries, not a deletion probability or causal effect.') for i,r in enumerate(I['quintile_rows'])]
bounds=[]
for i,removed in enumerate([500,1000,2500,5000]):
 n=10000;five=int(Fraction(467,500)*removed);remain=n-removed
 bounds.append(dict(record_id=f'MB{i+1}',record_type='constructed_identification_bound_not_observed',original_reviews=n,deleted_reviews=removed,deleted_five_star=five,deleted_five_star_share_percent=93.4,deletion_fraction_percent=removed/n*100,original_five_star_min=five,original_five_star_max=five+remain,original_five_star_share_min_percent=five/n*100,original_five_star_share_max_percent=(five+remain)/n*100,assumption='Deleted reviews are a subset of a fixed original review set; surviving star composition unknown. No new reviews or other changes.'))
examples=[]
for i,surviving_five in enumerate([500,1450]):
 n=2000;d=500;removed_five=467;remain=n-d
 examples.append(dict(record_id=f'ME{i+1}',record_type='synthetic_integer_example_not_google_or_client_data',label='A' if i==0 else 'B',original_reviews=n,deleted_reviews=d,deleted_five_star=removed_five,deleted_other_stars=33,surviving_reviews=remain,surviving_five_star=surviving_five,surviving_other_stars=remain-surviving_five,original_five_star=removed_five+surviving_five,deleted_five_star_share_percent=93.4,original_five_star_share_percent=round((removed_five+surviving_five)/n*100,6),surviving_five_star_share_percent=surviving_five/remain*100))
summary={'source_rows':len(velocity)+len(quintiles),'constructed_rows':len(bounds)+len(examples),'total_records':len(velocity)+len(quintiles)+len(bounds)+len(examples),'velocity_row_profile_sum':sum(r['profiles'] for r in velocity),'correlation_profiles':I['correlation_profiles'],'study_profiles':I['study_profiles'],'correlation_to_study_profile_percent':I['correlation_profiles']/I['study_profiles']*100,'example_original_share_gap_percentage_points':round(examples[1]['original_five_star_share_percent']-examples[0]['original_five_star_share_percent'],6)}
D={'title':'Google review risk-score denominators: source audit and identification models','version':'1.0','published':'2026-10-27','article_url':URL,'license':LICENCE,'source':I,'summary':summary,'velocity':velocity,'quintiles':quintiles,'bounds':bounds,'examples':examples,'limitations':['Secondary analysis; no individual review records or independent replication of Google moderation.','Published correlation set explicitly includes only profiles with more than ten deleted reviews.','Source percentage summaries refer to each profile deleted-review set, not all public reviews.','Constructed bounds and examples are mathematical and not observed Google or client outcomes.','The source does not identify a separate Canadian sample.','Source velocity labels are preserved; the exact 100 boundary is not redefined.','Source quintile scores cannot be externally validated without their full scoring rule and raw records.']}
(ROOT/'google-risk-denominator-study.json').write_text(json.dumps(D,indent=2,ensure_ascii=False)+'\n',encoding='utf-8')
descriptions={'record_type':'Distinguishes attributed source aggregates from constructed models; never a Canadian case outcome.','five_star_deleted_share_percent':'Published percentage of a profile deleted-review set; not five-star share of every review on a profile.','profiles':'Source profile count in the labelled velocity row; selected correlation set.','median_lifespan_days':'Source creation-to-removal median among selected deleted reviews; not report-processing time.','average_deletions_per_profile':'Source cumulative deletions over the measurement window; not an annual rate.','nominal_ratio':'Top displayed summary divided by bottom displayed summary; not an outcome probability.','original_five_star_share_min_percent':'Conditional mathematical lower bound under the fixed-original-set model.','original_five_star_share_max_percent':'Conditional mathematical upper bound with surviving star composition unknown.','deleted_five_star_share_percent':'Fixed 93.4% in the constructed example, numerically matching a source display, without asserting an observed cohort.'}
for filename,rows in [('google-risk-velocity.csv',velocity),('google-risk-quintiles.csv',quintiles),('google-risk-bounds.csv',bounds),('google-risk-examples.csv',examples)]:
 with (ROOT/filename).open('w',encoding='utf-8',newline='') as f:
  w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows)
 cols=[{'name':k,'datatype':'integer' if isinstance(v,int) else 'number' if isinstance(v,float) else 'string','null':[''],'dc:description':descriptions.get(k,k.replace('_',' '))} for k,v in rows[0].items()]
 meta={'@context':'http://www.w3.org/ns/csvw','url':'https://reviewremoval.ca/blog/data/'+filename,'dc:title':D['title']+' — '+filename,'dc:license':LICENCE,'tableSchema':{'columns':cols}}
 (ROOT/(filename+'-metadata.json')).write_text(json.dumps(meta,indent=2)+'\n',encoding='utf-8')
(ROOT/'google-risk-denominator-study.bib').write_text('@misc{reviewremoval2026risk,\n author={{ReviewRemoval}},\n title={Google Review Risk Scores: A Denominator Study},\n year={2026},\n version={1.0},\n url={'+URL+'},\n note={Secondary source audit and explicitly constructed identification models; source observations by Localo}\n}\n',encoding='utf-8')
print(json.dumps(summary,indent=2))
