papers-cli 2.10__tar.gz → 2.11__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.10 → papers_cli-2.11}/PKG-INFO +1 -1
  2. {papers_cli-2.10 → papers_cli-2.11}/papers/__main__.py +47 -38
  3. {papers_cli-2.10 → papers_cli-2.11}/papers/_version.py +2 -2
  4. {papers_cli-2.10 → papers_cli-2.11}/papers/bib.py +16 -7
  5. {papers_cli-2.10 → papers_cli-2.11}/papers/duplicate.py +11 -6
  6. {papers_cli-2.10 → papers_cli-2.11}/papers/extract.py +2 -4
  7. {papers_cli-2.10 → papers_cli-2.11}/papers_cli.egg-info/PKG-INFO +1 -1
  8. {papers_cli-2.10 → papers_cli-2.11}/tests/test_add.py +1 -1
  9. {papers_cli-2.10 → papers_cli-2.11}/tests/test_duplicates.py +1 -1
  10. {papers_cli-2.10 → papers_cli-2.11}/.gitattributes +0 -0
  11. {papers_cli-2.10 → papers_cli-2.11}/.github/workflows/pypi.yml +0 -0
  12. {papers_cli-2.10 → papers_cli-2.11}/.github/workflows/tests.yml +0 -0
  13. {papers_cli-2.10 → papers_cli-2.11}/.gitignore +0 -0
  14. {papers_cli-2.10 → papers_cli-2.11}/LICENSE +0 -0
  15. {papers_cli-2.10 → papers_cli-2.11}/README.md +0 -0
  16. {papers_cli-2.10 → papers_cli-2.11}/papers/__init__.py +0 -0
  17. {papers_cli-2.10 → papers_cli-2.11}/papers/config.py +0 -0
  18. {papers_cli-2.10 → papers_cli-2.11}/papers/encoding.py +0 -0
  19. {papers_cli-2.10 → papers_cli-2.11}/papers/filename.py +0 -0
  20. {papers_cli-2.10 → papers_cli-2.11}/papers/latexenc.py +0 -0
  21. {papers_cli-2.10 → papers_cli-2.11}/papers/utils.py +0 -0
  22. {papers_cli-2.10 → papers_cli-2.11}/papers_cli.egg-info/SOURCES.txt +0 -0
  23. {papers_cli-2.10 → papers_cli-2.11}/papers_cli.egg-info/dependency_links.txt +0 -0
  24. {papers_cli-2.10 → papers_cli-2.11}/papers_cli.egg-info/entry_points.txt +0 -0
  25. {papers_cli-2.10 → papers_cli-2.11}/papers_cli.egg-info/requires.txt +0 -0
  26. {papers_cli-2.10 → papers_cli-2.11}/papers_cli.egg-info/top_level.txt +0 -0
  27. {papers_cli-2.10 → papers_cli-2.11}/pyproject.toml +0 -0
  28. {papers_cli-2.10 → papers_cli-2.11}/requirements.txt +0 -0
  29. {papers_cli-2.10 → papers_cli-2.11}/setup.cfg +0 -0
  30. {papers_cli-2.10 → papers_cli-2.11}/setup.py +0 -0
  31. {papers_cli-2.10 → papers_cli-2.11}/tests/__init__.py +0 -0
  32. {papers_cli-2.10 → papers_cli-2.11}/tests/common.py +0 -0
  33. {papers_cli-2.10 → papers_cli-2.11}/tests/config.py +0 -0
  34. {papers_cli-2.10 → papers_cli-2.11}/tests/download.py +0 -0
  35. {papers_cli-2.10 → papers_cli-2.11}/tests/downloadedpapers/.gitignore +0 -0
  36. {papers_cli-2.10 → papers_cli-2.11}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
  37. {papers_cli-2.10 → papers_cli-2.11}/tests/test_biblio.py +0 -0
  38. {papers_cli-2.10 → papers_cli-2.11}/tests/test_encoding.py +0 -0
  39. {papers_cli-2.10 → papers_cli-2.11}/tests/test_extract.py +0 -0
  40. {papers_cli-2.10 → papers_cli-2.11}/tests/test_filecheck.py +0 -0
  41. {papers_cli-2.10 → papers_cli-2.11}/tests/test_install.py +0 -0
  42. {papers_cli-2.10 → papers_cli-2.11}/tests/test_list.py +0 -0
  43. {papers_cli-2.10 → papers_cli-2.11}/tests/test_misc.py +0 -0
  44. {papers_cli-2.10 → papers_cli-2.11}/tests/test_undo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.10
