diff options
Diffstat (limited to 'old')
-rwxr-xr-x | old/fix_invalid_issnl.py | 75 | ||||
-rw-r--r-- | old/invalid_fatcat_issnl.tsv | 118 | ||||
-rwxr-xr-x | old/parse_merge_metadata.py | 674 |
3 files changed, 867 insertions, 0 deletions
diff --git a/old/fix_invalid_issnl.py b/old/fix_invalid_issnl.py new file mode 100755 index 0000000..521f334 --- /dev/null +++ b/old/fix_invalid_issnl.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +""" +This is a one-off script for pushing ISSN-L fixes to fatcat via the API. + +It excpects to have fatcat python libraries available. Run it like: + + export FATCAT_API_AUTH_TOKEN="..." + ./fix_invalid_issnl.py ~/code/chocula/invalid_fatcat_issnl.tsv + +It creates a new editgroup, which you'll need to merge/accept manually. + +Defaults to QA API endpoint; edit the file to switch to prod. +""" + +import os, sys +import csv + +from fatcat_tools import authenticated_api +from fatcat_client import Editgroup, ContainerEntity +from fatcat_client.rest import ApiException + +API_ENDPOINT = "https://api.qa.fatcat.wiki/v0" + + +def run(api, row_iter): + + eg = api.create_editgroup(Editgroup(description= + "Update or merge containers with invalid (by checksum) ISSN-L. Using the fix_invalid_issnl.py script from chocula repo.")) + print("Editgroup ident: {}".format(eg.editgroup_id)) + for row in row_iter: + #print(row) + fixed_issnl = row['fixed_issnl'].strip() + if not fixed_issnl: + print("SKIP") + continue + assert row['issnl'].strip() != fixed_issnl + invalid = api.get_container(row['fatcat_ident']) + assert invalid.state == "active" + try: + fixed = api.lookup_container(issnl=row['fixed_issnl']) + except ApiException as ae: + if ae.status != 404: + raise ae + fixed = None + + if fixed: + # merge/redirect + assert fixed.state == "active" + print("MERGE: {} -> {}".format(invalid.ident, fixed.ident)) + invalid.redirect = fixed.ident + api.update_container(eg.editgroup_id, invalid.ident, + ContainerEntity(redirect=fixed.ident)) + else: + # update in-place with fixed ISSN-L + print("FIX: {}: {}".format(invalid.ident, fixed_issnl)) + invalid.issnl = fixed_issnl + api.update_container(eg.editgroup_id, invalid.ident, invalid) + + # intentionally not merging editgroup + print("Editgroup ident: {}".format(eg.editgroup_id)) + print("DONE") + +def main(): + api = authenticated_api( + API_ENDPOINT, + # token is an optional kwarg (can be empty string, None, etc) + token=os.environ.get("FATCAT_API_AUTH_TOKEN")) + + path = sys.argv[1] + reader = csv.DictReader(open(path), delimiter='\t') + run(api, reader) + +if __name__ == '__main__': + main() diff --git a/old/invalid_fatcat_issnl.tsv b/old/invalid_fatcat_issnl.tsv new file mode 100644 index 0000000..14a8dec --- /dev/null +++ b/old/invalid_fatcat_issnl.tsv @@ -0,0 +1,118 @@ +fixed_issnl issnl fatcat_ident name +1530-1311 1550-1311 2nklacmgkjdfjib7kqi3hdh76a International Symposium on Temporal Representation and Reasoning/TIME, Proceedings of the +2306-0441 2223-0441 rngvdeed65ffhmgfxti7s2z6by Journal of Local and Global Health Science +1641-6554 1641-6565 yviicehmubf4bcxo23q43sbkzu Kolposkopia +1526-7539 1526-7639 pnyefvclqfabjlnl4suox6pdte International Symposium on Modeling, Analysis and Simulation of Computer and Telecommunication Systems, Proceedings of the +0276-6574 0276-6547 hcrk2xeoknf7daxwurje2vg3n4 Computers in Cardiology Conference +0018-9413 0359-4237 ngthxcdwgzhovfgimtll6owdnm IEEE Transactions on Geoscience Electronics + 2630-4301 drgmggxvfjbjjkakrjegaujkgy Food Modelling Journal +1089-7771 1086-7771 sjjfcknh3zawndw6jdmvfrix7a IEEE transactions on information technology in biomedicine +1093-1139 1039-1139 nnrvd2qmhzbebk2hsopvtchodq Academic Physician and Scientist +0037-7996 0037-7796 klgctd3pbvbwpf6ynvd4wns53m The Social studies +1520-5363 1520-5263 v3zwf2ujd5flzlopo2h4ggmdhu Document Analysis and Recognition (ICDAR), Proceedings of the International Conference on +1992-4712 1992-4721 mmptdalzizepxi7uqfohnjj3uy NAFU Farmer +0066-7374 0066-7372 s5fjnmhtkbggtcn2itw4g34bxq Proceedings of the Aristotelian Society (Hardback) + 1234-5678 lkoo6d6lcvba7hhwswxc6x2sly Test s Publication +1088-7725 1080-7725 ikqqv7cnwfhpfkqy2lfugiz6ae AUTOTESTCON IEEE Systems Readiness Technology Conference, Proceedings of the + 0537-4737 kklzxgby4zfqvow7klnneyjj7e IEE Telecommunications Hot Topics Forum +1527-4160 1528-1145 q7j7ro4qfnbjdb2kkkcxatesna Journal of Psychiatric Practice +2468-4295 2648-4309 qtvq67fxfje4ro5mldpan35s34 Brill Research Perspectives in Art and Law + 1113-1114 qqkn2huytzfj5fw573bc5ihxyi A-to-Z Guide to Thermodynamics Heat and Mass Transfer and Fluids Engineering +1080-241X 1082-241X c3pwhzd5hvbqzfgqdrbxnyfn3i Proceedings Annual Simulation Symposium +1050-4729 1059-4729 b5exnobxjvd65flifftm6gy3f4 IEEE International Conference on Engineering of Complex Computer Systems, Proceedings of the +0295-1630 0296-1630 u7tretgoaffblbvtsxbtml6pbu Pleine Marge +1071-0485 1071-0483 6x6ijsddbva7xldh5ubqoxcf7a IEEE International Workshop on Future Trends of Distributed Computing Systems (FTDCS) +0093-2582 0093-2592 ajsbz23tbnghzmwleirmr7or3a The St. Croix review +1544-6301 1544-6307 tl5eyaqxbfcllmy7ip63up2sma Journal Of Cancer Integrative Medicine +2155-5745 2155-5754 b6s7uywtdfannklqblq6tghkte International Radar Symposium +0767-3701 3767-3701 wyh37ssfibcdtlncbi53m7ms4q Recherche et Applications en Marketing +2255-8691 2225-8691 bl6y7mpqgfdtxoqqbcq7bikwvu Applied Computer Systems +2561-3103 2561-3101 46uvtmw72zgpjhmbkvllrnwlau Journal of Contact Lens Research and Science +0939-7248 0939-7148 xma7s7w65zed3g4ojkmfz7r6ca European journal of pediatric surgery +1071-6084 1071-6048 dqthad6e3vfhxjf4px42u76dbe The Journal of technology studies +1528-8463 1538-8463 j4cxdxvaxrb2thxtvbda47gy2y Hospital law's Regan report + 2352-3951 pc6ftuth7ffvreuolz5rxbaye4 Nanoelectronics and Spintronics +2006-9820 2006-9802 wmugzbvj6rcrzg5hf5jtug6tja Journal of Toxicology and Environmental Health Sciences +0953-9182 0955-9182 mcpenq5ytrd6zpmuyc5crmx2fi Contemporary reviews in obstetrics and gynaecology +0259-188X 0459-188X iw7xtyujbrax7ojqqbgxjlrsem Indicator South Africa +0894-9115 0894-9112 wa3xjixz6ve75j2ecrk23sposq American Journal of Physical Medicine & Rehabilitation +0165-9367 0165-0937 32qxo574yjcbbbchqijyakggzm BABesch - Bulletin Antieke Beschaving +2586-1247 2968-9853 whexebkzebh5ncyb6hvjppkyqa International Journal of Sustainable Lighting +1811-7813 1511-7813 ipu26yclwnhw7ekbsve6wpb44a Point-Based Graphics (PBG), IEEE VGTC Symposium Proceedings +0394-1051 2532-4681 opjfte47erhb7dke5aeap5wbse Eupalino : cultura della città e della casa + 0538-9989 cjkaddjcfzhmveuuffmtycjxmm Professional Development Course on Railway Electrification Infrastructure and Systems +1081-7735 1085-7735 oam6q4knpjaq5enpfzra3jazfi Asian Test Symposium, Proceedings of the +1064-7546 1064-7564 su2chgwy2vf6zavx6ekbyv4sem Journal of Environmental Polymer Degradation +1817-4426 1817-4428 kwqvwiwyx5b5vbjxfetnpxt6ky The Retail and Marketing Review + 1432-3123 sa73qgbh25bgnd5q6o262u7inm china Frontiers +2141-2634 2142-2634 g2z2ld65nve2pdwjk7gr3qe25e Journal of Civil Engineering and Construction Technology + 9999-9998 nvya624wtbbgxl6bdsrqatslum Space Resolver 15 + 0006-0809 5gtixxmzm5aodhqpd4bqp3hqiq The Bridge +0547-3578 7964-0977 bonch7zygnadddv2fqoecmj4yy IEEE National Aerospace and Electronics Conference +0003-3839 0003-3829 yqozzqdrwvedvafwrokyx5puna Annales agronomiques +2213-9885 2213-0886 hdfn46db7bgw5e7lmmlx3iubxe Current Chemical Genomics and Translational Medicine +2378-5225 2333-5225 tgcsgbazhrfqzodbpdkjgtvv6i Pathophysiology of Cell Injury Journal +1761-7227 1761-7727 uynmrtrspngoxdjdihrwx3x6oi International orthodontics +1079-5545 1990-1993 goo5ob7jqzh7ro5pft7j3qdkaq Academy of Management Executive +1536-0040 1111-1111 7au72xtbafdwpdsz6d53elvk2a SIAM E-Journal on Applied Dynamical Systems +0163-514X 0613-514X jjup253sjjc2pkg3zz3vfz3nye The Journal of Prevention +1020-1491 1020-1497 gtn3v33zcrg5zndvyir7l65mna Image & Text : a Journal for Design +0972-8279 0972-8270 laktqf5ag5fpje2kfzzutnooyq Journal of maxillofacial and oral surgery + 0133-0189 kixvoietq5bhjeub36kqu6kwpa DCDS Supplements +1071-9458 1071-9658 xmwrlaklfbe53ayhlzrhinb5cu International Symposium on Software Reliability Engineering, Proceedings of the +0040-6325 0040-5325 qpbm4in22jevvfeht6mhawmllq The Thomist: A Speculative Quarterly Review +0039-4157 0039-4156 5xp3jodttzhmrp22xl5femz52q Study encounter +1122-5300 1125-5300 zeclu7ljprfkvauia5vljzvnfy Parolechiave + 1453-212X dhdssmbdbjfyfe53dpbfgcejti Research Journal of International Studies +1530-1834 1550-1834 d57htlky2nexlmy4wa36lva6wy Brazilian Symposium on Computer Graphics and Image Processing + 0686-3174 ajqi4yc43feitknsozbzgpjnre Y hoc Viet Nam +0015-8208 1521-3979 hoifudhajjgwfauzsgevzgogpa Fortschritte der Physik/Progress of Physics +2141-6613 2141-6113 xghlebvnzzco3j6eb6ywlwal3m International Journal of Water Resources and Environmental Engineering +0112-9120 0110-7903 6c6fql7levae5lyunnm6aiuzaq New Zealand monthly review + 0798-1881 xnblbncvezfuveglr5loa2rqym Construccion +0017-3703 0017-3701 5dfgot47anfwxk65i3biooh6we Greater amusements +0015-8577 0015-1287 xjwp4btqb5gojjcmdegglcyrli The Forumeer +0160-8371 1060-8371 jbubc4zldfegxfkstwvg2oh47i IEEE Photovoltaic Specialists Conference, Conference Record of the +2378-9530 2378-8953 4x4y2afasfg45iqoo47vphr6iq Veterinary Research and Case Reports +2162-4933 2162-4923 azachjfocbf6xciiz3m3ze564u Current Dermatology Reports +0882-5645 0882-5646 7fyviche6rbyzfsznpfrvvlyv4 Topics in Pain Management +0033-7196 0033-7916 hsp5k2eij5dwpe4tbqf2n7gkpa RWDSU record +1823-3902 1832-3902 sdw3bp3mk5b7vpkf22nj3dbttu Journal of tropical biology & conservation +1066-6192 1066-6172 jlzpb6temzauliox6i7ecma3mq Euromicro Workshop on Parallel and Distributed Processing, Proceedings of the +1818-9091 1819-9091 gom2wpnbtje6zfssyliobeup24 SA Horseman +2504-2289 8888-0000 eyngovpvzfen5hsd55udttyk2u Big Data and Cognitive Computing + 8164-9547 knfumdwvafcnjnwge24v3kaip4 IEEE International Conference on Communications (2006) +0369-8718 0368-8718 fatiuzqz7veebcr4n3t7wvn2n4 Proceedings of the Chemical Society +2379-6227 2397-6227 37z2akit2nbtbea5qcn5shreru Computational Psychiatry +0025-6978 0026-6978 vq6o3qyo3rezvercvuai2dciti Medical and biological illustration +2588-4174 4174-2588 lr5kb4tnq5bpjf3otub5umkdmy Afzalipour Journal of Clinical Research +2641-7928 1089-796X cff4m4os3bdznnc3fxbh2vv6ha International Conference on Parallel Architectures and Compilation Techniques, Proceedings of the +1931-7611 1931-7811 gmtif5qt6vddroc7uox4hxtch4 International journal of sexual health +0538-933X 0583-9364 xt6yxm3j7jdyzb6viwcltb6vsq International Reading Association. Proceedings of the annual convention + 7777-8888 3va7v7macvbalhxfujg5qfb7vi A Journal with Only 1 Volume +2157-3689 2157-3698 qegnzb72dzhf5jdunf3ge7jmji Limnology & Oceanography Fluids & Environment +1673-3487 1673-3508 z3jjoyzt45ecrh3c62afjmmxjm Frontiers of Physical in China +2571-6255 0013-0013 kwk3mhqddfddrd6hjqdnkg3f3y Fire +2141-2618 2141-2648 w5aoztaff5dg7harb4d2fyxzji Journal of Medical Laboratory and Diagnosis + 0804-3321 bqecated6jcwzoe56hmlxxydsq Utdanning + 0842-3446 wiidl3rotncxfpqoj46uegf5mq African Journal of Farm Child and Youth Development + 0539-9989 c7abz7liw5d53bskkcq7fcetvy Institution of Engineering and Technology International Conference on Medical Electrical Devices and Technology +1051-9173 1057-9173 auy44witavcmnejiss5h4ndlnq IEEE Symposium on Mass Storage Systems +0276-2234 0726-2234 6cbknzfrcfchhj73pmrlull2ky Oncology Times UK +0029-0564 1949-1986 s4poo6asmffuhlfw4lsneh6fq4 Nineteenth-Century Fiction + 9999-9999 zjdb7jqlxzcmlatzcjfjlrna6m Space Resolver 16 +0026-475X 0027-475X xmgc2fswtjaghmrocercgewwnu Minerva dietologica + 8164-2284 jkjfyzs3ijcnfbbfwxpewlwuaq IEEE International Electron Devices Meeting +1972-3792 2035-0167 nbo7hjouubbjba3kk5nn6oucmu Quaderni materialisti +0307-0565 0307-9565 mfgzm4p6zzgsxja4jjy7etl35q International journal of obesity +0738-6176 0723-6176 ynfkc2j7lzbpnchzpjic2orwlq The Psychotherapy patient +0963-0252 0965-0252 4s5mbkeut5g5tass6gz6qfivny Plasma sources science & technology +0144-3887 0143-3887 kdtbqfuuynd2fc64lprajnauvm Current Psychological Research +1470-3556 0741-9738 23er6bey4rgclaudewagavnf7m Advances in Mind-Body Medicine +0040-3261 0400-3261 arakq24urzgfhbz4abv6kz4mha Tennessee historical quarterly +0957-4484 0957-9207 k4v6ng56q5fb5p7yjyi7fsmv5a Nanotechnology +1087-9870 1089-9870 ubsj5pa5abbe7gqqdttkfibqle Thermal and Thermomechanical Phenomena in Electronic Systems (ITHERM), Intersociety Conference on +1812-6731 1812-6371 lhhrrjjxynd3lcadty56vqjk4u New Voices in Psychology +0163-0067 0613-0067 mlluqww5qnet7gqptazojr32bu Information world +1080-9775 1080-9175 yxjwuy4kj5hqvorfbmv4widyqa Biomedical Safety & Standards + 9999-9997 o6xy2oixgzag5o6oe26fdmmr4e Space Resolver diff --git a/old/parse_merge_metadata.py b/old/parse_merge_metadata.py new file mode 100755 index 0000000..b1d038b --- /dev/null +++ b/old/parse_merge_metadata.py @@ -0,0 +1,674 @@ +#!/usr/bin/env python3 + +import sys, csv, json +import ftfy +import pycountry +from collections import Counter + +ISSNL_FILE = 'data/20190220.ISSN-to-ISSN-L.txt' + +ENTREZ_FILE = 'data/entrez-journals.csv' +ROAD_FILE = 'data/road-2018-01-24.tsv' +ROAD_DATE = '2018-01-24' +DOAJ_FILE = 'data/doaj_20190124.csv' +DOAJ_DATE = '2019-01-24' +CROSSREF_FILE = 'data/doi_titles_file_2019-01-24.csv' +SHERPA_ROMEO_JOURNAL_FILE = 'data/romeo-journals.csv' +SHERPA_ROMEO_POLICY_FILE = 'data/romeo-policies.csv' +NORWEGIAN_FILE = 'data/2018-03-02 Norwegian Register for Scientific Journals and Series.csv' +NORWEGIAN_DATE = '2018-03-02' +LOCKSS_FILE = 'data/kbart_LOCKSS.txt' +CLOCKSS_FILE = 'data/kbart_CLOCKSS.txt' +PORTICO_FILE = 'data/Portico_Holding_KBart.txt' +JSTOR_FILE = 'data/jstor_all-archive-titles.txt' +SIM_FILE = 'data/MASTER TITLE_METADATA_LIST_20171019.converted.csv' +IA_CRAWL_FILE = 'data/journal_homepage_results.partial.tsv' + +# NOTE: this is a partial list, focusing on non-publisher hosted platforms and +# software frameworks +PLATFORM_MAP = { + 'OJS': 'ojs', + 'BMC': 'bmc', + 'SciELO Brazil': 'scielo', + 'SciELO Argentina': 'scielo', + 'SciELO': 'scielo', + 'SciELO Mexico': 'scielo', + 'SciELO Spain': 'scielo', + 'SciELO Portugal': 'scielo', + 'WordPress': 'wordpress', + 'Sciendo': 'sciendo', + 'Drupal': 'drupal', + 'revues.org': 'openedition', +} + +MIMETYPE_MAP = { + 'PDF': 'application/pdf', + 'HTML': 'text/html', + 'XML': 'application/xml', +} + +def unquote(s): + if s.startswith('"'): + s = s[1:] + if s.endswith('"'): + s = s[:-1] + if s.endswith('.'): + s = s[:-1] + return s.strip() + +def parse_lang(s): + if not s or s in ('Not applicable', 'Multiple languages', 'Unknown'): + return None + try: + if len(s) == 2: + lang = pycountry.languages.get(alpha2=s.lower()) + elif len(s) == 3: + lang = pycountry.languages.get(alpha3=s.lower()) + else: + lang = pycountry.languages.get(name=s) + return lang.alpha2.lower() + except KeyError: + return None + except AttributeError: + return None + +def parse_country(s): + if not s or s in ('Unknown'): + return None + try: + if len(s) == 2: + country = pycountry.countries.get(alpha2=s.lower()) + else: + country = pycountry.countries.get(name=s) + except KeyError: + return None + return country.alpha2.lower() + +def gaps_to_spans(first, last, gaps): + if not gaps: + return [[first, last]] + if not (last >= first and max(gaps) < last and min(gaps) > first): + # mangled + print("mangled years: {}".format((first, last, gaps))) + return [] + full = list(range(first, last+1)) + for missing in gaps: + full.remove(missing) + spans = [] + low = None + last = None + for year in full: + if not low: + low = year + last = year + continue + if year != last+1: + spans.append([low, last]) + low = year + last = year + last = year + if low: + spans.append([low, last]) + return spans + +def test_gaps(): + assert gaps_to_spans(1900, 1900, None) == \ + [[1900, 1900]] + assert gaps_to_spans(1900, 1903, None) == \ + [[1900, 1903]] + assert gaps_to_spans(1900, 1902, [1901]) == \ + [[1900, 1900], [1902, 1902]] + assert gaps_to_spans(1950, 1970, [1955, 1956, 1965]) == \ + [[1950, 1954], [1957, 1964], [1966, 1970]] + +def merge_spans(old, new): + if not new: + return old + if not old: + old = [] + old.extend(new) + years = set() + for span in old: + for y in range(span[0], span[1]+1): + years.add(y) + if not years: + return [] + spans = [] + start = None + last = None + todo = False + for y in sorted(list(years)): + if start == None: + # very first + start = y + last = y + todo = True + continue + if y == last + 1: + # span continues + last = y + todo = True + continue + # a gap just happened! + spans.append([start, last]) + start = y + last = y + todo = True + if todo: + spans.append([start, last]) + return spans + +def test_merge_spans(): + assert merge_spans([[5, 10]], [[10, 20]]) == \ + [[5, 20]] + assert merge_spans([[5, 9]], [[10, 20]]) == \ + [[5, 20]] + assert merge_spans([[5, 11]], [[10, 20]]) == \ + [[5, 20]] + assert merge_spans([], []) == \ + [] + assert merge_spans([[9, 11]], []) == \ + [[9,11]] + assert merge_spans([[2000, 2000]], [[1450, 1900]]) == \ + [[1450, 1900], [2000, 2000]] + +class Munger(): + """ + Top-level fields we'd like to fill in if possible: + + issnp: string + issne: string + first_year: year (integer) + last_year: if publishing has stopped + languages: array of ISO codes; first is the "primary" language + country: ISO shortcode of country published from + urls: homepage links + abbrev: string + default_license: slug + original_name: native name (if name is translated) + platform: hosting platform: OJS, wordpress, scielo, etc + mimetypes: array of strings (eg, 'application/pdf', 'text/html') + aliases: array of "also known as" + + Lower priority (TODO/later): + coden: string + oclc_id: string (lookup?) + lccn_id: string (lookup?) + dblb_id: string + region: TODO: continent/world-region + discipline: TODO: highest-level subject; "life science", "humanities", etc + field: TODO: narrower description of field + subjects: TODO? + + TODO: so many missing ISSN/ISSN-L + TODO: abbrev + """ + + def __init__(self): + self.data = dict() + with open(ISSNL_FILE, 'r') as f: + self.read_issn_map_file(f) + + def run(self, out_path): + self.load_doaj(DOAJ_FILE) + self.load_norwegian(NORWEGIAN_FILE) + self.load_crossref(CROSSREF_FILE) + self.load_sherpa_romeo(SHERPA_ROMEO_JOURNAL_FILE, SHERPA_ROMEO_POLICY_FILE) + self.load_road(ROAD_FILE) + self.load_kbart('lockss', LOCKSS_FILE) + self.load_kbart('clockss', CLOCKSS_FILE) + self.load_kbart('portico', PORTICO_FILE) + self.load_kbart('jstor', JSTOR_FILE) + self.load_entrez(ENTREZ_FILE) + self.load_sim(SIM_FILE) + self.load_homepage_crawl(IA_CRAWL_FILE) + self.summarize() + self.dump(out_path) + print("Done!") + + def dump(self, out_path): + print("#### Dumping to {}".format(out_path)) + with open(out_path, 'w') as out: + for issnl in self.data: + out.write(json.dumps(self.data[issnl]) + "\n") + + def summarize(self): + print("##### Loaded {} unique entries".format(len(self.data))) + + def read_issn_map_file(self, issn_map_file): + print("##### Loading ISSN map file...") + self._issn_issnl_map = dict() + for line in issn_map_file: + if line.startswith("ISSN") or len(line) == 0: + continue + (issn, issnl) = line.split()[0:2] + self._issn_issnl_map[issn] = issnl + # double mapping makes lookups easy + self._issn_issnl_map[issnl] = issnl + print("Got {} ISSN-L mappings.".format(len(self._issn_issnl_map))) + + def issn2issnl(self, issn): + if issn is None: + return None + return self._issn_issnl_map.get(issn) + + def add_issn(self, raw_issn=None, issne=None, issnp=None, name=None, publisher=None): + # do ISSN => ISSN-L mappings for any raw ISSNs + issnl = None + if not (raw_issn or issne or issnp): + return None, 'no-issn' + for lookup in (issnp, issne, raw_issn): + if not lookup: + continue + lookup = lookup.strip().upper() + #if not (len(lookup) == 9 and lookup[4] == '-'): + # print(lookup) + # print(len(lookup)) + # print(lookup[4]) + # return None, 'invalid-issn' + #assert len(lookup) == 9 and lookup[4] == '-' + issnl = self.issn2issnl(lookup) + if issnl: + break + if not issnl: + #print((raw_issn, issne, issnp)) + # UGH. + issnl = issne or issnp or raw_issn + if not issnl: + return None, 'no-issnl' + issnl = issnl.strip().upper() + assert len(issnl) == 9 and issnl[4] == '-' + status = 'found-munge' + else: + status = 'found' + # lookup ISSN-Ls in data (or create one) + if not issnl in self.data: + status = 'created' + self.data[issnl] = dict(issnl=issnl) + d = self.data[issnl] + # if name/publisher not set, do so + if name and not 'name' in d: + name = unquote(ftfy.fix_text(name)) + if name: + self.data[issnl]['name'] = name + if publisher and not 'publisher' in d: + publisher = unquote(ftfy.fix_text(publisher)) + if publisher: + self.data[issnl]['publisher'] = publisher + if issne and not 'issne' in d: + self.data[issnl]['issne'] = issne + if issnp and not 'issnp' in d: + self.data[issnl]['issnp'] = issnp + # always return ISSN-L + return issnl, status + + def add_lang(self, issnl, lang): + if not (lang and issnl): + return + lang = parse_lang(lang) + if not lang: + return + if 'languages' not in self.data[issnl]: + self.data[issnl]['languages'] = [lang] + elif lang not in self.data[issnl]['languages']: + self.data[issnl]['languages'].append(lang) + + def add_url(self, issnl, url): + if not (url and issnl) or 'mailto:' in url.lower() or url in ('http://n/a', 'http://N/A'): + return + if url.startswith('www.'): + url = "http://" + url + url.replace('Http://', 'http://') + if 'urls' not in self.data[issnl]: + self.data[issnl]['urls'] = [url] + elif url not in self.data[issnl]['urls']: + self.data[issnl]['urls'].append(url) + + def add_country(self, issnl, country): + if not (country and issnl): + return + country = parse_country(country) + if not country: + return + if 'country' not in self.data[issnl]: + self.data[issnl]['country'] = country + + def add_mimetype(self, issnl, val): + if not (val and issnl): + return + mimetype = None + if '/' in val: + mimetype = val + else: + mimetype = MIMETYPE_MAP.get(val) + if not mimetype: + return + if 'mimetypes' not in self.data[issnl]: + self.data[issnl]['mimestypes'] = [mimetype] + elif mimetype not in self.data[issnl]['mimestypes']: + self.data[issnl]['mimestypes'].append(mimetype) + + def load_entrez(self, path): + print("##### Loading Entrez...") + # JrId,JournalTitle,MedAbbr,"ISSN (Print)","ISSN (Online)",IsoAbbr,NlmId + reader = csv.DictReader(open(path)) + counts = Counter() + for row in reader: + if not (row.get('ISSN (Online)') or row.get('ISSN (Print)')): + counts['skipped'] += 1 + continue + issnl, status = self.add_issn( + issne=row.get('ISSN (Online)'), + issnp=row.get('ISSN (Print)'), + name=row['JournalTitle'], + ) + if row['IsoAbbr'] and not 'abbrev' in self.data[issnl]: + self.data[issnl]['abbrev'] = row['IsoAbbr'].strip() + counts[status] += 1 + print(counts) + + def load_road(self, path): + print("##### Loading ROAD...") + reader = csv.DictReader(open(path), delimiter='\t', + fieldnames=("ISSN", "ISSN-L", "Short Title", "Title", "Publisher", "URL1", "URL2", "Region", "Lang1", "Lang2") + ) + counts = Counter() + for row in reader: + issnl, status = self.add_issn( + raw_issn=row['ISSN-L'], + name=row['Short Title'], + publisher=row['Publisher'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + if row['URL1']: + self.add_url(issnl, row['URL1']) + if row['URL2']: + self.add_url(issnl, row['URL2']) + if row['Lang1']: + self.add_lang(issnl, row['Lang1']) + if row['Lang2']: + self.add_lang(issnl, row['Lang2']) + # TODO: region mapping: "Europe and North America" + # TODO: lang mapping: already alpha-3 + self.data[issnl]['road'] = dict(as_of=ROAD_DATE) + print(counts) + + def load_doaj(self, path): + print("##### Loading DOAJ...") + #Journal title,Journal URL,Alternative title,Journal ISSN (print version),Journal EISSN (online version),Publisher,Society or institution,"Platform, host or aggregator",Country of publisher,Journal article processing charges (APCs),APC information URL,APC amount,Currency,Journal article submission fee,Submission fee URL,Submission fee amount,Submission fee currency,Number of articles publish in the last calendar year,Number of articles information URL,Journal waiver policy (for developing country authors etc),Waiver policy information URL,Digital archiving policy or program(s),Archiving: national library,Archiving: other,Archiving infomation URL,Journal full-text crawl permission,Permanent article identifiers,Journal provides download statistics,Download statistics information URL,First calendar year journal provided online Open Access content,Full text formats,Keywords,Full text language,URL for the Editorial Board page,Review process,Review process information URL,URL for journal's aims & scope,URL for journal's instructions for authors,Journal plagiarism screening policy,Plagiarism information URL,Average number of weeks between submission and publication,URL for journal's Open Access statement,Machine-readable CC licensing information embedded or displayed in articles,URL to an example page with embedded licensing information,Journal license,License attributes,URL for license terms,Does this journal allow unrestricted reuse in compliance with BOAI?,Deposit policy directory,Author holds copyright without restrictions,Copyright information URL,Author holds publishing rights without restrictions,Publishing rights information URL,DOAJ Seal,Tick: Accepted after March 2014,Added on Date,Subjects + reader = csv.DictReader(open(path)) + counts = Counter() + for row in reader: + issnl, status = self.add_issn( + issnp=row['Journal ISSN (print version)'], + issne=row['Journal EISSN (online version)'], + name=row['Journal title'], + publisher=row['Publisher'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + doaj = dict(as_of=DOAJ_DATE) + # TODO: work_level: bool (are work-level publications deposited with DOAJ?) + + if row['Digital archiving policy or program(s)']: + doaj['archive'] = [a.strip() for a in row['Digital archiving policy or program(s)'].split(',') if a.strip()] + elif row['Archiving: national library']: + doaj['archive'] = ['national-library'] + + crawl_permission = row['Journal full-text crawl permission'] + if crawl_permission: + doaj['crawl-permission'] = dict(Yes=True, No=False)[crawl_permission] + # TODO: Permanent article identifiers + default_license = row['Journal license'] + if default_license and default_license.startswith('CC'): + self.data[issnl]['default_license'] = default_license.replace('CC ', 'CC-').strip() + + self.add_mimetype(issnl, row['Full text formats']) + platform = PLATFORM_MAP.get(row['Platform, host or aggregator']) + if platform: + self.data[issnl]['platform'] = platform + if row['DOAJ Seal']: + doaj['seal'] = {"no": False, "yes": True}[row['DOAJ Seal'].lower()] + if row['Country of publisher']: + self.add_country(issnl, row['Country of publisher']) + if row['Full text language']: + self.add_lang(issnl, row['Full text language']) + if row['Journal URL']: + self.add_url(issnl, row['Journal URL']) + # TODO: Subjects + self.data[issnl]['doaj'] = doaj + print(counts) + + def load_sherpa_romeo(self, journal_path, policy_path): + # first load policies + print("##### Loading SHERPA/ROMEO policies...") + #RoMEO Record ID,Publisher,Policy Heading,Country,RoMEO colour,Published Permission,Published Restrictions,Published Max embargo,Accepted Prmission,Accepted Restrictions,Accepted Max embargo,Submitted Permission,Submitted Restrictions,Submitted Max embargo,Open Access Publishing,Record Status,Updated + policies = dict() + fixed_policy_file = ftfy.fix_file(open(policy_path, 'rb')) + policy_reader = csv.DictReader(fixed_policy_file) + for row in policy_reader: + policies[row['RoMEO Record ID']] = row + print("##### Loading SHERPA/ROMEO journal metadata...") + #Journal Title,ISSN,ESSN,URL,RoMEO Record ID,Updated + # super mangled :( + raw_file = open(journal_path, 'rb').read().decode(errors='replace') + fixed_file = ftfy.fix_text(raw_file) + reader = csv.DictReader(fixed_file.split('\n')) + counts = Counter() + for row in reader: + #row['Journal Title'] = row.pop('\ufeffJournal Title') + row.update(policies[row['RoMEO Record ID']]) + issnl, status = self.add_issn( + issnp=row['ISSN'], + issne=row['ESSN'], + name=row['Journal Title'], + publisher=row['Publisher'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + sherpa_romeo = dict() + if row['RoMEO colour']: + sherpa_romeo['color'] = row['RoMEO colour'] + # row['Open Access Publishing'] + if row['Country']: + self.add_country(issnl, row['Country']) + self.data[issnl]['sherpa_romeo'] = sherpa_romeo + print(counts) + + def load_norwegian(self, path): + print("##### Loading Norwegian Registry...") + #pandas.read_csv(NORWEGIAN_FILE, sep=';', encoding="ISO-8859-1") + #NSD tidsskrift_id;Original title;International title;Present Level (2018);Print ISSN;Online ISSN;Open Access;NPI Scientific Field;NPI Academic Discipline;URL;Publishing Company;Publisher;Country of publication;Language;Level 2019;Level 2018;Level 2017;Level 2016;Level 2015;Level 2014;Level 2013;Level 2012;Level 2011;Level 2010;Level 2009;Level 2008;Level 2007;Level 2006;Level 2005;Level 2004;itar_id + reader = csv.DictReader(open(path, encoding="ISO-8859-1"), delimiter=";") + counts = Counter() + for row in reader: + issnp = row['Print ISSN'] + issne = row['Online ISSN'] + if issne and len(issne.strip()) != 9: + issne = None + if issnp and len(issnp.strip()) != 9: + issnp = None + if not (issnp or issne): + counts['no-issn'] += 1 + continue + issnl, status = self.add_issn( + issnp=issnp, + issne=issne, + name=row['International title'], + publisher=row['Publisher'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + norwegian = dict(as_of=NORWEGIAN_DATE) + norwegian['level'] = int(row['Present Level (2018)']) + norwegian['id'] = int(row['NSD tidsskrift_id']) + + if row['Original title'] != row['International title'] and not 'original_name' in d: + self.data[issnl]['original_name'] = row['Original title'] + if row['Country of publication']: + self.add_country(issnl, row['Country of publication']) + if row['Language']: + self.add_lang(issnl, row['Language']) + if row['URL']: + self.add_url(issnl, row['URL']) + self.data[issnl]['norwegian'] = norwegian + print(counts) + + def load_kbart(self, name, path): + print("##### Loading KBART file for {}...".format(name)) + #publication_title print_identifier online_identifier date_first_issue_online num_first_vol_online num_first_issue_online date_last_issue_online num_last_vol_online num_last_issue_online title_url first_author title_id embargo_info coverage_depth coverage_notes publisher_name + raw_file = open(path, 'rb').read().decode(errors='replace') + fixed_file = ftfy.fix_text(raw_file) + reader = csv.DictReader(fixed_file.split('\n'), delimiter='\t') + counts = Counter() + for row in reader: + if not row['print_identifier'] and not row['online_identifier']: + counts['no-issn'] += 1 + continue + issnl, status = self.add_issn( + issnp=row['print_identifier'], + issne=row['online_identifier'], + name=row['publication_title'], + publisher=row['publisher_name'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + if not 'kbart' in d: + self.data[issnl]['kbart'] = dict() + d = self.data[issnl] + if not name in d['kbart']: + self.data[issnl]['kbart'][name] = dict() + old_spans = self.data[issnl]['kbart'].get(name, dict()).get('year_spans', []) + kbart = dict() + if row['date_first_issue_online'] and row['date_last_issue_online']: + start = int(row['date_first_issue_online'][:4]) + end = int(row['date_last_issue_online'][:4]) + if not start <= end: + print("{}: {} not before {}! er, mangling".format( + issnl, + row['date_first_issue_online'], + row['date_last_issue_online'])) + new_spans = [[end, start]] + else: + new_spans = [[start, end]] + self.data[issnl]['kbart'][name]['year_spans'] = merge_spans(old_spans, new_spans) + print(counts) + + def load_crossref(self, path): + print("##### Loading Crossref...") + #"JournalTitle","JournalID","Publisher","pissn","eissn","additionalIssns","doi","(year1)[volume1]issue1,issue2,issue3(year2)[volume2]issue4,issues5" + reader = csv.DictReader(open(path)) + counts = Counter() + for row in reader: + if row['pissn'] and len(row['pissn']) == 8: + row['pissn'] = row['pissn'][:4] + '-' + row['pissn'][4:] + if row['eissn'] and len(row['eissn']) == 8: + row['eissn'] = row['eissn'][:4] + '-' + row['eissn'][4:] + if not (row['pissn'] or row['eissn']): + counts['no-issn'] += 1 + continue + issnl, status = self.add_issn( + issnp=row['pissn'], + issne=row['eissn'], + name=row['JournalTitle'], + publisher=row['Publisher'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + crossref = dict() + if row['doi']: + crossref['doi'] = row['doi'] + crossref['any'] = True + self.data[issnl]['crossref'] = crossref + print(counts) + + def load_sim(self, path): + print("##### Loading SIM Metadata...") + #NA Pub Cat ID,Title,Publisher,ISSN,Impact Rank,Total Cities,Journal Impact Factor,Eigenfact or Score,First Volume,Last Volume,NA Gaps,"Scholarly / Peer-\n Reviewed","Peer-\n Reviewed",Pub Type,Pub Language,Subjects + reader = csv.DictReader(open(path)) + counts = Counter() + for row in reader: + if not row['ISSN'] or row['ISSN'] == "NULL": + counts['no-issn'] += 1 + continue + issnl, status = self.add_issn( + raw_issn=row['ISSN'][:9], + name=row['Title'], + publisher=row['Publisher'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + sim = dict() + sim['id'] = row['NA Pub Cat ID'] + first_year = row['First Volume'] + if first_year: + first_year = int(first_year) + sim['first_year'] = int(row['First Volume']) + else: + first_year = None + last_year = row['Last Volume'] + if last_year: + last_year = int(last_year) + sim['last_year'] = last_year + else: + last_year = None + gaps = [int(g) for g in row['NA Gaps'].split(';') if g.strip()] + if gaps: + sim['gaps'] = gaps + if first_year and last_year: + sim['year_spans'] = gaps_to_spans(first_year, last_year, gaps) + if row['Pub Language']: + self.add_lang(issnl, row['Pub Language']) + # TODO: 'Pub Type' + all_keys = list(sim.keys()) + for k in all_keys: + if not sim[k]: + sim.pop(k) + self.data[issnl]['sim'] = sim + print(counts) + + def load_homepage_crawl(self, path): + print("##### Loading IA Homepage Crawl Results...") + reader = csv.DictReader(open(path), delimiter='\t', + fieldnames=("ISSN", "first_url", "first_status", "last_status", "last_url") + ) + counts = Counter() + for row in reader: + issnl, status = self.add_issn( + raw_issn=row['ISSN'], + ) + counts[status] += 1 + if not issnl: + continue + d = self.data[issnl] + ia = d.get('ia', dict()) + ia['homepage_status'] = int(row['last_status']) + if ia['homepage_status'] == 200: + ia['homepage_url'] = row['last_url'] + else: + ia['homepage_url'] = row['first_url'] + self.data[issnl]['ia'] = ia + print(counts) + +if __name__=='__main__': + if len(sys.argv) != 2 or sys.argv[1].startswith('-'): + print("pass me path for an output JSON lines file") + sys.exit(-1) + munger = Munger() + munger.run(sys.argv[1]) + |