papers-cli 2.12.1__tar.gz → 2.14__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 (45) hide show
  1. papers_cli-2.14/.github/FUNDING.yml +2 -0
  2. {papers_cli-2.12.1 → papers_cli-2.14}/PKG-INFO +11 -9
  3. {papers_cli-2.12.1 → papers_cli-2.14}/README.md +10 -8
  4. {papers_cli-2.12.1 → papers_cli-2.14}/papers/__main__.py +71 -21
  5. {papers_cli-2.12.1 → papers_cli-2.14}/papers/_version.py +9 -4
  6. {papers_cli-2.12.1 → papers_cli-2.14}/papers/bib.py +71 -8
  7. {papers_cli-2.12.1 → papers_cli-2.14}/papers/config.py +1 -1
  8. {papers_cli-2.12.1 → papers_cli-2.14}/papers/encoding.py +0 -1
  9. {papers_cli-2.12.1 → papers_cli-2.14}/papers_cli.egg-info/PKG-INFO +11 -9
  10. {papers_cli-2.12.1 → papers_cli-2.14}/papers_cli.egg-info/SOURCES.txt +1 -0
  11. {papers_cli-2.12.1 → papers_cli-2.14}/.gitattributes +0 -0
  12. {papers_cli-2.12.1 → papers_cli-2.14}/.github/workflows/pypi.yml +0 -0
  13. {papers_cli-2.12.1 → papers_cli-2.14}/.github/workflows/tests.yml +0 -0
  14. {papers_cli-2.12.1 → papers_cli-2.14}/.gitignore +0 -0
  15. {papers_cli-2.12.1 → papers_cli-2.14}/LICENSE +0 -0
  16. {papers_cli-2.12.1 → papers_cli-2.14}/papers/__init__.py +0 -0
  17. {papers_cli-2.12.1 → papers_cli-2.14}/papers/duplicate.py +0 -0
  18. {papers_cli-2.12.1 → papers_cli-2.14}/papers/extract.py +0 -0
  19. {papers_cli-2.12.1 → papers_cli-2.14}/papers/filename.py +0 -0
  20. {papers_cli-2.12.1 → papers_cli-2.14}/papers/latexenc.py +0 -0
  21. {papers_cli-2.12.1 → papers_cli-2.14}/papers/utils.py +0 -0
  22. {papers_cli-2.12.1 → papers_cli-2.14}/papers_cli.egg-info/dependency_links.txt +0 -0
  23. {papers_cli-2.12.1 → papers_cli-2.14}/papers_cli.egg-info/entry_points.txt +0 -0
  24. {papers_cli-2.12.1 → papers_cli-2.14}/papers_cli.egg-info/requires.txt +0 -0
  25. {papers_cli-2.12.1 → papers_cli-2.14}/papers_cli.egg-info/top_level.txt +0 -0
  26. {papers_cli-2.12.1 → papers_cli-2.14}/pyproject.toml +0 -0
  27. {papers_cli-2.12.1 → papers_cli-2.14}/requirements.txt +0 -0
  28. {papers_cli-2.12.1 → papers_cli-2.14}/setup.cfg +0 -0
  29. {papers_cli-2.12.1 → papers_cli-2.14}/setup.py +0 -0
  30. {papers_cli-2.12.1 → papers_cli-2.14}/tests/__init__.py +0 -0
  31. {papers_cli-2.12.1 → papers_cli-2.14}/tests/common.py +0 -0
  32. {papers_cli-2.12.1 → papers_cli-2.14}/tests/config.py +0 -0
  33. {papers_cli-2.12.1 → papers_cli-2.14}/tests/download.py +0 -0
  34. {papers_cli-2.12.1 → papers_cli-2.14}/tests/downloadedpapers/.gitignore +0 -0
  35. {papers_cli-2.12.1 → papers_cli-2.14}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
  36. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_add.py +0 -0
  37. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_biblio.py +0 -0
  38. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_duplicates.py +0 -0
  39. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_encoding.py +0 -0
  40. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_extract.py +0 -0
  41. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_filecheck.py +0 -0
  42. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_install.py +0 -0
  43. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_list.py +0 -0
  44. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_misc.py +0 -0
  45. {papers_cli-2.12.1 → papers_cli-2.14}/tests/test_undo.py +0 -0
