From 1fa77dab22e6a74eab325bd967a9eb16602f8e09 Mon Sep 17 00:00:00 2001 From: Lukas Winkler Date: Tue, 29 Dec 2020 17:16:16 +0100 Subject: [PATCH] use config file --- .gitignore | 1 - paperlibrary/api/models.py | 4 +- paperlibrary/config.py | 38 ++++++++++++++++ paperlibrary/library/library.py | 18 +++++--- paperlibrary/pap.py | 33 ++++++++++---- poetry.lock | 77 +++++++++++++++++++++------------ pyproject.toml | 1 + 7 files changed, 125 insertions(+), 47 deletions(-) create mode 100644 paperlibrary/config.py diff --git a/.gitignore b/.gitignore index 31cafbd..73b9618 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ __pycache__/ library/ !paperlibrary/library -paperlibrary/config.py diff --git a/paperlibrary/api/models.py b/paperlibrary/api/models.py index 574d1d8..cb3d563 100644 --- a/paperlibrary/api/models.py +++ b/paperlibrary/api/models.py @@ -47,11 +47,11 @@ class PaperComplete(Paper): publication: str doctype: str arxiv_id: Optional[str] - bibcode: str + bibcode: Optional[str] year: int pubdate: str # TODO: to datetime entry_date: str # TODO: to datetime - citation_count: int + citation_count: Optional[int] citation_key: Optional[str] recommended_by: List[str] tags: List[str] diff --git a/paperlibrary/config.py b/paperlibrary/config.py new file mode 100644 index 0000000..01d0a05 --- /dev/null +++ b/paperlibrary/config.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass +from pathlib import Path +from typing import Optional + +import yaml +from dataclasses_json import dataclass_json, DataClassJsonMixin + +config_path = Path("~/.config/paperlibrary.yaml").expanduser() + + +@dataclass_json +@dataclass +class Config(DataClassJsonMixin): + url: str + auth_token: str + basedir: str + + @property + def basedir_path(self): + return Path(self.basedir) + + +def get_config() -> Optional[Config]: + try: + with config_path.open() as f: + data = yaml.safe_load(f) + if not data: + raise ValueError("config file is empty") + return Config.from_dict(data) + except FileNotFoundError: + return + + +def save_config(config: Config) -> None: + with config_path.open("w") as f: + yaml.safe_dump(config.to_dict(), f) + + diff --git a/paperlibrary/library/library.py b/paperlibrary/library/library.py index 1a92077..9c1c25c 100644 --- a/paperlibrary/library/library.py +++ b/paperlibrary/library/library.py @@ -9,7 +9,7 @@ from tzlocal import get_localzone from paperlibrary.api import PaperLibraryAPI from paperlibrary.api.models import Paper -from paperlibrary.config import basedir +from paperlibrary.config import Config def format_filename(s: str) -> str: @@ -32,8 +32,8 @@ def link_file(pdf_dir: Path, directory: Path, paper: Paper, filename: str = None targetfile.symlink_to(sourcefile) -def write_symlinks(api: PaperLibraryAPI): - ... +def write_symlinks(api: PaperLibraryAPI, config: Config): + basedir = config.basedir_path pdf_dir = basedir / "pdfs" pdf_dir.mkdir(exist_ok=True) @@ -49,12 +49,16 @@ def write_symlinks(api: PaperLibraryAPI): directory.mkdir() for author in api.fetch_authors(): + if not author.papers: + continue author_subdir = author_dir / format_filename(author.display_name) author_subdir.mkdir() for paper in author.papers: link_file(pdf_dir, author_subdir, paper) for keyword in api.fetch_keywords(): + if not keyword.papers: + continue keyword_subdir = keyword_dir / format_filename(keyword.name) keyword_subdir.mkdir() for paper in keyword.papers: @@ -100,8 +104,8 @@ def hash_file(file: Path, buffer_size=65536) -> str: return sha256.hexdigest() -def update_pdfs(api: PaperLibraryAPI): - pdf_dir = basedir / "pdfs" +def update_pdfs(api: PaperLibraryAPI, config: Config): + pdf_dir = config.basedir_path / "pdfs" pdf_dir.mkdir(exist_ok=True) for pdf in api.fetch_pdfs(): @@ -122,8 +126,8 @@ def update_pdfs(api: PaperLibraryAPI): download_file(api, pdf.file, pdf_file) -def write_bibliography(api: PaperLibraryAPI): +def write_bibliography(api: PaperLibraryAPI, config: Config): bib = api.fetch_bibliography() - target_file = basedir / "bibliography.bib" + target_file = config.basedir_path / "bibliography.bib" with target_file.open("w") as f: f.write(bib) diff --git a/paperlibrary/pap.py b/paperlibrary/pap.py index edb559d..6c28922 100644 --- a/paperlibrary/pap.py +++ b/paperlibrary/pap.py @@ -1,29 +1,44 @@ import click from paperlibrary.api import PaperLibraryAPI -from paperlibrary.config import url, auth_token +from paperlibrary.config import get_config, Config, save_config from paperlibrary.library import write_symlinks, update_pdfs, write_bibliography @click.group() -def cli(): +@click.pass_context +def cli(ctx): + ctx.obj = get_config() pass @cli.command() -def update(): - api = PaperLibraryAPI(url, auth_token=auth_token) - write_bibliography(api) - write_symlinks(api) - update_pdfs(api) +@click.pass_obj +def update(config: Config): + api = PaperLibraryAPI(config.url, auth_token=config.auth_token) + write_bibliography(api, config) + write_symlinks(api, config) + update_pdfs(api, config) @cli.command() -def test(): - api = PaperLibraryAPI(url, auth_token=auth_token) +@click.pass_obj +def test(config: Config): + api = PaperLibraryAPI(config.url, auth_token=config.auth_token) print(api.fetch_papers()) +@cli.command() +def init(): + url = click.prompt("URL", type=str) + + auth_token = click.prompt("auth_token", type=str) + basedir = click.prompt("basedir", type=str) + + config = Config(url=url, auth_token=auth_token, basedir=basedir) + save_config(config) + + if __name__ == '__main__': cli() diff --git a/poetry.lock b/poetry.lock index 36e2193..871f816 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,7 +8,7 @@ python-versions = ">=2.7, <4" [[package]] name = "certifi" -version = "2020.6.20" +version = "2020.12.5" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -16,11 +16,11 @@ python-versions = "*" [[package]] name = "chardet" -version = "3.0.4" +version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "click" @@ -57,16 +57,16 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "marshmallow" -version = "3.8.0" +version = "3.10.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." category = "main" optional = false python-versions = ">=3.5" [package.extras] -dev = ["pytest", "pytz", "simplejson", "mypy (0.782)", "flake8 (3.8.3)", "flake8-bugbear (20.1.4)", "pre-commit (>=2.4,<3.0)", "tox"] -docs = ["sphinx (3.2.1)", "sphinx-issues (1.2.0)", "alabaster (0.7.12)", "sphinx-version-warning (1.1.2)", "autodocsumm (0.2.0)"] -lint = ["mypy (0.782)", "flake8 (3.8.3)", "flake8-bugbear (20.1.4)", "pre-commit (>=2.4,<3.0)"] +dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] +docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"] +lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -90,15 +90,23 @@ python-versions = "*" [[package]] name = "pytz" -version = "2020.1" +version = "2020.5" description = "World timezone definitions, modern and historical" category = "main" optional = false python-versions = "*" +[[package]] +name = "pyyaml" +version = "5.3.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "requests" -version = "2.24.0" +version = "2.25.1" description = "Python HTTP for Humans." category = "main" optional = false @@ -106,13 +114,13 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] name = "stringcase" @@ -155,7 +163,7 @@ pytz = "*" [[package]] name = "urllib3" -version = "1.25.10" +version = "1.26.2" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -163,13 +171,13 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "8bb7dbfcc0d218d22ff3dcafae140daff4799bfeb988731a3759412a256d0e29" +content-hash = "f28e815cebaa07fe26370dea5e304b5b63e3e9ea12221161c05c94c158191a91" [metadata.files] alive-progress = [ @@ -177,12 +185,12 @@ alive-progress = [ {file = "alive_progress-1.6.1-py3-none-any.whl", hash = "sha256:9a0fae6b94fb4e4bcd9fb51760506d29a33358ebbfef2c6516dce3e359a661b5"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, @@ -197,8 +205,8 @@ idna = [ {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, ] marshmallow = [ - {file = "marshmallow-3.8.0-py2.py3-none-any.whl", hash = "sha256:2272273505f1644580fbc66c6b220cc78f893eb31f1ecde2af98ad28011e9811"}, - {file = "marshmallow-3.8.0.tar.gz", hash = "sha256:47911dd7c641a27160f0df5fd0fe94667160ffe97f70a42c3cc18388d86098cc"}, + {file = "marshmallow-3.10.0-py2.py3-none-any.whl", hash = "sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"}, + {file = "marshmallow-3.10.0.tar.gz", hash = "sha256:4ab2fdb7f36eb61c3665da67a7ce281c8900db08d72ba6bf0e695828253581f7"}, ] marshmallow-enum = [ {file = "marshmallow-enum-1.5.1.tar.gz", hash = "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"}, @@ -209,12 +217,25 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] pytz = [ - {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, - {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, + {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, + {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, +] +pyyaml = [ + {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, + {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, + {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, + {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] stringcase = [ {file = "stringcase-1.2.0.tar.gz", hash = "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"}, @@ -234,6 +255,6 @@ tzlocal = [ {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, ] urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] diff --git a/pyproject.toml b/pyproject.toml index 5eef815..45adcb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ click = "^7.1.2" dataclasses-json = "^0.5.2" tzlocal = "^2.1" alive-progress = "^1.6.1" +PyYAML = "^5.3.1" [build-system] requires = ["poetry-core>=1.0.0"]