papers-cli 2.9__tar.gz → 2.10__tar.gz
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.
- {papers_cli-2.9 → papers_cli-2.10}/PKG-INFO +1 -1
- {papers_cli-2.9 → papers_cli-2.10}/papers/__main__.py +1 -1
- {papers_cli-2.9 → papers_cli-2.10}/papers/_version.py +2 -2
- {papers_cli-2.9 → papers_cli-2.10}/papers/extract.py +108 -62
- {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/PKG-INFO +1 -1
- {papers_cli-2.9 → papers_cli-2.10}/tests/common.py +16 -22
- {papers_cli-2.9 → papers_cli-2.10}/.gitattributes +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/.github/workflows/pypi.yml +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/.github/workflows/tests.yml +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/.gitignore +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/LICENSE +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/README.md +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/__init__.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/bib.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/config.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/duplicate.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/encoding.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/filename.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/latexenc.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers/utils.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/SOURCES.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/dependency_links.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/entry_points.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/requires.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/top_level.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/pyproject.toml +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/requirements.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/setup.cfg +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/setup.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/__init__.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/config.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/download.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/downloadedpapers/.gitignore +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_add.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_biblio.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_duplicates.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_encoding.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_extract.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_filecheck.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_install.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_list.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_misc.py +0 -0
- {papers_cli-2.9 → papers_cli-2.10}/tests/test_undo.py +0 -0
|
@@ -1152,7 +1152,7 @@ def get_parser(config=None):
|
|
|
1152
1152
|
|
|
1153
1153
|
grp = listp.add_argument_group('action on listed results (pipe)')
|
|
1154
1154
|
grp.add_argument('--delete', action='store_true')
|
|
1155
|
-
grp.add_argument('--edit', action='store_true', help='interactive edit text file with entries, and re-insert them')
|
|
1155
|
+
grp.add_argument('-e', '--edit', action='store_true', help='interactive edit text file with entries, and re-insert them')
|
|
1156
1156
|
grp.add_argument('--fetch', action='store_true', help='fetch and fix metadata')
|
|
1157
1157
|
grp.add_argument('--add-keywords', '--add-tag', nargs='+', help='add keywords to the selected entries')
|
|
1158
1158
|
grp.add_argument('--add-files', nargs='+', help='add files to the selected entries (only one entry must be listed)')
|
|
@@ -14,7 +14,7 @@ from papers.encoding import family_names
|
|
|
14
14
|
from bibtexparser.customization import convert_to_unicode
|
|
15
15
|
|
|
16
16
|
my_etiquette = Etiquette('papers', papers.__version__, 'https://github.com/perrette/papers', 'mahe.perrette@gmail.com')
|
|
17
|
-
|
|
17
|
+
work = Works(etiquette=my_etiquette)
|
|
18
18
|
|
|
19
19
|
class DOIParsingError(ValueError):
|
|
20
20
|
pass
|
|
@@ -208,27 +208,29 @@ def extract_pdf_metadata(pdf, search_doi=True, search_fulltext=True, maxpages=10
|
|
|
208
208
|
txt = pdfhead(pdf, maxpages, minwords, image=image)
|
|
209
209
|
return extract_txt_metadata(txt, search_doi, search_fulltext, **kw)
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def fetch_bibtex_by_doi(doi):
|
|
215
|
-
url = "http://api.crossref.org/works/"+doi+"/transform/application/x-bibtex"
|
|
216
|
-
work = Works(etiquette=my_etiquette)
|
|
211
|
+
@cached('crossref.json')
|
|
212
|
+
def fetch_crossref_by_doi(doi):
|
|
213
|
+
url = "http://api.crossref.org/works/"+doi
|
|
217
214
|
response = work.do_http_request('get', url, custom_header={'user-agent': str(work.etiquette)})
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
215
|
+
try:
|
|
216
|
+
response.raise_for_status()
|
|
217
|
+
except Exception as error:
|
|
218
|
+
raise DOIRequestError(repr(doi)+': '+repr(error))
|
|
219
|
+
return response.json()
|
|
222
220
|
|
|
221
|
+
# @cached('crossref-bibtex.json')
|
|
222
|
+
def fetch_bibtex_by_doi(doi):
|
|
223
|
+
json_data = fetch_crossref_by_doi(doi)
|
|
224
|
+
return crossref_to_bibtex(json_data)
|
|
223
225
|
|
|
224
|
-
@cached('crossref.json')
|
|
226
|
+
@cached('crossref-json.json')
|
|
225
227
|
def fetch_json_by_doi(doi):
|
|
226
228
|
url = "http://api.crossref.org/works/"+doi+"/transform/application/json"
|
|
227
|
-
work = Works(etiquette=my_etiquette)
|
|
228
229
|
jsontxt = work.do_http_request('get', url, custom_header={'user-agent': str(work.etiquette)}).text
|
|
229
230
|
return jsontxt.dumps(json)
|
|
230
231
|
|
|
231
232
|
|
|
233
|
+
|
|
232
234
|
def _get_page_fast(pagerequest):
|
|
233
235
|
"""Return the data for a page on scholar.google.com"""
|
|
234
236
|
from scholarly import scholarly
|
|
@@ -267,11 +269,6 @@ def fetch_bibtex_by_fulltext_scholar(txt, assess_results=True):
|
|
|
267
269
|
|
|
268
270
|
return scholarly.bibtex(result)
|
|
269
271
|
|
|
270
|
-
|
|
271
|
-
def _crossref_get_author(res, sep='; '):
|
|
272
|
-
return sep.join([p.get('given','') + p['family'] for p in res.get('author',[]) if 'family' in p])
|
|
273
|
-
|
|
274
|
-
|
|
275
272
|
def _crossref_score(txt, r):
|
|
276
273
|
# high score means high similarity
|
|
277
274
|
from rapidfuzz.fuzz import token_set_ratio
|
|
@@ -285,56 +282,105 @@ def _crossref_score(txt, r):
|
|
|
285
282
|
score += token_set_ratio(r['abstract'], txt)
|
|
286
283
|
return score
|
|
287
284
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
ID
|
|
285
|
+
def map_crossref_to_bibtex_type(crossref_type):
|
|
286
|
+
mapping = {
|
|
287
|
+
"journal-article": "article",
|
|
288
|
+
"book": "book",
|
|
289
|
+
"book-chapter": "incollection", # or sometimes inbook
|
|
290
|
+
"proceedings-article": "inproceedings",
|
|
291
|
+
"conference-paper": "inproceedings",
|
|
292
|
+
"report": "techreport",
|
|
293
|
+
"thesis": "phdthesis", # or "mastersthesis" based on additional info
|
|
294
|
+
}
|
|
295
|
+
return mapping.get(crossref_type, "misc")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def format_authors(authors):
|
|
299
|
+
"""Converts a list of author dicts to a BibTeX-friendly string."""
|
|
300
|
+
author_list = []
|
|
301
|
+
for author in authors:
|
|
302
|
+
given = author.get("given", "")
|
|
303
|
+
family = author.get("family", "")
|
|
304
|
+
# Format as "Family, Given" for BibTeX
|
|
305
|
+
formatted = f"{family}, {given}" if family else given
|
|
306
|
+
# author_list.append(unidecode(formatted))
|
|
307
|
+
author_list.append(formatted)
|
|
308
|
+
return " and ".join(author_list)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def crossref_to_bibtex(json_data):
|
|
312
|
+
message = json_data['message']
|
|
313
|
+
entry_type = message.get('type', 'misc') # Default to 'misc' if type is not specified
|
|
314
|
+
|
|
315
|
+
# Common fields
|
|
316
|
+
bib_entry = {
|
|
317
|
+
'title': message.get('title', [''])[0],
|
|
318
|
+
'author': format_authors(message.get('author', [])),
|
|
319
|
+
'doi': message.get('DOI', ''),
|
|
320
|
+
'url': message.get('URL', ''),
|
|
321
|
+
'ENTRYTYPE': map_crossref_to_bibtex_type(entry_type),
|
|
322
|
+
'ID': message.get('DOI', '')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Year: Look for publication date in several possible places
|
|
326
|
+
date_parts = None
|
|
327
|
+
for date_field in ["published-print", "published-online", "issued"]:
|
|
328
|
+
if date_field in message and "date-parts" in message[date_field]:
|
|
329
|
+
date_parts = message[date_field]["date-parts"][0]
|
|
330
|
+
break
|
|
331
|
+
if date_parts and len(date_parts) > 0:
|
|
332
|
+
bib_entry["year"] = str(date_parts[0])
|
|
326
333
|
else:
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
334
|
+
bib_entry["year"] = "0000"
|
|
335
|
+
|
|
336
|
+
# Fields specific to entry types
|
|
337
|
+
if entry_type == 'journal-article':
|
|
338
|
+
bib_entry.update({
|
|
339
|
+
'journal': message.get('container-title', [''])[0],
|
|
340
|
+
'volume': message.get('volume', ''),
|
|
341
|
+
'number': message.get('issue', ''),
|
|
342
|
+
'pages': message.get('page', ''),
|
|
343
|
+
})
|
|
344
|
+
elif entry_type == 'book':
|
|
345
|
+
bib_entry.update({
|
|
346
|
+
'publisher': message.get('publisher', ''),
|
|
347
|
+
'address': message.get('publisher-location', ''),
|
|
348
|
+
'editor': format_authors(message.get('editor', [])),
|
|
349
|
+
'isbn': message.get('ISBN', [''])[0],
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
elif entry_type == 'proceedings-article':
|
|
354
|
+
bib_entry.update({
|
|
355
|
+
'booktitle': message.get('container-title', [''])[0],
|
|
356
|
+
'publisher': message.get('publisher', ''),
|
|
357
|
+
'editor': format_authors(message.get('editor', [])),
|
|
358
|
+
})
|
|
359
|
+
elif entry_type == 'report':
|
|
360
|
+
bib_entry.update({
|
|
361
|
+
'institution': message.get('institution', {}).get('name', ''),
|
|
362
|
+
})
|
|
363
|
+
elif entry_type == 'thesis':
|
|
364
|
+
bib_entry.update({
|
|
365
|
+
'school': message.get('institution', {}).get('name', ''),
|
|
366
|
+
'type': message.get('type', ''),
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
bib_entry = {k: v for k, v in bib_entry.items() if v} # Remove empty fields
|
|
370
|
+
|
|
371
|
+
# Create a BibDatabase object
|
|
330
372
|
db = bibtexparser.bibdatabase.BibDatabase()
|
|
331
|
-
db.entries
|
|
332
|
-
|
|
373
|
+
db.entries = [bib_entry]
|
|
374
|
+
|
|
375
|
+
# Write to a BibTeX string
|
|
376
|
+
writer = bibtexparser.bwriter.BibTexWriter()
|
|
377
|
+
bibtex_str = writer.write(db)
|
|
378
|
+
|
|
379
|
+
return bibtex_str
|
|
333
380
|
|
|
334
381
|
|
|
335
382
|
# @cached('crossref-bibtex-fulltext.json', hashed_key=True)
|
|
336
383
|
def fetch_bibtex_by_fulltext_crossref(txt, **kw):
|
|
337
|
-
work = Works(etiquette=my_etiquette)
|
|
338
384
|
logger.debug('crossref fulltext seach:\n'+txt)
|
|
339
385
|
|
|
340
386
|
# get the most likely match of the first results
|
|
@@ -77,23 +77,21 @@ def run(cmd, sp_cmd=None, **kw):
|
|
|
77
77
|
def prepare_paper():
|
|
78
78
|
pdf = downloadpdf('bg-8-515-2011.pdf')
|
|
79
79
|
doi = '10.5194/bg-8-515-2011'
|
|
80
|
-
key = '
|
|
80
|
+
key = '10.5194/bg-8-515-2011'
|
|
81
81
|
newkey = 'perrette_yool2011'
|
|
82
82
|
year = '2011'
|
|
83
|
-
bibtex = """@article{
|
|
83
|
+
bibtex = """@article{10.5194/bg-8-515-2011,
|
|
84
84
|
author = {Perrette, M. and Yool, A. and Quartly, G. D. and Popova, E. E.},
|
|
85
85
|
doi = {10.5194/bg-8-515-2011},
|
|
86
|
-
issn = {1726-4189},
|
|
87
86
|
journal = {Biogeosciences},
|
|
88
|
-
month = {February},
|
|
89
87
|
number = {2},
|
|
90
|
-
pages = {515
|
|
91
|
-
publisher = {Copernicus GmbH},
|
|
88
|
+
pages = {515-524},
|
|
92
89
|
title = {Near-ubiquity of ice-edge blooms in the Arctic},
|
|
93
|
-
url = {
|
|
90
|
+
url = {https://doi.org/10.5194/bg-8-515-2011},
|
|
94
91
|
volume = {8},
|
|
95
92
|
year = {2011}
|
|
96
|
-
}
|
|
93
|
+
}
|
|
94
|
+
"""
|
|
97
95
|
|
|
98
96
|
file_rename = "perrette_et_al_2011_near-ubiquity-of-ice-edge-blooms-in-the-arctic.pdf"
|
|
99
97
|
# The above corresponds to --name-template "{authorX}_{year}_{title}"
|
|
@@ -104,20 +102,17 @@ def prepare_paper2():
|
|
|
104
102
|
pdf = downloadpdf('esd-4-11-2013.pdf')
|
|
105
103
|
si = downloadpdf('esd-4-11-2013-supplement.pdf')
|
|
106
104
|
doi = '10.5194/esd-4-11-2013'
|
|
107
|
-
key = '
|
|
105
|
+
key = '10.5194/esd-4-11-2013'
|
|
108
106
|
newkey = 'perrette_landerer2013'
|
|
109
107
|
year = '2013'
|
|
110
|
-
bibtex = """@article{
|
|
108
|
+
bibtex = """@article{10.5194/esd-4-11-2013,
|
|
111
109
|
author = {Perrette, M. and Landerer, F. and Riva, R. and Frieler, K. and Meinshausen, M.},
|
|
112
110
|
doi = {10.5194/esd-4-11-2013},
|
|
113
|
-
issn = {2190-4987},
|
|
114
111
|
journal = {Earth System Dynamics},
|
|
115
|
-
month = {January},
|
|
116
112
|
number = {1},
|
|
117
|
-
pages = {11
|
|
118
|
-
publisher = {Copernicus GmbH},
|
|
113
|
+
pages = {11-29},
|
|
119
114
|
title = {A scaling approach to project regional sea level rise and its uncertainties},
|
|
120
|
-
url = {
|
|
115
|
+
url = {https://doi.org/10.5194/esd-4-11-2013},
|
|
121
116
|
volume = {4},
|
|
122
117
|
year = {2013}
|
|
123
118
|
}"""
|
|
@@ -185,19 +180,18 @@ class BibTest(unittest.TestCase):
|
|
|
185
180
|
|
|
186
181
|
|
|
187
182
|
|
|
188
|
-
bibtex = """@article{
|
|
189
|
-
author = {M.
|
|
183
|
+
bibtex = """@article{10.5194/bg-8-515-2011,
|
|
184
|
+
author = {Perrette, M. and Yool, A. and Quartly, G. D. and Popova, E. E.},
|
|
190
185
|
doi = {10.5194/bg-8-515-2011},
|
|
191
186
|
journal = {Biogeosciences},
|
|
192
|
-
link = {https://doi.org/10.5194%2Fbg-8-515-2011},
|
|
193
|
-
month = {feb},
|
|
194
187
|
number = {2},
|
|
195
|
-
pages = {515
|
|
196
|
-
publisher = {Copernicus {GmbH}},
|
|
188
|
+
pages = {515-524},
|
|
197
189
|
title = {Near-ubiquity of ice-edge blooms in the Arctic},
|
|
190
|
+
url = {https://doi.org/10.5194/bg-8-515-2011},
|
|
198
191
|
volume = {8},
|
|
199
192
|
year = {2011}
|
|
200
|
-
}
|
|
193
|
+
}
|
|
194
|
+
"""
|
|
201
195
|
|
|
202
196
|
bibtex2 = """@article{SomeOneElse2000,
|
|
203
197
|
author = {Some One},
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|