@@ -0,0 +1,2 @@
1
+ # These are supported funding model platforms
2
+ github: [perrette]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.12.1
3
+ Version: 2.14
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?branch=main)](https://github.com/perrette/papers/actions)
20
+ [![test](https://github.com/perrette/papers/workflows/CI/badge.svg?query=branch%3Amaster)](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)]()
@@ -306,6 +306,14 @@ That command is also convenient to check on what's actually tracked and what is
306
306
  # ...
307
307
  papers filecheck --rename --filesdir files
308
308
 
309
+ There is also a command specifically designed to clean up the zombie files and folders:
310
+
311
+ papers filecheck --clean-filesdir
312
+
313
+ That command will ask before removig anything, unless `--force` is passed. Currently
314
+ it ignores hidden files and folders, and will only consider folder that have a `.{folder}.bib` file inside,
315
+ which is the convention `papers` follows to store multiple attachments. That command works best
316
+ when the files are in their own folder, and not mixed up with other things, obviously.
309
317
 
310
318
  ### Setup git-tracked library (optional)
311
319
 
@@ -321,14 +329,8 @@ If `--git-lfs` is passed, the files will be backed-up along with the bibtex.
321
329
  Under the hood, bibtex and files (if applicable) are copied (hard-linked) to a back-up directory.
322
330
  Details are described in [issue 51](https://github.com/perrette/papers/issues/51).
323
331
 
324
- In local installs, backup occurs in `.papers/`. In global installs, defaults to `~/.local/.share/papers`.
325
- Type `papers status -v` to find out.
326
-
332
+ Backup occurs in a subfolder of `~/.local/.share/papers` regardless of the type of installation. Type `papers status -v` to find out.
327
333
  For local install that are already git-tracked, the feature remains useful as it is the basis for `papers undo` and `papers redo`.
328
- You might want to add `.papers` to your .gitignore to avoid messing up with your larger project.
329
-
330
- This feature is experimental.
331
-
332
334
 
333
335
  ### undo / redo
334
336
 
@@ -1,4 +1,4 @@
1
- [![test](https://github.com/perrette/papers/workflows/CI/badge.svg?branch=main)](https://github.com/perrette/papers/actions)
1
+ [![test](https://github.com/perrette/papers/workflows/CI/badge.svg?query=branch%3Amaster)](https://github.com/perrette/papers/actions)
2
2
  [![python](https://img.shields.io/badge/python-3.9-blue.svg)]()
3
3
  [![python](https://img.shields.io/badge/python-3.10-blue.svg)]()
4
4
  [![python](https://img.shields.io/badge/python-3.11-blue.svg)]()
@@ -287,6 +287,14 @@ That command is also convenient to check on what's actually tracked and what is
287
287
  # ...
288
288
  papers filecheck --rename --filesdir files
289
289
 
290
+ There is also a command specifically designed to clean up the zombie files and folders:
291
+
292
+ papers filecheck --clean-filesdir
293
+
294
+ That command will ask before removig anything, unless `--force` is passed. Currently
295
+ it ignores hidden files and folders, and will only consider folder that have a `.{folder}.bib` file inside,
296
+ which is the convention `papers` follows to store multiple attachments. That command works best
297
+ when the files are in their own folder, and not mixed up with other things, obviously.
290
298
 
291
299
  ### Setup git-tracked library (optional)
292
300
 
@@ -302,14 +310,8 @@ If `--git-lfs` is passed, the files will be backed-up along with the bibtex.
302
310
  Under the hood, bibtex and files (if applicable) are copied (hard-linked) to a back-up directory.
303
311
  Details are described in [issue 51](https://github.com/perrette/papers/issues/51).
304
312
 
305
- In local installs, backup occurs in `.papers/`. In global installs, defaults to `~/.local/.share/papers`.
306
- Type `papers status -v` to find out.
307
-
313
+ Backup occurs in a subfolder of `~/.local/.share/papers` regardless of the type of installation. Type `papers status -v` to find out.
308
314
  For local install that are already git-tracked, the feature remains useful as it is the basis for `papers undo` and `papers redo`.
309
- You might want to add `.papers` to your .gitignore to avoid messing up with your larger project.
310
-
311
- This feature is experimental.
312
-
313
315
 
314
316
  ### undo / redo
315
317
 
@@ -9,17 +9,16 @@ import subprocess as sp
9
9
  import shutil
10
10
  import itertools
11
11
  import fnmatch # unix-like match
12
- import hashlib
13
- import time
14
12
 
15
13
  import papers
16
14
  from papers import logger
17
15
  from papers.extract import extract_pdf_doi, isvaliddoi, extract_pdf_metadata
18
- from papers.extract import fetch_bibtex_by_doi
16
+ from papers.extract import fetch_bibtex_by_doi, fetch_bibtex_by_fulltext_crossref, fetch_bibtex_by_fulltext_scholar
19
17
  from papers.encoding import parse_file, format_file, family_names, format_entries, standard_name, format_entry, parse_keywords, format_key
20
18
  from papers.config import bcolors, Config, search_config, CONFIG_FILE, CONFIG_FILE_LOCAL, DATA_DIR, CONFIG_FILE_LEGACY, BACKUP_DIR
21
19
  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, DuplicateKeyError
20
+ from papers.bib import (Biblio, FUZZY_RATIO, DEFAULT_SIMILARITY, entry_filecheck,
21
+ backupfile as backupfile_func, isvalidkey, DuplicateKeyError, clean_filesdir)
23
22
  from papers.utils import move, checksum, view_pdf, open_folder
24
23
  from papers import __version__
25
24
 
@@ -89,6 +88,9 @@ def _backup_bib(biblio, config, message=None):
89
88
  biblio.rename_entries_files(copy=True, relative_to=backupdir, hardlink=True)
90
89
  biblio.save(config.backupfile_clean)
91
90
  config.gitcmd(f"add {config.backupfile_clean.name}")
91
+
92
+ # Remove unlink files
93
+ clean_filesdir(biblio, interactive=False, ignore_files=[config.backupfile, config.backupfile_clean])
92
94
  config.gitcmd(f"add files")
93
95
 
94
96
  else:
@@ -288,19 +290,41 @@ def installcmd(parser, o, config):
288
290
  """
289
291
  prompt = o.prompt and not o.edit
290
292
  # installed = config.file is not None
293
+ # WARNING if pre-existing config file
291
294
  if config.file is not None and prompt:
292
- while True:
293
- ans = input(f'An existing {"local" if config.local else "global"} install was found: {config.file}. Overwrite (O) or Edit (E) ? [o / e]')
294
- if ans.lower() in ('o', 'e'):
295
- break
296
- else:
297
- print('Use the --edit option to selectively edit existing configuration, or --force to ignore pre-existing configuration.')
298
- o.edit = ans.lower() == 'e'
295
+ if ((o.local and config.local) or ((not o.local and not config.local))):
296
+ while True:
297
+ ans = input(f'An existing {"local" if config.local else "global"} install was found: {config.file}. Overwrite (O) or Edit (E) ? [o / e]')
298
+ if ans.lower() in ('o', 'e'):
299
+ break
300
+ else:
301
+ print('Use the --edit option to selectively edit existing configuration, or --force to ignore pre-existing configuration.')
302
+ o.edit = ans.lower() == 'e'
303
+
304
+ elif not o.local and config.local:
305
+ print(f"A local configuration file was found that will have precedence over the global install: {config.file}. Delete (D) or Ignore (any other key) ?")
306
+ ans = input()
307
+ if ans.lower() == "d":
308
+ logger.info(f"Removing pre-existing local configuration file {config.file}")
309
+ os.remove(config.file)
310
+ config = Config()
311
+
312
+ elif o.local and not config.local:
313
+ logger.warning(f"A global configuration file was found, but the local install will have precedence: {config.file}. Delete the global configuration file (D) or Ignore (any other key) ?")
314
+ ans = input()
315
+ if ans.lower() == "d":
316
+ logger.info(f"Removing pre-existing global configuration file {config.file}")
317
+ os.remove(config.file)
318
+ config = Config()
319
+
320
+ else:
321
+ raise PapersExit("Unexpected configuration state")
322
+
299
323
 
300
324
  if not o.edit:
301
- if config.local and os.path.exists(config.file):
325
+ if config.file and os.path.exists(config.file) and ((o.local and config.local) or ((not o.local and not config.local))):
302
326
  # if we don't do that the local install will take precedence over global install
303
- logger.warning(f"remove pre-existing local configuration file: {config.file}")
327
+ logger.warning(f"remove pre-existing configuration file: {config.file}")
304
328
  os.remove(config.file)
305
329
  config = Config()
306
330
 
@@ -673,6 +697,10 @@ def filecheckcmd(parser, o, config):
673
697
  if o.rename:
674
698
  biblio.rename_entries_files(o.copy)
675
699
 
700
+ if o.clean_filesdir:
701
+ print("Check files directory for unlinked files")
702
+ clean_filesdir(biblio, interactive=not o.force, ignore_files=[config.bibtex])
703
+
676
704
  savebib(biblio, config)
677
705
 
678
706
  def redocmd(parser, o, config):
@@ -717,7 +745,20 @@ def doicmd(parser, o):
717
745
  print(extract_pdf_doi(o.pdf, image=o.image))
718
746
 
719
747
  def fetchcmd(parser, o):
720
- print(fetch_bibtex_by_doi(o.doi))
748
+ # either one or several DOIs
749
+ if all(isvaliddoi(field) for field in o.doi_or_text):
750
+ if o.scholar:
751
+ parser.error("Fetching from DOI does not support Google Scholar option")
752
+ for doi in o.doi_or_text:
753
+ print(fetch_bibtex_by_doi(doi))
754
+ return
755
+
756
+ # or one full text search
757
+ field = " ".join(o.doi_or_text)
758
+ if o.scholar:
759
+ print(fetch_bibtex_by_fulltext_scholar(field))
760
+ else:
761
+ print(fetch_bibtex_by_fulltext_crossref(field))
721
762
 
722
763
  def extractcmd(parser, o):
723
764
  print(extract_pdf_metadata(o.pdf, search_doi=not o.fulltext, search_fulltext=True, scholar=o.scholar, minwords=o.word_count, max_query_words=o.word_count, image=o.image))
@@ -1103,6 +1144,9 @@ def get_parser(config=None):
1103
1144
  filecheckp.add_argument('-d', '--delete-broken', action='store_true',
1104
1145
  help='remove file entry if the file link is broken')
1105
1146
 
1147
+ filecheckp.add_argument('--clean-filesdir', action='store_true',
1148
+ help='remove files in filesdir if not referred to in any entry')
1149
+
1106
1150
  filecheckp.add_argument('--fix-mendeley', action='store_true',
1107
1151
  help='fix a Mendeley bug where the leading "/" is omitted.')
1108
1152
 
@@ -1174,15 +1218,15 @@ def get_parser(config=None):
1174
1218
 
1175
1219
  # doi
1176
1220
  # ===
1177
- doip = subparsers.add_parser('doi', description='parse DOI from PDF')
1221
+ doip = subparsers.add_parser('doi', description='parse DOI from PDF', parents=[loggingp])
1178
1222
  doip.add_argument('pdf')
1179
1223
  doip.add_argument('--image', action='store_true', help='convert to image and use tesseract instead of pdftotext')
1180
1224
 
1181
1225
  # fetch
1182
1226
  # =====
1183
- fetchp = subparsers.add_parser('fetch', description='fetch bibtex from DOI')
1184
- fetchp.add_argument('doi')
1185
-
1227
+ fetchp = subparsers.add_parser('fetch', description='fetch bibtex from DOI or full-text', parents=[loggingp])
1228
+ fetchp.add_argument('doi_or_text', nargs='+', help='DOI or full text.')
1229
+ fetchp.add_argument('--scholar', action='store_true', help='use google scholar instead of default crossref for fulltext search')
1186
1230
 
1187
1231
  # extract
1188
1232
  # ========
@@ -1219,6 +1263,13 @@ def get_parser(config=None):
1219
1263
  # Main script
1220
1264
  #############
1221
1265
 
1266
+ def handle_logging(o):
1267
+ # verbosity
1268
+ if getattr(o,'logging_level',None):
1269
+ logger.setLevel(o.logging_level)
1270
+ logger.debug("LOGGER LEVEL: "+logging.getLevelName(logger.getEffectiveLevel()))
1271
+
1272
+
1222
1273
  def main(args=None):
1223
1274
  papers.config.DRYRUN = False # reset in case main() if called directly
1224
1275
  if args is not None:
@@ -1242,9 +1293,8 @@ def main(args=None):
1242
1293
  print(__version__)
1243
1294
  return
1244
1295
 
1245
- # verbosity
1246
- if getattr(o,'logging_level',None):
1247
- logger.setLevel(o.logging_level)
1296
+ handle_logging(o)
1297
+
1248
1298
  # modify disk state?
1249
1299
  if hasattr(o,'dry_run'):
1250
1300
  papers.config.DRYRUN = o.dry_run
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '2.12.1'
16
- __version_tuple__ = version_tuple = (2, 12, 1)
20
+ __version__ = version = '2.14'
21
+ __version_tuple__ = version_tuple = (2, 14)
@@ -1,13 +1,8 @@
1
1
  import os
2
2
  from pathlib import Path
3
- import logging
4
- # logger.basicConfig(level=logger.INFO)
5
- import argparse
6
- import subprocess as sp
7
3
  import shutil
8
4
  import bisect
9
- import itertools
10
- import fnmatch # unix-like match
5
+ from unidecode import unidecode as unicode_to_ascii
11
6
  import bibtexparser
12
7
  from bibtexparser.customization import convert_to_unicode
13
8
 
@@ -18,8 +13,8 @@ from papers.extract import extract_pdf_doi, isvaliddoi, parse_doi
18
13
  from papers.extract import extract_pdf_metadata
19
14
  from papers.extract import fetch_bibtex_by_fulltext_crossref, fetch_bibtex_by_doi
20
15
 
21
- from papers.encoding import unicode_to_latex, unicode_to_ascii
22
16
  from papers.encoding import parse_file, format_file, standard_name, family_names, format_entries, update_file_path, format_entry
17
+ from papers.latexenc import unicode_to_latex, latex_to_unicode
23
18
 
24
19
  from papers.filename import NAMEFORMAT, KEYFORMAT
25
20
  from papers.utils import bcolors, checksum, move as _move
@@ -309,7 +304,7 @@ class Biblio:
309
304
  else:
310
305
  print(format_entry(self, entry, prefix="New entry"))
311
306
  print(format_entry(self, self.entries[i], prefix="Conflicts with"))
312
- raise DuplicateKeyError('Key conflict. Try update_key is True?')
307
+ raise DuplicateKeyError('Key conflict. Try update_key is True or specify --key KEY explicitly')
313
308
 
314
309
  else:
315
310
  logger.info('new entry: '+self.key(entry))
@@ -821,3 +816,71 @@ def entry_filecheck(e, delete_broken=False, fix_mendeley=False,
821
816
  newfiles.append(file)
822
817
 
823
818
  e['file'] = format_file(newfiles, relative_to=relative_to)
819
+
820
+
821
+ def clean_filesdir(biblio, interactive=True, ignore_files=None, ignore_folders=None):
822
+ if biblio.filesdir is None:
823
+ raise ValueError('filesdir is not defined, cannot clean')
824
+ removed_files = []
825
+
826
+ allfiles = set(os.path.abspath(file) for e in biblio.entries for file in biblio.get_files(e))
827
+ if ignore_files:
828
+ for f in ignore_files:
829
+ allfiles.add(os.path.abspath(f))
830
+ allfolders = set(os.path.abspath(folder) for e in biblio.entries for folder in {os.path.dirname(file) for file in biblio.get_files(e)})
831
+
832
+ if ignore_folders is None:
833
+ ignore_folders = []
834
+
835
+ for root, direcs, files in os.walk(biblio.filesdir):
836
+
837
+ ANS = None # Y(es) or N(o) for all files in this folder (reset at each pass)
838
+ if root in ignore_folders:
839
+ continue
840
+ if '.git' in root.split(os.path.sep):
841
+ continue
842
+ for file in files:
843
+ path = os.path.abspath(os.path.join(root, file))
844
+ if file.startswith('.') or file.endswith('.bib'):
845
+ continue
846
+ if path not in allfiles:
847
+ if interactive:
848
+ if ANS is None:
849
+ ans = input(f"File: {os.path.relpath(path, biblio.filesdir)} not in library. Remove ? [Y/n/Y/N] ")
850
+ else:
851
+ ans = ANS
852
+ if ans in ['Y', 'N']:
853
+ ans = ANS = ans.lower() # same reply for all files in this folder
854
+ if ans.lower() == 'y':
855
+ os.remove(path)
856
+ removed_files.append(path)
857
+ else:
858
+ logger.info(f"Remove unlinked file {os.path.relpath(path, biblio.filesdir)}.")
859
+ os.remove(path)
860
+ removed_files.append(path)
861
+
862
+ for direc in direcs:
863
+ if direc.startswith('.'):
864
+ continue
865
+ if direc in ignore_folders:
866
+ continue
867
+
868
+ # Check multifile entries
869
+ bibtex = os.path.join(root, direc, '.'+direc+'.bib')
870
+ if not os.path.exists(bibtex):
871
+ continue
872
+
873
+ direcpath = os.path.abspath(os.path.join(root, direc))
874
+
875
+ if direcpath not in allfolders:
876
+ if interactive:
877
+ ans = input(f"Folder: {os.path.relpath(direcpath, root)} not in library. Remove ? [Y/n] ")
878
+ if ans.lower() == 'y':
879
+ shutil.rmtree(os.path.join(root, direcpath))
880
+ else:
881
+ logger.info(f"Remove folder {direc}.")
882
+ shutil.rmtree(os.path.join(root, direcpath))
883
+
884
+ break
885
+
886
+ return removed_files
@@ -85,7 +85,7 @@ class Config:
85
85
  return Path(os.path.sep)
86
86
 
87
87
  def gitcmd(self, cmd, check=True, **kw):
88
- logger.debug(f"git add {self.backupfile.name}")
88
+ logger.debug(f"git {cmd} -C {self.gitdir}")
89
89
  try:
90
90
  sp.run(
91
91
  f"git {cmd}",
@@ -2,7 +2,6 @@ import os
2
2
  import bibtexparser
3
3
  from pathlib import Path
4
4
  from unidecode import unidecode as unicode_to_ascii
5
- from papers.latexenc import unicode_to_latex
6
5
  from papers.utils import ansi_link as link, bcolors
7
6
  from papers import logger
8
7
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: papers-cli
3
- Version: 2.12.1
3
+ Version: 2.14
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?branch=main)](https://github.com/perrette/papers/actions)
20
+ [![test](https://github.com/perrette/papers/workflows/CI/badge.svg?query=branch%3Amaster)](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)]()
@@ -306,6 +306,14 @@ That command is also convenient to check on what's actually tracked and what is
306
306
  # ...
307
307
  papers filecheck --rename --filesdir files
308
308
 
309
+ There is also a command specifically designed to clean up the zombie files and folders:
310
+
311
+ papers filecheck --clean-filesdir
312
+
313
+ That command will ask before removig anything, unless `--force` is passed. Currently
314
+ it ignores hidden files and folders, and will only consider folder that have a `.{folder}.bib` file inside,
315
+ which is the convention `papers` follows to store multiple attachments. That command works best
316
+ when the files are in their own folder, and not mixed up with other things, obviously.
309
317
 
310
318
  ### Setup git-tracked library (optional)
311
319
 
@@ -321,14 +329,8 @@ If `--git-lfs` is passed, the files will be backed-up along with the bibtex.
321
329
  Under the hood, bibtex and files (if applicable) are copied (hard-linked) to a back-up directory.
322
330
  Details are described in [issue 51](https://github.com/perrette/papers/issues/51).
323
331
 
324
- In local installs, backup occurs in `.papers/`. In global installs, defaults to `~/.local/.share/papers`.
325
- Type `papers status -v` to find out.
326
-
332
+ Backup occurs in a subfolder of `~/.local/.share/papers` regardless of the type of installation. Type `papers status -v` to find out.
327
333
  For local install that are already git-tracked, the feature remains useful as it is the basis for `papers undo` and `papers redo`.
328
- You might want to add `.papers` to your .gitignore to avoid messing up with your larger project.
329
-
330
- This feature is experimental.
331
-
332
334
 
333
335
  ### undo / redo
334
336
 
@@ -5,6 +5,7 @@ README.md
5
5
  pyproject.toml
6
6
  requirements.txt
7
7
  setup.py
8
+ .github/FUNDING.yml
8
9
  .github/workflows/pypi.yml
9
10
  .github/workflows/tests.yml
10
11
  papers/__init__.py
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