filltex 1.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fillbib.py ADDED
@@ -0,0 +1,465 @@
1
+ #!/usr/bin/env python
2
+
3
+ '''
4
+ Query the ADS and INSPIRE databases to collect the citations used in a LaTeX file into a BibTeX file
5
+
6
+ Idea and main implementation is taken by Michele Vallisneri, see http://www.vallis.org/salon/summary-2.html
7
+ I changed the script to avoid the requests module, and added the INSPIRE database part.
8
+
9
+ Usage:
10
+ python fillbib.py <tex_file> <bib_file>. If <bib_file> is absent, it will try to guess it from the aux file"
11
+
12
+ '''
13
+ from __future__ import absolute_import, print_function
14
+ import argparse
15
+ import sys, os, re, html
16
+ import json
17
+
18
+ if sys.version_info.major>=3:
19
+ import urllib.request as urllib
20
+ else:
21
+ import urllib
22
+
23
+
24
+ def ads_citation(c): # download single ADS citation
25
+ #f= urllib.urlopen("http://adsabs.harvard.edu/cgi-bin/nph-bib_query?bibcode="+c+"&data_type=BIBTEX&db_key=AST&nocookieset=1")
26
+ #print("http://adsabs.harvard.edu/cgi-bin/nph-bib_query?bibcode="+c+"&data_type=BIBTEX&db_key=AST&nocookieset=1")
27
+ f= urllib.urlopen("https://ui.adsabs.harvard.edu/abs/"+c+"/exportcitation")
28
+
29
+ bib = f.read()
30
+ f.close()
31
+ if sys.version_info.major>=3:
32
+ bib=bib.decode()
33
+ bib = "@"+list(filter(lambda x:'adsnote' in x, bib.split("@")))[0].split("</textarea>")[0]
34
+ bib=html.unescape(bib)
35
+
36
+ return bib
37
+
38
+ def inspire_citation(key,
39
+ generate=False,
40
+ max_num_authors=None,
41
+ num_authors_short=None,
42
+ journal_arXiv_fallback=False):
43
+ """
44
+ Generate a custom BiBTeX entry
45
+
46
+ * generate
47
+ if True we generate the BibTeX rather than using the default from iNSPIRE
48
+ * max_num_authors
49
+ maximum number of authors to include (all of them if None)
50
+ * num_authors_short
51
+ number of authors to include if the number of authors is longer than max_num_authors
52
+ if not specified, this defaults to max_num_authors
53
+ * journal_arXiv_fallback
54
+ use arXiv:eprint as the journal field if no journal is found
55
+ """
56
+ request = 'https://inspirehep.net/api/literature?q=' + key
57
+ data = json.loads(urllib.urlopen(request).read())
58
+ if data['hits']['total'] != 1:
59
+ return None
60
+ if not generate:
61
+ bib = urllib.urlopen(data['hits']['hits'][0]['links']['bibtex']).read()
62
+ inspire_key = data['hits']['hits'][0]['metadata']['texkeys'][0]
63
+ if sys.version_info.major >= 3:
64
+ return bib.decode().replace(inspire_key, key)
65
+ else:
66
+ return bib.replace(inspire_key, key)
67
+ metadata = data['hits']['hits'][0]['metadata']
68
+
69
+ doctype = metadata["document_type"][0]
70
+
71
+ # BibTeX entry as a dictionary
72
+ bibtex = {}
73
+
74
+ # Find all authors
75
+ if "authors" in metadata:
76
+ num_authors = len(metadata["authors"])
77
+ if max_num_authors is not None and num_authors > max_num_authors:
78
+ short_author_list = True
79
+ if num_authors_short is None:
80
+ num_authors_short = max_num_authors
81
+ else:
82
+ short_author_list = False
83
+ author_list = []
84
+ for idx, author in enumerate(metadata['authors']):
85
+ if short_author_list and idx >= num_authors_short:
86
+ author_list.append("others")
87
+ break
88
+ author_list.append(author['full_name'])
89
+ bibtex['author'] = " and ".join(author_list)
90
+
91
+ # Find all collaborations
92
+ if "collaborations" in metadata:
93
+ collab_list = []
94
+ for collab in metadata["collaborations"]:
95
+ collab_list.append(collab["value"])
96
+ bibtex["collaboration"] = ", ".join(collab_list)
97
+
98
+ try:
99
+ bibtex["title"] = "{" + metadata["titles"][0]["title"] + "}"
100
+ except KeyError:
101
+ pass
102
+
103
+ try:
104
+ bibtex["eprint"] = metadata["arxiv_eprints"][0]["value"]
105
+ bibtex["archivePrefix"] = "arXiv"
106
+ bibtex["primaryClass"] = metadata["arxiv_eprints"][0]["categories"][0]
107
+ except KeyError:
108
+ pass
109
+
110
+ try:
111
+ bibtex["doi"] = metadata["dois"][0]["value"]
112
+ except KeyError:
113
+ pass
114
+
115
+ try:
116
+ bibtex["journal"] = metadata["publication_info"][0]["journal_title"]
117
+ except KeyError:
118
+ if journal_arXiv_fallback:
119
+ bibtex["journal"] = "arXiv:" + bibtex["eprint"]
120
+ pass
121
+
122
+ try:
123
+ bibtex["volume"] = metadata["publication_info"][0]["journal_volume"]
124
+ except KeyError:
125
+ pass
126
+
127
+ try:
128
+ bibtex["number"] = metadata["publication_info"][0]["journal_issue"]
129
+ except KeyError:
130
+ pass
131
+
132
+ try:
133
+ bibtex["pages"] = metadata["publication_info"][0]["page_start"]
134
+ except KeyError:
135
+ pass
136
+
137
+ try:
138
+ bibtex["year"] = metadata["publication_info"][0]["year"]
139
+ except KeyError:
140
+ try:
141
+ bibtex["year"] = metadata["preprint_date"][0:4]
142
+ bibtex["month"] = str(int(metadata["preprint_date"][5:7]))
143
+ except KeyError:
144
+ pass
145
+
146
+ # Format BibTeX entry
147
+ s = "@{}{{{}".format(doctype, key)
148
+ for field, value in bibtex.items():
149
+ s += ",\n {} = \"{}\"".format(field, value)
150
+ s += "\n}"
151
+
152
+ return s
153
+
154
+ def test_ads(): # test single ADS web scraping (both published articles and preprints)
155
+ test_key = ["2016PhRvL.116f1102A","2016arXiv160203837T"]
156
+ known_output= '@ARTICLE{2016PhRvL.116f1102A,\n author = {{Abbott}, B.~P. and {Abbott}, R. and {Abbott}, T.~D. and {Abernathy}, M.~R. and \n\t{Acernese}, F. and {Ackley}, K. and {Adams}, C. and {Adams}, T. and \n\t{Addesso}, P. and {Adhikari}, R.~X. and et al.},\n title = "{Observation of Gravitational Waves from a Binary Black Hole Merger}",\n journal = {Physical Review Letters},\narchivePrefix = "arXiv",\n eprint = {1602.03837},\n primaryClass = "gr-qc",\n year = 2016,\n month = feb,\n volume = 116,\n number = 6,\n eid = {061102},\n pages = {061102},\n doi = {10.1103/PhysRevLett.116.061102},\n adsurl = {http://adsabs.harvard.edu/abs/2016PhRvL.116f1102A},\n adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n'
157
+ assert [ads_citation(tk) == known_output for tk in test_key]
158
+
159
+ def test_inspire(): # test single INSPIRE web scraping
160
+ test_key = "Abbott:2016blz"
161
+ known_output = '@article{Abbott:2016blz,\n author = "Abbott, B.P. and Abbott, R. and Abbott, T.D. and Abernathy, M.R. and Acernese, F. and others",\n collaboration = "LIGO Scientific, Virgo",\n title = "{Observation of Gravitational Waves from a Binary Black Hole Merger}",\n eprint = "1602.03837",\n archivePrefix = "arXiv",\n primaryClass = "gr-qc",\n doi = "10.1103/PhysRevLett.116.061102",\n journal = "Phys.Rev.Lett.",\n volume = "116",\n number = "6",\n year = "2016"\n}'
162
+ assert inspire_citation(test_key, generate=True, max_num_authors=5) == known_output
163
+
164
+ def fillbib_tex(args):
165
+
166
+ if args.bibtex is None: # Get the name of the bibfile from the aux file
167
+ basename = args.texfile[0].split('.tex')[0]
168
+ auxfile = basename + '.aux'
169
+ for line in open(auxfile,'r'):
170
+ m = re.search(r'\\bibdata\{(.*)\}',line) # match \citation{...}, collect the ... note that we escape \, {, and }
171
+ if m:
172
+ bibfile = list(filter(lambda x:x!=basename+'Notes', m.group(1).split(',')))[0] # Remove that annyoing feature of revtex which creates a *Notes.bib bibfile. Note this is not solid if you want to handle multiple bib files.
173
+ bibfile = bibfile + '.bib'
174
+
175
+ else: # Bibfile specified from argv
176
+ basename = args.texfile[0].split('.tex')[0]
177
+ auxfile = basename + '.aux'
178
+ bibfile = args.bibtex.split('.bib')[0] + '.bib'
179
+
180
+ if args.updatepublished:
181
+ with open(basename+".tex", 'r') as texfile:
182
+ texdata = texfile.read()
183
+
184
+ # Get all citations from aux file. Citations will look like \citation{2004PhRvD..69j4017P,2004PhRvD..69j4017P}
185
+ cites = set() # use a set (no repetitions)
186
+ for line in open(auxfile,'r'):
187
+ m = re.search(r'\\citation\{(.*)\}',line) # find \citation{...}
188
+ if m:
189
+ cites.update(m.group(1).split(',')) # split by commas
190
+
191
+ cites= cites.difference([
192
+ 'REVTEX41Control','apsrev41Control',
193
+ 'REVTEX42Control','apsrev42Control']) # Remove annoying entries of revtex
194
+
195
+ print("Seek:", cites)
196
+
197
+ # Check what you already have in the bib file
198
+ haves = []
199
+ if os.path.isfile(bibfile):
200
+ for line in open(bibfile,'r'):
201
+ m = re.search(r'@.*?\{(.*),',line) # .*\{ means "any # of any char followed by {"; .*?\{ means "the shortest string matching any # of any char followed by {"
202
+ if m:
203
+ haves.append(m.group(1))
204
+ print("Have:", haves)
205
+
206
+
207
+ # Query ADS and INSPIRE
208
+ bibtex = open(bibfile,'a') # open for appending
209
+
210
+ for c in cites:
211
+ if c and c not in haves: # c is something and you don't have it already
212
+
213
+ if not c[0].isalpha(): # The first charachter is a number: could be on ADS
214
+
215
+ try:
216
+ bib = ads_citation(c)
217
+
218
+ cfound = bib.split("{")[1].split(',')[0]
219
+
220
+ if cfound == c: # Check you got what you where looking for
221
+ pass
222
+ elif 'arXiv' in c: # Take care of preprint on ADS
223
+
224
+ if args.updatepublished and '.tmp.' not in cfound:
225
+ print("ADS replace", c, "-->", cfound)
226
+ # This substitute the new ID into the tex file. Use at your own risk. The .tmp. condition fixes those stupid MNRAS temp entries.
227
+ texdata = texdata.replace(c, cfound)
228
+
229
+ else:
230
+ # This subsitute the arxiv id back in to the bib file
231
+ bib = bib.split("{")[0]+"{"+c+","+",".join(bib.split(",")[1:])
232
+
233
+ bibtex.write(bib)
234
+ print("ADS Found:", c)
235
+ except:
236
+ print("ADS Not found:", c)
237
+
238
+ else: # The first charachter is not a number: could be on INSPIRE
239
+
240
+ try:
241
+ bib = inspire_citation(c,
242
+ generate=args.generate,
243
+ max_num_authors=args.max_num_authors,
244
+ num_authors_short=args.num_authors_short,
245
+ journal_arXiv_fallback=args.journal_arXiv_fallback)
246
+ bibtex.write(bib)
247
+ print("INSPIRE Found:", c)
248
+ except:
249
+ print("INSPIRE Not found:", c)
250
+
251
+
252
+
253
+ bibtex.close()
254
+
255
+ if args.updatepublished:
256
+ with open(basename+".tex", 'w') as texfile:
257
+ texfile.write(texdata)
258
+
259
+ # Clean up journal names
260
+ if args.journals:
261
+ journals(bibfile)
262
+
263
+
264
+ def fillbib_list(args):
265
+ for c in args.keys:
266
+ if not c[0].isalpha():
267
+ bib = ads_citation(c)
268
+ if bib is None:
269
+ sys.stderr.write("ADS Not Found: {}\n".format(c))
270
+ else:
271
+ print(bib)
272
+ else:
273
+ bib = inspire_citation(c,
274
+ generate=args.generate,
275
+ max_num_authors=args.max_num_authors,
276
+ num_authors_short=args.num_authors_short,
277
+ journal_arXiv_fallback=args.journal_arXiv_fallback)
278
+ if bib is None:
279
+ sys.stderr.write("INSPIRE Not Found: {}\n".format(c))
280
+ else:
281
+ print(bib)
282
+
283
+
284
+ def curly(x):
285
+ '''Just a curly bracket sandwich.'''
286
+ return "{"+x+"}"
287
+
288
+ def journals(bibfile):
289
+ '''
290
+ Clean up the names of some journals using their ISO4 standards.
291
+ Journal abbreviations are taken from https://images.webofknowledge.com/images/help/WOS/A_abrvjt.html
292
+ If your favourite journal is missing, please add it and send a pull request. Thanks!
293
+ '''
294
+
295
+ # The format is: [ADS name, INSPIRE name, ISO4 abbreviation]
296
+ journals = [
297
+ ####
298
+ # These are the journals from the ADS macros: https://ui.adsabs.harvard.edu/help/actions/journal-macros
299
+ # I could not find them all on INSPIRE, some are missing.
300
+ ####
301
+ ['\\aj', 'Astron. J.', 'Astron. J.'],
302
+ ['\\actaa', 'Acta Astron.', 'Acta Astronom.'],
303
+ ['\\araa', 'Ann. Rev. Astron. Astrophys.', 'Annu. Rev. Astron. Astrophys.'],
304
+ ['\\apj', 'Astrophys. J.', 'Astrophys. J.'],
305
+ ['\\apjl', 'Astrophys. J. Lett.', 'Astrophys. J. Lett.'],
306
+ ['\\apjs', 'Astrophys. J. Suppl.', 'Astrophys. J. Supp. S.'],
307
+ ['\\ao', 'Appl. Opt.', 'Appl. Optics'],
308
+ ['\\apss', 'Astrophys. Space Sci.', 'Astrophys. Space Sci.'],
309
+ ['\\aap', 'Astron. Astrophys.', 'Astron. Astrophys.'],
310
+ ['\\aapr', 'Astron. Astrophys. Rev.', 'Astron. Astrophys. Rev.'],
311
+ ['\\aaps', 'Astron. Astrophys. Suppl. Ser.', 'Astron. Astrophys. Sup.'],
312
+ ['\\azh', '', 'Astron. Zh.'], #Not sure. Various names on inspire
313
+ ['\\baas', 'Bull. Am. Astron. Soc.', 'Bull. Am. Astron. Soc.'],
314
+ ['\\bac', 'Bull. Astron. Inst. Czech.', 'B. Astron. I. Czech.'],
315
+ ['\\caa', 'Chin. Astron. Astrophys.', 'Chinese Astron. Astr.'],
316
+ ['\\cjaa', 'Chin. J. Astron. Astrophys.', 'Chinese J. Astron. Ast.'],
317
+ ['\\icarus', 'Icarus', 'Icarus'],
318
+ ['\\jcap', 'JCAP', 'J. Cosmology Astropart. Phys.'],
319
+ ['\\jrasc', 'J. Roy. Astron. Soc. Canada', 'J. Roy Astron. Soc. Can.'],
320
+ ['\\memras', 'Mem. Roy. Astron. Soc.', 'Mem. R. Astron. Soc.'],
321
+ ['\\mnras', 'Mon. Not. Roy. Astron. Soc.', 'Mon. Not. R. Astron. Soc.'],
322
+ ['\\na', 'New Astron.', 'New Astron.'],
323
+ ['\\nar', 'New Astron. Rev.', 'New Astron. Rev.'],
324
+ ['\\pra', 'Phys. Rev. A', 'Phys. Rev. A'],
325
+ ['\\prb', 'Phys. Rev. B', 'Phys. Rev. B'],
326
+ ['\\prc', 'Phys. Rev. C', 'Phys. Rev. C'],
327
+ ['\\prd', 'Phys. Rev. D', 'Phys. Rev. D'],
328
+ ['\\pre', 'Phys. Rev. E', 'Phys. Rev. E'],
329
+ ['\\prl', 'Phys. Rev. Lett.', 'Phys. Rev. Lett.'],
330
+ ['\\pasa', 'Publ. Astron. Soc. Austral.', 'Publ. Astron. Soc. Aust.'],
331
+ ['\\pasp', 'Publ. Astron. Soc. Pac.', 'Publ. Astron. Soc. Pac.'],
332
+ ['\\pasj', 'Publ. Astron. Soc. Jap.', 'Publ. Astron. Soc. Jpn.'],
333
+ ['\\rmxaa', 'Rev. Mex. Astron. Astrofis.', 'Rev. Mex. Astron. Astr.'],
334
+ ['\\qjras', 'Q. J. Roy. Astron. Soc.', 'Q. J. Roy. Astron. Soc.'],
335
+ ['\\skytel', 'Sky Telesc.', 'Sky Telescope'],
336
+ ['\\solphys', 'Solar Phys.', 'Sol. Phys.'],
337
+ ['\\sovast', 'Sov. Astron.', 'Sov. Astron.'],
338
+ ['\\ssr', 'Space Sci. Rev.', 'Space Sci. Rev.'],
339
+ ['\\zap', 'Z. Astrophys.', 'Z. Astrophys.'],
340
+ ['\\nat', 'Nature', 'Nature'],
341
+ ['\\iaucirc', 'IAU Circ.', 'IAU Circ.'],
342
+ ['\\aplett', 'Astrophys. Lett.', 'Astrophys. Lett.'],
343
+ ['\\apspr', '', 'Astrophys.~Space~Phys.~Res.'], #Could not find it on INSPIRE
344
+ ['\\bain', 'Bull. Astron. Inst. Netherlands', 'B. Astron. I. Neth.'],
345
+ ['\\fcp', 'Fund. Cosmic Phys.', 'Fund. Cosmic Phys.'],
346
+ ['\\gca', 'Geochim. Cosmochim. Acta', 'Geochim. Cosmochim. Ac.'],
347
+ ['\\grl', 'Geophys. Res. Lett.', 'Geophys. Res. Lett.'],
348
+ ['\\jcp', 'J. Chem. Phys.', 'J. Chem. Phys.'],
349
+ ['\\jgr', 'J. Geophys. Res.', 'J. Geophys. Res.'],
350
+ ['\\jqsrt', 'J. Quant. Spectrosc. Radiat. Trans.', 'J. Quant. Sprectrosc. Ra.'],
351
+ ['\\memsai', 'Mem. Soc. Ast. It.', 'Mem. Soc. Astron. Ital.'],
352
+ ['\\nphysa', 'Nucl. Phys. A', 'Nucl. Phys. A'],
353
+ ['\\physrep', 'Phys. Rept.', 'Phys. Rep.'],
354
+ ['\\physscr', 'Phys. Scripta', 'Phys. Scripta'],
355
+ ['\\planss', 'Planet. Space Sci.', 'Planet. Space Sci.'],
356
+ ['\\procspie', 'Proc. SPIE Int. Soc. Opt. Eng.', 'P. Soc. Photo.-Opt. Ins.'],
357
+ ####
358
+ # [Davide Gerosa] These are journals that I personally encountered.
359
+ # Will keep on adding to this list.
360
+ ####
361
+ ['Advances in Space Research','Adv. Space Res.','Adv. Space Res.'], #ISI list not correct
362
+ ['American Institute of Physics Conference Series','AIP Conf. Proc.','AIP Conf. Proc.'],
363
+ ['Annals of Data Science','Ann. Data Sci.','Ann. Data Sci.'],
364
+ ['Astronomy and Computing','Astron. Comput.','Astron. Comput.'],
365
+ ['Astroparticle Physics','Astropart. Phys.','Astropart. Phys.'],
366
+ ['Astrophysics and Space Science Library','Astrophys. Space Sci. Libr.','Astrophys. Space Sc. L.'],
367
+ ['Astrophysics and Space Science Library','Astrophys. Space Sci. Libr.','Astrophys. Space Sc. L.'],
368
+ ['Bulletin of the American Astronomical Society','Bull. Am. Astron. Soc.','Bull. Am. Astron. Soc.'],
369
+ ['Classical and Quantum Gravity','Class. Quant. Grav.','Class. Quantum Grav.'], #ISI list not correct
370
+ ['Communications in Mathematical Physics', 'Commun. Math. Phys.', 'Commun. Math. Phys.'],
371
+ ['Frontiers in Astronomy and Space Sciences','Front. Astron. Space Sci.','Front. Astron. Space Sci.'],
372
+ ['General Relativity and Gravitation', 'Gen. Rel. Grav.', 'Gen. Relat. Gravit.'],
373
+ ['International Journal of Modern Physics D', 'Int. J. Mod. Phys. D', 'Int. J. Mod. Phys. D'],
374
+ ['iScience', 'iScience', 'iScience'],
375
+ ['Journal of High Energy Physics','JHEP','J. High Energy Phys.'],
376
+ ['Journal of Machine Learning Research','J. Machine Learning Res.','J. Mach. Learn. Res.'],
377
+ ['Journal of Mathematical Analysis and Applications','J. Math. Anal. Appl.','J. Math. Anal. Appl'],
378
+ ['Journal of Physics Conference Series','J. Phys. Conf. Ser.','J. Phys. Conf. Ser.'],
379
+ ['Journal of Statistical Physics','J. Statist. Phys.','J. Stat. Phys.'],
380
+ ['Journal of the Royal Statistical Society B','J. Roy. Statist. Soc. B','J. Roy. Statist. Soc. B'],
381
+ ['Living Reviews in Relativity', 'Living Rev. Rel.', 'Living Rev. Relativ.'],
382
+ ['Machine Learning: Science and Technology','Mach. Learn. Sci. Tech.','Mach. Learn. Sci. Tech.'], #ISI list not correct
383
+ ['Machine Learning','Machine Learning','Mach. Learn.'],
384
+ ['Nature Astronomy', 'Nature Astron.', 'Nat. Astron.'],
385
+ ['Nature Methods', 'Nature Meth.', 'Nat. Methods'],
386
+ ['Nature Reviews Physics','Nature Rev. Phys.','Nat. Rev. Phys.'],
387
+ ['Physica A Statistical Mechanics and its Applications','Physica A','Physica A'],
388
+ ['Physica D Nonlinear Phenomena', 'Physica D', 'Physica D'],
389
+ ['Physical Review', 'Phys. Rev.', 'Phys. Rev.'],
390
+ ['Physical Review Research', 'Phys. Rev. Res.', 'Phys. Rev. Res.'],
391
+ ['Physical Review X', 'Phys. Rev. X', 'Phys. Rev. X'],
392
+ ['Physics Letters A', 'Phys. Lett. A', 'Phys. Lett. A'],
393
+ ['Proceedings of the Royal Society of London Series A', 'Proc. Roy. Soc. Lond. A', 'P. R. Soc. Lond. A'],
394
+ ['Rendiconti Lincei. Scienze Fisiche e Naturali', 'Rend. Lincei Sci. Fis. Nat.', 'Rend. Lincei-Sci. Fis.'],
395
+ ['Reports on Progress in Physics', 'Rept. Prog. Phys.', 'Rep. Prog. Phys.'],
396
+ ['Research Notes of the American Astronomical Society', 'Res. Notes AAS','Res. Notes AAS'],
397
+ ['Reviews of Modern Physics', 'Rev. Mod. Phys.', 'Rev. Mod. Phys.'],
398
+ ['Science Advances','Sci. Adv.','Sci. Adv.'],
399
+ ['SIAM Journal on Scientific Computing','SIAM J. Sci. Comput.','SIAM J. Sci. Comput.'],
400
+ ['The Journal of Open Source Software','J. Open Source Softw.','J. Open Source Softw.'],
401
+ ]
402
+
403
+ with open(bibfile, 'r') as bibtex :
404
+ filedata = bibtex.read()
405
+
406
+ for j in journals:
407
+ print(j)
408
+ if j[0]:
409
+ filedata = filedata.replace(curly(j[0]), curly(j[2]))
410
+ if j[1]:
411
+ filedata = filedata.replace(curly(j[1]), curly(j[2]))
412
+ filedata = filedata.replace('journal = "'+j[1], 'journal = "'+j[2])
413
+
414
+ ### Clean up arxiv repeated information in ADS records:
415
+ filedata = filedata.replace('arXiv e-prints', '{}')
416
+ filedata = re.sub('pages = {arXiv:[0-9]+.[0-9]+},','',filedata)
417
+ filedata = re.sub('Pages = {arXiv:[0-9]+.[0-9]+},','',filedata)
418
+ filedata = re.sub('eid = {arXiv:[0-9]+.[0-9]+},','',filedata)
419
+ filedata = re.sub('Eid = {arXiv:[0-9]+.[0-9]+},','',filedata)
420
+ filedata = re.sub('doi = {[0-9]+.[0-9]+/arXiv.[0-9]+.[0-9]+},','',filedata)
421
+
422
+ with open(bibfile, 'w') as bibtex :
423
+ bibtex.write(filedata)
424
+
425
+
426
+
427
+
428
+ if __name__ == "__main__":
429
+
430
+ parser = argparse.ArgumentParser()
431
+ parser.add_argument("--generate", action="store_true",
432
+ help="Generate the BibTeX entries from the metadata "
433
+ "(this is useful to customize the generated BiBTeX file)")
434
+ parser.add_argument("--journal_arXiv_fallback", dest="journal_arXiv_fallback", action="store_true",
435
+ help="Set the journal entry to be arXiv for unpublished preprints "
436
+ "(iNSPIRE entries only, requres --generate)")
437
+ parser.add_argument("--max-num-authors", dest="max_num_authors", type=int,
438
+ help="Include at most this many authors for each bibtex entry"
439
+ "(iNSPIRE entries only, requres --generate)")
440
+ parser.add_argument("--num-authors-short", type=int,
441
+ help="Number of authors to list if the number of authors is larger than max_num_authors"
442
+ "(iNSPIRE entries only, defaults to max-num-authors, requres --generate)")
443
+ subparsers = parser.add_subparsers(help="Subcommands")
444
+
445
+ parser_tex = subparsers.add_parser("tex", help="Create a bibliography for a tex document")
446
+ parser_tex.add_argument("--bibtex", help="The BiBTeX file to use (if not specified we try to find out)")
447
+ parser_tex.add_argument("texfile", nargs=1, help="The (La)TeX file to process")
448
+ parser_tex.add_argument('--journals', dest='journals', help="Replace known journal abbreviations", default=True, action='store_true')
449
+ parser_tex.add_argument('--updatepublished', dest='updatepublished', help="Replace ADS arxiv entries in .tex file if published", default=True, action='store_true')
450
+ parser_tex.set_defaults(func=fillbib_tex)
451
+
452
+ parser_list = subparsers.add_parser("list", help="Create a bibliography given a list of ADS/iNSPIRE keys")
453
+ parser_list.add_argument("keys", nargs="+", help="ADS/iNSPIRE keys to fetch")
454
+ parser_list.set_defaults(func=fillbib_list)
455
+
456
+
457
+ args = parser.parse_args()
458
+ args.func(args)
459
+ #try:
460
+ # args.func(args)
461
+ #except:
462
+ # parser.print_usage()
463
+
464
+
465
+
@@ -0,0 +1,465 @@
1
+ #!python
2
+
3
+ '''
4
+ Query the ADS and INSPIRE databases to collect the citations used in a LaTeX file into a BibTeX file
5
+
6
+ Idea and main implementation is taken by Michele Vallisneri, see http://www.vallis.org/salon/summary-2.html
7
+ I changed the script to avoid the requests module, and added the INSPIRE database part.
8
+
9
+ Usage:
10
+ python fillbib.py <tex_file> <bib_file>. If <bib_file> is absent, it will try to guess it from the aux file"
11
+
12
+ '''
13
+ from __future__ import absolute_import, print_function
14
+ import argparse
15
+ import sys, os, re, html
16
+ import json
17
+
18
+ if sys.version_info.major>=3:
19
+ import urllib.request as urllib
20
+ else:
21
+ import urllib
22
+
23
+
24
+ def ads_citation(c): # download single ADS citation
25
+ #f= urllib.urlopen("http://adsabs.harvard.edu/cgi-bin/nph-bib_query?bibcode="+c+"&data_type=BIBTEX&db_key=AST&nocookieset=1")
26
+ #print("http://adsabs.harvard.edu/cgi-bin/nph-bib_query?bibcode="+c+"&data_type=BIBTEX&db_key=AST&nocookieset=1")
27
+ f= urllib.urlopen("https://ui.adsabs.harvard.edu/abs/"+c+"/exportcitation")
28
+
29
+ bib = f.read()
30
+ f.close()
31
+ if sys.version_info.major>=3:
32
+ bib=bib.decode()
33
+ bib = "@"+list(filter(lambda x:'adsnote' in x, bib.split("@")))[0].split("</textarea>")[0]
34
+ bib=html.unescape(bib)
35
+
36
+ return bib
37
+
38
+ def inspire_citation(key,
39
+ generate=False,
40
+ max_num_authors=None,
41
+ num_authors_short=None,
42
+ journal_arXiv_fallback=False):
43
+ """
44
+ Generate a custom BiBTeX entry
45
+
46
+ * generate
47
+ if True we generate the BibTeX rather than using the default from iNSPIRE
48
+ * max_num_authors
49
+ maximum number of authors to include (all of them if None)
50
+ * num_authors_short
51
+ number of authors to include if the number of authors is longer than max_num_authors
52
+ if not specified, this defaults to max_num_authors
53
+ * journal_arXiv_fallback
54
+ use arXiv:eprint as the journal field if no journal is found
55
+ """
56
+ request = 'https://inspirehep.net/api/literature?q=' + key
57
+ data = json.loads(urllib.urlopen(request).read())
58
+ if data['hits']['total'] != 1:
59
+ return None
60
+ if not generate:
61
+ bib = urllib.urlopen(data['hits']['hits'][0]['links']['bibtex']).read()
62
+ inspire_key = data['hits']['hits'][0]['metadata']['texkeys'][0]
63
+ if sys.version_info.major >= 3:
64
+ return bib.decode().replace(inspire_key, key)
65
+ else:
66
+ return bib.replace(inspire_key, key)
67
+ metadata = data['hits']['hits'][0]['metadata']
68
+
69
+ doctype = metadata["document_type"][0]
70
+
71
+ # BibTeX entry as a dictionary
72
+ bibtex = {}
73
+
74
+ # Find all authors
75
+ if "authors" in metadata:
76
+ num_authors = len(metadata["authors"])
77
+ if max_num_authors is not None and num_authors > max_num_authors:
78
+ short_author_list = True
79
+ if num_authors_short is None:
80
+ num_authors_short = max_num_authors
81
+ else:
82
+ short_author_list = False
83
+ author_list = []
84
+ for idx, author in enumerate(metadata['authors']):
85
+ if short_author_list and idx >= num_authors_short:
86
+ author_list.append("others")
87
+ break
88
+ author_list.append(author['full_name'])
89
+ bibtex['author'] = " and ".join(author_list)
90
+
91
+ # Find all collaborations
92
+ if "collaborations" in metadata:
93
+ collab_list = []
94
+ for collab in metadata["collaborations"]:
95
+ collab_list.append(collab["value"])
96
+ bibtex["collaboration"] = ", ".join(collab_list)
97
+
98
+ try:
99
+ bibtex["title"] = "{" + metadata["titles"][0]["title"] + "}"
100
+ except KeyError:
101
+ pass
102
+
103
+ try:
104
+ bibtex["eprint"] = metadata["arxiv_eprints"][0]["value"]
105
+ bibtex["archivePrefix"] = "arXiv"
106
+ bibtex["primaryClass"] = metadata["arxiv_eprints"][0]["categories"][0]
107
+ except KeyError:
108
+ pass
109
+
110
+ try:
111
+ bibtex["doi"] = metadata["dois"][0]["value"]
112
+ except KeyError:
113
+ pass
114
+
115
+ try:
116
+ bibtex["journal"] = metadata["publication_info"][0]["journal_title"]
117
+ except KeyError:
118
+ if journal_arXiv_fallback:
119
+ bibtex["journal"] = "arXiv:" + bibtex["eprint"]
120
+ pass
121
+
122
+ try:
123
+ bibtex["volume"] = metadata["publication_info"][0]["journal_volume"]
124
+ except KeyError:
125
+ pass
126
+
127
+ try:
128
+ bibtex["number"] = metadata["publication_info"][0]["journal_issue"]
129
+ except KeyError:
130
+ pass
131
+
132
+ try:
133
+ bibtex["pages"] = metadata["publication_info"][0]["page_start"]
134
+ except KeyError:
135
+ pass
136
+
137
+ try:
138
+ bibtex["year"] = metadata["publication_info"][0]["year"]
139
+ except KeyError:
140
+ try:
141
+ bibtex["year"] = metadata["preprint_date"][0:4]
142
+ bibtex["month"] = str(int(metadata["preprint_date"][5:7]))
143
+ except KeyError:
144
+ pass
145
+
146
+ # Format BibTeX entry
147
+ s = "@{}{{{}".format(doctype, key)
148
+ for field, value in bibtex.items():
149
+ s += ",\n {} = \"{}\"".format(field, value)
150
+ s += "\n}"
151
+
152
+ return s
153
+
154
+ def test_ads(): # test single ADS web scraping (both published articles and preprints)
155
+ test_key = ["2016PhRvL.116f1102A","2016arXiv160203837T"]
156
+ known_output= '@ARTICLE{2016PhRvL.116f1102A,\n author = {{Abbott}, B.~P. and {Abbott}, R. and {Abbott}, T.~D. and {Abernathy}, M.~R. and \n\t{Acernese}, F. and {Ackley}, K. and {Adams}, C. and {Adams}, T. and \n\t{Addesso}, P. and {Adhikari}, R.~X. and et al.},\n title = "{Observation of Gravitational Waves from a Binary Black Hole Merger}",\n journal = {Physical Review Letters},\narchivePrefix = "arXiv",\n eprint = {1602.03837},\n primaryClass = "gr-qc",\n year = 2016,\n month = feb,\n volume = 116,\n number = 6,\n eid = {061102},\n pages = {061102},\n doi = {10.1103/PhysRevLett.116.061102},\n adsurl = {http://adsabs.harvard.edu/abs/2016PhRvL.116f1102A},\n adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n'
157
+ assert [ads_citation(tk) == known_output for tk in test_key]
158
+
159
+ def test_inspire(): # test single INSPIRE web scraping
160
+ test_key = "Abbott:2016blz"
161
+ known_output = '@article{Abbott:2016blz,\n author = "Abbott, B.P. and Abbott, R. and Abbott, T.D. and Abernathy, M.R. and Acernese, F. and others",\n collaboration = "LIGO Scientific, Virgo",\n title = "{Observation of Gravitational Waves from a Binary Black Hole Merger}",\n eprint = "1602.03837",\n archivePrefix = "arXiv",\n primaryClass = "gr-qc",\n doi = "10.1103/PhysRevLett.116.061102",\n journal = "Phys.Rev.Lett.",\n volume = "116",\n number = "6",\n year = "2016"\n}'
162
+ assert inspire_citation(test_key, generate=True, max_num_authors=5) == known_output
163
+
164
+ def fillbib_tex(args):
165
+
166
+ if args.bibtex is None: # Get the name of the bibfile from the aux file
167
+ basename = args.texfile[0].split('.tex')[0]
168
+ auxfile = basename + '.aux'
169
+ for line in open(auxfile,'r'):
170
+ m = re.search(r'\\bibdata\{(.*)\}',line) # match \citation{...}, collect the ... note that we escape \, {, and }
171
+ if m:
172
+ bibfile = list(filter(lambda x:x!=basename+'Notes', m.group(1).split(',')))[0] # Remove that annyoing feature of revtex which creates a *Notes.bib bibfile. Note this is not solid if you want to handle multiple bib files.
173
+ bibfile = bibfile + '.bib'
174
+
175
+ else: # Bibfile specified from argv
176
+ basename = args.texfile[0].split('.tex')[0]
177
+ auxfile = basename + '.aux'
178
+ bibfile = args.bibtex.split('.bib')[0] + '.bib'
179
+
180
+ if args.updatepublished:
181
+ with open(basename+".tex", 'r') as texfile:
182
+ texdata = texfile.read()
183
+
184
+ # Get all citations from aux file. Citations will look like \citation{2004PhRvD..69j4017P,2004PhRvD..69j4017P}
185
+ cites = set() # use a set (no repetitions)
186
+ for line in open(auxfile,'r'):
187
+ m = re.search(r'\\citation\{(.*)\}',line) # find \citation{...}
188
+ if m:
189
+ cites.update(m.group(1).split(',')) # split by commas
190
+
191
+ cites= cites.difference([
192
+ 'REVTEX41Control','apsrev41Control',
193
+ 'REVTEX42Control','apsrev42Control']) # Remove annoying entries of revtex
194
+
195
+ print("Seek:", cites)
196
+
197
+ # Check what you already have in the bib file
198
+ haves = []
199
+ if os.path.isfile(bibfile):
200
+ for line in open(bibfile,'r'):
201
+ m = re.search(r'@.*?\{(.*),',line) # .*\{ means "any # of any char followed by {"; .*?\{ means "the shortest string matching any # of any char followed by {"
202
+ if m:
203
+ haves.append(m.group(1))
204
+ print("Have:", haves)
205
+
206
+
207
+ # Query ADS and INSPIRE
208
+ bibtex = open(bibfile,'a') # open for appending
209
+
210
+ for c in cites:
211
+ if c and c not in haves: # c is something and you don't have it already
212
+
213
+ if not c[0].isalpha(): # The first charachter is a number: could be on ADS
214
+
215
+ try:
216
+ bib = ads_citation(c)
217
+
218
+ cfound = bib.split("{")[1].split(',')[0]
219
+
220
+ if cfound == c: # Check you got what you where looking for
221
+ pass
222
+ elif 'arXiv' in c: # Take care of preprint on ADS
223
+
224
+ if args.updatepublished and '.tmp.' not in cfound:
225
+ print("ADS replace", c, "-->", cfound)
226
+ # This substitute the new ID into the tex file. Use at your own risk. The .tmp. condition fixes those stupid MNRAS temp entries.
227
+ texdata = texdata.replace(c, cfound)
228
+
229
+ else:
230
+ # This subsitute the arxiv id back in to the bib file
231
+ bib = bib.split("{")[0]+"{"+c+","+",".join(bib.split(",")[1:])
232
+
233
+ bibtex.write(bib)
234
+ print("ADS Found:", c)
235
+ except:
236
+ print("ADS Not found:", c)
237
+
238
+ else: # The first charachter is not a number: could be on INSPIRE
239
+
240
+ try:
241
+ bib = inspire_citation(c,
242
+ generate=args.generate,
243
+ max_num_authors=args.max_num_authors,
244
+ num_authors_short=args.num_authors_short,
245
+ journal_arXiv_fallback=args.journal_arXiv_fallback)
246
+ bibtex.write(bib)
247
+ print("INSPIRE Found:", c)
248
+ except:
249
+ print("INSPIRE Not found:", c)
250
+
251
+
252
+
253
+ bibtex.close()
254
+
255
+ if args.updatepublished:
256
+ with open(basename+".tex", 'w') as texfile:
257
+ texfile.write(texdata)
258
+
259
+ # Clean up journal names
260
+ if args.journals:
261
+ journals(bibfile)
262
+
263
+
264
+ def fillbib_list(args):
265
+ for c in args.keys:
266
+ if not c[0].isalpha():
267
+ bib = ads_citation(c)
268
+ if bib is None:
269
+ sys.stderr.write("ADS Not Found: {}\n".format(c))
270
+ else:
271
+ print(bib)
272
+ else:
273
+ bib = inspire_citation(c,
274
+ generate=args.generate,
275
+ max_num_authors=args.max_num_authors,
276
+ num_authors_short=args.num_authors_short,
277
+ journal_arXiv_fallback=args.journal_arXiv_fallback)
278
+ if bib is None:
279
+ sys.stderr.write("INSPIRE Not Found: {}\n".format(c))
280
+ else:
281
+ print(bib)
282
+
283
+
284
+ def curly(x):
285
+ '''Just a curly bracket sandwich.'''
286
+ return "{"+x+"}"
287
+
288
+ def journals(bibfile):
289
+ '''
290
+ Clean up the names of some journals using their ISO4 standards.
291
+ Journal abbreviations are taken from https://images.webofknowledge.com/images/help/WOS/A_abrvjt.html
292
+ If your favourite journal is missing, please add it and send a pull request. Thanks!
293
+ '''
294
+
295
+ # The format is: [ADS name, INSPIRE name, ISO4 abbreviation]
296
+ journals = [
297
+ ####
298
+ # These are the journals from the ADS macros: https://ui.adsabs.harvard.edu/help/actions/journal-macros
299
+ # I could not find them all on INSPIRE, some are missing.
300
+ ####
301
+ ['\\aj', 'Astron. J.', 'Astron. J.'],
302
+ ['\\actaa', 'Acta Astron.', 'Acta Astronom.'],
303
+ ['\\araa', 'Ann. Rev. Astron. Astrophys.', 'Annu. Rev. Astron. Astrophys.'],
304
+ ['\\apj', 'Astrophys. J.', 'Astrophys. J.'],
305
+ ['\\apjl', 'Astrophys. J. Lett.', 'Astrophys. J. Lett.'],
306
+ ['\\apjs', 'Astrophys. J. Suppl.', 'Astrophys. J. Supp. S.'],
307
+ ['\\ao', 'Appl. Opt.', 'Appl. Optics'],
308
+ ['\\apss', 'Astrophys. Space Sci.', 'Astrophys. Space Sci.'],
309
+ ['\\aap', 'Astron. Astrophys.', 'Astron. Astrophys.'],
310
+ ['\\aapr', 'Astron. Astrophys. Rev.', 'Astron. Astrophys. Rev.'],
311
+ ['\\aaps', 'Astron. Astrophys. Suppl. Ser.', 'Astron. Astrophys. Sup.'],
312
+ ['\\azh', '', 'Astron. Zh.'], #Not sure. Various names on inspire
313
+ ['\\baas', 'Bull. Am. Astron. Soc.', 'Bull. Am. Astron. Soc.'],
314
+ ['\\bac', 'Bull. Astron. Inst. Czech.', 'B. Astron. I. Czech.'],
315
+ ['\\caa', 'Chin. Astron. Astrophys.', 'Chinese Astron. Astr.'],
316
+ ['\\cjaa', 'Chin. J. Astron. Astrophys.', 'Chinese J. Astron. Ast.'],
317
+ ['\\icarus', 'Icarus', 'Icarus'],
318
+ ['\\jcap', 'JCAP', 'J. Cosmology Astropart. Phys.'],
319
+ ['\\jrasc', 'J. Roy. Astron. Soc. Canada', 'J. Roy Astron. Soc. Can.'],
320
+ ['\\memras', 'Mem. Roy. Astron. Soc.', 'Mem. R. Astron. Soc.'],
321
+ ['\\mnras', 'Mon. Not. Roy. Astron. Soc.', 'Mon. Not. R. Astron. Soc.'],
322
+ ['\\na', 'New Astron.', 'New Astron.'],
323
+ ['\\nar', 'New Astron. Rev.', 'New Astron. Rev.'],
324
+ ['\\pra', 'Phys. Rev. A', 'Phys. Rev. A'],
325
+ ['\\prb', 'Phys. Rev. B', 'Phys. Rev. B'],
326
+ ['\\prc', 'Phys. Rev. C', 'Phys. Rev. C'],
327
+ ['\\prd', 'Phys. Rev. D', 'Phys. Rev. D'],
328
+ ['\\pre', 'Phys. Rev. E', 'Phys. Rev. E'],
329
+ ['\\prl', 'Phys. Rev. Lett.', 'Phys. Rev. Lett.'],
330
+ ['\\pasa', 'Publ. Astron. Soc. Austral.', 'Publ. Astron. Soc. Aust.'],
331
+ ['\\pasp', 'Publ. Astron. Soc. Pac.', 'Publ. Astron. Soc. Pac.'],
332
+ ['\\pasj', 'Publ. Astron. Soc. Jap.', 'Publ. Astron. Soc. Jpn.'],
333
+ ['\\rmxaa', 'Rev. Mex. Astron. Astrofis.', 'Rev. Mex. Astron. Astr.'],
334
+ ['\\qjras', 'Q. J. Roy. Astron. Soc.', 'Q. J. Roy. Astron. Soc.'],
335
+ ['\\skytel', 'Sky Telesc.', 'Sky Telescope'],
336
+ ['\\solphys', 'Solar Phys.', 'Sol. Phys.'],
337
+ ['\\sovast', 'Sov. Astron.', 'Sov. Astron.'],
338
+ ['\\ssr', 'Space Sci. Rev.', 'Space Sci. Rev.'],
339
+ ['\\zap', 'Z. Astrophys.', 'Z. Astrophys.'],
340
+ ['\\nat', 'Nature', 'Nature'],
341
+ ['\\iaucirc', 'IAU Circ.', 'IAU Circ.'],
342
+ ['\\aplett', 'Astrophys. Lett.', 'Astrophys. Lett.'],
343
+ ['\\apspr', '', 'Astrophys.~Space~Phys.~Res.'], #Could not find it on INSPIRE
344
+ ['\\bain', 'Bull. Astron. Inst. Netherlands', 'B. Astron. I. Neth.'],
345
+ ['\\fcp', 'Fund. Cosmic Phys.', 'Fund. Cosmic Phys.'],
346
+ ['\\gca', 'Geochim. Cosmochim. Acta', 'Geochim. Cosmochim. Ac.'],
347
+ ['\\grl', 'Geophys. Res. Lett.', 'Geophys. Res. Lett.'],
348
+ ['\\jcp', 'J. Chem. Phys.', 'J. Chem. Phys.'],
349
+ ['\\jgr', 'J. Geophys. Res.', 'J. Geophys. Res.'],
350
+ ['\\jqsrt', 'J. Quant. Spectrosc. Radiat. Trans.', 'J. Quant. Sprectrosc. Ra.'],
351
+ ['\\memsai', 'Mem. Soc. Ast. It.', 'Mem. Soc. Astron. Ital.'],
352
+ ['\\nphysa', 'Nucl. Phys. A', 'Nucl. Phys. A'],
353
+ ['\\physrep', 'Phys. Rept.', 'Phys. Rep.'],
354
+ ['\\physscr', 'Phys. Scripta', 'Phys. Scripta'],
355
+ ['\\planss', 'Planet. Space Sci.', 'Planet. Space Sci.'],
356
+ ['\\procspie', 'Proc. SPIE Int. Soc. Opt. Eng.', 'P. Soc. Photo.-Opt. Ins.'],
357
+ ####
358
+ # [Davide Gerosa] These are journals that I personally encountered.
359
+ # Will keep on adding to this list.
360
+ ####
361
+ ['Advances in Space Research','Adv. Space Res.','Adv. Space Res.'], #ISI list not correct
362
+ ['American Institute of Physics Conference Series','AIP Conf. Proc.','AIP Conf. Proc.'],
363
+ ['Annals of Data Science','Ann. Data Sci.','Ann. Data Sci.'],
364
+ ['Astronomy and Computing','Astron. Comput.','Astron. Comput.'],
365
+ ['Astroparticle Physics','Astropart. Phys.','Astropart. Phys.'],
366
+ ['Astrophysics and Space Science Library','Astrophys. Space Sci. Libr.','Astrophys. Space Sc. L.'],
367
+ ['Astrophysics and Space Science Library','Astrophys. Space Sci. Libr.','Astrophys. Space Sc. L.'],
368
+ ['Bulletin of the American Astronomical Society','Bull. Am. Astron. Soc.','Bull. Am. Astron. Soc.'],
369
+ ['Classical and Quantum Gravity','Class. Quant. Grav.','Class. Quantum Grav.'], #ISI list not correct
370
+ ['Communications in Mathematical Physics', 'Commun. Math. Phys.', 'Commun. Math. Phys.'],
371
+ ['Frontiers in Astronomy and Space Sciences','Front. Astron. Space Sci.','Front. Astron. Space Sci.'],
372
+ ['General Relativity and Gravitation', 'Gen. Rel. Grav.', 'Gen. Relat. Gravit.'],
373
+ ['International Journal of Modern Physics D', 'Int. J. Mod. Phys. D', 'Int. J. Mod. Phys. D'],
374
+ ['iScience', 'iScience', 'iScience'],
375
+ ['Journal of High Energy Physics','JHEP','J. High Energy Phys.'],
376
+ ['Journal of Machine Learning Research','J. Machine Learning Res.','J. Mach. Learn. Res.'],
377
+ ['Journal of Mathematical Analysis and Applications','J. Math. Anal. Appl.','J. Math. Anal. Appl'],
378
+ ['Journal of Physics Conference Series','J. Phys. Conf. Ser.','J. Phys. Conf. Ser.'],
379
+ ['Journal of Statistical Physics','J. Statist. Phys.','J. Stat. Phys.'],
380
+ ['Journal of the Royal Statistical Society B','J. Roy. Statist. Soc. B','J. Roy. Statist. Soc. B'],
381
+ ['Living Reviews in Relativity', 'Living Rev. Rel.', 'Living Rev. Relativ.'],
382
+ ['Machine Learning: Science and Technology','Mach. Learn. Sci. Tech.','Mach. Learn. Sci. Tech.'], #ISI list not correct
383
+ ['Machine Learning','Machine Learning','Mach. Learn.'],
384
+ ['Nature Astronomy', 'Nature Astron.', 'Nat. Astron.'],
385
+ ['Nature Methods', 'Nature Meth.', 'Nat. Methods'],
386
+ ['Nature Reviews Physics','Nature Rev. Phys.','Nat. Rev. Phys.'],
387
+ ['Physica A Statistical Mechanics and its Applications','Physica A','Physica A'],
388
+ ['Physica D Nonlinear Phenomena', 'Physica D', 'Physica D'],
389
+ ['Physical Review', 'Phys. Rev.', 'Phys. Rev.'],
390
+ ['Physical Review Research', 'Phys. Rev. Res.', 'Phys. Rev. Res.'],
391
+ ['Physical Review X', 'Phys. Rev. X', 'Phys. Rev. X'],
392
+ ['Physics Letters A', 'Phys. Lett. A', 'Phys. Lett. A'],
393
+ ['Proceedings of the Royal Society of London Series A', 'Proc. Roy. Soc. Lond. A', 'P. R. Soc. Lond. A'],
394
+ ['Rendiconti Lincei. Scienze Fisiche e Naturali', 'Rend. Lincei Sci. Fis. Nat.', 'Rend. Lincei-Sci. Fis.'],
395
+ ['Reports on Progress in Physics', 'Rept. Prog. Phys.', 'Rep. Prog. Phys.'],
396
+ ['Research Notes of the American Astronomical Society', 'Res. Notes AAS','Res. Notes AAS'],
397
+ ['Reviews of Modern Physics', 'Rev. Mod. Phys.', 'Rev. Mod. Phys.'],
398
+ ['Science Advances','Sci. Adv.','Sci. Adv.'],
399
+ ['SIAM Journal on Scientific Computing','SIAM J. Sci. Comput.','SIAM J. Sci. Comput.'],
400
+ ['The Journal of Open Source Software','J. Open Source Softw.','J. Open Source Softw.'],
401
+ ]
402
+
403
+ with open(bibfile, 'r') as bibtex :
404
+ filedata = bibtex.read()
405
+
406
+ for j in journals:
407
+ print(j)
408
+ if j[0]:
409
+ filedata = filedata.replace(curly(j[0]), curly(j[2]))
410
+ if j[1]:
411
+ filedata = filedata.replace(curly(j[1]), curly(j[2]))
412
+ filedata = filedata.replace('journal = "'+j[1], 'journal = "'+j[2])
413
+
414
+ ### Clean up arxiv repeated information in ADS records:
415
+ filedata = filedata.replace('arXiv e-prints', '{}')
416
+ filedata = re.sub('pages = {arXiv:[0-9]+.[0-9]+},','',filedata)
417
+ filedata = re.sub('Pages = {arXiv:[0-9]+.[0-9]+},','',filedata)
418
+ filedata = re.sub('eid = {arXiv:[0-9]+.[0-9]+},','',filedata)
419
+ filedata = re.sub('Eid = {arXiv:[0-9]+.[0-9]+},','',filedata)
420
+ filedata = re.sub('doi = {[0-9]+.[0-9]+/arXiv.[0-9]+.[0-9]+},','',filedata)
421
+
422
+ with open(bibfile, 'w') as bibtex :
423
+ bibtex.write(filedata)
424
+
425
+
426
+
427
+
428
+ if __name__ == "__main__":
429
+
430
+ parser = argparse.ArgumentParser()
431
+ parser.add_argument("--generate", action="store_true",
432
+ help="Generate the BibTeX entries from the metadata "
433
+ "(this is useful to customize the generated BiBTeX file)")
434
+ parser.add_argument("--journal_arXiv_fallback", dest="journal_arXiv_fallback", action="store_true",
435
+ help="Set the journal entry to be arXiv for unpublished preprints "
436
+ "(iNSPIRE entries only, requres --generate)")
437
+ parser.add_argument("--max-num-authors", dest="max_num_authors", type=int,
438
+ help="Include at most this many authors for each bibtex entry"
439
+ "(iNSPIRE entries only, requres --generate)")
440
+ parser.add_argument("--num-authors-short", type=int,
441
+ help="Number of authors to list if the number of authors is larger than max_num_authors"
442
+ "(iNSPIRE entries only, defaults to max-num-authors, requres --generate)")
443
+ subparsers = parser.add_subparsers(help="Subcommands")
444
+
445
+ parser_tex = subparsers.add_parser("tex", help="Create a bibliography for a tex document")
446
+ parser_tex.add_argument("--bibtex", help="The BiBTeX file to use (if not specified we try to find out)")
447
+ parser_tex.add_argument("texfile", nargs=1, help="The (La)TeX file to process")
448
+ parser_tex.add_argument('--journals', dest='journals', help="Replace known journal abbreviations", default=True, action='store_true')
449
+ parser_tex.add_argument('--updatepublished', dest='updatepublished', help="Replace ADS arxiv entries in .tex file if published", default=True, action='store_true')
450
+ parser_tex.set_defaults(func=fillbib_tex)
451
+
452
+ parser_list = subparsers.add_parser("list", help="Create a bibliography given a list of ADS/iNSPIRE keys")
453
+ parser_list.add_argument("keys", nargs="+", help="ADS/iNSPIRE keys to fetch")
454
+ parser_list.set_defaults(func=fillbib_list)
455
+
456
+
457
+ args = parser.parse_args()
458
+ args.func(args)
459
+ #try:
460
+ # args.func(args)
461
+ #except:
462
+ # parser.print_usage()
463
+
464
+
465
+
@@ -0,0 +1,73 @@
1
+ #!/bin/bash
2
+ # Full tex compiler: run pdflatex, bibtex, query ADS and INSPIRE for citations and count words
3
+ # Davide Gerosa
4
+
5
+ # Install texshop engine
6
+ if [[ $1 = 'install-texshop' ]]; then
7
+ TEXSHOP="${HOME}/Library/TeXshop/Engines/filltex.engine"
8
+ echo "Installing texshop engine in $TEXSHOP"
9
+ echo ''#!/bin/bash '' > $TEXSHOP
10
+ echo ''export PATH=$(dirname $(which filltex)):\${PATH}'' >> $TEXSHOP
11
+ echo ''echo \$1 '' >> $TEXSHOP
12
+ echo ''FILENAME=\${1%%.*} '' >> $TEXSHOP
13
+ echo ''echo \$FILENAME '' >> $TEXSHOP
14
+ echo ''filltex \$FILENAME '' >> $TEXSHOP
15
+ chmod +x $TEXSHOP
16
+ exit
17
+ fi
18
+
19
+
20
+ # Actual script
21
+ #SCRIPT=`realpath $0`
22
+ #SCRIPT_LOCATION=`dirname $SCRIPT`
23
+
24
+
25
+ START=$PWD #Where you started
26
+
27
+ if [ ! -f ${START}/${1%.*}.tex ]; then
28
+ echo "File not found!"
29
+ exit 1
30
+ fi
31
+ FILE=${1%.*}
32
+ echo "filltex is compiling ${FILE}.tex"
33
+
34
+ # Get the journal abbreviations from ADS
35
+ if grep -q 'aas_macros' ${FILE}.tex; then # check if you need them
36
+ if [ ! -f 'aas_macros.sty' ]; then
37
+ curl -s 'https://adsabs.harvard.edu/abs_doc/aas_macros.sty' > 'aas_macros.sty'
38
+ fi
39
+ fi
40
+
41
+ # The pdflatex command returns 0 if everyting is ok, or 1 if he gets an error. If there's an error, I want the script to exit.
42
+ pdflatex --synctex=1 -halt-on-error ${FILE}.tex
43
+ [[ $? -eq 1 ]] && echo "pdflatex got an error" && exit
44
+
45
+ # Fill the bib fil with the ADS and INSPIRE references.
46
+ # Use python 2
47
+ #python ${SCRIPT_LOCATION}/fillbib.py "${FILE}"
48
+ # Use python 3
49
+ #python3 ${SCRIPT_LOCATION}/fillbib.py "${FILE}"
50
+ #python ../filltex/fillbib.py tex ${FILE}
51
+ fillbib tex ${FILE}
52
+
53
+
54
+ for i in {1..2}; do
55
+
56
+ # Fill the bbl file from the bib file
57
+ for file in *.aux ; do
58
+ bibtex $file
59
+ done
60
+
61
+ pdflatex --synctex=1 -halt-on-error ${FILE}.tex
62
+ [[ $? -eq 1 ]] && echo "pdflatex got an error" && exit
63
+
64
+ pdflatex --synctex=1 -halt-on-error ${FILE}.tex
65
+ [[ $? -eq 1 ]] && echo "pdflatex got an error" && exit
66
+ done
67
+
68
+ # Count the words
69
+ #perl ${SCRIPT_LOCATION}/texcount.pl "${FILE}".tex
70
+ texcount "${FILE}".tex
71
+
72
+ # open the pdf file if you like
73
+ #open "${FILE}".pdf
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: filltex
3
+ Version: 1.7.0
4
+ Summary: Automatic queries to ADS and InSPIRE databases to fill LATEX bibliography
5
+ Home-page: https://github.com/dgerosa/filltex
6
+ Author: Davide Gerosa and Michele Vallisneri
7
+ Author-email: dgerosa@star.sr.bham.ac.uk
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Dynamic: author
11
+ Dynamic: author-email
12
+ Dynamic: description
13
+ Dynamic: home-page
14
+ Dynamic: license
15
+ Dynamic: license-file
16
+ Dynamic: summary
17
+
18
+ See: `github.com/dgerosa/filltex <https://github.com/dgerosa/filltex>`_.
@@ -0,0 +1,8 @@
1
+ fillbib.py,sha256=ypKf452OmfHToVtEMDTG2cBRG8_OWDwdCHDaSgooGnU,22636
2
+ filltex-1.7.0.data/scripts/fillbib,sha256=lj_39ocfku8_MfdL8XVkc9k11eWhzyyrrC9HcuQv0PM,22623
3
+ filltex-1.7.0.data/scripts/filltex,sha256=cwUCJB2vxM_aUnH5ELC_DyDhuA-k4G3mYiKnQeWMnvk,2025
4
+ filltex-1.7.0.dist-info/licenses/LICENSE,sha256=02z8PApk_0N4ZQ8_rKArOOzClS7B0i4wNurOuUlHG0w,1070
5
+ filltex-1.7.0.dist-info/METADATA,sha256=TGugmQARK4iqVZDSbZ6hg2DearLVN0QADrBYxT5r_TI,509
6
+ filltex-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ filltex-1.7.0.dist-info/top_level.txt,sha256=dsEFVd7-rpq7Y4b2xsbUT1rUMnS46mx4cN6EnytACpI,8
8
+ filltex-1.7.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Davide Gerosa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ fillbib