From fbb4c8b74dbc50318da933d7f7cb7fa9eb7d9f2d Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Wed, 23 Jun 2021 11:00:49 +0200 Subject: Fix fzf documentation --- doc/source/default-settings.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/source/default-settings.rst b/doc/source/default-settings.rst index e09afffa..601f99fb 100644 --- a/doc/source/default-settings.rst +++ b/doc/source/default-settings.rst @@ -655,7 +655,7 @@ The following configuration fzf-extra-flags = ["--ansi", "--multi", "-i", "--preview", "echo {} | sed -r 's/~~/\\n/g; /^ *$/d' ", - "--preview-window", "bottom:wrap:20%", + "--preview-window", "bottom:wrap:20%%", "--color", "preview-fg:#F6E6E4,preview-bg:#5B6D5B"] fzf-extra-bindings = ["ctrl-s:jump", @@ -666,6 +666,9 @@ The following configuration will have unrestricted titles, author, journal etc fields against which the query will match and it will show in the ``fzf`` preview window a tidy description of the currently selected field by replacing the token ``~~`` by a newline. You can try this out and play with ``fzf`` customizations. +Please note that ``bottom:wrap:20%%`` has two ``%`` since the config file +interpolator uses ``%`` as a reserved symbol, so it must be escaped +by writing two of them. Other ----- -- cgit v1.2.3 From 99e5e78f91d85f26bdb2b29d63b0c874b59d83f7 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Sun, 4 Jul 2021 14:44:56 +0200 Subject: Fix faq link for gihtub --- doc/source/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/faq.rst b/doc/source/faq.rst index 50967143..8e98ec3e 100644 --- a/doc/source/faq.rst +++ b/doc/source/faq.rst @@ -16,4 +16,4 @@ Here are some problems that users have come across often: For more information you can also check the -`github faq `_ link. +`github faq `_ link. -- cgit v1.2.3 From f9064196caf3344ccbf27fb4c0ea01d1f5c1ab01 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Wed, 22 Sep 2021 23:13:58 +0200 Subject: Fix issue #338 --- AUTHORS | 16 +++++++++++++--- papis/commands/bibtex.py | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 24fdd2e8..776d5a4e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,25 +1,35 @@ -1933 Alejandro Gallo +2018 Alejandro Gallo 38 Nicolò Balzarotti 16 michaelplews 13 Jackson Woodruff +10 Alex Fikl 10 Matthieu Coudron 10 Michael R. Plews 9 Matthieu Coudron 9 alejandrogallo -6 Alexander Von Moll +7 Alexander Von Moll 6 Alexandru Fikl -5 Alex Fikl 2 JP-Ellis 2 Katrin Leinweber <9948149+katrinleinweber@users.noreply.github.com> 2 Michael Plews +2 sebastien.popoff@espci.fr 1 Andrew +1 Andrey Akinshin 1 CosmosAtlas 1 Dennis Bruggner 1 Felix Hummel 1 Fritzkefit +1 Henning Timm +1 Henrik Grimler +1 Michael McMahon 1 Oliver Lantwin +1 Sébastien M. P 1 Theo Tsatsoulis 1 Theodoros Tsatsoulis 1 Theodoros Tsatsoulis 1 Zach Langley +1 ccat3z +1 d70-t +1 jghauser 1 mftrhu +1 prataffel <24615963+prataffel@users.noreply.github.com> diff --git a/papis/commands/bibtex.py b/papis/commands/bibtex.py index 7ec7db10..60781a95 100644 --- a/papis/commands/bibtex.py +++ b/papis/commands/bibtex.py @@ -67,8 +67,8 @@ And use like such: |asciicast| Cli ^^^ -.. click:: papis.commands.add:cli - :prog: papis add +.. click:: papis.commands.bibtex:cli + :prog: papis bibtex """ import os -- cgit v1.2.3 From 9b96433e4a65530fed3a35e950a20dc7f5f01b08 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Wed, 22 Sep 2021 23:51:25 +0200 Subject: Address issue #339 --- papis/commands/bibtex.py | 68 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/papis/commands/bibtex.py b/papis/commands/bibtex.py index 60781a95..a0ec003c 100644 --- a/papis/commands/bibtex.py +++ b/papis/commands/bibtex.py @@ -6,12 +6,6 @@ This command helps to interact with `bib` files in your LaTeX projects. Examples ^^^^^^^^ -:: - - papis bibtex \ - read new_papers.bib \ # Read bib file - cmd 'papis add --from-doi {doc[doi]}' # For every entry run the command - I use it for opening some papers for instance :: @@ -37,7 +31,65 @@ or if I update some information in my papis ``yaml`` files then I can do update -f \ # Update what has been read from papis library save new_papers.bib # save everything to new_papers.bib, overwriting -Maybe this is also interesting for you guys! +Local configuration file +^^^^^^^^^^^^^^^^^^^^^^^^ + +If you are working in a local folder where you have +a bib file called ``main.bib``, you'll grow sick and tired +of writing always ``read main.bib`` and ``save main.bib``, so you can +write a local configuration file ``.papis.config`` for ``papis bibtex`` +to read and write automatically + +:: + + [bibtex] + default-read-bibfile = main.bib + default-save-bibfile = main.bib + auto-read = True + +with this setup, you can just do + +:: + + papis bibtex add einstein save + +Check references quality +^^^^^^^^^^^^^^^^^^^^^^^^ + +When you're collaborating with someone, you might come accross malformed +or incomplete references. Most journals want to have all the ``doi``s +and urls available. You can automate this diagnostic with + +For this you kan use the command ``doctor`` + +:: + + papis bibtex read mybib.bib doctor + +Mostly I want to have only the references in my project's bib file +that are actually cited in the latex file, you can check +which references are not cited in the tex files by doing + + +:: + + papis bibtex iscited -f main.tex -f chapter-2.tex + +and you can then filter them out using the command ``filter-cited``. + +To monitor the health of the bib project's file, I mostly have a +target in the project's ``Makefile`` like + +.. code:: make + + .PHONY: check-bib + check-bib: + papis bibtex iscited -f main.tex doctor + +it does not solve all problems under the sun, but it is really better than no +check! + + Vim integration ^^^^^^^^^^^^^^^ @@ -360,7 +412,7 @@ def _unique(ctx: click.Context, key: str, o: Optional[str]) -> None: def _doctor(ctx: click.Context, key: List[str]) -> None: """ Check bibfile for correctness, missing keys etc. - e.g. papis bibtex -k title -k url -k doi + e.g. papis bibtex doctor -k title -k url -k doi """ logger.info("Checking for existence of %s", ", ".join(key)) -- cgit v1.2.3 From 13e26d3ca6f8aff637cc370aa5542c4f27d51a67 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Sun, 3 Oct 2021 22:23:32 +0200 Subject: Turn on mark capabilities for the papis picker related to suggestion in issue #340 --- CHANGELOG.md | 3 +++ doc/source/default-settings.rst | 3 +++ papis/tui/__init__.py | 1 + papis/tui/app.py | 4 ++++ papis/tui/widgets/list.py | 2 ++ 5 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af5132f0..33520e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ use emacs-like hooks for some commands. - Add command `exec` to run python scripts in the environment of the papis executable. +## papis picker +- You can now pick several elements with the key binding `c-t` + VERSION v0.11 ============= diff --git a/doc/source/default-settings.rst b/doc/source/default-settings.rst index 601f99fb..15c3a103 100644 --- a/doc/source/default-settings.rst +++ b/doc/source/default-settings.rst @@ -583,6 +583,9 @@ or inside the library sections prepending a ``tui-``, .. papis-config:: go_bottom_key :section: tui +.. papis-config:: mark_key + :section: tui + FZF integration --------------- diff --git a/papis/tui/__init__.py b/papis/tui/__init__.py index dfd71b50..8b7fcc78 100644 --- a/papis/tui/__init__.py +++ b/papis/tui/__init__.py @@ -28,6 +28,7 @@ def get_default_settings() -> PapisConfigType: 'show_info_key': 's-tab', 'go_top_key': 'home', 'go_bottom_key': 'end', + 'mark_key': 'c-t', "editmode": "emacs", }) diff --git a/papis/tui/app.py b/papis/tui/app.py index a0f7f6e9..86c2cfa4 100644 --- a/papis/tui/app.py +++ b/papis/tui/app.py @@ -91,6 +91,10 @@ def get_keys_info() -> Dict[str, KeyInfo]: 'key': config.getstring('go_bottom_key', section='tui'), 'help': 'Go to the bottom of the list', }, + "mark_key": { + 'key': config.getstring('mark_key', section='tui'), + 'help': 'Mark current item to be selected', + }, } return _KEYS_INFO diff --git a/papis/tui/widgets/list.py b/papis/tui/widgets/list.py index 07b8b46c..fb67525a 100644 --- a/papis/tui/widgets/list.py +++ b/papis/tui/widgets/list.py @@ -247,6 +247,8 @@ class OptionsList(ConditionalContainer, Generic[Option]): # type: ignore def get_selection(self) -> Sequence[Option]: """Get the selected item, if there is Any""" + if len(self.marks): + return [self.get_options()[m] for m in self.marks] if self.indices and self.current_index is not None: return [self.get_options()[self.current_index]] return [] -- cgit v1.2.3 From 8c981d2edbe9473b21a34de51ff5e42bd76bc187 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Sun, 3 Oct 2021 23:38:36 +0200 Subject: Add more options to tui.select_range --- papis/tui/utils.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/papis/tui/utils.py b/papis/tui/utils.py index f86c4d60..629977fd 100644 --- a/papis/tui/utils.py +++ b/papis/tui/utils.py @@ -189,12 +189,17 @@ def get_range(range_str: str) -> List[int]: return [] -def select_range(options: List[Any], message: str) -> List[int]: +def select_range(options: List[Any], + message: str, + accept_none: bool = False, + bottom_toolbar: Optional[str] = None) -> List[int]: for i, o in enumerate(options): print("{i}. {o}".format(i=i, o=o)) possible_indices = range(len(options)) all_keywords = ["all", "a"] + none_keywords = ["n", "none"] + valid_keywords = all_keywords + (none_keywords if accept_none else []) if not options: return [] @@ -202,12 +207,17 @@ def select_range(options: List[Any], message: str) -> List[int]: selection = prompt( prompt_string=message, default="", - dirty_message="Range not valid, example: 0, 2, 3-10, a, all, ...", + bottom_toolbar=bottom_toolbar, + dirty_message="Range not valid, example: " + "0, 2, 3-10, {}, ...".format(", ".join(valid_keywords)), validator_function=lambda string: - string in all_keywords or + string in valid_keywords or len(set(get_range(string)) & set(possible_indices)) > 0) if selection in all_keywords: selection = ",".join(map(str, range(len(options)))) + if selection in none_keywords: + return [] + return [i for i in get_range(selection) if i in possible_indices] -- cgit v1.2.3 From 6fdeec3ec0492a104022ac17a870bc060baa4c02 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Sun, 3 Oct 2021 23:39:33 +0200 Subject: Add papis merge to address request #340 --- CHANGELOG.md | 2 + papis/commands/merge.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 5 +- 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 papis/commands/merge.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 33520e72..a4e66cf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ use emacs-like hooks for some commands. ## papis picker - You can now pick several elements with the key binding `c-t` +## `papis merge` +- Add the command `papis merge` to merge documents in pairs. VERSION v0.11 ============= diff --git a/papis/commands/merge.py b/papis/commands/merge.py new file mode 100644 index 00000000..48056228 --- /dev/null +++ b/papis/commands/merge.py @@ -0,0 +1,137 @@ +""" +Merge two documents that might be potentially repeated. + +If your papis picker do not support selecting two items, then +pass the ``--pick`` flag to pick twice for the documents. + +TODO: Write more documentation + +Cli +^^^ +.. click:: papis.commands.merge:cli + :prog: papis open +""" +import os +import os.path +import logging +from typing import Optional, List, Dict, Any +import click +import shutil + +import papis +import papis.api +import papis.pick +import papis.utils +import papis.config +import papis.cli +import papis.database +from papis.document import Document, to_dict +import papis.format +import papis.strings +import papis.commands.rm as rm +import papis.commands.update as update + + +def run(keep: Document, + erase: Document, + data: Dict[str, Any], + files: List[str], + keep_both: bool, + git: bool = False) -> None: + + logger = logging.getLogger('merge:run') + files_to_move = set(files) - set(keep.get_files()) + for f in files_to_move: + to_folder = keep.get_main_folder() + if to_folder: + logger.info("Moving %s", f) + shutil.copy(f, to_folder) + keep["files"] += [os.path.basename(f)] + update.run(keep, data, git=git) + if not keep_both: + logger.info("removing {}".format(erase)) + rm.run(erase, git=git) + else: + logger.info("keeping both documents") + + +@click.command("merge") +@click.help_option('-h', '--help') +@papis.cli.query_option() +@papis.cli.sort_option() +@click.option("-s", + "--second", + help="Keep the second document after merge and erase the first," + "the default is keep the first", + default=False, + is_flag=True) +@click.option("-p", + "--pick", + help="If your picker does not support picking two documents" + " at once, call twice the picker to get two documents", + default=False, + is_flag=True) +@click.option("-k", + "--keep", + "keep_both", + help="Do not erase any document", + default=False, + is_flag=True) +@papis.cli.git_option(help="Merge in git") +def cli(query: str, + sort_field: Optional[str], + second: bool, + git: bool, + keep_both: bool, + sort_reverse: bool, + pick: bool) -> None: + """Merge two documents from a given library""" + logger = logging.getLogger('cli:merge') + + documents = papis.database.get().query(query) + + if sort_field: + documents = papis.document.sort(documents, sort_field, sort_reverse) + + if not documents: + logger.warning(papis.strings.no_documents_retrieved_message) + return + + documents = [d for d in papis.pick.pick_doc(documents)] + + if pick: + other_documents = [d for d in papis.pick.pick_doc(documents)] + documents += other_documents + + if len(documents) != 2: + logger.error("You have to pick exactly two documents!") + return + + a = documents[0] + data_a = to_dict(a) + data_a.pop("ref") + data_a.pop("files") + b = documents[1] + data_b = to_dict(b) + data_b.pop("ref") + data_b.pop("files") + papis.utils.update_doc_from_data_interactively(data_a, + data_b, + papis.document.describe(b)) + + files = [] # type: List[str] + for doc in documents: + indices = papis.tui.utils.select_range( + doc.get_files(), + "Documents from A to keep", + accept_none=True, + bottom_toolbar=papis.document.describe(a)) + files += [doc.get_files()[i] for i in indices] + + if not papis.tui.utils.confirm("Are you sure you want to merge?"): + logger.info("Exiting safely") + return + + keep = b if second else a + erase = a if second else b + run(keep, erase, data_a, files, keep_both, git) diff --git a/setup.py b/setup.py index 8b0b7f70..a159c1a6 100644 --- a/setup.py +++ b/setup.py @@ -167,21 +167,22 @@ setup( 'papis.command': [ "add=papis.commands.add:cli", "addto=papis.commands.addto:cli", + "bibtex=papis.commands.bibtex:cli", "browse=papis.commands.browse:cli", "config=papis.commands.config:cli", "edit=papis.commands.edit:cli", + "exec=papis.commands.exec:cli", "explore=papis.commands.explore:cli", "export=papis.commands.export:cli", "git=papis.commands.git:cli", "list=papis.commands.list:cli", + "merge=papis.commands.merge:cli", "mv=papis.commands.mv:cli", - "bibtex=papis.commands.bibtex:cli", "open=papis.commands.open:cli", "rename=papis.commands.rename:cli", "rm=papis.commands.rm:cli", "run=papis.commands.run:cli", "update=papis.commands.update:cli", - "exec=papis.commands.exec:cli", ], 'papis.downloader': [ "acs=papis.downloaders.acs:Downloader", -- cgit v1.2.3 From 341c371ec4ec5fc11a3bac0518b36220cf2f4750 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Mon, 4 Oct 2021 14:32:41 +0200 Subject: Pop files carefully Oops! --- papis/commands/merge.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/papis/commands/merge.py b/papis/commands/merge.py index 48056228..ae02be9e 100644 --- a/papis/commands/merge.py +++ b/papis/commands/merge.py @@ -107,14 +107,18 @@ def cli(query: str, logger.error("You have to pick exactly two documents!") return + a = documents[0] data_a = to_dict(a) - data_a.pop("ref") - data_a.pop("files") b = documents[1] data_b = to_dict(b) - data_b.pop("ref") - data_b.pop("files") + + to_pop = ["files"] + for d in [data_a, data_b]: + for key in to_pop: + if key in d: + d.pop(key) + papis.utils.update_doc_from_data_interactively(data_a, data_b, papis.document.describe(b)) -- cgit v1.2.3 From 89fe3a78ad237e9773e18d86af52641497842074 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Mon, 4 Oct 2021 14:47:53 +0200 Subject: Add --out flag in merge for testing and sanity --- papis/commands/merge.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/papis/commands/merge.py b/papis/commands/merge.py index ae02be9e..9ee1d042 100644 --- a/papis/commands/merge.py +++ b/papis/commands/merge.py @@ -25,7 +25,7 @@ import papis.utils import papis.config import papis.cli import papis.database -from papis.document import Document, to_dict +from papis.document import Document, to_dict, from_folder import papis.format import papis.strings import papis.commands.rm as rm @@ -77,9 +77,14 @@ def run(keep: Document, help="Do not erase any document", default=False, is_flag=True) +@click.option("-o", + "--out", + help="Create the resulting document in this path", + default=None) @papis.cli.git_option(help="Merge in git") def cli(query: str, sort_field: Optional[str], + out: Optional[str], second: bool, git: bool, keep_both: bool, @@ -138,4 +143,17 @@ def cli(query: str, keep = b if second else a erase = a if second else b + + if out is not None: + os.makedirs(out, exist_ok=True) + keep = from_folder(out) + keep["files"] = [] + for f in files: + shutil.copy(f, out) + keep["files"] += [os.path.basename(f)] + keep.update(data_a) + keep.save() + logger.info("saving the new document in %s", out) + return + run(keep, erase, data_a, files, keep_both, git) -- cgit v1.2.3 From d5ade34f3ff27b4ebc830e6ea140deef7723d792 Mon Sep 17 00:00:00 2001 From: Alejandro Gallo Date: Thu, 28 Oct 2021 19:54:23 +0200 Subject: Add -n and --print to papis-browse --- CHANGELOG.md | 3 +++ papis/commands/browse.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e66cf8..745c1be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ use emacs-like hooks for some commands. ## `papis merge` - Add the command `papis merge` to merge documents in pairs. +## `papis browse` +- Add `-n` and `--print` to just print the url to be opened. + VERSION v0.11 ============= diff --git a/papis/commands/browse.py b/papis/commands/browse.py index fb77e49b..3fe0106f 100644 --- a/papis/commands/browse.py +++ b/papis/commands/browse.py @@ -71,7 +71,7 @@ from typing import Optional logger = logging.getLogger('browse') -def run(document: papis.document.Document) -> Optional[str]: +def run(document: papis.document.Document, browse: bool=True) -> Optional[str]: """Browse document's url whenever possible and returns the url :document: Document object @@ -106,8 +106,11 @@ def run(document: papis.document.Document) -> Optional[str]: + '/?' + urlencode(params)) - logger.info("Opening url %s:" % url) - papis.utils.general_open(url, "browser", wait=False) + if browse: + logger.info("Opening url %s:" % url) + papis.utils.general_open(url, "browser", wait=False) + else: + print(url) return url @@ -118,11 +121,14 @@ def run(document: papis.document.Document) -> Optional[str]: @click.option('-k', '--key', default='', help='Use the value of the document\'s key to open in' ' the browser, e.g. doi, url, doc_url ...') +@click.option('-n', '--print', "_print", default=False, is_flag=True, + help='Just print out the url, do not open it with browser') @papis.cli.all_option() @papis.cli.doc_folder_option() def cli(query: str, key: str, _all: bool, + _print: bool, doc_folder: str, sort_field: Optional[str], sort_reverse: bool) -> None: @@ -153,4 +159,4 @@ def cli(query: str, logger.info("Using key = %s", papis.config.get("browse-key")) for document in documents: - run(document) + run(document, browse=not _print) -- cgit v1.2.3 From 9647e4a08f49c237fed2c9a437d575788d3cff5e Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Thu, 25 Nov 2021 14:08:12 +0100 Subject: Add python3.10 to versions to run tests on (#343) * Add python 3.10 to versions to run tests on Need to quote 3.10, or else it CI tries (and fails) to install python3.1. * commands/merge: silence coverage error about too many empty lines * commands/browse: fix coverage error from whitespace around = * Fix missing space in non-existing-library message Before this change the exception was printed as: Exception: Library 'papers' does not seem to exist To add a library simply write the followingin your configuration ... --- .github/workflows/main.yml | 2 +- papis/commands/browse.py | 3 ++- papis/commands/merge.py | 1 - papis/config.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a41feed1..aed1406b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + python-version: ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v2 diff --git a/papis/commands/browse.py b/papis/commands/browse.py index 3fe0106f..b292f473 100644 --- a/papis/commands/browse.py +++ b/papis/commands/browse.py @@ -71,7 +71,8 @@ from typing import Optional logger = logging.getLogger('browse') -def run(document: papis.document.Document, browse: bool=True) -> Optional[str]: +def run(document: papis.document.Document, + browse: bool = True) -> Optional[str]: """Browse document's url whenever possible and returns the url :document: Document object diff --git a/papis/commands/merge.py b/papis/commands/merge.py index 9ee1d042..df13a5b0 100644 --- a/papis/commands/merge.py +++ b/papis/commands/merge.py @@ -112,7 +112,6 @@ def cli(query: str, logger.error("You have to pick exactly two documents!") return - a = documents[0] data_a = to_dict(a) b = documents[1] diff --git a/papis/config.py b/papis/config.py index 2f9860ea..25dd2cf3 100644 --- a/papis/config.py +++ b/papis/config.py @@ -544,7 +544,7 @@ def get_lib_from_name(libname: str) -> papis.library.Library: raise Exception("Library '{0}' does not seem to exist" "\n\n" "To add a library simply write the following" - "in your configuration file located at '{cpath}'" + " in your configuration file located at '{cpath}'" "\n\n" "\t[{0}]\n" "\tdir = path/to/your/{0}/folder" -- cgit v1.2.3 From be0b8b2eae451b9e9f55f8b4a0dcabf141f73b98 Mon Sep 17 00:00:00 2001 From: Alex Fikl Date: Thu, 25 Nov 2021 07:12:53 -0600 Subject: Small fixes to various tests (#341) * small fixes to various tests * fix mypy issues on python 3.9 and mypy 0.910 * fix flake8 errors --- papis/__init__.py | 12 ++++++------ papis/arxiv.py | 4 ++-- papis/bibtex.py | 16 ++++++++-------- papis/commands/add.py | 14 +++++++------- papis/commands/bibtex.py | 6 +++--- papis/commands/default.py | 16 ++++++++-------- papis/commands/explore.py | 3 ++- papis/commands/list.py | 4 ++-- papis/commands/mv.py | 3 +-- papis/commands/update.py | 4 ++-- papis/config.py | 12 ++++++------ papis/crossref.py | 7 ++++--- papis/docmatcher.py | 4 ++-- papis/downloaders/__init__.py | 4 ++-- papis/downloaders/acs.py | 4 ++-- papis/downloaders/base.py | 5 +++-- papis/downloaders/thesesfr.py | 3 ++- papis/importer.py | 3 ++- papis/tui/__init__.py | 6 +++--- papis/tui/utils.py | 4 ++-- papis/tui/widgets/diff.py | 16 ++++++++-------- tests/test_document.py | 3 --- tests/tui/widgets/test_options_list.py | 4 ++-- tools/ci-install.sh | 1 + tools/ci-run-tests.sh | 4 ++-- 25 files changed, 82 insertions(+), 80 deletions(-) diff --git a/papis/__init__.py b/papis/__init__.py index afa983c3..74b1e9f5 100644 --- a/papis/__init__.py +++ b/papis/__init__.py @@ -10,11 +10,11 @@ __email__ = 'aamsgallo@gmail.com' if os.environ.get('PAPIS_DEBUG'): import logging log_format = ( - '%(relativeCreated)d-' + - '%(levelname)s' + - ':' + - '%(name)s' + - ':' + - '%(message)s' + '%(relativeCreated)d-' + + '%(levelname)s' + + ':' + + '%(name)s' + + ':' + + '%(message)s' ) logging.basicConfig(format=log_format, level=logging.DEBUG) diff --git a/papis/arxiv.py b/papis/arxiv.py index 3b442472..3f5f5d6d 100644 --- a/papis/arxiv.py +++ b/papis/arxiv.py @@ -306,8 +306,8 @@ class ArxividFromPdfImporter(papis.importer.Importer): @classmethod def match(cls, uri: str) -> Optional[papis.importer.Importer]: - if (os.path.isdir(uri) or not os.path.exists(uri) or - not papis.filetype.get_document_extension(uri) == 'pdf'): + if (os.path.isdir(uri) or not os.path.exists(uri) + or not papis.filetype.get_document_extension(uri) == 'pdf'): return None importer = ArxividFromPdfImporter(uri=uri) importer.arxivid = pdf_to_arxivid(uri, maxlines=2000) diff --git a/papis/bibtex.py b/papis/bibtex.py index 0d77a625..8827fa28 100644 --- a/papis/bibtex.py +++ b/papis/bibtex.py @@ -16,15 +16,15 @@ import papis.format logger = logging.getLogger("bibtex") # type: logging.Logger bibtex_types = [ - "article", "book", "booklet", "conference", "inbook", "incollection", - "inproceedings", "manual", "mastersthesis", "misc", "phdthesis", - "proceedings", "techreport", "unpublished" + "article", "book", "booklet", "conference", "inbook", "incollection", + "inproceedings", "manual", "mastersthesis", "misc", "phdthesis", + "proceedings", "techreport", "unpublished" ] + papis.config.getlist('extra-bibtex-types') # type: List[str] bibtex_type_converter = { - "conferencePaper": "inproceedings", - "journalArticle": "article", - "journal": "article" + "conferencePaper": "inproceedings", + "journalArticle": "article", + "journal": "article" } # type: Dict[str, str] bibtex_keys = [ @@ -66,8 +66,8 @@ class Importer(papis.importer.Importer): @classmethod def match(cls, uri: str) -> Optional[papis.importer.Importer]: - if (not os.path.exists(uri) or os.path.isdir(uri) or - papis.filetype.get_document_extension(uri) == 'pdf'): + if (not os.path.exists(uri) or os.path.isdir(uri) + or papis.filetype.get_document_extension(uri) == 'pdf'): return None importer = Importer(uri=uri) importer.fetch() diff --git a/papis/commands/add.py b/papis/commands/add.py index ce236be6..68d4dc90 100644 --- a/papis/commands/add.py +++ b/papis/commands/add.py @@ -236,10 +236,10 @@ def get_hash_folder(data: Dict[str, Any], document_paths: List[str]) -> str: document_strings += fd.read(2000) md5 = hashlib.md5( - ''.join(document_paths).encode() + - str(data).encode() + - str(random.random()).encode() + - document_strings + ''.join(document_paths).encode() + + str(data).encode() + + str(random.random()).encode() + + document_strings ).hexdigest() result = md5 + author @@ -386,9 +386,9 @@ def run(paths: List[str], logger.info("No document matching found already in the library") else: logger.warning( - colorama.Fore.RED + - "DUPLICATION WARNING" + - colorama.Style.RESET_ALL) + colorama.Fore.RED + + "DUPLICATION WARNING" + + colorama.Style.RESET_ALL) logger.warning( "The document beneath is in your library and it seems to match") logger.warning( diff --git a/papis/commands/bibtex.py b/papis/commands/bibtex.py index a0ec003c..d4ba6a33 100644 --- a/papis/commands/bibtex.py +++ b/papis/commands/bibtex.py @@ -172,9 +172,9 @@ def cli(ctx: click.Context, no_auto_read: bool) -> None: config.set('auto-read', 'False', section='bibtex') bibfile = config.get('default-read-bibfile', section='bibtex') - if (bool(config.getboolean('auto-read', section='bibtex')) and - bibfile and - os.path.exists(bibfile)): + if (bool(config.getboolean('auto-read', section='bibtex')) + and bibfile + and os.path.exists(bibfile)): logger.info("auto reading {0}".format(bibfile)) explorer_mgr['bibtex'].plugin.callback(bibfile) diff --git a/papis/commands/default.py b/papis/commands/default.py index 64f064fe..c74d9937 100644 --- a/papis/commands/default.py +++ b/papis/commands/default.py @@ -214,14 +214,14 @@ def run(verbose: bool, else: colorama.init() - log_format = (colorama.Fore.YELLOW + - "%(levelname)s" + - ":" + - colorama.Fore.GREEN + - "%(name)s" + - colorama.Style.RESET_ALL + - ":" + - "%(message)s" + log_format = (colorama.Fore.YELLOW + + "%(levelname)s" + + ":" + + colorama.Fore.GREEN + + "%(name)s" + + colorama.Style.RESET_ALL + + ":" + + "%(message)s" ) if verbose: log = "DEBUG" diff --git a/papis/commands/explore.py b/papis/commands/explore.py index aeb6a3b8..39dc8524 100644 --- a/papis/commands/explore.py +++ b/papis/commands/explore.py @@ -101,6 +101,7 @@ import papis.pick import papis.format import papis.crossref import papis.plugin +from stevedore import ExtensionManager from typing import List, Optional, Union, Any, Dict # noqa: ignore logger = logging.getLogger('explore') @@ -114,7 +115,7 @@ def get_available_explorers() -> List[click.Command]: return papis.plugin.get_available_plugins(_extension_name()) -def get_explorer_mgr() -> papis.plugin.ExtensionManager: +def get_explorer_mgr() -> ExtensionManager: return papis.plugin.get_extension_manager(_extension_name()) diff --git a/papis/commands/list.py b/papis/commands/list.py index c78e8564..6ffd9f28 100644 --- a/papis/commands/list.py +++ b/papis/commands/list.py @@ -186,8 +186,8 @@ def cli( logger = logging.getLogger('cli:list') documents = [] # type: List[papis.document.Document] - if (not libraries and not downloaders and - not _file and not info and not _dir): + if (not libraries and not downloaders + and not _file and not info and not _dir): _dir = True if not libraries and not downloaders: diff --git a/papis/commands/mv.py b/papis/commands/mv.py index 3f05e935..77780b00 100644 --- a/papis/commands/mv.py +++ b/papis/commands/mv.py @@ -92,8 +92,7 @@ def cli(query: str, "Enter directory : (Tab completion enabled)\n" "Current directory: ({dir})\n".format( dir=document.get_main_folder_name() - ) + - "> " + ) + "> " ), completer=completer, complete_while_typing=True diff --git a/papis/commands/update.py b/papis/commands/update.py index b87d0f7d..541ec611 100644 --- a/papis/commands/update.py +++ b/papis/commands/update.py @@ -96,8 +96,8 @@ def run(document: papis.document.Document, is_flag=True) @click.option("--from", "from_importer", help="Add document from a specific importer ({0})".format( - ", ".join(papis.importer.available_importers()) - ), + ", ".join(papis.importer.available_importers()) + ), type=(click.Choice(papis.importer.available_importers()), str), nargs=2, multiple=True, diff --git a/papis/config.py b/papis/config.py index 25dd2cf3..d89a24fd 100644 --- a/papis/config.py +++ b/papis/config.py @@ -151,12 +151,12 @@ class Configuration(configparser.ConfigParser): self.file_location = get_config_file() self.logger = logging.getLogger("Configuration") self.default_info = { - "papers": { - 'dir': '~/Documents/papers' - }, - get_general_settings_name(): { - 'default-library': 'papers' - } + "papers": { + 'dir': '~/Documents/papers' + }, + get_general_settings_name(): { + 'default-library': 'papers' + } } # type: PapisConfigType self.initialize() diff --git a/papis/crossref.py b/papis/crossref.py index 9e3edbad..c34d0323 100644 --- a/papis/crossref.py +++ b/papis/crossref.py @@ -245,7 +245,7 @@ def doi_to_data(doi_string: str) -> Dict[str, Any]: '--max', '-m', '_ma', help='Maximum number of results', default=20) @click.option( '--filter', '-f', '_filters', help='Filters to apply', default=(), - type=(click.Choice(_filter_names), str), + type=(click.Choice(list(_filter_names)), str), multiple=True) @click.option( '--order', '-o', help='Order of appearance according to sorting', @@ -298,8 +298,9 @@ class DoiFromPdfImporter(papis.importer.Importer): def match(cls, uri: str) -> Optional[papis.importer.Importer]: """The uri should be a filepath""" filepath = uri - if (os.path.isdir(filepath) or not os.path.exists(filepath) or - not papis.filetype.get_document_extension(filepath) == 'pdf'): + if (os.path.isdir(filepath) or not os.path.exists(filepath) + or not papis.filetype.get_document_extension(filepath) == 'pdf' + ): # noqa: E124 return None importer = DoiFromPdfImporter(filepath) importer.fetch() diff --git a/papis/docmatcher.py b/papis/docmatcher.py index efe9f194..a3330ef7 100644 --- a/papis/docmatcher.py +++ b/papis/docmatcher.py @@ -129,9 +129,9 @@ def parse_query(query_string: str) -> List[List[str]]: ) ^ papis_value_word equal = ( - pyparsing.ZeroOrMore(" ") + - pyparsing.Literal(':') + pyparsing.ZeroOrMore(" ") + + pyparsing.Literal(':') + + pyparsing.ZeroOrMore(" ") ) papis_query = pyparsing.ZeroOrMore( diff --git a/papis/downloaders/__init__.py b/papis/downloaders/__init__.py index 1e5f8622..5303fff4 100644 --- a/papis/downloaders/__init__.py +++ b/papis/downloaders/__init__.py @@ -61,8 +61,8 @@ class Downloader(papis.importer.Importer): papis.importer.Importer.__init__(self, uri=uri, ctx=ctx, - name=name or - os.path.basename(__file__)) + name=name + or os.path.basename(__file__)) self.logger = logging.getLogger("downloader:"+self.name) self.logger.debug("uri {0}".format(uri)) self.expected_document_extension = None # type: Optional[str] diff --git a/papis/downloaders/acs.py b/papis/downloaders/acs.py index 14da1400..a5832f36 100644 --- a/papis/downloaders/acs.py +++ b/papis/downloaders/acs.py @@ -32,8 +32,8 @@ class Downloader(papis.downloaders.Downloader): data['type'] = meta.attrs.get('content') elif meta.attrs.get('name') == 'dc.Subject': data['note'] = meta.attrs.get('content') - elif (meta.attrs.get('name') == 'dc.Identifier' and - meta.attrs.get('scheme') == 'doi'): + elif (meta.attrs.get('name') == 'dc.Identifier' + and meta.attrs.get('scheme') == 'doi'): data['doi'] = meta.attrs.get('content') elif meta.attrs.get('name') == 'dc.Publisher': data['publisher'] = meta.attrs.get('content') diff --git a/papis/downloaders/base.py b/papis/downloaders/base.py index bc78bb90..8a1ea32b 100644 --- a/papis/downloaders/base.py +++ b/papis/downloaders/base.py @@ -1,5 +1,5 @@ import re -from typing import Any, List, Dict, Union, Pattern +from typing import Any, List, Dict, Iterator, Tuple, Union, Pattern from typing_extensions import TypedDict import bs4 @@ -126,8 +126,9 @@ def parse_meta_authors(soup: bs4.BeautifulSoup) -> List[Dict[str, Any]]: affs = soup.find_all( name='meta', attrs={'name': 'citation_author_institution'}) + if affs and authors: - tuples = zip(authors, affs) + tuples = zip(authors, affs) # type: Iterator[Tuple[Any, Any]] elif authors: tuples = ((a, None) for a in authors) else: diff --git a/papis/downloaders/thesesfr.py b/papis/downloaders/thesesfr.py index 0aec79ed..b716e63c 100644 --- a/papis/downloaders/thesesfr.py +++ b/papis/downloaders/thesesfr.py @@ -36,6 +36,7 @@ class Downloader(papis.downloaders.Downloader): 'http://thesesups.ups-tlse.fr/2722/1/2014TOU30305.pdf' >>> d = Downloader("http://theses.fr/1998ENPC9815") >>> d.get_document_url() + 'https://pastel.archives-ouvertes.fr/tel-00005590v2/file/Cances.pdf' """ # TODO: Simplify this function for typing raw_data = self.session.get(self.uri).content.decode('utf-8') @@ -53,7 +54,7 @@ class Downloader(papis.downloaders.Downloader): raw_data = self.session.get(second_url).content.decode('utf-8') soup = bs4.BeautifulSoup(raw_data, "html.parser") a = list(filter( - lambda t: re.match(r'.*pdf$', t['href']), + lambda t: re.match(r'.*pdf$', t.get('href', '')), soup.find_all('a') )) diff --git a/papis/importer.py b/papis/importer.py index 6d27b096..976e3f4a 100644 --- a/papis/importer.py +++ b/papis/importer.py @@ -1,4 +1,5 @@ import logging +from stevedore import ExtensionManager from typing import Optional, List, Dict, Any, Callable, Type import os.path @@ -89,7 +90,7 @@ def _extension_name() -> str: return "papis.importer" -def get_import_mgr() -> papis.plugin.ExtensionManager: +def get_import_mgr() -> ExtensionManager: """Get the import manager :returns: Import manager """ diff --git a/papis/tui/__init__.py b/papis/tui/__init__.py index 8b7fcc78..134f5f2c 100644 --- a/papis/tui/__init__.py +++ b/papis/tui/__init__.py @@ -5,9 +5,9 @@ PapisConfigType = Dict[str, Dict[str, Any]] def get_default_settings() -> PapisConfigType: return dict(tui={ "status_line_format": ( - "{selected_index}/{number_of_documents} " + - "F1:help " + - "c-l:redraw " + "{selected_index}/{number_of_documents} " + + "F1:help " + + "c-l:redraw " ), "status_line_style": 'bg:ansiwhite fg:ansiblack', diff --git a/papis/tui/utils.py b/papis/tui/utils.py index 629977fd..aa7c6016 100644 --- a/papis/tui/utils.py +++ b/papis/tui/utils.py @@ -211,8 +211,8 @@ def select_range(options: List[Any], dirty_message="Range not valid, example: " "0, 2, 3-10, {}, ...".format(", ".join(valid_keywords)), validator_function=lambda string: - string in valid_keywords or - len(set(get_range(string)) & set(possible_indices)) > 0) + string in valid_keywords + or len(set(get_range(string)) & set(possible_indices)) > 0) if selection in all_keywords: selection = ",".join(map(str, range(len(options)))) diff --git a/papis/tui/widgets/diff.py b/papis/tui/widgets/diff.py index 814cce24..9506818e 100644 --- a/papis/tui/widgets/diff.py +++ b/papis/tui/widgets/diff.py @@ -58,8 +58,8 @@ def prompt(text: Union[str, FormattedText], for a in actions )) ) - )] + - ([ + )] + + ([ Window( height=1, align=WindowAlign.LEFT, always_hide_cursor=True, @@ -117,12 +117,12 @@ def diffshow(texta: str, formatted_text = list(map( lambda line: # match line values - line.startswith('@') and ('fg:violet bg:ansiblack', line) or - line.startswith('+') and ('fg:ansigreen bg:ansiblack', line) or - line.startswith('-') and ('fg:ansired bg:ansiblack', line) or - line.startswith('?') and ('fg:ansiyellow bg:ansiblack', line) or - line.startswith('^^^') and ('bg:ansiblack fg:ansipurple', line) or - ('fg:ansiwhite', line), raw_text)) + line.startswith('@') and ('fg:violet bg:ansiblack', line) + or line.startswith('+') and ('fg:ansigreen bg:ansiblack', line) + or line.startswith('-') and ('fg:ansired bg:ansiblack', line) + or line.startswith('?') and ('fg:ansiyellow bg:ansiblack', line) + or line.startswith('^^^') and ('bg:ansiblack fg:ansipurple', line) + or ('fg:ansiwhite', line), raw_text)) prompt(title=title, text=formatted_text, diff --git a/tests/test_document.py b/tests/test_document.py index 9a2e8df5..86a02b52 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -98,7 +98,6 @@ def test_to_bibtex() -> None: " author = {Fernandez, Gilgamesh},\n" " journal = {jcp},\n" " title = {Hello},\n" - " type = {book},\n" " year = {3200BCE},\n" "}\n") doc['journal_abbrev'] = 'j' @@ -108,7 +107,6 @@ def test_to_bibtex() -> None: ' author = {Fernandez, Gilgamesh},\n' ' journal = {j},\n' ' title = {Hello},\n' - ' type = {book},\n' ' year = {3200BCE},\n' '}\n') del doc['title'] @@ -118,7 +116,6 @@ def test_to_bibtex() -> None: '@book{hello1992,\n' ' journal = {j},\n' ' author = {Fernandez, Gilgamesh},\n', - ' type = {book},\n' ' year = {3200BCE},\n' '}\n' ) diff --git a/tests/tui/widgets/test_options_list.py b/tests/tui/widgets/test_options_list.py index 8f68ac22..20c09d46 100644 --- a/tests/tui/widgets/test_options_list.py +++ b/tests/tui/widgets/test_options_list.py @@ -76,5 +76,5 @@ def test_basic(): def test_match_against_regex(): - assert match_against_regex(re.compile(r'.*he.*'), 'he', 2) == 2 - assert match_against_regex(re.compile(r'hes'), 'he', 2) is None + assert match_against_regex(re.compile(r'.*he.*'), (2, 'he')) == 2 + assert match_against_regex(re.compile(r'hes'), (2, 'he')) is None diff --git a/tools/ci-install.sh b/tools/ci-install.sh index 11030670..5d425ac5 100755 --- a/tools/ci-install.sh +++ b/tools/ci-install.sh @@ -3,5 +3,6 @@ pip install setuptools pip install flake8 pip install python-coveralls +pip install types-requests types-PyYAML types-contextvars pip install -e .[develop] pip install -e .[optional] diff --git a/tools/ci-run-tests.sh b/tools/ci-run-tests.sh index b984040a..98ca381f 100755 --- a/tools/ci-run-tests.sh +++ b/tools/ci-run-tests.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash python -m pytest papis/ tests/ --cov=papis -mypy papis -flake8 papis +python -m mypy papis +python -m flake8 papis -- cgit v1.2.3 From 850327f0754af58962a2ef8b500fd9fff5fe5a7d Mon Sep 17 00:00:00 2001 From: Alex Fikl Date: Fri, 26 Nov 2021 17:56:25 -0600 Subject: add a downloader for Project Euclid (#344) --- papis/downloaders/projecteuclid.py | 34 + setup.py | 1 + tests/downloaders/resources/projecteuclid_1.html | 2976 ++++++++++++++++++++ .../downloaders/resources/projecteuclid_1_out.json | 1 + tests/downloaders/test_project_euclid.py | 31 + 5 files changed, 3043 insertions(+) create mode 100644 papis/downloaders/projecteuclid.py create mode 100644 tests/downloaders/resources/projecteuclid_1.html create mode 100644 tests/downloaders/resources/projecteuclid_1_out.json create mode 100644 tests/downloaders/test_project_euclid.py diff --git a/papis/downloaders/projecteuclid.py b/papis/downloaders/projecteuclid.py new file mode 100644 index 00000000..436e05ec --- /dev/null +++ b/papis/downloaders/projecteuclid.py @@ -0,0 +1,34 @@ +import re +from typing import Optional + +import papis.downloaders.fallback + + +class Downloader(papis.downloaders.fallback.Downloader): + _BIBTEX_URL = "https://projecteuclid.org/citation/download/citation-{}.bib" + + def __init__(self, url: str) -> None: + super().__init__(uri=url, name="projecteuclid") + + self.expected_document_extension = "pdf" + self.priority = 10 + + @classmethod + def match(cls, url: str) -> Optional["Downloader"]: + if re.match(r".*projecteuclid\.org.*", url): + return Downloader(url) + else: + return None + + def get_bibtex_url(self) -> Optional[str]: + try: + # NOTE: this was determined heuristically by looking at the IDs + # generated for a couple of papers and may change in the future + identifier = "{}{}_{}".format( + self.ctx.data["journal_abbrev"], + self.ctx.data["volume"], + self.ctx.data["firstpage"]) + + return self._BIBTEX_URL.format(identifier) + except KeyError: + return None diff --git a/setup.py b/setup.py index a159c1a6..b563d555 100644 --- a/setup.py +++ b/setup.py @@ -203,6 +203,7 @@ setup( "worldscientific=papis.downloaders.worldscientific:Downloader", "fallback=papis.downloaders.fallback:Downloader", "arxiv=papis.arxiv:Downloader", + "projecteuclid=papis.downloaders.projecteuclid:Downloader", ], 'papis.explorer': [ "lib=papis.commands.explore:lib", diff --git a/tests/downloaders/resources/projecteuclid_1.html b/tests/downloaders/resources/projecteuclid_1.html new file mode 100644 index 00000000..2d9a89fb --- /dev/null +++ b/tests/downloaders/resources/projecteuclid_1.html @@ -0,0 +1,2976 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An analysis of the renormalization group method for asymptotic expansions with logarithmic switchback terms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +