zotlib 0.4.3__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.
- zotlib-0.4.3/.github/workflows/publish.yml +37 -0
- zotlib-0.4.3/.github/workflows/test.yml +29 -0
- zotlib-0.4.3/.gitignore +32 -0
- zotlib-0.4.3/.python-version +1 -0
- zotlib-0.4.3/PKG-INFO +219 -0
- zotlib-0.4.3/README.md +207 -0
- zotlib-0.4.3/TODO.md +19 -0
- zotlib-0.4.3/pyproject.toml +35 -0
- zotlib-0.4.3/scripts/create-parent-item.js +74 -0
- zotlib-0.4.3/scripts/extract-annotations.js +189 -0
- zotlib-0.4.3/scripts/run-extract.sh +29 -0
- zotlib-0.4.3/tests/__init__.py +0 -0
- zotlib-0.4.3/tests/conftest.py +49 -0
- zotlib-0.4.3/tests/test_backup.py +74 -0
- zotlib-0.4.3/tests/test_cli.py +61 -0
- zotlib-0.4.3/tests/test_config.py +99 -0
- zotlib-0.4.3/tests/test_database.py +59 -0
- zotlib-0.4.3/tests/test_exporters.py +357 -0
- zotlib-0.4.3/tests/test_extractors.py +64 -0
- zotlib-0.4.3/tests/test_formatters.py +40 -0
- zotlib-0.4.3/tests/test_paths.py +45 -0
- zotlib-0.4.3/uv.lock +1116 -0
- zotlib-0.4.3/zotlib/__init__.py +24 -0
- zotlib-0.4.3/zotlib/backup.py +98 -0
- zotlib-0.4.3/zotlib/cli.py +448 -0
- zotlib-0.4.3/zotlib/config.py +184 -0
- zotlib-0.4.3/zotlib/covers.py +113 -0
- zotlib-0.4.3/zotlib/database.py +67 -0
- zotlib-0.4.3/zotlib/exporters.py +601 -0
- zotlib-0.4.3/zotlib/extractors.py +230 -0
- zotlib-0.4.3/zotlib/formatters/__init__.py +5 -0
- zotlib-0.4.3/zotlib/formatters/apa.py +86 -0
- zotlib-0.4.3/zotlib/paths.py +43 -0
- zotlib-0.4.3/zotlib/tables.py +828 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
types:
|
|
8
|
+
- closed
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
pypi-publish:
|
|
15
|
+
name: Upload release to PyPI
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
if: github.event.pull_request.merged == true
|
|
18
|
+
environment:
|
|
19
|
+
name: pypi
|
|
20
|
+
url: https://pypi.org/project/zotlib/
|
|
21
|
+
permissions:
|
|
22
|
+
id-token: write
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout code
|
|
25
|
+
uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v5
|
|
29
|
+
|
|
30
|
+
- name: Set up Python 3.14
|
|
31
|
+
run: uv python install 3.14
|
|
32
|
+
|
|
33
|
+
- name: Package project
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
- name: Publish package distributions to PyPI
|
|
37
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [dev, main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [dev, main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v5
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
run: uv python install ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --all-groups --python ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Run tests with coverage
|
|
29
|
+
run: uv run pytest -v --cov=zotlib --cov-report=term-missing
|
zotlib-0.4.3/.gitignore
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
|
|
8
|
+
# Environment
|
|
9
|
+
.env
|
|
10
|
+
.venv/
|
|
11
|
+
|
|
12
|
+
# IDE
|
|
13
|
+
.vscode/
|
|
14
|
+
.idea/
|
|
15
|
+
.claude
|
|
16
|
+
|
|
17
|
+
# Testing
|
|
18
|
+
.pytest_cache
|
|
19
|
+
.coverage
|
|
20
|
+
|
|
21
|
+
# Linting
|
|
22
|
+
.ruff_cache
|
|
23
|
+
|
|
24
|
+
# Data/output
|
|
25
|
+
data
|
|
26
|
+
output/
|
|
27
|
+
*.csv
|
|
28
|
+
*.sqlite
|
|
29
|
+
zotlib.toml
|
|
30
|
+
|
|
31
|
+
# Archive
|
|
32
|
+
.archive/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
zotlib-0.4.3/PKG-INFO
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zotlib
|
|
3
|
+
Version: 0.4.3
|
|
4
|
+
Summary: Extract and format bibliographic data from Zotero databases
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: pillow<13.0.0,>=12.1.0
|
|
7
|
+
Requires-Dist: polars>=1.39.2
|
|
8
|
+
Requires-Dist: pymupdf<2.0.0,>=1.26.7
|
|
9
|
+
Requires-Dist: rich>=13.0.0
|
|
10
|
+
Requires-Dist: typer>=0.12.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# zotlib
|
|
14
|
+
|
|
15
|
+
Tools for extracting and formatting bibliographic data from Zotero 8 databases.
|
|
16
|
+
|
|
17
|
+
Reads directly from Zotero's local SQLite database — no API key needed. Export collections as CSV or APA-formatted references, generate PDF cover images with thumbnails, and extract annotated PDFs with baked-in highlights and markdown notes. Includes a CLI for common workflows and a Python API for custom pipelines. Built for Zotero 8; older versions are untested and likely incompatible due to schema differences.
|
|
18
|
+
|
|
19
|
+
## Project Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
zotlib/
|
|
23
|
+
├── zotlib/ # Python library
|
|
24
|
+
│ ├── cli.py # CLI commands
|
|
25
|
+
│ ├── config.py # Database path discovery
|
|
26
|
+
│ ├── database.py # SQLite interface
|
|
27
|
+
│ ├── extractors.py # Data extraction functions
|
|
28
|
+
│ ├── exporters.py # Collection export (annotations + PDFs)
|
|
29
|
+
│ ├── backup.py # Zotero directory backup
|
|
30
|
+
│ ├── tables.py # Zotero database table definitions
|
|
31
|
+
│ ├── covers.py # PDF cover generation
|
|
32
|
+
│ ├── paths.py # Path resolution and filename utilities
|
|
33
|
+
│ └── formatters/apa.py # APA citation formatter
|
|
34
|
+
├── scripts/ # Utility scripts
|
|
35
|
+
│ ├── extract-annotations.js # Annotation extractor (interactive + headless)
|
|
36
|
+
│ ├── create-parent-item.js # Create parents for standalone PDFs
|
|
37
|
+
│ └── run-extract.sh # Shell wrapper for headless extraction
|
|
38
|
+
├── tests/ # Test suite
|
|
39
|
+
└── pyproject.toml # Project configuration
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add zotlib
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
From source:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/gitronald/zotlib.git
|
|
52
|
+
cd zotlib
|
|
53
|
+
uv sync
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
From a specific branch:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv add git+https://github.com/gitronald/zotlib.git@dev
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
Run `zotlib init` to auto-discover Zotero paths and save them to `zotlib.toml`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
zotlib init
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
database: /mnt/c/Users/rer/Zotero/zotero.sqlite
|
|
72
|
+
pdfs_dir: /mnt/i/My Drive/zotero-pdfs
|
|
73
|
+
|
|
74
|
+
Saved to zotlib.toml
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The config file stores the database and linked PDFs directory:
|
|
78
|
+
|
|
79
|
+
```toml
|
|
80
|
+
[zotlib]
|
|
81
|
+
database = "/path/to/zotero.sqlite"
|
|
82
|
+
pdfs_dir = "/path/to/linked-pdfs"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Path resolution priority (for both database and PDFs dir):
|
|
86
|
+
|
|
87
|
+
1. **CLI flag**: `--database`, `--pdfs-dir`
|
|
88
|
+
2. **Environment variable**: `ZOTERO_DATABASE`
|
|
89
|
+
3. **Config file**: `zotlib.toml`
|
|
90
|
+
4. **Auto-discovery**: Checks common locations (Linux, WSL, macOS)
|
|
91
|
+
|
|
92
|
+
## CLI Commands
|
|
93
|
+
|
|
94
|
+
### Explore
|
|
95
|
+
|
|
96
|
+
Browse collections and inspect database schema. The `show-tables` command documents Zotero's largely undocumented SQLite table structure, including column descriptions and types.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# List available collections
|
|
100
|
+
zotlib show-collections
|
|
101
|
+
|
|
102
|
+
# Show database tables
|
|
103
|
+
zotlib show-tables
|
|
104
|
+
zotlib show-tables items
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Export
|
|
108
|
+
|
|
109
|
+
Export collection data in multiple formats. Supports linked attachments via `--pdfs-dir` for PDFs stored outside Zotero's default storage.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Export all tables as CSV
|
|
113
|
+
zotlib export-csv
|
|
114
|
+
|
|
115
|
+
# Export a collection as CSV
|
|
116
|
+
zotlib export-csv -c publications
|
|
117
|
+
|
|
118
|
+
# Format a collection as APA references
|
|
119
|
+
zotlib export-apa -c publications
|
|
120
|
+
|
|
121
|
+
# Generate cover images and thumbnails
|
|
122
|
+
zotlib export-covers -c publications
|
|
123
|
+
|
|
124
|
+
# Export annotated PDFs and markdown notes
|
|
125
|
+
zotlib export-annotations -c mycollection
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Backup
|
|
129
|
+
|
|
130
|
+
Archive the entire Zotero data directory as a compressed `.tar.bz2` file with a progress bar. Saves to `data/backups/zotero-YYYY-MM-DD.tar.bz2` by default. Use `-o` to specify a custom output path or `-d` to point to a different database.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
zotlib backup
|
|
134
|
+
zotlib backup -o ~/backups/zotero-2026-03-21.tar.bz2
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Output structure
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
output/
|
|
141
|
+
├── export-csv/ # Bibliographic metadata
|
|
142
|
+
│ └── publications.csv
|
|
143
|
+
├── export-apa/ # APA-formatted references
|
|
144
|
+
│ └── publications.md
|
|
145
|
+
├── export-covers/ # PDF cover images
|
|
146
|
+
│ └── publications/
|
|
147
|
+
│ ├── fullsize/
|
|
148
|
+
│ └── thumbnails/
|
|
149
|
+
└── export-annotations/ # Annotated PDFs + notes
|
|
150
|
+
└── mycollection/
|
|
151
|
+
└── author-year-title/
|
|
152
|
+
├── paper.pdf
|
|
153
|
+
└── annotations.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Export annotations features
|
|
157
|
+
|
|
158
|
+
- Multi-attachment support: each PDF gets only its own annotations
|
|
159
|
+
- Standalone attachment support: PDFs added directly to a collection
|
|
160
|
+
- Linked attachment resolution via `--pdfs-dir`
|
|
161
|
+
- "REVIEW: " prefix stripping from titles
|
|
162
|
+
|
|
163
|
+
### Python API
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from zotlib import ZoteroDatabase, extract_cv_items, format_cv_as_apa
|
|
167
|
+
|
|
168
|
+
db = ZoteroDatabase("/path/to/zotero.sqlite")
|
|
169
|
+
items = extract_cv_items(db, collection_name="mypapers")
|
|
170
|
+
apa_output = format_cv_as_apa(items, output_path="output/apa.md")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Zotero JavaScript Scripts
|
|
174
|
+
|
|
175
|
+
**WIP** — Utilities for Zotero's JavaScript console (Tools > Developer > Run JavaScript). The Zotero SQLite database should never be modified directly via Python — use these JS scripts (which run through Zotero's API) for any write operations.
|
|
176
|
+
|
|
177
|
+
### create-parent-item.js
|
|
178
|
+
|
|
179
|
+
Creates parent document items for standalone PDF attachments in a collection. Useful when PDFs were added directly without metadata — creates a parent item using the filename as the title and re-parents the attachment.
|
|
180
|
+
|
|
181
|
+
### extract-annotations.js
|
|
182
|
+
|
|
183
|
+
Extracts annotations from the selected item's PDFs as markdown. Auto-detects its context:
|
|
184
|
+
|
|
185
|
+
- **Interactive** (Tools > Developer > Run JavaScript): shows a file save dialog
|
|
186
|
+
- **Headless** (via HTTP debug API): writes to `~/Desktop/zotero-annotations/`
|
|
187
|
+
|
|
188
|
+
To run headlessly:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
./scripts/run-extract.sh
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Requires: Settings > Advanced > "Allow other applications to communicate with Zotero"
|
|
195
|
+
|
|
196
|
+
The shell script should work on macOS where Zotero and the terminal share the same `localhost`. On WSL, the script calls Zotero's debug HTTP endpoint on `127.0.0.1:23119`, but `localhost` does not bridge to the Windows host by default. You may need to use the Windows host IP or run the curl command from PowerShell instead.
|
|
197
|
+
|
|
198
|
+
## Annotation Format
|
|
199
|
+
|
|
200
|
+
### Types
|
|
201
|
+
|
|
202
|
+
| Type | Extracted Data |
|
|
203
|
+
|------|----------------|
|
|
204
|
+
| Highlight | Text + comment + color |
|
|
205
|
+
| Note | Comment text |
|
|
206
|
+
| Underline | Text + comment |
|
|
207
|
+
| Image | Comment only (image not exported) |
|
|
208
|
+
|
|
209
|
+
### Color Labels
|
|
210
|
+
|
|
211
|
+
| Hex Code | Label |
|
|
212
|
+
|----------|-------|
|
|
213
|
+
| `#ffd400` | yellow |
|
|
214
|
+
| `#ff6666` | red |
|
|
215
|
+
| `#5fb236` | green |
|
|
216
|
+
| `#2ea8e5` | blue |
|
|
217
|
+
| `#a28ae5` | purple |
|
|
218
|
+
| `#e56eee` | magenta |
|
|
219
|
+
| `#f19837` | orange |
|
zotlib-0.4.3/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# zotlib
|
|
2
|
+
|
|
3
|
+
Tools for extracting and formatting bibliographic data from Zotero 8 databases.
|
|
4
|
+
|
|
5
|
+
Reads directly from Zotero's local SQLite database — no API key needed. Export collections as CSV or APA-formatted references, generate PDF cover images with thumbnails, and extract annotated PDFs with baked-in highlights and markdown notes. Includes a CLI for common workflows and a Python API for custom pipelines. Built for Zotero 8; older versions are untested and likely incompatible due to schema differences.
|
|
6
|
+
|
|
7
|
+
## Project Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
zotlib/
|
|
11
|
+
├── zotlib/ # Python library
|
|
12
|
+
│ ├── cli.py # CLI commands
|
|
13
|
+
│ ├── config.py # Database path discovery
|
|
14
|
+
│ ├── database.py # SQLite interface
|
|
15
|
+
│ ├── extractors.py # Data extraction functions
|
|
16
|
+
│ ├── exporters.py # Collection export (annotations + PDFs)
|
|
17
|
+
│ ├── backup.py # Zotero directory backup
|
|
18
|
+
│ ├── tables.py # Zotero database table definitions
|
|
19
|
+
│ ├── covers.py # PDF cover generation
|
|
20
|
+
│ ├── paths.py # Path resolution and filename utilities
|
|
21
|
+
│ └── formatters/apa.py # APA citation formatter
|
|
22
|
+
├── scripts/ # Utility scripts
|
|
23
|
+
│ ├── extract-annotations.js # Annotation extractor (interactive + headless)
|
|
24
|
+
│ ├── create-parent-item.js # Create parents for standalone PDFs
|
|
25
|
+
│ └── run-extract.sh # Shell wrapper for headless extraction
|
|
26
|
+
├── tests/ # Test suite
|
|
27
|
+
└── pyproject.toml # Project configuration
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv add zotlib
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
From source:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/gitronald/zotlib.git
|
|
40
|
+
cd zotlib
|
|
41
|
+
uv sync
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
From a specific branch:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv add git+https://github.com/gitronald/zotlib.git@dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Run `zotlib init` to auto-discover Zotero paths and save them to `zotlib.toml`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
zotlib init
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
database: /mnt/c/Users/rer/Zotero/zotero.sqlite
|
|
60
|
+
pdfs_dir: /mnt/i/My Drive/zotero-pdfs
|
|
61
|
+
|
|
62
|
+
Saved to zotlib.toml
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The config file stores the database and linked PDFs directory:
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
[zotlib]
|
|
69
|
+
database = "/path/to/zotero.sqlite"
|
|
70
|
+
pdfs_dir = "/path/to/linked-pdfs"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Path resolution priority (for both database and PDFs dir):
|
|
74
|
+
|
|
75
|
+
1. **CLI flag**: `--database`, `--pdfs-dir`
|
|
76
|
+
2. **Environment variable**: `ZOTERO_DATABASE`
|
|
77
|
+
3. **Config file**: `zotlib.toml`
|
|
78
|
+
4. **Auto-discovery**: Checks common locations (Linux, WSL, macOS)
|
|
79
|
+
|
|
80
|
+
## CLI Commands
|
|
81
|
+
|
|
82
|
+
### Explore
|
|
83
|
+
|
|
84
|
+
Browse collections and inspect database schema. The `show-tables` command documents Zotero's largely undocumented SQLite table structure, including column descriptions and types.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# List available collections
|
|
88
|
+
zotlib show-collections
|
|
89
|
+
|
|
90
|
+
# Show database tables
|
|
91
|
+
zotlib show-tables
|
|
92
|
+
zotlib show-tables items
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Export
|
|
96
|
+
|
|
97
|
+
Export collection data in multiple formats. Supports linked attachments via `--pdfs-dir` for PDFs stored outside Zotero's default storage.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Export all tables as CSV
|
|
101
|
+
zotlib export-csv
|
|
102
|
+
|
|
103
|
+
# Export a collection as CSV
|
|
104
|
+
zotlib export-csv -c publications
|
|
105
|
+
|
|
106
|
+
# Format a collection as APA references
|
|
107
|
+
zotlib export-apa -c publications
|
|
108
|
+
|
|
109
|
+
# Generate cover images and thumbnails
|
|
110
|
+
zotlib export-covers -c publications
|
|
111
|
+
|
|
112
|
+
# Export annotated PDFs and markdown notes
|
|
113
|
+
zotlib export-annotations -c mycollection
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Backup
|
|
117
|
+
|
|
118
|
+
Archive the entire Zotero data directory as a compressed `.tar.bz2` file with a progress bar. Saves to `data/backups/zotero-YYYY-MM-DD.tar.bz2` by default. Use `-o` to specify a custom output path or `-d` to point to a different database.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
zotlib backup
|
|
122
|
+
zotlib backup -o ~/backups/zotero-2026-03-21.tar.bz2
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Output structure
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
output/
|
|
129
|
+
├── export-csv/ # Bibliographic metadata
|
|
130
|
+
│ └── publications.csv
|
|
131
|
+
├── export-apa/ # APA-formatted references
|
|
132
|
+
│ └── publications.md
|
|
133
|
+
├── export-covers/ # PDF cover images
|
|
134
|
+
│ └── publications/
|
|
135
|
+
│ ├── fullsize/
|
|
136
|
+
│ └── thumbnails/
|
|
137
|
+
└── export-annotations/ # Annotated PDFs + notes
|
|
138
|
+
└── mycollection/
|
|
139
|
+
└── author-year-title/
|
|
140
|
+
├── paper.pdf
|
|
141
|
+
└── annotations.md
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Export annotations features
|
|
145
|
+
|
|
146
|
+
- Multi-attachment support: each PDF gets only its own annotations
|
|
147
|
+
- Standalone attachment support: PDFs added directly to a collection
|
|
148
|
+
- Linked attachment resolution via `--pdfs-dir`
|
|
149
|
+
- "REVIEW: " prefix stripping from titles
|
|
150
|
+
|
|
151
|
+
### Python API
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from zotlib import ZoteroDatabase, extract_cv_items, format_cv_as_apa
|
|
155
|
+
|
|
156
|
+
db = ZoteroDatabase("/path/to/zotero.sqlite")
|
|
157
|
+
items = extract_cv_items(db, collection_name="mypapers")
|
|
158
|
+
apa_output = format_cv_as_apa(items, output_path="output/apa.md")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Zotero JavaScript Scripts
|
|
162
|
+
|
|
163
|
+
**WIP** — Utilities for Zotero's JavaScript console (Tools > Developer > Run JavaScript). The Zotero SQLite database should never be modified directly via Python — use these JS scripts (which run through Zotero's API) for any write operations.
|
|
164
|
+
|
|
165
|
+
### create-parent-item.js
|
|
166
|
+
|
|
167
|
+
Creates parent document items for standalone PDF attachments in a collection. Useful when PDFs were added directly without metadata — creates a parent item using the filename as the title and re-parents the attachment.
|
|
168
|
+
|
|
169
|
+
### extract-annotations.js
|
|
170
|
+
|
|
171
|
+
Extracts annotations from the selected item's PDFs as markdown. Auto-detects its context:
|
|
172
|
+
|
|
173
|
+
- **Interactive** (Tools > Developer > Run JavaScript): shows a file save dialog
|
|
174
|
+
- **Headless** (via HTTP debug API): writes to `~/Desktop/zotero-annotations/`
|
|
175
|
+
|
|
176
|
+
To run headlessly:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
./scripts/run-extract.sh
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Requires: Settings > Advanced > "Allow other applications to communicate with Zotero"
|
|
183
|
+
|
|
184
|
+
The shell script should work on macOS where Zotero and the terminal share the same `localhost`. On WSL, the script calls Zotero's debug HTTP endpoint on `127.0.0.1:23119`, but `localhost` does not bridge to the Windows host by default. You may need to use the Windows host IP or run the curl command from PowerShell instead.
|
|
185
|
+
|
|
186
|
+
## Annotation Format
|
|
187
|
+
|
|
188
|
+
### Types
|
|
189
|
+
|
|
190
|
+
| Type | Extracted Data |
|
|
191
|
+
|------|----------------|
|
|
192
|
+
| Highlight | Text + comment + color |
|
|
193
|
+
| Note | Comment text |
|
|
194
|
+
| Underline | Text + comment |
|
|
195
|
+
| Image | Comment only (image not exported) |
|
|
196
|
+
|
|
197
|
+
### Color Labels
|
|
198
|
+
|
|
199
|
+
| Hex Code | Label |
|
|
200
|
+
|----------|-------|
|
|
201
|
+
| `#ffd400` | yellow |
|
|
202
|
+
| `#ff6666` | red |
|
|
203
|
+
| `#5fb236` | green |
|
|
204
|
+
| `#2ea8e5` | blue |
|
|
205
|
+
| `#a28ae5` | purple |
|
|
206
|
+
| `#e56eee` | magenta |
|
|
207
|
+
| `#f19837` | orange |
|
zotlib-0.4.3/TODO.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# TODO
|
|
2
|
+
|
|
3
|
+
- [ ] Test JS scripts across Windows/macOS and experiment with record modification (no plan)
|
|
4
|
+
- [x] Code review cleanup: naming, SQL params, dead code, tests ([plan](.claude/plans/011-code-review-cleanup.md))
|
|
5
|
+
- [x] Reorganize `zotero-js/` folder into `scripts/` ([plan](.claude/plans/010-reorganize-zotero-js.md))
|
|
6
|
+
- [x] Add descriptions and columns for remaining tables in `show-tables --all` (no plan)
|
|
7
|
+
- [x] Add `zotlib init` command with `zotlib.toml` config ([plan](.claude/plans/009-init-command.md))
|
|
8
|
+
- [x] Restructure CLI commands with export-* naming ([plan](.claude/plans/008-restructure-cli-commands.md))
|
|
9
|
+
- [x] Polish CLI schema command and standardize output dirs ([plan](.claude/plans/007-cli-polish.md))
|
|
10
|
+
- [x] Convert pandas to polars ([plan](.claude/plans/006-pandas-to-polars.md))
|
|
11
|
+
- [x] Export with `--base-dir` for linked attachments ([plan](.claude/plans/003-export-reviews.md))
|
|
12
|
+
- [x] Verify PDF annotation baking ([plan](.claude/plans/003-export-reviews.md))
|
|
13
|
+
- [x] Strip "REVIEW: " prefix from titles ([plan](.claude/plans/003-export-reviews.md))
|
|
14
|
+
- [x] Add `outputs/` to `.gitignore` (no plan)
|
|
15
|
+
- [x] Support standalone PDF attachments ([plan](.claude/plans/003-export-reviews.md))
|
|
16
|
+
- [x] Fix multi-attachment annotation baking ([plan](.claude/plans/003-export-reviews.md))
|
|
17
|
+
- [x] Rename `reviews.py` to `export.py` ([plan](.claude/plans/003-export-reviews.md))
|
|
18
|
+
- [x] Create parent items for standalone attachments via Zotero JS (no plan)
|
|
19
|
+
- [x] Extract titles from PDFs for untitled items ([plan](.claude/plans/003-export-reviews.md))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "zotlib"
|
|
3
|
+
version = "0.4.3"
|
|
4
|
+
description = "Extract and format bibliographic data from Zotero databases"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"typer>=0.12.0",
|
|
9
|
+
"rich>=13.0.0",
|
|
10
|
+
"pymupdf>=1.26.7,<2.0.0",
|
|
11
|
+
"pillow>=12.1.0,<13.0.0",
|
|
12
|
+
"polars>=1.39.2",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
zotlib = "zotlib.cli:app"
|
|
17
|
+
|
|
18
|
+
[dependency-groups]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=8.0.0",
|
|
21
|
+
"ipykernel>=6.29.0",
|
|
22
|
+
"ruff>=0.15.7",
|
|
23
|
+
"pytest-cov>=7.1.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["hatchling"]
|
|
28
|
+
build-backend = "hatchling.build"
|
|
29
|
+
|
|
30
|
+
[tool.ruff]
|
|
31
|
+
target-version = "py310"
|
|
32
|
+
line-length = 100
|
|
33
|
+
|
|
34
|
+
[tool.ruff.lint]
|
|
35
|
+
select = ["E", "F", "I", "W", "UP"]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zotero Script: Create parent items for standalone PDF attachments
|
|
3
|
+
*
|
|
4
|
+
* Finds standalone PDF attachments in a collection and creates a parent
|
|
5
|
+
* document item for each, using the PDF filename (minus extension) as
|
|
6
|
+
* the title. The attachment is then re-parented under the new item.
|
|
7
|
+
*
|
|
8
|
+
* Usage: Run via Tools > Developer > Run JavaScript, or via the debug
|
|
9
|
+
* endpoint with run-create-parents.sh
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
var COLLECTION_NAME = 'reviews';
|
|
13
|
+
var output = [];
|
|
14
|
+
|
|
15
|
+
// reviews collection (id=7, subcollection of writing)
|
|
16
|
+
var collection = Zotero.Collections.get(7);
|
|
17
|
+
|
|
18
|
+
if (!collection) {
|
|
19
|
+
output.push("");
|
|
20
|
+
output.push("ERROR: Collection '" + COLLECTION_NAME + "' not found in any library.");
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
output.push("");
|
|
25
|
+
output.push("Found collection '" + COLLECTION_NAME + "' in library " + collection.libraryID);
|
|
26
|
+
|
|
27
|
+
var itemIDs = collection.getChildItems(true);
|
|
28
|
+
var items = Zotero.Items.get(itemIDs);
|
|
29
|
+
output.push("Items in collection: " + items.length);
|
|
30
|
+
|
|
31
|
+
var standalone = items.filter(function(item) {
|
|
32
|
+
return item.isAttachment()
|
|
33
|
+
&& !item.parentItemID
|
|
34
|
+
&& item.attachmentContentType === 'application/pdf';
|
|
35
|
+
});
|
|
36
|
+
output.push("Standalone PDFs found: " + standalone.length);
|
|
37
|
+
|
|
38
|
+
function cleanTitle(rawPath) {
|
|
39
|
+
// Strip "attachments:" or "storage:" prefix, then get filename without extension
|
|
40
|
+
var clean = rawPath.replace(/^(attachments|storage):/, '');
|
|
41
|
+
var filename = clean.split('/').pop().split('\\').pop();
|
|
42
|
+
return filename.replace(/\.pdf$/i, '').replace(/_/g, ' ') || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (var i = 0; i < standalone.length; i++) {
|
|
46
|
+
var att = standalone[i];
|
|
47
|
+
var path = att.attachmentPath || att.attachmentFilename || '';
|
|
48
|
+
var title = cleanTitle(path) || ('Untitled attachment ' + att.id);
|
|
49
|
+
|
|
50
|
+
// Create parent item
|
|
51
|
+
var parent = new Zotero.Item('document');
|
|
52
|
+
parent.libraryID = att.libraryID;
|
|
53
|
+
parent.setField('title', title);
|
|
54
|
+
await parent.saveTx();
|
|
55
|
+
|
|
56
|
+
// Add parent to the same collection
|
|
57
|
+
collection.addItem(parent.id);
|
|
58
|
+
await collection.saveTx();
|
|
59
|
+
|
|
60
|
+
// Re-parent the attachment under the new item
|
|
61
|
+
att.parentItemID = parent.id;
|
|
62
|
+
await att.saveTx();
|
|
63
|
+
|
|
64
|
+
// Remove the now-child attachment from the collection
|
|
65
|
+
collection.removeItem(att.id);
|
|
66
|
+
await collection.saveTx();
|
|
67
|
+
|
|
68
|
+
output.push(" [" + (i+1) + "/" + standalone.length + "] Created parent: " + title);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
output.push("");
|
|
72
|
+
output.push("Done. Created " + standalone.length + " parent items.");
|
|
73
|
+
|
|
74
|
+
return output;
|