3
+ Version: 2.11
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
@@ -19,7 +19,7 @@ from papers.extract import fetch_bibtex_by_doi
19
19
  from papers.encoding import parse_file, format_file, family_names, format_entries, standard_name, format_entry, parse_keywords, format_key
20
20
  from papers.config import bcolors, Config, search_config, CONFIG_FILE, CONFIG_FILE_LOCAL, DATA_DIR, CONFIG_FILE_LEGACY, BACKUP_DIR
21
21
  from papers.duplicate import list_duplicates, list_uniques, edit_entries
22
- from papers.bib import Biblio, FUZZY_RATIO, DEFAULT_SIMILARITY, entry_filecheck, backupfile as backupfile_func, isvalidkey
22
+ from papers.bib import Biblio, FUZZY_RATIO, DEFAULT_SIMILARITY, entry_filecheck, backupfile as backupfile_func, isvalidkey, DuplicateKeyError
23
23
  from papers.utils import move, checksum, view_pdf, open_folder
24
24
  from papers import __version__
25
25
 
@@ -538,30 +538,45 @@ def addcmd(parser, o, config):
538
538
 
539
539
  biblio = get_biblio(config)
540
540
 
541
- kw = {'on_conflict':o.mode, 'check_duplicate':not o.no_check_duplicate,
542
- 'mergefiles':not o.no_merge_files, 'update_key':o.update_key}
543
-
544
541
  entries = []
545
542
 
543
+ metadata = {k: v for k, v in o.metadata or []}
544
+ if o.doi: metadata['doi'] = o.doi
545
+ if o.key: metadata['ID'] = o.key
546
+ if o.title: metadata['title'] = o.title
547
+ if o.author: metadata['author'] = o.author
548
+ if o.journal: metadata['journal'] = o.journal
549
+ if o.year: metadata['year'] = o.year
550
+ if o.type: metadata['ENTRYTYPE'] = o.type
551
+ if "author" in metadata:
552
+ metadata["author"] = standard_name(metadata["author"])
553
+ if o.attachment:
554
+ metadata['file'] = format_file(biblio.get_files(metadata) + o.attachment, relative_to=biblio.relative_to)
555
+
546
556
  if len(o.file) > 1:
547
- if o.attachment:
548
- logger.error('--attachment is only valid for one PDF / BIBTEX entry')
557
+ if metadata:
558
+ logger.error('--doi, --metadata, --key, --attachment and other metadata keys are only valid for one PDF / BIBTEX entry')
549
559
  raise PapersExit()
550
- if o.doi:
551
- logger.error('--doi is only valid for one added file')
560
+
561
+ kw = {'on_conflict':o.mode, 'check_duplicate':not o.no_check_duplicate,
562
+ 'mergefiles':not o.no_merge_files, 'update_key':o.update_key, 'metadata':metadata}
563
+
564
+ if len(o.file) == 0 and o.doi and not o.no_query_doi:
565
+ entries.extend( biblio.fetch_doi(o.doi, attachments=o.attachment, rename=o.rename, copy=o.copy, **kw) )
566
+
567
+ elif len(o.file) == 0:
568
+ if not o.edit and not o.metadata and not o.key and not o.doi and not o.attachment and not o.title and not o.author and not o.journal and not o.year:
569
+ logger.error("No entry added: use --doi, --metadata, --key, --attachment, --title, --author, --journal, --year to add a new entry")
552
570
  raise PapersExit()
