76 lines
4.2 KiB
Python
76 lines
4.2 KiB
Python
"""Collect explicit public ICLR decisions from the authors' PeerRead release.
|
|
|
|
Review bodies and parsed full paper text are deliberately not persisted.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import time
|
|
import urllib.request
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
|
|
from paper_atlas.io.http import fetch
|
|
|
|
|
|
def collect(cache,out):
|
|
root = Path(out)
|
|
root.mkdir(parents=True,exist_ok=True)
|
|
commit = json.loads(fetch('https://api.github.com/repos/allenai/PeerRead/commits/master',cache))['sha']
|
|
tree = json.loads(fetch(f'https://api.github.com/repos/allenai/PeerRead/git/trees/{commit}?recursive=1',cache))
|
|
if tree.get('truncated'):
|
|
raise RuntimeError('Truncated repository tree')
|
|
paths = [v['path'] for v in tree['tree'] if v['path'].startswith('data/iclr_2017/') and '/reviews/' in v['path'] and v['path'].endswith('.json')]
|
|
if not paths:
|
|
raise RuntimeError('No explicit ICLR2017 metadata files found')
|
|
cache_dir = root/'peerread-normalized'/commit
|
|
cache_dir.mkdir(parents=True,exist_ok=True)
|
|
grouped = defaultdict(list)
|
|
for number,path in enumerate(paths,1):
|
|
external = '/'.join(path.split('/')[1:])
|
|
normalized_cache = cache_dir/(path.replace('/','_'))
|
|
if normalized_cache.exists():
|
|
record = json.loads(normalized_cache.read_text())
|
|
else:
|
|
raw_url = f'https://raw.githubusercontent.com/allenai/PeerRead/{commit}/{path}'
|
|
with urllib.request.urlopen(urllib.request.Request(raw_url,headers={'User-Agent':'PaperAtlas/0.1 metadata research'}),timeout=60) as response:
|
|
payload = response.read(3*1024**2+1)
|
|
if len(payload)>3*1024**2:
|
|
raise RuntimeError('Unexpected metadata size')
|
|
data = json.loads(payload)
|
|
conference = data.get('conference','')
|
|
if 'iclr' not in conference.lower() or '2017' not in conference:
|
|
raise RuntimeError('Unexpected conference metadata: '+conference)
|
|
accepted = data.get('accepted')
|
|
status = ('accepted' if accepted else 'rejected') if isinstance(accepted,bool) else 'unknown'
|
|
authors = data.get('authors') or []
|
|
if isinstance(authors,str):
|
|
authors = [author.strip() for author in authors.split(',') if author.strip()]
|
|
record = {'source':'peerread','external_id':external,'url':f'https://github.com/allenai/PeerRead/blob/{commit}/{path}','title':data['title'],'abstract':data.get('abstract'),'authors':authors,'identifiers':{},'venue':'ICLR','year':2017,'track':'workshop' if 'workshop' in conference.lower() else 'main','status':status,'raw_decision':json.dumps({'accepted':accepted}),'metadata':{'conference':conference,'dataset_commit':commit,'dataset_path':path,'original_id':data.get('id'),'accepted':accepted,'decision_provenance':'PeerRead authors public release; explicit label, not inferred from publication absence'}}
|
|
normalized_cache.write_text(json.dumps(record,ensure_ascii=False))
|
|
time.sleep(0.25)
|
|
grouped[record['track']].append(record)
|
|
if number%25==0:
|
|
print(json.dumps({'source':'peerread','collected':number,'expected':len(paths)}),flush=True)
|
|
for track,records in grouped.items():
|
|
target = root/f'peerread-iclr2017-{track}.jsonl'
|
|
part = target.with_suffix('.part')
|
|
with part.open('w') as output:
|
|
for record in records:
|
|
output.write(json.dumps(record,ensure_ascii=False)+'\n')
|
|
part.replace(target)
|
|
manifest = {'source':'peerread','venue':'ICLR','year':2017,'track':track,'count':len(records),'list_url':f'https://github.com/allenai/PeerRead/tree/{commit}/data/iclr_2017','dataset_commit':commit,'file':str(target),'decision_counts':{status:sum(r['status']==status for r in records) for status in ('accepted','rejected','unknown')}}
|
|
target.with_suffix('.manifest.json').write_text(json.dumps(manifest,indent=2))
|
|
print(json.dumps(manifest),flush=True)
|
|
|
|
|
|
def main(argv=None, *, prog=None):
|
|
parser = argparse.ArgumentParser(prog=prog)
|
|
parser.add_argument('--cache',required=True)
|
|
parser.add_argument('--out',required=True)
|
|
args = parser.parse_args(argv)
|
|
collect(args.cache,args.out)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|