107 lines
7.3 KiB
Python
107 lines
7.3 KiB
Python
"""Explicit bibliographic aliases; a directory family alone is not venue evidence."""
|
|
import re
|
|
|
|
|
|
# Ordered long/joint names precede their shorter forms.
|
|
BOOKS = {
|
|
'aaai': [('AAAI/IAAI','AAAI+IAAI',['AAAI','IAAI']),('AAAI','AAAI',[])],
|
|
'ijcai': [('IJCAI/ECAI','ECAI+IJCAI',['ECAI','IJCAI']),('IJCAI','IJCAI',[])],
|
|
'ecai': [('ECAI','ECAI',[]),('AISB/GI (ECAI)','ECAI',[]),('AISB (ECAI)','ECAI',[])],
|
|
'nips': [('NeurIPS Datasets and Benchmarks','NeurIPS',[]),('NeurIPS','NeurIPS',[]),('NIPS','NeurIPS',[])],
|
|
'icml': [('ICML','ICML',[]),('ML','ICML',[])], 'iclr':[('ICLR','ICLR',[])],
|
|
'colm':[('COLM','COLM',[])], 'colt':[('COLT/EuroCOLT','COLT',[]),('COLT','COLT',[])],
|
|
'aistats':[('AISTATS','AISTATS',[])], 'uai':[('UAI','UAI',[])],
|
|
'clear2':[('CLeaR','CLeaR',[])],
|
|
'acl':[('COLING-ACL','ACL+COLING',['ACL','COLING']),('ACL/IJCNLP','ACL',[]),('ACL','ACL',[])],
|
|
'emnlp':[('EMNLP-CoNLL','CoNLL+EMNLP',['CoNLL','EMNLP']),('EMNLP/IJCNLP','EMNLP',[]),('EMNLP','EMNLP',[])],
|
|
'naacl':[('HLT/EMNLP','EMNLP',[]),('NAACL-HLT','NAACL',[]),('HLT-NAACL','NAACL',[]),('NAACL','NAACL',[]),('HLT','NAACL',[])],
|
|
'eacl':[('EACL','EACL',[])],
|
|
'coling':[('LREC/COLING','COLING+LREC',['COLING','LREC']),('COLING-ACL','ACL+COLING',['ACL','COLING']),('COLING','COLING',[])],
|
|
'lrec':[('LREC/COLING','COLING+LREC',['COLING','LREC'])],
|
|
'conll':[('CoNLL/LLL','CoNLL',[]),('CoNLL','CoNLL',[])],
|
|
'cvpr':[('CVPR','CVPR',[])], 'iccv':[('ICCVW','ICCV',[]),('ICCV','ICCV',[])],
|
|
'eccv':[('ECCV','ECCV',[])], 'kdd':[('KDD','KDD',[])], 'sigir':[('SIGIR','SIGIR',[])],
|
|
'www':[('WWW','WWW',[])], 'wsdm':[('WSDM','WSDM',[])], 'recsys':[('RecSys','RecSys',[])],
|
|
'cikm':[('CIKM','CIKM',[])], 'icdm':[('ICDM','ICDM',[])], 'sdm':[('SDM','SDM',[])],
|
|
'ecml':[('ECML','ECML',['ECML-PKDD']),('EWSL','EWSL',['ECML-PKDD'])],
|
|
'pkdd':[('ECML/PKDD','ECML-PKDD',[]),('ECML PKDD','ECML-PKDD',[]),('PKDD','PKDD',['ECML-PKDD'])],
|
|
'icra':[('ICRA','ICRA',[])], 'iros':[('IROS','IROS',[])],
|
|
'rss':[('Robotics: Science and Systems','RSS',[])], 'corl':[('CoRL','CoRL',[])],
|
|
'aips':[('ICAPS','ICAPS',[]),('AIPS','AIPS',['ICAPS'])], 'icaps':[('ICAPS','ICAPS',[])],
|
|
'kr':[('KR','KR',[])], 'atal':[('AAMAS','AAMAS',[])],
|
|
'icassp':[('ICASSP','ICASSP',[])],
|
|
'interspeech':[('INTERSPEECH','INTERSPEECH',[]),('EUROSPEECH','EUROSPEECH',['INTERSPEECH']),('ICSLP','ICSLP',['INTERSPEECH']),('ECST','ECST',['INTERSPEECH'])],
|
|
'ismir':[('ISMIR','ISMIR',[])], 'miccai':[('MICCAI','MICCAI',[])],
|
|
'fat':[('FAccT','FAccT',[]),('FAT*','FAccT',[]),('FAT','FAccT',[])],
|
|
'mm':[('ACM Multimedia','MM',['ACMMM','ACM MM'])],
|
|
'mlsys':[('MLSys','MLSys',[]),('SysML','MLSys',['SysML'])],
|
|
}
|
|
|
|
|
|
def scope(row,volume,through_year=2026):
|
|
fields=row['fields'];books=fields.get('booktitle',[])
|
|
if len(books)!=1:return None,'ambiguous_booktitle'
|
|
book=books[0];key=row.get('key','');parts=key.split('/')
|
|
family=parts[1] if len(parts)>=3 and parts[0]=='conf' else ''
|
|
if family not in BOOKS:return None,'outside_target_family'
|
|
if not volume:return None,'missing_proceedings'
|
|
title=' '.join(volume['fields'].get('title',[]))
|
|
if family=='clear2' and 'causal learning and reasoning' not in title.casefold():return None,'ambiguous_venue_name'
|
|
if family=='mlsys' and 'machine learning and systems' not in title.casefold():return None,'ambiguous_venue_name'
|
|
matched=None
|
|
for base,venue,aliases in BOOKS[family]:
|
|
match=re.fullmatch(re.escape(base)+r'(?:\s+\((.*)\)|\s+(.*)|,\s*(Vol\.\s*\d+))?',book,re.I)
|
|
if match:
|
|
matched=(base,venue,aliases,' '.join(value for value in match.groups() if value));break
|
|
if not matched:return None,'unclassified_booktitle'
|
|
base,venue,aliases,suffix=matched
|
|
volume_family=volume['key'].split('/')[1] if volume['key'].startswith('conf/') else ''
|
|
if volume_family!=family and not any(item[1]==venue for item in BOOKS.get(volume_family,[])):
|
|
return None,'crossref_venue_conflict'
|
|
year_match=re.match(r'conf/[^/]+/((?:19|20)\d{2})',volume['key'])
|
|
year_basis='proceedings key, checked against bibliographic year or explicit event year in proceedings title'
|
|
if year_match:year=int(year_match[1])
|
|
else:
|
|
# Old SIGIR/CIKM/IJCAI keys use two digits. Never guess their century.
|
|
short=re.fullmatch(r'conf/[^/]+/(\d{2})',volume['key'])
|
|
years=set(re.findall(r'\b(?:19|20)\d{2}\b',title))
|
|
if not short or len(years)!=1:return None,'unverified_edition_year'
|
|
year=int(next(iter(years)))
|
|
if year%100!=int(short[1]):return None,'unverified_edition_year'
|
|
year_basis='unique explicit event year in proceedings title, corroborated by two-digit proceedings key'
|
|
if year>through_year:return None,'future_edition'
|
|
if venue=='MLSys' and year<2019:return None,'nonarchival_predecessor'
|
|
bibliographic=fields.get('year',[])
|
|
if len(bibliographic)!=1 or not re.fullmatch(r'\d{4}',bibliographic[0]):return None,'ambiguous_bibliographic_year'
|
|
if int(bibliographic[0])!=year and not re.search(r'\b'+str(year)+r'\b',title):return None,'unverified_year_difference'
|
|
text=(suffix+' '+title).casefold();track='main'
|
|
if 'findings' in text:track='findings'
|
|
elif 'shared task' in text:track='shared_task'
|
|
elif 'student research' in text or 'workshop' in text or 'wokshop' in text or base in ('ICCVW','EWSL'):track='workshop'
|
|
elif ('demo track' in text and ('industrial track' in text or 'applied data science track' in text)):track='proceedings_unspecified'
|
|
elif 'demonstration' in text or re.search(r'\bdemos?\b',text):track='demo'
|
|
elif 'tutorial' in text:track='tutorial'
|
|
elif 'industry' in text or 'industrial track' in text or 'applied data science' in text:track='industry'
|
|
elif 'position paper' in text:track='position'
|
|
elif 'companion' in text:track='companion'
|
|
elif 'late-breaking' in text or 'late breaking' in text:track='late_breaking'
|
|
elif 'doctoral' in text:track='doctoral'
|
|
elif 'challenge' in text or 'competition' in text:track='competition'
|
|
elif 'bridge program' in text:track='bridge'
|
|
elif 'special interest tracks' in text:track='special_interest'
|
|
elif 'alternate track' in text:track='alternate'
|
|
elif base=='NeurIPS Datasets and Benchmarks':track='datasets_benchmarks'
|
|
elif suffix and not (re.fullmatch(r'(?:\d+|Vol\.\s*\d+|Volume \d+: (?:Long|Short) Papers|Long Papers|Short Papers|Selected Papers|Fall)',suffix,re.I)
|
|
or suffix.casefold() in ('poster','posters','oral','spotlight')):return None,'unclassified_track'
|
|
elif suffix.casefold() in ('poster','posters') and venue not in ('ICLR','ACL','EMNLP','NAACL','EACL','COLING','CoNLL'):track='poster'
|
|
if venue=='AAAI+IAAI':track='proceedings_unspecified'
|
|
if venue=='AAMAS' and year<2002:return None,'predecessor_not_aamas'
|
|
result = {'venue':venue,'venue_aliases':aliases,'year':year,'track':track,'bibliographic_year':int(bibliographic[0]),
|
|
'rule':family+':'+base,'proceedings_title':title,'edition_year_basis':year_basis}
|
|
if venue=='MM' and track=='main':
|
|
# A main MM volume also includes doctoral, tutorials, companion papers
|
|
# and other sections. DBLP XML omits the per-paper TOC section.
|
|
result['track']='proceedings_unspecified'
|
|
result['track_scope_note']='Main proceedings volume; per-paper section is absent from the bibliography. Do not infer a regular research track.'
|
|
return result,None
|