553
571
 
554
- if len(o.file) == 0:
572
+ metadata.setdefault('ID', biblio.generate_key(metadata))
573
+ metadata.setdefault('ENTRYTYPE', 'article')
555
574
  if o.edit:
556
- pass
557
- elif not o.doi:
558
- logger.error('Please provide either a PDF file or BIBTEX entry or specify `--doi DOI` or `--edit`')
559
- raise PapersExit()
560
- elif o.no_query_doi:
561
- logger.error('If no file is present, --no-query-doi is not compatible with --doi')
562
- raise PapersExit()
563
- else:
564
- entries.extend( biblio.fetch_doi(o.doi, attachments=o.attachment, rename=o.rename, copy=o.copy, **kw) )
575
+ metadata = edit_entries(metadata)
576
+ o.edit = False
577
+
578
+ entries.extend( biblio.insert_entry(metadata, **kw) )
579
+
565
580
 
566
581
  for file in o.file:
567
582
  try:
@@ -601,30 +616,18 @@ def addcmd(parser, o, config):
601
616
  entries = [e for e in biblio.entries if biblio.key(e) in unique_keys]
602
617
 
603
618
  if o.edit:
604
- if len(entries) == 0:
605
- entries = [{
606
- "ID": "EDIT-NEW-KEY",
607
- "ENTRYTYPE": "article",
608
- "author": standard_name("John Doe and Jane Roe"),
609
- "title": "",
610
- "journal": "",
611
- "year": "",
612
- "doi": o.doi or "",
613
- "file": format_file(o.attachment or [], relative_to=biblio.relative_to),
614
- }]
619
+ entry_keys = [biblio.key(e) for e in entries]
620
+ otherentries = [e for e in biblio.entries if biblio.key(e) not in entry_keys]
615
621
 
616
622
  try:
617
- otherentries = [e for e in biblio.entries if e not in entries]
618
623
  entries = edit_entries(entries)
619
- entries = [{k:v for k,v in e.items() if v != ""} for e in entries]
620
- biblio.db.entries = otherentries
621
- for e in entries:
622
- biblio.insert_entry(e, **kw)
623
-
624
624
  except Exception as error:
625
625
  logger.error(str(error))
626
626
  return
627
627
 
628
+ entries = [{k:v for k,v in e.items() if v != ""} for e in entries]
629
+ biblio.db.entries = otherentries + entries
630
+
628
631
  savebib(biblio, config)
629
632
 
630
633
  if o.open:
@@ -888,7 +891,6 @@ def statuscmd(parser, o, config):
888
891
  print(config.status(check_files=not o.no_check_files, verbose=o.verbose))
889
892
 
890
893
 
891
-
892
894
  def get_parser(config=None):
893
895
  if config is None:
894
896
  config = papers.config.Config()
@@ -1022,8 +1024,15 @@ def get_parser(config=None):
1022
1024
  grp.add_argument('--ignore-errors', action='store_true',
1023
1025
  help='ignore errors when adding multiple files')
1024
1026
 
1025
- grp = addp.add_argument_group('pdf metadata')
1027
+ grp = addp.add_argument_group('metadata')
1026
1028
  grp.add_argument('--doi', help='provide DOI -- skip parsing PDF')
1029
+ grp.add_argument('--key', help='manually set the key')
1030
+ grp.add_argument('--type', help='document type')
1031
+ grp.add_argument('--title', help='manually set the title')
1032
+ grp.add_argument('--author', help='manually set the author')
1033
+ grp.add_argument('--journal', help='manually set the journal')
1034
+ grp.add_argument('--year', help='manually set the year')
1035
+ grp.add_argument('--metadata', nargs="+", metavar="KEY=VALUE", type=lambda meta: meta.split('=', 1), help='the metadata fields manually')
1027
1036
  grp.add_argument('--no-query-doi', action='store_true', help='do not attempt to parse and query doi')
