papers-cli 2.7.2__py3-none-any.whl → 2.9__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.
papers/__main__.py CHANGED
@@ -16,11 +16,11 @@ import papers
16
16
  from papers import logger
17
17
  from papers.extract import extract_pdf_doi, isvaliddoi, extract_pdf_metadata
18
18
  from papers.extract import fetch_bibtex_by_doi
19
- from papers.encoding import parse_file, format_file, family_names, format_entries, standard_name
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
22
  from papers.bib import Biblio, FUZZY_RATIO, DEFAULT_SIMILARITY, entry_filecheck, backupfile as backupfile_func, isvalidkey
23
- from papers.utils import move, checksum, ansi_link as link, view_pdf
23
+ from papers.utils import move, checksum, view_pdf, open_folder
24
24
  from papers import __version__
25
25
 
26
26
 
@@ -233,7 +233,27 @@ def savebib(biblio, config):
233
233
  # config.gitcommit()
234
234
 
235
235
 
236
+
237
+ def is_subdirectory(parent, child):
238
+ # Resolve paths to absolute paths
239
+ parent = Path(parent).resolve()
240
+ child = Path(child).resolve()
241
+
242
+ # Check if the child path is relative to the parent path
243
+ return parent in child.parents
244
+
245
+
236
246
  def view_entry_files(biblio, entry):
247
+ files = biblio.get_files(entry)
248
+
249
+ # in case several attachments are present and the files are ordered under filesdir,
250
+ # open the subfolder instead of the single files
251
+ if len(files) > 1:
252
+ parent = os.path.commonpath(files)
253
+ if is_subdirectory(biblio.filesdir, parent):
254
+ open_folder(parent)
255
+ return
256
+
237
257
  for f in biblio.get_files(entry):
238
258
  logger.info(f"opening {f} ...")
239
259
  view_pdf(f)
@@ -801,15 +821,6 @@ def listcmd(parser, o, config):
801
821
  eq = lambda a, b: a['ID'] == b['ID'] or are_duplicates(a, b, similarity="PARTIAL", fuzzy_ratio=o.fuzzy_ratio)
802
822
  entries = list_dup(entries, eq=eq)
803
823
 
804
- if o.no_key:
805
- key = lambda e: ''
806
- else:
807
- # key = lambda e: bcolors.OKBLUE+e['ID']+filetag(e)+':'+bcolors.ENDC
808
- key = lambda e: _nfiles(e)*(bcolors.BOLD)+bcolors.OKBLUE+e['ID']+':'+bcolors.ENDC
809
-
810
- def parse_keywords(e):
811
- return [w.strip() for w in e.get('keywords', '').split(',') if w.strip()]
812
-
813
824
  if o.add_keywords:
814
825
  for e in entries:
815
826
  keywords = parse_keywords(e)
@@ -861,27 +872,13 @@ def listcmd(parser, o, config):
861
872
  elif o.field:
862
873
  # entries = [{k:e[k] for k in e if k in o.field+['ID','ENTRYTYPE']} for e in entries]
863
874
  for e in entries:
864
- print(key(e),*[e.get(k, "") for k in o.field])
875
+ print(format_key(e, no_key=o.no_key),*[e.get(k, "") for k in o.field])
865
876
  elif o.key_only:
866
877
  for e in entries:
867
878
  print(e['ID'])
868
879
  elif o.one_liner:
869
880
  for e in entries:
870
- tit = e.get('title', '')[:60]+ ('...' if len(e.get('title', ''))>60 else '')
871
- info = []
872
- if e.get('doi',''):
873
- info.append(link(f"https://doi.org/{e['doi']}", 'doi:'+e['doi']))
874
- n = _nfiles(e)
875
- if n:
876
- files = parse_file(e.get('file',''), relative_to=biblio.relative_to)
877
- file_link = f"file:///{Path(files[0]).resolve()}"
878
- ansi_link = link(file_link, f'{"file" if n == 1 else "files"}:{str(n)}')
879
- info.append(bcolors.OKGREEN+ansi_link+bcolors.ENDC)
880
- if e.get('keywords',''):
881
- keywords = parse_keywords(e)
882
- info.append(bcolors.WARNING+" | ".join(keywords)+bcolors.ENDC)
883
- infotag = '('+', '.join(info)+')' if info else ''
884
- print(key(e), tit, infotag)
881
+ print(format_entry(biblio, e, no_key=o.no_key))
885
882
 
886
883
  else:
887
884
  print(format_entries(entries))
@@ -1147,6 +1144,7 @@ def get_parser(config=None):
1147
1144
 
1148
1145
  grp = listp.add_argument_group('formatting')
1149
1146
  mgrp = grp.add_mutually_exclusive_group()
1147
+ mgrp.add_argument('--plain', action='store_false', dest="one_liner", help='print in bibtex format')
1150
1148
  mgrp.add_argument('-l', '-1', '--one-liner', action='store_true', help='one liner')
1151
1149
  mgrp.add_argument('--key-only', action='store_true')
1152
1150
  mgrp.add_argument('-f', '--field', nargs='+', help='specific field(s) only')
papers/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.7.2'
16
- __version_tuple__ = version_tuple = (2, 7, 2)
15
+ __version__ = version = '2.9'
16
+ __version_tuple__ = version_tuple = (2, 9)
papers/bib.py CHANGED
@@ -19,7 +19,7 @@ from papers.extract import extract_pdf_metadata
19
19
  from papers.extract import fetch_bibtex_by_fulltext_crossref, fetch_bibtex_by_doi
20
20
 
21
21
  from papers.encoding import unicode_to_latex, unicode_to_ascii
22
- from papers.encoding import parse_file, format_file, standard_name, family_names, format_entries, update_file_path
22
+ from papers.encoding import parse_file, format_file, standard_name, family_names, format_entries, update_file_path, format_entry
23
23
 
24
24
  from papers.filename import NAMEFORMAT, KEYFORMAT
25
25
  from papers.utils import bcolors, checksum, move as _move
@@ -298,14 +298,14 @@ class Biblio:
298
298
  newkey = self.append_abc_to_key(entry) # add abc
299
299
  logger.info('update key: {} => {}'.format(entry['ID'], newkey))
300
300
  entry['ID'] = newkey
301
- print("Updated entry:", self.key(entry))
301
+ print(format_entry(self, entry, prefix="Updated"))
302
302
 
303
303
  else:
304
304
  raise DuplicateKeyError('this error can be avoided if update_key is True')
305
305
 
306
306
  else:
307
307
  logger.info('new entry: '+self.key(entry))
308
- print("New entry:", self.key(entry))
308
+ print(format_entry(self, entry, prefix="Added"))
309
309
 
310
310
  self.entries.insert(i, entry)
311
311
 
@@ -335,7 +335,7 @@ class Biblio:
335
335
  if entry == candidate:
336
336
  logger.debug('exact duplicate')
337
337
  if rename: self.rename_entry_files(candidate, copy=copy)
338
- print("Existing entry:", self.key(entry))
338
+ print(format_entry(self, entry, prefix="Existing"))
339
339
  return [ entry ] # do nothing
340
340
 
341
341
  if update_key and entry['ID'] != candidate['ID']:
@@ -354,7 +354,7 @@ class Biblio:
354
354
  logger.debug('fixed: exact duplicate')
355
355
  entry = candidate
356
356
  if rename: self.rename_entry_files(candidate, copy=copy)
357
- print("Existing entry:", self.key(entry))
357
+ print(format_entry(self, entry, prefix="Existing"))
358
358
  return [ entry ] # do nothing
359
359
 
360
360
  logger.debug('conflict resolution: '+on_conflict)
papers/encoding.py CHANGED
@@ -1,7 +1,9 @@
1
1
  import os
2
2
  import bibtexparser
3
+ from pathlib import Path
3
4
  from unidecode import unidecode as unicode_to_ascii
4
5
  from papers.latexenc import unicode_to_latex
6
+ from papers.utils import ansi_link as link, bcolors
5
7
  from papers import logger
6
8
 
7
9
  # fix bibtexparser call on empty strings
@@ -89,6 +91,45 @@ def format_entries(entries):
89
91
  db.entries.extend(entries)
90
92
  return bibtexparser.dumps(db)
91
93
 
94
+ def parse_keywords(e):
95
+ return [w.strip() for w in e.get('keywords', '').split(',') if w.strip()]
96
+
97
+ def format_key(e, no_key=False):
98
+ if no_key:
99
+ key = lambda e: ''
100
+ else:
101
+ n = len(parse_file(e.get('file','')))
102
+ key = lambda e: n*(bcolors.BOLD)+bcolors.OKBLUE+e['ID']+':'+bcolors.ENDC
103
+ return key(e)
104
+
105
+ def format_entry(biblio, e, no_key=False, prefix=""):
106
+ """One-liner formatter
107
+ """
108
+ tit = e.get('title', '')[:60]+ ('...' if len(e.get('title', ''))>60 else '')
109
+ info = []
110
+ if e.get('doi',''):
111
+ info.append(link(f"https://doi.org/{e['doi']}", 'doi:'+e['doi']))
112
+
113
+ files = parse_file(e.get('file',''), relative_to=biblio.relative_to)
114
+ n = len(files)
115
+
116
+ if n:
117
+ file_link = f"file:///{Path(files[0]).resolve()}" if n == 1 else f"file:///{Path(os.path.commonpath(files)).resolve()}"
118
+ ansi_link = link(file_link, f'{"file" if n == 1 else "files"}:{str(n)}')
119
+ info.append(bcolors.OKGREEN+ansi_link+bcolors.ENDC)
120
+
121
+ if e.get('keywords',''):
122
+ keywords = parse_keywords(e)
123
+ info.append(bcolors.WARNING+" | ".join(keywords)+bcolors.ENDC)
124
+
125
+ infotag = '('+', '.join(info)+')' if info else ''
126
+ if prefix:
127
+ prefixtag = f"{bcolors.WARNING}{prefix} -> {bcolors.ENDC}"
128
+ else:
129
+ prefixtag = ""
130
+ return f"{prefixtag}{format_key(e, no_key=no_key)} {tit} {infotag}"
131
+
132
+
92
133
  # Parse name entry
