Mercurial > hg > cc > azure
view master/src/wecu/sac_schemes.py @ 67:13182e98a1ab
use sorted insertion into tuple list for properties
author | Henry S. Thompson <ht@markup.co.uk> |
---|---|
date | Thu, 04 Jun 2020 17:58:10 +0000 |
parents | b04870ab3035 |
children |
line wrap: on
line source
#!/usr/bin/python3 '''Assumes export PYTHONIOENCODING=utf-8 has been done if necessary Usage: uz ...wat.gz | sac_schemes.py [-d] [altStorageScheme] where altStorageScheme if present selects an alternative approach to storing triple counts: [absent]: three nested dictionaries 1: one dictionary indexed by 4-tuple 2: one dictionary indexed by ".".join(keys)''' import sys, json, regex print(sys.argv,file=sys.stderr) if len(sys.argv)>1 and sys.argv[1]=='-d': sys.argv.pop(1) dictRes=True else: dictRes=False META_PATH=['Envelope', 'Payload-Metadata', 'HTTP-Response-Metadata'] PATHS={'hdr':['Headers'], 'head':['HTML-Metadata','Head'], 'body':['HTML-Metadata','Links']} SCHEME=regex.compile('(<?[a-zA-Z][a-zA-Z0-9+.-]*):') URN=regex.compile('(<?urn:[a-z][a-z0-9+.-]*):',regex.I) EMPTY='' D={} def insert(e,tbt): '''insert something into a trivial pair-impl of a list, not balanced!''' if isinstance(tbt,tuple): assert not isinstance(tbt[0],tuple) if e<=tbt[0]: return (e,tbt) else: return (tbt[0],insert(e,tbt[1])) elif e<=tbt: return (e,tbt) else: return (tbt,e) def walk(o,f,r,path=None): '''Apply f to every key+leaf of a json object reached via p in region r''' if isinstance(o,dict): for k,v in o.items(): if isinstance(v,dict): walk(v,f,r,(path,k)) elif isinstance(v,(list,tuple)): walked=False for i in v: if isinstance(i,dict): if (not walked) and (i is not v[0]): print('oops',key,path,k,i,file=sys.stderr) walked=True walk(i,f,r,(path,k)) elif walked: print('oops2',key,path,k,i,file=sys.stderr) if not walked: f(v,k,path,r) else: kk=f(v,k,path,r,o) if kk is not None: #print(v,D,kk,file=sys.stderr) if v in D: (rr,pp,jj,ss)=D[v] D[v]=(rr,pp,insert(k,jj),ss) else: D[v]=kk if D: for kk in D.values(): res[kk]=res.get(kk,0)+1 D.clear() elif isinstance(o,(list,tuple)): for i in o: walk(i,f,r,path) def pp(v,k,p,r,parent=None): '''Handle a leaf value v, with key k in parent, under path p from r Uses nested dictionaries''' if isinstance(v,str): m=SCHEME.match(v) if m is not None: n=URN.match(v) if n is not None: m=n s=m.group(1) # The following assumes paths are always either length 1 or length 2!!! # by open-coding rather than using qq(p) if p is not None: assert p[0] is None p=p[1] d=res[r].setdefault(p,dict()) d=d.setdefault(k,dict()) d[s]=d.get(s,0)+1 def pp_tuple(v,k,p,r,parent=None): '''Handle a leaf value v, with key k in parent, under path p from r Uses one dict and 4-tuple''' if isinstance(v,str): m=SCHEME.match(v) if m is not None: n=URN.match(v) if n is not None: m=n s=m.group(1) # The following assumes paths are always either length 1 or length 2!!! # by open-coding rather than using qq(p) if p is not None: assert p[0] is None p=p[1] if parent is None: res[kk]=res.get(kk,0)+1 else: return (r,p,k,s) SEP='\x00' DOT='.' def pp_concat(v,k,p,r,parent=None): '''Handle a leaf value v, with key k in parent, under path p from r Uses one dict and one string''' if isinstance(v,str): m=SCHEME.match(v) if m is not None: n=URN.match(v) if n is not None: m=n s=m.group(1) # The following assumes paths are always either length 1 or length 2!!! # by open-coding rather than using qq(p) if p is None: p=EMPTY else: assert p[0] is None p=p[1] k=SEP.join((r,p,k,s)) res[k]=res.get(k,0)+1 def dump(res): for r in res.keys(): rv=res[r] for p in rv.keys(): pv=rv[p] for k,v in pv.items(): for s,c in v.items(): print(r,end=EMPTY) if p is None: print(EMPTY,end='\t') else: print('.',p,sep=EMPTY,end='\t') print(k,end='\t') print(s,c,sep='\t') def dump_tuple(res): for (r,p,k,s),c in res.items(): print(r,end=EMPTY) # The following assumes paths are always either length 1 or length 2!!! # by open-coding rather than using qq(p) if p is None: print(EMPTY,end='\t') else: print(DOT,p,sep=EMPTY,end='\t') while isinstance(k,tuple): print(k[0],end='&') k=k[1] print(k,end='\t') print(s,c,sep='\t') def dump_concat(res): for ks,c in res.items(): (r,p,k,s)=ks.split(SEP) print(r,end=EMPTY) # The following assumes paths are always either length 1 or length 2!!! # by open-coding rather than using qq(p) if p==EMPTY: print(EMPTY,end='\t') else: print('.',p,sep=EMPTY,end='\t') print(k,end='\t') print(s,c,sep='\t') if len(sys.argv)>=2: res=dict() if sys.argv[1]=='1': print('using tuple',file=sys.stderr) pp=pp_tuple dump=dump_tuple elif sys.argv[1]=='2': print('using concat',file=sys.stderr) pp=pp_concat dump=dump_concat else: print('using nested',file=sys.stderr) res=dict((r,dict()) for r in PATHS.keys()) def main(): global n # for debugging n=0 for l in sys.stdin: if l[0]=='{' and '"WARC-Type":"response"' in l: j=json.loads(l) n+=1 for s in META_PATH: j=j[s] for k,v in PATHS.items(): p=j try: for s in v: p=p[s] except KeyError as e: continue walk(p,pp,k) print(n,file=sys.stderr) if dictRes: print('res=',end=EMPTY) from pprint import pprint pprint(res) else: dump(res) def qq(p): if p is None: sys.stdout.write('\t') else: qq1(p[0]) print(p[1],end='\t') def qq1(p): if p is None: return else: qq1(p[0]) print(p[1],end='.') if __name__=="__main__": main()