1028
1037
  grp.add_argument('--no-query-fulltext', action='store_true', help='do not attempt to query fulltext in case doi query fails')
1029
1038
  grp.add_argument('--scholar', action='store_true', help='use google scholar instead of crossref')
@@ -1298,7 +1307,7 @@ class PapersExit(Exception):
1298
1307
  def main_clean_exit(args=None):
1299
1308
  try:
1300
1309
  main(args)
1301
- except PapersExit as error:
1310
+ except (PapersExit, DuplicateKeyError) as error:
1302
1311
  if logger.getEffectiveLevel() == logging.DEBUG:
1303
1312
  raise
1304
1313
  if error.args:
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.10'
16
- __version_tuple__ = version_tuple = (2, 10)
15
+ __version__ = version = '2.11'
16
+ __version_tuple__ = version_tuple = (2, 11)
@@ -279,9 +279,16 @@ class Biblio:
279
279
  return bisect.bisect_left(keys, self.key(entry))
280
280
 
281
281
 
282
- def insert_entry(self, entry, update_key=False, check_duplicate=False, rename=False, copy=False, **checkopt):
282
+ def insert_entry(self, entry, update_key=False, check_duplicate=False, rename=False, copy=False, metadata={}, **checkopt):
283
283
  """
284
284
  """
285
+ if metadata:
286
+ files = self.get_files(entry) + self.get_files(metadata)
287
+ self.set_files(metadata, files)
288
+
289
+ if update_key:
290
+ entry['ID'] = self.generate_key(entry)
291
+
285
292
  # additional checks on DOI etc...
286
293
  if check_duplicate:
287
294
  logger.debug('check duplicates : TRUE')
@@ -298,15 +305,16 @@ class Biblio:
298
305
  newkey = self.append_abc_to_key(entry) # add abc
299
306
  logger.info('update key: {} => {}'.format(entry['ID'], newkey))
300
307
  entry['ID'] = newkey
301
- print(format_entry(self, entry, prefix="Updated"))
302
308
 
303
309
  else:
304
- raise DuplicateKeyError('this error can be avoided if update_key is True')
310
+ print(format_entry(self, entry, prefix="New entry"))
311
+ print(format_entry(self, self.entries[i], prefix="Conflicts with"))
312
+ raise DuplicateKeyError('Key conflict. Try update_key is True?')
305
313
 
306
314
  else:
307
315
  logger.info('new entry: '+self.key(entry))
308
- print(format_entry(self, entry, prefix="Added"))
309
316
 
317
+ print(format_entry(self, entry, prefix="Added"))
310
318
  self.entries.insert(i, entry)
311
319
 
312
320
  if rename: self.rename_entry_files(entry, copy=copy)
@@ -413,8 +421,10 @@ class Biblio:
413
421
 
414
422
  def fetch_doi(self, doi, **kw):
415
423
  bibtex = fetch_bibtex_by_doi(doi)
424
+ kw["update_key"] = True # fetched key is always updated
416
425
  return self.add_bibtex(bibtex, **kw)
417
426
 
427
+
418
428
  def add_pdf(self, pdf, attachments=None, search_doi=True, search_fulltext=True, scholar=False, doi=None, **kw):
419
429
 
420
430
  if doi:
@@ -436,9 +446,8 @@ class Biblio:
436
446
  entry['ID'] = self.generate_key(entry)
437
447
  logger.debug('generated PDF key: '+entry['ID'])
438
448
 
439
- kw.pop('update_key', True)
440
- # logger.warning('fetched key is always updated when adding PDF to existing bib')
441
- return self.insert_entry(entry, update_key=True, **kw)
449
+ kw["update_key"] = False # already updated
450
+ return self.insert_entry(entry, **kw)
442
451
 
443
452
 