93
134
  # ================
94
135
 
papers/utils.py CHANGED
@@ -2,7 +2,7 @@ import os
2
2
  import re
3
3
  import shutil
4
4
  import hashlib
5
-
5
+ import subprocess, platform
6
6
  from contextlib import contextmanager
7
7
  from pathlib import Path
8
8
 
@@ -191,10 +191,22 @@ def view_pdf(filepath):
191
191
  """
192
192
  https://stackoverflow.com/questions/434597/open-document-with-default-os-application-in-python-both-in-windows-and-mac-os
193
193
  """
194
- import subprocess, os, platform
195
194
  if platform.system() == 'Darwin': # macOS
196
195
  subprocess.call(('open', filepath))
197
196
  elif platform.system() == 'Windows': # Windows
198
197
  os.startfile(filepath)
199
198
  else: # linux variants
200
- subprocess.call(('xdg-open', filepath))
199
+ subprocess.call(('xdg-open', filepath))
200
+
201
+
202
+ def open_folder(path):
203
+ system_platform = platform.system()
204
+
205
+ if system_platform == "Windows":
206
+ os.startfile(path)
207
+ elif system_platform == "Darwin": # macOS
208
+ os.system(f'open "{path}"')
209
+ elif system_platform == "Linux":
210
+ os.system(f'xdg-open "{path}"')
211
+ else:
212
+ print("Unsupported operating system.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.7.2
3
+ Version: 2.9
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
@@ -17,7 +17,7 @@ Requires-Dist: scholarly
17
17
  Requires-Dist: rapidfuzz
18
18
  Requires-Dist: normality
19
19
 
20
- [![test](https://github.com/perrette/papers/workflows/CI/badge.svg)](https://github.com/perrette/papers/actions)
20
+ [![test](https://github.com/perrette/papers/workflows/CI/badge.svg?branch=main)](https://github.com/perrette/papers/actions)
21
21
  [![python](https://img.shields.io/badge/python-3.9-blue.svg)]()
22
22
  [![python](https://img.shields.io/badge/python-3.10-blue.svg)]()
23
23
  [![python](https://img.shields.io/badge/python-3.11-blue.svg)]()
@@ -121,25 +121,25 @@ Some old files don't have a DOI. Best is to add the entry from its bibtex:
121
121
 
122
122
  ### List entries (and edit etc...)
123
123
 
124
- Pretty listing (-1 or -l for one-liner listing, otherwise plain bibtex):
124
+ Pretty listing by default (otherwise pass --plain for plain bibtex):
125
125
 
126
- $> papers list -1
126
+ $> papers list
127
127
  Perrette2013: A scaling approach to project regional sea level rise and it... (doi:10.5194/esd-4-11-2013, file:1)
128
128
 
129
129
  Search with any number of keywords:
130
130
 
131
- $> papers list perrette scaling approach sea level -1
131
+ $> papers list perrette scaling approach sea level
132
132
  ... (short list)
133
- $> papers list perrette scaling approach sea level --any -1
133
+ $> papers list perrette scaling approach sea level --any
134
134
  ... (long list)
135
- $> papers list --key perrette2013 --author perrette --year 2013 --title scaling approach sea level -1
135
+ $> papers list --key perrette2013 --author perrette --year 2013 --title scaling approach sea level
136
136
  ... (precise list)
137
137
 
138
138
  Add tags to view papers by topic:
139
139
 
140
- $> papers list perrette2013 --add-tag sea-level projections -1
140
+ $> papers list perrette2013 --add-tag sea-level projections
141
141
  ...
142
- $> papers list --tag sea-level projections -1
142
+ $> papers list --tag sea-level projections
143
143
  Perrette2013: A scaling approach to project regional sea level rise and it... (doi:10.5194/esd-4-11-2013, file:1, sea-level | projections )
144
144
 
145
145
  `papers list` is a powerful command, inspired from unix's `find` and `grep`.
@@ -0,0 +1,17 @@
1
+ papers/__init__.py,sha256=gEfqa4nhhKYjbk9g07WpJuzU5iAeEHFRGUSvA4VXm8k,108
2
+ papers/__main__.py,sha256=3Ly_JNajRA3H4FPx6JDdyPgmWn930EBO7mzkqKSfjBE,56936
3
+ papers/_version.py,sha256=kerpau_VRaLumk1H9Bkb0gPPzn5FlTh1lEgD7R2rfnk,406
4
+ papers/bib.py,sha256=JSPzIlahE3pqyxbdpxMGvOZTWQYRF9SSDaifIfmbS9I,29817
5
+ papers/config.py,sha256=ufLMp0rBP62bEtesh1s7DZ3FsY4v9Ys669JBua4K_HU,9761
6
+ papers/duplicate.py,sha256=AgGh3QHgE1ZqqqAm8_j13Ao3T-1K-Qe6O8tC4Cyw4DQ,18723
7
+ papers/encoding.py,sha256=5I6v2rK0XQN_xqzpv9dHmRTqObRdyyoWLY2DAlS1ElM,5376
8
+ papers/extract.py,sha256=-RJ8N_AMimjL3jp91txpL7E2vUhEAO-QlT-XssxjqAM,11757
9
+ papers/filename.py,sha256=1uuVnbNkpRogALvDnsy2THfxAuQ2kTH2vlgpfEzsKoI,4744
10
+ papers/latexenc.py,sha256=qH03t6yVUnGZvkZT2-9OWXU-iHqZvVthhQopaM281D4,98127
11
+ papers/utils.py,sha256=DYFufF2YbcBZAIl9uoqtTB-yHzH4A467xC9kuxoMAFE,5502
12
+ papers_cli-2.9.dist-info/LICENSE,sha256=Uc7FclCyMqKU9cc366vVpOLy9gU2w5btUJX9EDzQVIw,1071
13
+ papers_cli-2.9.dist-info/METADATA,sha256=wkg_oIB9KLopnp_ypppm-u6EzTaJJww7se2IxEJOYQ4,18313
14
+ papers_cli-2.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
15
+ papers_cli-2.9.dist-info/entry_points.txt,sha256=E1H8yJ7ieFOJT8rLGPQOQB7I1KbqTrcjze3FPfgk8uI,59
16
+ papers_cli-2.9.dist-info/top_level.txt,sha256=sLdcLzjapCbtCY-yXhtEHZEniJB7I35rxRLLLURc8mo,7
17
+ papers_cli-2.9.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- papers/__init__.py,sha256=gEfqa4nhhKYjbk9g07WpJuzU5iAeEHFRGUSvA4VXm8k,108
2
- papers/__main__.py,sha256=Ua07_Qb57lce0jOYp3jrKH7kPGM8QvVUiwtIkFOFxAc,57292
3
- papers/_version.py,sha256=FBqtgBb5hG2kHWDiVQT5o8_jJfQmv2FROzH4xAlajxU,411
4
- papers/bib.py,sha256=SFXwNnF0KR71TbtHymPqQ9rrugjjSCeQfAH_HH0MYF4,29761
5
- papers/config.py,sha256=ufLMp0rBP62bEtesh1s7DZ3FsY4v9Ys669JBua4K_HU,9761
6
- papers/duplicate.py,sha256=AgGh3QHgE1ZqqqAm8_j13Ao3T-1K-Qe6O8tC4Cyw4DQ,18723
7
- papers/encoding.py,sha256=S-36AltnOCTCrJ-eSQWQYR4T095KRnPwJQB_15M7Ai0,3929
8
- papers/extract.py,sha256=-RJ8N_AMimjL3jp91txpL7E2vUhEAO-QlT-XssxjqAM,11757
9
- papers/filename.py,sha256=1uuVnbNkpRogALvDnsy2THfxAuQ2kTH2vlgpfEzsKoI,4744
10
- papers/latexenc.py,sha256=qH03t6yVUnGZvkZT2-9OWXU-iHqZvVthhQopaM281D4,98127
11
- papers/utils.py,sha256=Hql6Rlnd4SKb8CcumTx0Vw41kUUeJuGfo8UTrOt98PQ,5164
12
- papers_cli-2.7.2.dist-info/LICENSE,sha256=Uc7FclCyMqKU9cc366vVpOLy9gU2w5btUJX9EDzQVIw,1071
13
- papers_cli-2.7.2.dist-info/METADATA,sha256=vQv1NB70X2zg35kellxT9JM1-ZOCNySDEkNXMoUiVds,18325
14
- papers_cli-2.7.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
15
- papers_cli-2.7.2.dist-info/entry_points.txt,sha256=E1H8yJ7ieFOJT8rLGPQOQB7I1KbqTrcjze3FPfgk8uI,59
16
- papers_cli-2.7.2.dist-info/top_level.txt,sha256=sLdcLzjapCbtCY-yXhtEHZEniJB7I35rxRLLLURc8mo,7
17
- papers_cli-2.7.2.dist-info/RECORD,,