pyzotero 1.6.8__tar.gz → 1.6.10__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.
- pyzotero-1.6.10/.github/pull_request_template.md +23 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.github/workflows/tests.yml +43 -18
- {pyzotero-1.6.8 → pyzotero-1.6.10}/CONTRIBUTING.md +10 -9
- {pyzotero-1.6.8 → pyzotero-1.6.10}/CONTRIBUTORS.md +2 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/PKG-INFO +5 -2
- {pyzotero-1.6.8 → pyzotero-1.6.10}/doc/conf.py +4 -5
- {pyzotero-1.6.8 → pyzotero-1.6.10}/doc/index.rst +6 -0
- pyzotero-1.6.10/dump_contributors.py +13 -0
- pyzotero-1.6.10/example/.gitignore +3 -0
- pyzotero-1.6.10/example/local_base_use.py +7 -0
- pyzotero-1.6.10/example/local_copy_pdf.py +63 -0
- pyzotero-1.6.10/example/local_get_item_detail.py +68 -0
- pyzotero-1.6.10/example/local_search_title.py +43 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/pyproject.toml +46 -1
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/_version.py +9 -4
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero/filetransport.py +3 -3
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero/zotero.py +317 -393
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero/zotero_errors.py +17 -17
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero.egg-info/PKG-INFO +5 -2
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero.egg-info/SOURCES.txt +5 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero.egg-info/requires.txt +1 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/test_async.py +3 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/test_zotero.py +10 -9
- {pyzotero-1.6.8 → pyzotero-1.6.10}/uv.lock +89 -0
- pyzotero-1.6.8/.github/pull_request_template.md +0 -18
- pyzotero-1.6.8/dump_contributors.py +0 -14
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.coveragerc +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.github/ISSUE_TEMPLATE/usage_question.md +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.github/dependabot.yml +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.gitignore +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/.readthedocs.yaml +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/AUTHORS +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/CITATION.cff +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/LICENSE.md +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/README.md +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/__init__.py +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/doc/Makefile +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/doc/_templates/layout.html +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/doc/cat.png +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/setup.cfg +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/setup.py +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero/__init__.py +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero.egg-info/dependency_links.txt +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/src/pyzotero.egg-info/top_level.txt +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/__init__.py +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/attachments_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/citation_doc.xml +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/collection_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/collection_tags.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/collection_versions.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/collections_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/creation_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/groups_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/item_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/item_fields.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/item_file.pdf +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/item_template.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/item_types.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/item_versions.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/items_doc.json +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/keys_doc.txt +0 -0
- {pyzotero-1.6.8 → pyzotero-1.6.10}/tests/api_responses/tags_doc.json +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!-- Thanks for opening a PR. Please read the following:
|
|
2
|
+
|
|
3
|
+
- **Base your changes on the `main` branch**
|
|
4
|
+
- If necessary, rebase against `main` before opening a pull request
|
|
5
|
+
- This codebase uses Ruff. PRs that reformat code will not be accepted.
|
|
6
|
+
- Ensure that all methods added have a proper docstring. **Please do not use Doctest**
|
|
7
|
+
- If at all possible, don't add dependencies
|
|
8
|
+
- If it is unavoidable, you must ensure that the dependency is maintained, and supported
|
|
9
|
+
- Ensure that you add your dependency to [pyproject.toml](pyproject.toml)
|
|
10
|
+
- Run the tests and ensure that they pass. If you are adding a feature **you must add tests that exercise it**
|
|
11
|
+
- If your pull request is a feature **document the feature**
|
|
12
|
+
- One feature per pull request
|
|
13
|
+
- [squash](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) your commits before opening a pull request unless it makes no sense to do so
|
|
14
|
+
- If in doubt, comment your code.
|
|
15
|
+
|
|
16
|
+
## License of Contributed Code
|
|
17
|
+
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed under the Blua Oak Model License 1.0, without any additional terms or conditions.
|
|
18
|
+
Please note that pull requests with licenses that are more restrictive than or otherwise incompatible with the license will not be accepted. -->
|
|
19
|
+
|
|
20
|
+
Description of changes:
|
|
21
|
+
Issue reference (if applicable):
|
|
22
|
+
|
|
23
|
+
- [ ] I have read [the CONTRIBUTING doc](CONTRIBUTING.md)
|
|
@@ -3,6 +3,19 @@ name: Build wheel, release and publish on new tag
|
|
|
3
3
|
on: [push, pull_request]
|
|
4
4
|
|
|
5
5
|
jobs:
|
|
6
|
+
lint:
|
|
7
|
+
name: run Ruff
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
with:
|
|
12
|
+
fetch-depth: 0 # Optional, use if you use setuptools_scm
|
|
13
|
+
submodules: false # Optional, use if you have submodules
|
|
14
|
+
name: Check out repo
|
|
15
|
+
- uses: astral-sh/ruff-action@v3
|
|
16
|
+
with:
|
|
17
|
+
args: "check --verbose"
|
|
18
|
+
|
|
6
19
|
build:
|
|
7
20
|
name: Build and test
|
|
8
21
|
runs-on: ubuntu-latest
|
|
@@ -11,27 +24,25 @@ jobs:
|
|
|
11
24
|
python-version: [3.9, 3.10.9, 3.11, 3.12, 3.13]
|
|
12
25
|
|
|
13
26
|
steps:
|
|
14
|
-
-
|
|
27
|
+
- name: check out repo
|
|
28
|
+
uses: actions/checkout@v4
|
|
15
29
|
with:
|
|
16
30
|
fetch-depth: 0 # Optional, use if you use setuptools_scm
|
|
17
31
|
submodules: false # Optional, use if you have submodules
|
|
18
|
-
|
|
19
|
-
- name:
|
|
20
|
-
uses:
|
|
32
|
+
|
|
33
|
+
- name: Install uv and set the python version
|
|
34
|
+
uses: astral-sh/setup-uv@v5
|
|
21
35
|
with:
|
|
22
36
|
python-version: ${{ matrix.python-version }}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
pytest
|
|
37
|
+
|
|
38
|
+
- name: Install the project and deps
|
|
39
|
+
run: uv sync --all-extras --dev
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: uv run pytest
|
|
43
|
+
|
|
31
44
|
- name: Build wheel
|
|
32
|
-
run:
|
|
33
|
-
python -m pip install --upgrade pip
|
|
34
|
-
pip wheel . -w dist --no-deps --use-pep517
|
|
45
|
+
run: uv build --no-sources --wheel -o dist
|
|
35
46
|
|
|
36
47
|
- uses: actions/upload-artifact@v4
|
|
37
48
|
name: Upload wheel as artifact
|
|
@@ -49,17 +60,31 @@ jobs:
|
|
|
49
60
|
fetch-depth: 0 # Optional, use if you use setuptools_scm
|
|
50
61
|
submodules: true # Optional, use if you have submodules
|
|
51
62
|
|
|
63
|
+
- name: Install uv
|
|
64
|
+
uses: astral-sh/setup-uv@v5
|
|
65
|
+
|
|
52
66
|
- name: Build SDist
|
|
53
|
-
run:
|
|
67
|
+
run: uv build --no-sources --sdist -o dist
|
|
54
68
|
|
|
55
69
|
- uses: actions/upload-artifact@v4
|
|
56
70
|
with:
|
|
57
71
|
path: dist/*.tar.gz
|
|
58
72
|
|
|
73
|
+
status_check:
|
|
74
|
+
name: All Checks
|
|
75
|
+
needs: [lint, build]
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
if: always()
|
|
78
|
+
steps:
|
|
79
|
+
- name: Check status
|
|
80
|
+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
|
81
|
+
run: exit 1
|
|
82
|
+
|
|
83
|
+
|
|
59
84
|
release_artifacts:
|
|
60
85
|
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
|
61
86
|
name: Release repaired and tested wheels
|
|
62
|
-
needs:
|
|
87
|
+
needs: [status_check, make_sdist]
|
|
63
88
|
runs-on: ubuntu-latest
|
|
64
89
|
environment: release
|
|
65
90
|
permissions:
|
|
@@ -85,6 +110,6 @@ jobs:
|
|
|
85
110
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
86
111
|
|
|
87
112
|
- name: PyPI Publish
|
|
88
|
-
uses: pypa/gh-action-pypi-publish@v1
|
|
113
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
89
114
|
with:
|
|
90
115
|
packages-dir: ${{ steps.download.outputs.download-path }}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# Contributing
|
|
2
2
|
Contributions are welcome. Please bear the following in mind:
|
|
3
3
|
|
|
4
|
-
- **Base your changes on the
|
|
5
|
-
- If necessary, rebase against `
|
|
6
|
-
-
|
|
7
|
-
-
|
|
4
|
+
- **Base your changes on the `main` branch**
|
|
5
|
+
- If necessary, rebase against `main` before opening a pull request
|
|
6
|
+
- This codebase uses Ruff, and PRs are gated on `ruff check` passing. Run `ruff check` locally before opening a PR
|
|
7
|
+
- PRs that reformat code will not be accepted.
|
|
8
|
+
- Ensure that all methods added have a proper docstring. **Please do not use Doctest**
|
|
8
9
|
- If at all possible, don't add dependencies
|
|
9
|
-
- If it
|
|
10
|
-
- Ensure that you add your dependency to [
|
|
11
|
-
- Run the tests
|
|
12
|
-
- If your pull request is a feature
|
|
10
|
+
- If it is unavoidable, you must ensure that the dependency is maintained, and supported
|
|
11
|
+
- Ensure that you add your dependency to [pyproject.toml](pyproject.toml)
|
|
12
|
+
- Run the tests and ensure that they pass. If you are adding a feature **you must add tests that exercise it**
|
|
13
|
+
- If your pull request is a feature **document the feature**
|
|
13
14
|
- One feature per pull request
|
|
14
|
-
-
|
|
15
|
+
- [squash](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) your commits before opening a pull request unless it makes no sense to do so
|
|
15
16
|
- If in doubt, comment your code.
|
|
16
17
|
|
|
17
18
|
# License of Contributed Code
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
| 1 | [christianbrodbeck](https://github.com/urschrei/pyzotero/commits?author=christianbrodbeck) |
|
|
15
15
|
| 1 | [egh](https://github.com/urschrei/pyzotero/commits?author=egh) |
|
|
16
16
|
| 1 | [fmagin](https://github.com/urschrei/pyzotero/commits?author=fmagin) |
|
|
17
|
+
| 1 | [pnb](https://github.com/urschrei/pyzotero/commits?author=pnb) |
|
|
17
18
|
| 1 | [porduna](https://github.com/urschrei/pyzotero/commits?author=porduna) |
|
|
18
19
|
| 1 | [stakats](https://github.com/urschrei/pyzotero/commits?author=stakats) |
|
|
19
20
|
| 1 | [epistemery](https://github.com/urschrei/pyzotero/commits?author=epistemery) |
|
|
21
|
+
| 1 | [tnajdek](https://github.com/urschrei/pyzotero/commits?author=tnajdek) |
|
|
20
22
|
| 1 | [jghauser](https://github.com/urschrei/pyzotero/commits?author=jghauser) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyzotero
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.10
|
|
4
4
|
Summary: Python wrapper for the Zotero API
|
|
5
5
|
Author-email: Stephan Hügel <urschrei@gmail.com>
|
|
6
6
|
License: # Blue Oak Model License
|
|
@@ -41,6 +41,7 @@ License: # Blue Oak Model License
|
|
|
41
41
|
|
|
42
42
|
Project-URL: Repository, https://github.com/urschrei/pyzotero
|
|
43
43
|
Project-URL: Tracker, https://github.com/urschrei/pyzotero/issues
|
|
44
|
+
Project-URL: documentation, https://pyzotero.readthedocs.org
|
|
44
45
|
Keywords: Zotero,DH
|
|
45
46
|
Classifier: Programming Language :: Python
|
|
46
47
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -69,6 +70,8 @@ Requires-Dist: httpretty; extra == "test"
|
|
|
69
70
|
Requires-Dist: python-dateutil; extra == "test"
|
|
70
71
|
Requires-Dist: ipython; extra == "test"
|
|
71
72
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
73
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == "test"
|
|
74
|
+
Dynamic: license-file
|
|
72
75
|
|
|
73
76
|
[](https://pypi.python.org/pypi/Pyzotero/) [](http://pyzotero.readthedocs.org/en/latest/?badge=latest) [](https://pypi.python.org/pypi/Pyzotero) [](https://anaconda.org/conda-forge/pyzotero) [](https://pepy.tech/project/pyzotero)
|
|
74
77
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Pyzotero documentation build configuration file, created by
|
|
4
3
|
# sphinx-quickstart on Mon Jul 4 19:52:05 2011.
|
|
@@ -11,9 +10,9 @@
|
|
|
11
10
|
# All configuration values have a default; values that are commented out
|
|
12
11
|
# serve to show the default.
|
|
13
12
|
|
|
13
|
+
import datetime
|
|
14
14
|
import os
|
|
15
15
|
import sys
|
|
16
|
-
from datetime import date
|
|
17
16
|
|
|
18
17
|
sys.path.insert(1, "..")
|
|
19
18
|
import pyzotero
|
|
@@ -26,7 +25,7 @@ if os.environ.get("READTHEDOCS", "") == "True":
|
|
|
26
25
|
html_context["READTHEDOCS"] = True
|
|
27
26
|
|
|
28
27
|
author = zot.__author__
|
|
29
|
-
current_year =
|
|
28
|
+
current_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
|
|
30
29
|
|
|
31
30
|
html_context = {
|
|
32
31
|
"display_github": True, # Integrate GitHub
|
|
@@ -63,7 +62,7 @@ master_doc = "index"
|
|
|
63
62
|
|
|
64
63
|
# General information about the project.
|
|
65
64
|
project = "Pyzotero"
|
|
66
|
-
copyright = "2011
|
|
65
|
+
copyright = f"2011 - {current_year}, {author}"
|
|
67
66
|
|
|
68
67
|
# The version info for the project you're documenting, acts as replacement for
|
|
69
68
|
# |version| and |release|, also used in various other places throughout the
|
|
@@ -240,7 +239,7 @@ man_pages = [("index", "pyzotero", "Pyzotero Documentation", [author], 1)]
|
|
|
240
239
|
epub_title = "Pyzotero"
|
|
241
240
|
epub_author = author
|
|
242
241
|
epub_publisher = author
|
|
243
|
-
epub_copyright = "2011
|
|
242
|
+
epub_copyright = f"2011 - {current_year}, {author}"
|
|
244
243
|
|
|
245
244
|
# The language of the text. It defaults to the language option
|
|
246
245
|
# or en if the language is not set.
|
|
@@ -168,6 +168,12 @@ The following methods will retrieve either user or group items, depending on the
|
|
|
168
168
|
|
|
169
169
|
:rtype: list of dicts
|
|
170
170
|
|
|
171
|
+
.. py:method:: Zotero.settings([since])
|
|
172
|
+
|
|
173
|
+
Returns synced Zotero user library settings such as feeds and PDF reading progress. Use the optional ``since`` parameter to specify the retrieval of changes since a known previous library version.
|
|
174
|
+
|
|
175
|
+
:rtype: list of dicts
|
|
176
|
+
|
|
171
177
|
.. py:method:: Zotero.count_items()
|
|
172
178
|
|
|
173
179
|
Returns a count of all items in a library / group
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
3
|
+
url = "https://api.github.com/repos/urschrei/pyzotero/contributors"
|
|
4
|
+
result = httpx.get(url)
|
|
5
|
+
result.raise_for_status()
|
|
6
|
+
as_dict = [d for d in result.json() if not d["login"].lower().startswith("dependabot")]
|
|
7
|
+
# remove me from the list
|
|
8
|
+
as_dict.pop(0)
|
|
9
|
+
header = "# This is the list of people (as distinct from [AUTHORS](AUTHORS)) who have contributed code to Pyzotero.\n\n| **Commits** | **Contributor**<br/> |\n| --- |--- |\n"
|
|
10
|
+
template = "| {contributions} | [{login}](https://github.com/urschrei/pyzotero/commits?author={login}) |\n"
|
|
11
|
+
with open("CONTRIBUTORS.md", "w", encoding="utf-8") as f:
|
|
12
|
+
f.write(header)
|
|
13
|
+
f.writelines(template.format(**dct) for dct in as_dict)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from pyzotero import zotero
|
|
2
|
+
zot = zotero.Zotero(library_id='000000', library_type = 'user', local=True) # local=True for read access to local Zotero
|
|
3
|
+
items = zot.top(limit=5)
|
|
4
|
+
# we've retrieved the latest five top-level items in our library
|
|
5
|
+
# we can print each item's item type and ID
|
|
6
|
+
for item in items:
|
|
7
|
+
print(f"Item: {item['data']['itemType']} | Key: {item['data']['key']}")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from pyzotero import zotero
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
def copy_specific_pdf(item_id, output_dir, new_name=None):
|
|
5
|
+
"""
|
|
6
|
+
Copy a specific PDF attachment from Zotero library to specified folder using dump()
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
item_id (str): Zotero item ID
|
|
10
|
+
output_dir (str): Path to output directory
|
|
11
|
+
new_name (str, optional): New filename for the PDF. If None, uses original name
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
# Initialize Zotero client with local=True
|
|
15
|
+
zot = zotero.Zotero(library_id='000000', library_type='user', local=True)
|
|
16
|
+
|
|
17
|
+
# Create output directory if it doesn't exist
|
|
18
|
+
output_path = Path(output_dir)
|
|
19
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
# Get the item metadata first
|
|
23
|
+
item = zot.item(item_id)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Get original filename or use default
|
|
27
|
+
default_filename = f"{item_id}.pdf"
|
|
28
|
+
original_filename = item['data'].get('filename', default_filename)
|
|
29
|
+
|
|
30
|
+
# Use new_name if provided, otherwise use original filename
|
|
31
|
+
filename = new_name if new_name else original_filename
|
|
32
|
+
|
|
33
|
+
# Add .pdf extension if not present
|
|
34
|
+
if not filename.lower().endswith('.pdf'):
|
|
35
|
+
filename += '.pdf'
|
|
36
|
+
|
|
37
|
+
# Use dump() with explicit filename
|
|
38
|
+
full_path = zot.dump(
|
|
39
|
+
item_id,
|
|
40
|
+
filename=filename,
|
|
41
|
+
path=str(output_path)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
print(f"\nSuccessfully copied file to: {full_path}")
|
|
45
|
+
print(f"Title: {item['data'].get('title', 'No title')}")
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
print(f"Error copying file: {e!s}")
|
|
49
|
+
print(f"Item data: {item['data']}") # Debug info
|
|
50
|
+
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"Error accessing Zotero item: {e!s}")
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
# Example usage with specific item ID
|
|
56
|
+
item_id = '8M9FYC2W'
|
|
57
|
+
data_dir = "./example/data/pdfs"
|
|
58
|
+
|
|
59
|
+
# Example 1: Copy with new name
|
|
60
|
+
copy_specific_pdf(item_id, data_dir, new_name="my_paper")
|
|
61
|
+
|
|
62
|
+
# Example 2: Copy with original filename
|
|
63
|
+
copy_specific_pdf(item_id, data_dir)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from pyzotero import zotero
|
|
2
|
+
from pprint import pprint
|
|
3
|
+
|
|
4
|
+
def get_item_detail(item_id):
|
|
5
|
+
"""
|
|
6
|
+
Get detailed information about a specific Zotero item
|
|
7
|
+
Args:
|
|
8
|
+
item_id (str): Zotero item ID
|
|
9
|
+
"""
|
|
10
|
+
# Initialize Zotero client with local=True
|
|
11
|
+
zot = zotero.Zotero(library_id='000000', library_type='user', local=True)
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
# Get the item
|
|
15
|
+
item = zot.item(item_id)
|
|
16
|
+
|
|
17
|
+
# Print basic information
|
|
18
|
+
print("\nItem Details:")
|
|
19
|
+
print("-" * 50)
|
|
20
|
+
print(f"Item ID: {item['key']}")
|
|
21
|
+
print(f"Item Type: {item['data'].get('itemType', 'Not specified')}")
|
|
22
|
+
print(f"Title: {item['data'].get('title', 'No title')}")
|
|
23
|
+
|
|
24
|
+
# If it's an attachment, show parent item
|
|
25
|
+
if item['data'].get('parentItem'):
|
|
26
|
+
try:
|
|
27
|
+
parent = zot.item(item['data']['parentItem'])
|
|
28
|
+
print("\nParent Item:")
|
|
29
|
+
print(f"Parent ID: {parent['key']}")
|
|
30
|
+
print(f"Parent Title: {parent['data'].get('title', 'No title')}")
|
|
31
|
+
except Exception as e:
|
|
32
|
+
print(f"Error getting parent item: {e!s}")
|
|
33
|
+
|
|
34
|
+
# If it has child items (attachments), show them
|
|
35
|
+
children = zot.children(item_id)
|
|
36
|
+
if children:
|
|
37
|
+
print("\nChild Items:")
|
|
38
|
+
for child in children:
|
|
39
|
+
print(f"- {child['data'].get('title', 'No title')} "
|
|
40
|
+
f"(ID: {child['key']}, "
|
|
41
|
+
f"Type: {child['data'].get('itemType', 'Unknown')})")
|
|
42
|
+
|
|
43
|
+
# Show collections this item belongs to
|
|
44
|
+
collections = item['data'].get('collections', [])
|
|
45
|
+
if collections:
|
|
46
|
+
print("\nCollections:")
|
|
47
|
+
try:
|
|
48
|
+
for coll_id in collections:
|
|
49
|
+
coll = zot.collection(coll_id)
|
|
50
|
+
print(f"- {coll['data'].get('name', 'Unnamed')} (ID: {coll_id})")
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"Error retrieving collections: {e!s}")
|
|
53
|
+
|
|
54
|
+
# Show all metadata
|
|
55
|
+
print("\nFull Metadata:")
|
|
56
|
+
print("-" * 50)
|
|
57
|
+
pprint(item['data'])
|
|
58
|
+
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"Error getting item details: {e!s}")
|
|
61
|
+
return None
|
|
62
|
+
else:
|
|
63
|
+
return item
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
# Example usage with a specific item ID
|
|
67
|
+
item_id = 'K9V7JFXY' # Replace with your item ID
|
|
68
|
+
item_detail = get_item_detail(item_id)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from pyzotero import zotero
|
|
2
|
+
|
|
3
|
+
def search_by_title(title_query):
|
|
4
|
+
"""
|
|
5
|
+
Search Zotero items by title
|
|
6
|
+
Args:
|
|
7
|
+
title_query (str): The title or part of title to search for
|
|
8
|
+
Returns:
|
|
9
|
+
list: List of matching items
|
|
10
|
+
"""
|
|
11
|
+
# Initialize Zotero client with local=True
|
|
12
|
+
zot = zotero.Zotero(library_id='000000', library_type='user', local=True)
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
# Search for items where title contains the query string
|
|
16
|
+
results = zot.items(q=title_query)
|
|
17
|
+
|
|
18
|
+
# Print results
|
|
19
|
+
print(f"\nFound {len(results)} items matching '{title_query}':")
|
|
20
|
+
for item in results:
|
|
21
|
+
# Get the item data
|
|
22
|
+
title = item['data'].get('title', 'No title')
|
|
23
|
+
item_type = item['data'].get('itemType', 'Unknown type')
|
|
24
|
+
date = item['data'].get('date', 'No date')
|
|
25
|
+
item_id = item['data'].get('key', 'No ID')
|
|
26
|
+
|
|
27
|
+
print("\n##########################")
|
|
28
|
+
print(f"ID: {item_id}")
|
|
29
|
+
print(f"Title: {title}")
|
|
30
|
+
print(f"Type: {item_type}")
|
|
31
|
+
print(f"Date: {date}")
|
|
32
|
+
print("##########################\n")
|
|
33
|
+
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(f"Error searching Zotero: {e!s}")
|
|
36
|
+
return []
|
|
37
|
+
else:
|
|
38
|
+
return results
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
# Example usage
|
|
42
|
+
search_query = "uORFs" # Change this to your search term
|
|
43
|
+
search_by_title(search_query)
|
|
@@ -30,6 +30,7 @@ classifiers = [
|
|
|
30
30
|
[project.urls]
|
|
31
31
|
Repository = "https://github.com/urschrei/pyzotero"
|
|
32
32
|
Tracker = "https://github.com/urschrei/pyzotero/issues"
|
|
33
|
+
documentation = "https://pyzotero.readthedocs.org"
|
|
33
34
|
|
|
34
35
|
[project.optional-dependencies]
|
|
35
36
|
test = [
|
|
@@ -38,6 +39,7 @@ test = [
|
|
|
38
39
|
"python-dateutil",
|
|
39
40
|
"ipython",
|
|
40
41
|
"pytest-asyncio",
|
|
42
|
+
"pytest-cov>=6.0.0",
|
|
41
43
|
]
|
|
42
44
|
|
|
43
45
|
[tool.setuptools.dynamic]
|
|
@@ -53,7 +55,7 @@ requires = [
|
|
|
53
55
|
[tool.pytest.ini_options]
|
|
54
56
|
minversion = "6.2.2"
|
|
55
57
|
addopts = [
|
|
56
|
-
"--import-mode=importlib",
|
|
58
|
+
"--import-mode=importlib", "--cov=pyzotero", "--cov-report=xml",
|
|
57
59
|
]
|
|
58
60
|
testpaths = [
|
|
59
61
|
"tests",
|
|
@@ -63,3 +65,46 @@ asyncio_default_fixture_loop_scope = "function"
|
|
|
63
65
|
|
|
64
66
|
[tool.setuptools_scm]
|
|
65
67
|
write_to = "src/_version.py"
|
|
68
|
+
|
|
69
|
+
[tool.ruff]
|
|
70
|
+
exclude = [
|
|
71
|
+
".bzr",
|
|
72
|
+
".direnv",
|
|
73
|
+
".eggs",
|
|
74
|
+
".git",
|
|
75
|
+
".git-rewrite",
|
|
76
|
+
".hg",
|
|
77
|
+
".ipynb_checkpoints",
|
|
78
|
+
".mypy_cache",
|
|
79
|
+
".nox",
|
|
80
|
+
".pants.d",
|
|
81
|
+
".pyenv",
|
|
82
|
+
".pytest_cache",
|
|
83
|
+
".pytype",
|
|
84
|
+
".ruff_cache",
|
|
85
|
+
".svn",
|
|
86
|
+
".tox",
|
|
87
|
+
".venv",
|
|
88
|
+
".vscode",
|
|
89
|
+
"__pypackages__",
|
|
90
|
+
"_build",
|
|
91
|
+
"buck-out",
|
|
92
|
+
"build",
|
|
93
|
+
"dist",
|
|
94
|
+
"node_modules",
|
|
95
|
+
"site-packages",
|
|
96
|
+
"venv",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint]
|
|
100
|
+
select = ["F", "E", "W", "N", "D4", "UP", "PL", "TRY", "PERF", "FURB", "RUF", "S", "DTZ"]
|
|
101
|
+
ignore = ["ANN001", "ANN003", "ANN202", "ANN201", "DOC201", "E501", "PLR0904", "PLR0913", "PLR0917", "SLF001", "FIX002", "D400", "D415"]
|
|
102
|
+
fixable = ["ALL"]
|
|
103
|
+
unfixable = []
|
|
104
|
+
|
|
105
|
+
[tool.ruff.format]
|
|
106
|
+
# Like Black, use double quotes for strings.
|
|
107
|
+
quote-style = "double"
|
|
108
|
+
|
|
109
|
+
# Like Black, indent with spaces, rather than tabs.
|
|
110
|
+
indent-style = "space"
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# file generated by
|
|
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
|
|
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 = '1.6.
|
|
16
|
-
__version_tuple__ = version_tuple = (1, 6,
|
|
20
|
+
__version__ = version = '1.6.10'
|
|
21
|
+
__version_tuple__ = version_tuple = (1, 6, 10)
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
import asyncio
|
|
34
34
|
from pathlib import Path
|
|
35
|
-
from typing import Optional
|
|
35
|
+
from typing import Optional
|
|
36
36
|
|
|
37
37
|
import httpx
|
|
38
38
|
from httpx import (
|
|
@@ -65,7 +65,7 @@ httpx.URL.is_absolute_url = property(is_absolute_url) # type: ignore
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
class FileTransport(AsyncBaseTransport, BaseTransport):
|
|
68
|
-
def _handle(self, request: Request) ->
|
|
68
|
+
def _handle(self, request: Request) -> tuple[Optional[int], httpx.Headers]:
|
|
69
69
|
if request.url.host and request.url.host != "localhost":
|
|
70
70
|
raise NotImplementedError("Only local paths are allowed")
|
|
71
71
|
if request.method in {"PUT", "DELETE"}:
|
|
@@ -148,4 +148,4 @@ class AsyncClient(_AsyncClient):
|
|
|
148
148
|
self._mounts.update({URLPattern(protocol): transport})
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
__all__ = ["
|
|
151
|
+
__all__ = ["AsyncClient", "Client", "FileTransport"]
|