o
    ~oj                     @  s   d Z ddlmZ ddlZddlZddlmZmZ ddlmZ ddl	m
Z
mZmZmZ dZd ddZd!ddZd"ddZd#d$ddZd%ddZdS )&a  Detect when the law behind an answer has changed.

A hand-written summary of a statute is correct on the day it is written and
decays silently after that. Nothing in the archive would show it: the answer
still reads well, still cites a real source, and is simply out of date. That is
the worst failure this project can have, because it looks exactly like success.

This compares the current text of each source against a recorded fingerprint
and names the answers that depend on anything that moved.

The fingerprint is taken over the *cleaned* text, not the HTML. Government
pages regenerate constantly with unchanged content: banners, session ids, build
timestamps. Hashing the raw response would cry wolf every week, and a monitor
that cries wolf is one people learn to ignore.
    )annotationsN)datetimetimezone)Path   )PLAIN_SOURCEScleanfetchsplit_sectionszdata/freshness.jsontextstrreturnc                 C  s&   t d|  d dd S )z0Hash of the cleaned text, whitespace normalised. utf-8N   )hashlibsha256joinsplitencode	hexdigest)r    r   2/root/openrights-assistant/openrights/freshness.pyfingerprint   s   &r   rootr   dict[str, list[str]]c                 C  sv   | d }i }|  s|S t|dD ]%}t|jd\}}}|s"qdd t|jddD }||g 	| q|S )zFWhich answers rest on which statute, so a change names its casualties.zdata/curatedz*.md) r   r   c                 S  s   g | ]\}}}|qS r   r   ).0h_r   r   r   
<listcomp>,   s    z&answers_by_statute.<locals>.<listcomp>r   encoding)
existssortedglobr   getstemr
   	read_text
setdefaultextend)r   	directorymappingpathr   statuteheadingsr   r   r   answers_by_statute"   s   r0   titlec                 C  s4   t  D ]\}\}}}| |  v r|  S qdS )z=Match a source title to the statute name used by the answers.r   )r   itemslower)r1   r   r.   r   r   r   statute_for_source1   s
   r4   Fupdatebooldictc                 C  s  t | d jdd}| t }i }| r"t |jdddi }t| }tt	j
jdd }i }g }g }	|D ]}
|
d }zt|
d | d	 | d
 }W n- ty} } z!|	| ||v rsi || dt|d d i||< W Y d }~q:d }~ww tt|}||i }|
d |||d|d}|dr|d |krt|
d }|||
d |d ||dd||g d |d||d< n|r||d< |||< q:|s| s|jjddd |jt j||dddd dd t|||	| dS )Nzdata/sources.jsonr   r!   sourcesr   )microsecondidurlzdata/rawz.htmlerrorx   r1   verified)r1   r   checkedr>   r   unknown)r:   r1   wasnowr>   answersT)parentsexist_ok)updatedr8      )indent
)r?   changedunreachable	first_run)jsonloadsr(   MANIFESTr#   r&   r0   r   rB   r   utcreplace	isoformatr	   	Exceptionappendr   r   r   r4   parentmkdir
write_textdumpslen)r   r5   r8   manifest_pathprevious
dependantsrB   currentrJ   rK   source	source_idrawr<   digestbeforerecordr.   r   r   r   check9   sj    
$



rd   resultintc                 C  s  | d rt d| d  d dS | d D ]
}t d| d q| d	 s4t | d  d
 | d r2dS dS t t| d	  d| d  d | d	 D ]:}t d|d   t d|d   |d r{t dt|d  d |d D ]	}t d|  qpnt d t   qHt d t d dS )zEPrint the result. Returns the exit code: non-zero when review is due.rL   zRecorded fingerprints for r?   z! sources. Nothing to compare yet.r   rK   zUNREACHABLE: z  (kept the previous fingerprint)rJ   z sources checked, none changed.r   z of z sources changed.
z	CHANGED: r1   z  last verified: r>   rC   z  z answers to re-read:z    - z(  no plain-language answers depend on itz=Re-read the answers above against the current text, then run:z)  python -m openrights freshness --acceptrG   )printrY   )re   r_   itemheadingr   r   r   reportz   s,    rj   )r   r   r   r   )r   r   r   r   )r1   r   r   r   )F)r   r   r5   r6   r   r7   )re   r7   r   rf   )__doc__
__future__r   r   rM   r   r   pathlibr   ingestr   r   r	   r
   rO   r   r0   r4   rd   rj   r   r   r   r   <module>   s    


A