#!/usr/bin/env python3 """Reparse frozen synthetic title fields. Not a Google or complete feed validator.""" import csv import hashlib import io import json import platform import sys from datetime import datetime, timezone from pathlib import Path from xml.dom import minidom from xml.parsers.expat import ExpatError BASE = Path(__file__).resolve().parent G = 'http://base.google.com/ns/1.0' def structured(value): parts = next(csv.reader([value], delimiter=':', quotechar='"', doublequote=True, strict=True)) if len(parts) != 2 or not parts[1]: raise ValueError('Expected exactly digital_source_type:content, with nonempty content') origin, content = parts if origin not in ('', 'default', 'trained_algorithmic_media'): raise ValueError('Unknown source type') return {'digital_source_type': origin or 'default', 'content': content} def tsv(path): with path.open(encoding='utf-8', newline='') as f: # Inner quotes occur after trained_algorithmic_media:, not at TSV field start. rows = list(csv.DictReader(f, delimiter='\t', strict=True)) if len(rows) != 1 or None in rows[0] or any(v is None for v in rows[0].values()): raise ValueError('Expected exactly one complete synthetic record') d = rows[0] return {'id': d['id'], 'title': d.get('title'), 'structured_title': structured(d['structured_title']) if d.get('structured_title') else None} def text(element): return ''.join(n.data for n in element.childNodes if n.nodeType in (n.TEXT_NODE, n.CDATA_SECTION_NODE)) def xml(path): doc = minidom.parse(str(path)) items = doc.getElementsByTagName('item') if len(items) != 1: raise ValueError('Expected exactly one item') item = items[0] def one(parent, name, required=False): nodes = [n for n in parent.childNodes if n.nodeType == n.ELEMENT_NODE and n.namespaceURI == G and n.localName == name] if len(nodes) > 1 or (required and not nodes): raise ValueError('Unexpected occurrence count: '+name) return nodes[0] if nodes else None def val(parent, name, required=False): el = one(parent, name, required) return text(el) if el is not None else None st = one(item, 'structured_title') return {'id': val(item,'id',True), 'title': val(item,'title'), 'structured_title': {'digital_source_type':val(st,'digital_source_type') or 'default', 'content':val(st,'content',True)} if st is not None else None} def model(d): # This is a projection of documentary precedence, NOT observed Google processing. if d['title']: return {'document_selected':'title', 'text':d['title'], 'warning':'title overrides structured_title' if d['structured_title'] else None} return {'document_selected':'structured_title','text':d['structured_title']['content'],'warning':None} def main(): cases = json.loads((BASE / 'cases.json').read_text(encoding='utf-8'))['cases'] for n, case in enumerate(cases, 1): stem = f'{n:02}-{case["case"]}' left, right = tsv(BASE / (stem + '.tsv')), xml(BASE / (stem + '.xml')) expected = {key: case[key] for key in ('id', 'title', 'structured_title')} if left != right or left != expected: raise SystemExit('ERROR: local fields differ: ' + case['id']) chosen = model(left) origin = (left['structured_title'] or {}).get('digital_source_type', 'not-set') print(case['id'] + ' | TSV=XML: OK | selected=' + chosen['document_selected'] + ' | origin=' + origin) print('text=' + chosen['text']) if chosen['warning']: print('warning=' + chosen['warning']) print('4 cases read locally; Google acceptance NOT checked') return 0 if __name__ == '__main__': raise SystemExit(main())