From ef0bee2809bbd15ff1f93829ccf8a8dca924f006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20M=C3=A4hr?= Date: Wed, 23 Oct 2024 08:42:37 +0200 Subject: [PATCH 1/2] feat: retries, update Python version and restructure project dependencies --- .github/workflows/zotero-bib-to-git.yml | 59 +++---- .gitignore | 215 +++++++++++++++++++++++- .python-version | 1 + pyproject.toml | 12 ++ requirements.txt | 2 - uv.lock | 150 +++++++++++++++++ zotero-bib-to-gh.py | 137 ++++++++------- 7 files changed, 481 insertions(+), 95 deletions(-) create mode 100644 .python-version create mode 100644 pyproject.toml delete mode 100644 requirements.txt create mode 100644 uv.lock diff --git a/.github/workflows/zotero-bib-to-git.yml b/.github/workflows/zotero-bib-to-git.yml index 8cb195b..7cc9a27 100644 --- a/.github/workflows/zotero-bib-to-git.yml +++ b/.github/workflows/zotero-bib-to-git.yml @@ -2,7 +2,7 @@ name: GitHub action to retrieve Zotero collection in BibLaTeX format and save it on: schedule: - - cron: "0 8,12,18 * * *" + - cron: "0 8,12,18 * * *" push: branches: - test @@ -12,33 +12,30 @@ jobs: zotero-bib-to-gh: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Cache pip - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - ${{ runner.os }}- - - name: Install dependencies - run: pip install -r requirements.txt - - name: Run - run: python zotero-bib-to-gh.py - env: - ZOTERO_USER_ID: ${{ secrets.ZOTERO_USER_ID }} - ZOTERO_BEARER_TOKEN: ${{ secrets.ZOTERO_BEARER_TOKEN }} - - name: Commit files - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - echo `git add -A && git commit -m "Add changes"` - - name: Push changes - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ github.ref }} + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Run + run: uv run zotero-bib-to-gh.py + env: + ZOTERO_USER_ID: ${{ secrets.ZOTERO_USER_ID }} + ZOTERO_BEARER_TOKEN: ${{ secrets.ZOTERO_BEARER_TOKEN }} + + - name: Commit files + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + echo `git add -A && git commit -m "Add changes"` + + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref }} diff --git a/.gitignore b/.gitignore index 2c8e2c4..ced8f32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Created by https://www.gitignore.io/api/git,jetbrains,linux,osx,windows -# Edit at https://www.gitignore.io/?templates=git,jetbrains,linux,osx,windows +# Created by https://www.toptal.com/developers/gitignore/api/git,osx,linux,windows,jetbrains,python +# Edit at https://www.toptal.com/developers/gitignore?templates=git,osx,linux,windows,jetbrains,python ### Git ### # Created by git for backups. To disable backups in Git: @@ -17,7 +17,7 @@ *_REMOTE_*.txt ### JetBrains ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff @@ -27,6 +27,9 @@ .idea/**/dictionaries .idea/**/shelf +# AWS User-specific +.idea/**/aws.xml + # Generated files .idea/**/contentModel.xml @@ -47,6 +50,9 @@ # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml # .idea/modules.xml # .idea/*.iml # .idea/modules @@ -74,6 +80,9 @@ atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml +# SonarLint plugin +.idea/sonarlint/ + # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties @@ -95,7 +104,30 @@ fabric.properties # *.ipr # Sonarlint plugin -.idea/sonarlint +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml ### Linux ### *~ @@ -121,6 +153,7 @@ fabric.properties # Icon must end with two \r Icon + # Thumbnails ._* @@ -140,6 +173,178 @@ Network Trash Folder Temporary Items .apdisk +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + ### Windows ### # Windows thumbnail cache files Thumbs.db @@ -166,4 +371,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.gitignore.io/api/git,jetbrains,linux,osx,windows +# End of https://www.toptal.com/developers/gitignore/api/git,osx,linux,windows,jetbrains,python \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e15a285 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "zotero-bib-to-gh" +version = "0.1.0" +description = "GitHub action to automatically retrieve your Zotero collection in BibLaTeX format at 8:00, 12:00 and 18:00 and save it as GitHub repository." +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "aiofiles>=24.1.0", + "httpx>=0.27.2", + "logzero>=1.7.0", + "stamina>=24.3.0", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 05d140f..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -httpx==0.24.0 -logzero==1.7.0 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..33a29e2 --- /dev/null +++ b/uv.lock @@ -0,0 +1,150 @@ +version = 1 +requires-python = ">=3.13" + +[[package]] +name = "aiofiles" +version = "24.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 }, +] + +[[package]] +name = "anyio" +version = "4.6.2.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "httpcore" +version = "1.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/44/ed0fa6a17845fb033bd885c03e842f08c1b9406c86a2e60ac1ae1b9206a6/httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f", size = 85180 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/89/b161908e2f51be56568184aeb4a880fd287178d176fd1c860d2217f41106/httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f", size = 78011 }, +] + +[[package]] +name = "httpx" +version = "0.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "logzero" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/9a/018883ee64df0900bde1ac314868f81d12cbc450e51b216ab55e6e4dfc7d/logzero-1.7.0.tar.gz", hash = "sha256:7f73ddd3ae393457236f081ffebd044a3aa2e423a47ae6ddb5179ab90d0ad082", size = 577803 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/68/aa714515d65090fcbcc9a1f3debd5a644b14aad11e59238f42f00bd4b298/logzero-1.7.0-py2.py3-none-any.whl", hash = "sha256:23eb1f717a2736f9ab91ca0d43160fd2c996ad49ae6bad34652d47aba908769d", size = 16162 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "stamina" +version = "24.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/35/afe1f3467e840f414cfea90993c5a94490a6360eb3653236c3a7de099cf6/stamina-24.3.0.tar.gz", hash = "sha256:1d763c98962ca11f1729c357422926a750a138e803e7beb9f9d6c99d33d9997d", size = 558536 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/81/818b0b93812cc0a69a0b17c591dca1a3779aeab7f0ff094dee51f4a6bcd3/stamina-24.3.0-py3-none-any.whl", hash = "sha256:28caf1a5db514256d86e32b60621630552fa9a60dace4e6fb5c78ba15f26236e", size = 16445 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "zotero-bib-to-gh" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "aiofiles" }, + { name = "httpx" }, + { name = "logzero" }, + { name = "stamina" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofiles", specifier = ">=24.1.0" }, + { name = "httpx", specifier = ">=0.27.2" }, + { name = "logzero", specifier = ">=1.7.0" }, + { name = "stamina", specifier = ">=24.3.0" }, +] diff --git a/zotero-bib-to-gh.py b/zotero-bib-to-gh.py index c24d72a..9d6e06f 100644 --- a/zotero-bib-to-gh.py +++ b/zotero-bib-to-gh.py @@ -1,39 +1,50 @@ +import asyncio from os import environ +import aiofiles import httpx +import stamina from logzero import logger -def follow_and_extract(client, url, headers): - request = client.get(url=url, headers=headers) - logger.info(f"{url} returned {request.status_code} after {request.elapsed}") - # If there is s link to next url recursive call + +@stamina.retry(on=httpx.HTTPError, attempts=3) +async def fetch_url(client, url, headers): + response = await client.get(url=url, headers=headers) + response.raise_for_status() + logger.info(f"{url} returned {response.status_code} after {response.elapsed}") + return response + + +async def follow_and_extract(client, url, headers): + request = await fetch_url(client, url, headers) try: next_url = request.links["next"].get("url") - return request.text + follow_and_extract(client, next_url, headers) - # If there is no more link to next url recursion terminates + return request.text + await follow_and_extract(client, next_url, headers) except KeyError: return request.text -def download_and_write_bib(client, zotero_headers, zotero_url, file_name="zotero.bib"): - zotero_connection = client.get(url=zotero_url, headers=zotero_headers) - error = f"{zotero_url} returned {zotero_connection.status_code} after {zotero_connection.elapsed}" +async def download_and_write_bib( + client, zotero_headers, zotero_url, file_name="zotero.bib" +): + zotero_connection = await fetch_url(client, zotero_url, zotero_headers) + if zotero_connection.status_code == 403: - logger.error("Access to library not granted.") - return - if zotero_connection.status_code != 200: - logger.error(error) - exit(error) - logger.info(error) + logger.error("Access to library not granted.") + return + try: - with open(f"bibliography/{file_name}-last-modified-version", "r") as file: - cached_version = int(file.readline()) + async with aiofiles.open( + f"bibliography/{file_name}-last-modified-version", "r" + ) as file: + cached_version = int(await file.readline()) logger.info(f"last-modified-version is {cached_version}") - except: + except (FileNotFoundError, ValueError): cached_version = 0 + latest_version = int(zotero_connection.headers.get("last-modified-version")) - if cached_version is latest_version: + if cached_version == latest_version: logger.info( f"online version {latest_version} is not different from cache {cached_version}. Done!" ) @@ -42,50 +53,62 @@ def download_and_write_bib(client, zotero_headers, zotero_url, file_name="zotero logger.info( f"online version {latest_version} is different from cache {cached_version}. Fetching data..." ) - biblatex_file_content = follow_and_extract(client, url=zotero_url, headers=zotero_headers) - with open(f"bibliography/{file_name}", "w") as file: - file.write(biblatex_file_content) + biblatex_file_content = await follow_and_extract( + client, url=zotero_url, headers=zotero_headers + ) + + async with aiofiles.open(f"bibliography/{file_name}", "w") as file: + await file.write(biblatex_file_content) logger.info(f"{file_name} updated") - with open(f"bibliography/{file_name}-last-modified-version", "w") as file: - file.write(str(latest_version)) + async with aiofiles.open( + f"bibliography/{file_name}-last-modified-version", "w" + ) as file: + await file.write(str(latest_version)) logger.info(f"last-modified-version updated to {latest_version}") -timeout = httpx.Timeout(10.0, connect=60.0) -client = httpx.Client(timeout=timeout) - -zotero_user_id = environ.get("ZOTERO_USER_ID") -if zotero_user_id is None: - error = 'ZOTERO_USER_ID not set in GitHub secrets' - logger.error(error) - exit(error) - -zotero_bearer_token = environ.get("ZOTERO_BEARER_TOKEN") -if zotero_bearer_token is None: - error = 'ZOTERO_BEARER_TOKEN not set in GitHub secrets' - logger.error(error) - exit(error) - -zotero_headers = {"Authorization": f"Bearer {zotero_bearer_token}"} -if zotero_user_id is not None: - zotero_user_url = ( - f"https://api.zotero.org/users/{zotero_user_id}/items?v=3&format=biblatex" - ) - download_and_write_bib(client, zotero_headers, zotero_user_url) -logger.info("Downloading all groups!") +async def main(): + timeout = httpx.Timeout(10.0, connect=60.0) + async with httpx.AsyncClient(timeout=timeout) as client: + zotero_user_id = environ.get("ZOTERO_USER_ID") + if zotero_user_id is None: + logger.error("ZOTERO_USER_ID not set in GitHub secrets") + return + + zotero_bearer_token = environ.get("ZOTERO_BEARER_TOKEN") + if zotero_bearer_token is None: + logger.error("ZOTERO_BEARER_TOKEN not set in GitHub secrets") + return + + zotero_headers = {"Authorization": f"Bearer {zotero_bearer_token}"} + zotero_user_url = ( + f"https://api.zotero.org/users/{zotero_user_id}/items?v=3&format=biblatex" + ) + + await download_and_write_bib(client, zotero_headers, zotero_user_url) + + logger.info("Downloading all groups!") + + groups_response = await fetch_url( + client, + f"https://api.zotero.org/users/{zotero_user_id}/groups/", + zotero_headers, + ) + groups = groups_response.json() + + for group in groups: + group_id = group.get("id") + if group_id: + zotero_group_url = f"https://api.zotero.org/groups/{group_id}/items?v=3&format=biblatex" + await download_and_write_bib( + client, zotero_headers, zotero_group_url, f"{group_id}.bib" + ) -groups = client.get( - f"https://api.zotero.org/users/{zotero_user_id}/groups/", headers=zotero_headers -) + logger.info("Done!") -for group in groups.json(): - for attribute, value in group.items(): - if attribute == "id": - zotero_group_url = ( - f"https://api.zotero.org/groups/{value}/items?v=3&format=biblatex" - ) - download_and_write_bib(client, zotero_headers, zotero_group_url, f"{value}.bib") -logger.info("Done!") +# Entry point for the script +if __name__ == "__main__": + asyncio.run(main()) From f03da12ac88eb3a71b0b0f97274241d7e4ab52ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20M=C3=A4hr?= Date: Wed, 23 Oct 2024 08:56:32 +0200 Subject: [PATCH 2/2] chore: update stale action to version 9 in workflow configuration --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index a135e91..34824b9 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'Stale issue message'