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.
Files changed (44) hide show
  1. {papers_cli-2.9 → papers_cli-2.10}/PKG-INFO +1 -1
  2. {papers_cli-2.9 → papers_cli-2.10}/papers/__main__.py +1 -1
  3. {papers_cli-2.9 → papers_cli-2.10}/papers/_version.py +2 -2
  4. {papers_cli-2.9 → papers_cli-2.10}/papers/extract.py +108 -62
  5. {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/PKG-INFO +1 -1
  6. {papers_cli-2.9 → papers_cli-2.10}/tests/common.py +16 -22
  7. {papers_cli-2.9 → papers_cli-2.10}/.gitattributes +0 -0
  8. {papers_cli-2.9 → papers_cli-2.10}/.github/workflows/pypi.yml +0 -0
  9. {papers_cli-2.9 → papers_cli-2.10}/.github/workflows/tests.yml +0 -0
  10. {papers_cli-2.9 → papers_cli-2.10}/.gitignore +0 -0
  11. {papers_cli-2.9 → papers_cli-2.10}/LICENSE +0 -0
  12. {papers_cli-2.9 → papers_cli-2.10}/README.md +0 -0
  13. {papers_cli-2.9 → papers_cli-2.10}/papers/__init__.py +0 -0
  14. {papers_cli-2.9 → papers_cli-2.10}/papers/bib.py +0 -0
  15. {papers_cli-2.9 → papers_cli-2.10}/papers/config.py +0 -0
  16. {papers_cli-2.9 → papers_cli-2.10}/papers/duplicate.py +0 -0
  17. {papers_cli-2.9 → papers_cli-2.10}/papers/encoding.py +0 -0
  18. {papers_cli-2.9 → papers_cli-2.10}/papers/filename.py +0 -0
  19. {papers_cli-2.9 → papers_cli-2.10}/papers/latexenc.py +0 -0
  20. {papers_cli-2.9 → papers_cli-2.10}/papers/utils.py +0 -0
  21. {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/SOURCES.txt +0 -0
  22. {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/dependency_links.txt +0 -0
  23. {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/entry_points.txt +0 -0
  24. {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/requires.txt +0 -0
  25. {papers_cli-2.9 → papers_cli-2.10}/papers_cli.egg-info/top_level.txt +0 -0
  26. {papers_cli-2.9 → papers_cli-2.10}/pyproject.toml +0 -0
  27. {papers_cli-2.9 → papers_cli-2.10}/requirements.txt +0 -0
  28. {papers_cli-2.9 → papers_cli-2.10}/setup.cfg +0 -0
  29. {papers_cli-2.9 → papers_cli-2.10}/setup.py +0 -0
  30. {papers_cli-2.9 → papers_cli-2.10}/tests/__init__.py +0 -0
  31. {papers_cli-2.9 → papers_cli-2.10}/tests/config.py +0 -0
  32. {papers_cli-2.9 → papers_cli-2.10}/tests/download.py +0 -0
  33. {papers_cli-2.9 → papers_cli-2.10}/tests/downloadedpapers/.gitignore +0 -0
  34. {papers_cli-2.9 → papers_cli-2.10}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
  35. {papers_cli-2.9 → papers_cli-2.10}/tests/test_add.py +0 -0
  36. {papers_cli-2.9 → papers_cli-2.10}/tests/test_biblio.py +0 -0
  37. {papers_cli-2.9 → papers_cli-2.10}/tests/test_duplicates.py +0 -0
  38. {papers_cli-2.9 → papers_cli-2.10}/tests/test_encoding.py +0 -0
  39. {papers_cli-2.9 → papers_cli-2.10}/tests/test_extract.py +0 -0
  40. {papers_cli-2.9 → papers_cli-2.10}/tests/test_filecheck.py +0 -0
  41. {papers_cli-2.9 → papers_cli-2.10}/tests/test_install.py +0 -0
  42. {papers_cli-2.9 → papers_cli-2.10}/tests/test_list.py +0 -0
  43. {papers_cli-2.9 → papers_cli-2.10}/tests/test_misc.py +0 -0
  44. {papers_cli-2.9 → papers_cli-2.10}/tests/test_undo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.9
3
+ Version: 2.10
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
@@ -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)')
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.9'
16
- __version_tuple__ = version_tuple = (2, 9)
15
+ __version__ = version = '2.10'
16
+ __version_tuple__ = version_tuple = (2, 10)
@@ -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
- @cached('crossref-bibtex.json')
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
- if response.ok:
219
- bibtex = response.text.strip()
220
- return bibtex
221
- raise DOIRequestError(repr(doi)+': '+response.text)
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
- def crossref_to_bibtex(r):
290
- """convert crossref result to bibtex
291
- """
292
- bib = {}
293
-
294
- if 'author' in r:
295
- family = lambda p: p['family'] if len(p['family'].split()) == 1 else '{'+p['family']+'}'
296
- bib['author'] = ' and '.join([family(p) + ', '+ p.get('given','')
297
- for p in r.get('author',[]) if 'family' in p])
298
-
299
- # for k in ['issued','published-print', 'published-online']:
300
- k = 'issued'
301
- if k in r and 'date-parts' in r[k] and len(r[k]['date-parts'])>0:
302
- date = r[k]['date-parts'][0]
303
- bib['year'] = str(date[0])
304
- if len(date) >= 2:
305
- bib['month'] = str(date[1])
306
- # break
307
-
308
- if 'DOI' in r: bib['doi'] = r['DOI']
309
- if 'URL' in r: bib['url'] = r['URL']
310
- if 'title' in r: bib['title'] = r['title'][0]
311
- if 'container-title' in r: bib['journal'] = r['container-title'][0]
312
- if 'volume' in r: bib['volume'] = r['volume']
313
- if 'issue' in r: bib['number'] = r['issue']
314
- if 'page' in r: bib['pages'] = r['page']
315
- if 'publisher' in r: bib['publisher'] = r['publisher']
316
-
317
- # entry type
318
- type = bib.get('type','journal-article')
319
- type_mapping = {'journal-article':'article'}
320
- bib['ENTRYTYPE'] = type_mapping.get(type, type)
321
-
322
- # bibtex key
323
- year = str(bib.get('year','0000'))
324
- if 'author' in r:
325
- ID = r['author'][0]['family'] + '_' + year
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
- ID = year
328
- bib['ID'] = ID
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.append(bib)
332
- return bibtexparser.dumps(db)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.9
3
+ Version: 2.10
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
@@ -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 = 'Perrette_2011'
80
+ key = '10.5194/bg-8-515-2011'
81
81
  newkey = 'perrette_yool2011'
82
82
  year = '2011'
83
- bibtex = """@article{Perrette_2011,
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–524},
91
- publisher = {Copernicus GmbH},
88
+ pages = {515-524},
92
89
  title = {Near-ubiquity of ice-edge blooms in the Arctic},
93
- url = {http://dx.doi.org/10.5194/bg-8-515-2011},
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 = 'Perrette_2013'
105
+ key = '10.5194/esd-4-11-2013'
108
106
  newkey = 'perrette_landerer2013'
109
107
  year = '2013'
110
- bibtex = """@article{Perrette_2013,
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–29},
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 = {http://dx.doi.org/10.5194/esd-4-11-2013},
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{Perrette_2011,
189
- author = {M. Perrette and A. Yool and G. D. Quartly and E. E. Popova},
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--524},
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