444
453
  def scan_dir_iter(self, direc, search_doi=True, search_fulltext=True, **kw):
@@ -341,7 +341,6 @@ def choose_entry_interactive(entries, extra=[], msg='', select=False, best=None)
341
341
  return _ask_pick_loop(entries, extra, select)
342
342
 
343
343
 
344
-
345
344
  def edit_entries(entries, diff=False, ndiff=False, editor=None):
346
345
  '''edit entries and insert result in database
347
346
  '''
@@ -367,12 +366,18 @@ def edit_entries(entries, diff=False, ndiff=False, editor=None):
367
366
 
368
367
  if res == 0:
369
368
  logger.info('sucessfully edited file, insert edited entries')
370
- db = bibtexparser.loads(open(filename).read())
371
- return db.entries
372
-
373
- raise ValueError('error when editing entries file: '+filename)
374
-
369
+ db_updated = bibtexparser.loads(open(filename).read())
370
+ keys_before = [e.get('ID') for e in entries]
371
+ keys_after = [e.get('ID') for e in db_updated.entries]
372
+ if set(keys_before) != set(keys_after):
373
+ print(f"{bcolors.WARNING}Keys edited ->", bcolors.OKBLUE ," ".join(sorted(set(keys_before).difference(keys_after))), bcolors.ENDC,
374
+ "=>", bcolors.OKBLUE,
375
+ " ".join(sorted(set(keys_after).difference(keys_before))), bcolors.ENDC) if keys_after else '(deleted)'
376
+ return db_updated.entries
375
377
 
378
+ else:
379
+ logger.info('failed to edit file')
380
+ return db
376
381
 
377
382
 
378
383
  def score(e):
@@ -221,7 +221,7 @@ def fetch_crossref_by_doi(doi):
221
221
  # @cached('crossref-bibtex.json')
222
222
  def fetch_bibtex_by_doi(doi):
223
223
  json_data = fetch_crossref_by_doi(doi)
224
- return crossref_to_bibtex(json_data)
224
+ return crossref_to_bibtex(json_data['message'])
225
225
 
226
226
  @cached('crossref-json.json')
227
227
  def fetch_json_by_doi(doi):
@@ -308,8 +308,7 @@ def format_authors(authors):
308
308
  return " and ".join(author_list)
309
309
 
310
310
 
311
- def crossref_to_bibtex(json_data):
312
- message = json_data['message']
311
+ def crossref_to_bibtex(message):
313
312
  entry_type = message.get('type', 'misc') # Default to 'misc' if type is not specified
314
313
 
315
314
  # Common fields
@@ -413,7 +412,6 @@ def fetch_bibtex_by_fulltext_crossref(txt, **kw):
413
412
  return crossref_to_bibtex(result).strip()
414
413
 
415
414
 
416
-
417
415
  def fetch_entry(e):
418
416
  if 'doi' in e and isvaliddoi(e['doi']):
419
417
  bibtex = fetch_bibtex_by_doi(e['doi'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.10
3
+ Version: 2.11
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
@@ -305,7 +305,7 @@ class TestAddConflict(BibTest):
305
305
  title = {Something else entirely}
306
306
  }"""
307
307
 
308
- bibtex_conflict_key_fixed = """@article{Perrette_2011b,
308
+ bibtex_conflict_key_fixed = """@article{perrette_author0000,
309
309
  author = {M. Perrette and Another author},
310
310
  doi = {10.5194/bg-8-515-2011XXX},
311
311
  title = {Something else entirely}
@@ -266,7 +266,7 @@ class TestAddResolveDuplicate(BibTest):
266
266
 
267
267
  def test_conflict_updated_from_original_but_originalkey(self):
268
268
 
269
- expected = """@article{Perrette_2011,
269
+ expected = """@article{fieldRareYear,
270
270
  author = {New Author Field},
271
271
  doi = {10.5194/bg-8-515-2011},
272
272
  journal = {ConflictJournal},
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