mirror of
https://github.com/napnap75/multiarch-docker-images.git
synced 2026-11-11 00:22:12 +01:00
* Add sidecar.py module for User Story 0 (Paperless-ngx import) Implements the sidecar data model, schema validation, atomic writes, and I/O helpers per PRD section 7. - Sidecar and SidecarEvent dataclasses with full PRD schema - SidecarStatus and EventType enums - build_sidecar(), serialize(), deserialize() functions - validate_sidecar() with type and required field checking - get_sidecar_key() for .meta.json suffix generation - write_atomic() for crash-safe temp-key + copy + delete pattern - read_sidecar(), sidecar_exists(), matching_sha256() for storage I/O This is a reusable component for both the import script and main service. Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Add storage/ module with S3 and File backends Implements the storage layer abstraction for User Story 0. - StorageBackend ABC: defines interface for all storage implementations - S3Storage: boto3-based backend for Garage S3 (production) - FileStorage: filesystem-based backend for testing - get_storage_backend() factory function - Atomic operations: put, get, delete, copy, exists, list, head Both backends implement the same interface, allowing seamless switching between production S3 and local testing. Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Add period.py, extractor.py, and config.py reusable modules - period.py: Period parsing from filenames and computation from dates - parse_period_from_filename(): Extracts month-year, quarter, year, date patterns - compute_period_from_date(): Computes period per template rules (granularity + offset) - get_period(): Main entry point with filename fallback to rule-based computation - validate_period_consistency(): Checks period vs emission_date + rule - Supports French month names (août, janvier, etc.) - extractor.py: PDF text extraction using PyMuPDF - extract_first_page_text(): Extracts text from first PDF page - extract_and_validate(): Extracts and checks minimum text length - TextExtractor class for dependency injection and mocking - config.py: Configuration management - S3Config, PaperlessConfig, PollerConfig, ClassifierConfig dataclasses - AppConfig: Main application configuration from env vars - TemplateRegistry: Loads and manages template YAML configurations - load_config(), load_templates() helper functions All modules are reusable by both import script and main service. Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Add import_paperless.py script for User Story 0 Implements the standalone import script that: - Connects to Paperless-ngx REST API (PAPERLESS_URL + token from env) - Connects to Garage S3 bucket via boto3 (S3_ENDPOINT_URL, credentials, bucket) - Lists all documents from Paperless via paginated /api/documents/ - Fetches document metadata and maps to sidecar fields: - template: first path segment of storage_path - emission_date: document created field - emitting_company: correspondent name - document_type: document type name - period: parsed from filename tail, fallback to month-year from emission_date - extracted_text: first page text via PyMuPDF - optional_fields: from Paperless labels - Downloads PDF via /api/documents/{id}/download/ - Stores PDF at files/{template}/{original filename} - Writes sidecar {key}.meta.json with atomic temp-key + copy pattern - Supports resumability (skip if PDF + sidecar exist with matching SHA256) - Dry-run flag for testing - Limit flag for trial runs - Summary reporting at end - Exit code: 0 if all succeed, 1 if any failures Dependencies: requests, boto3, pymupdf, python-dateutil Usage: python scripts/import_paperless.py [--dry-run] [--limit N] [--verbose] Environment: PAPERLESS_URL, PAPERLESS_TOKEN, S3_ENDPOINT_URL, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET_NAME Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Add requirements.txt for import script dependencies Lists all Python dependencies needed to run scripts/import_paperless.py: - requests: Paperless-ngx API client - boto3: Garage S3 client - pymupdf: PDF text extraction - python-dateutil: Date parsing - PyYAML: YAML config parsing (optional) Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Update import script: add storage backend selection (s3/file) - Added --storage-backend argument (choices: s3, file, default: s3) - Added --file-base-dir argument for file storage - Storage backend validation based on selection - Updated help text and epilog with backend-specific env vars - Importer class now accepts storage_backend and file_base_dir params - _init_storage() method handles backend initialization - Summary now shows which storage backend was used Usage examples: # S3 storage (default) python scripts/import_paperless.py # File storage for testing python scripts/import_paperless.py --storage-backend file --file-base-dir ./test_storage # Explicit S3 python scripts/import_paperless.py --storage-backend s3 --s3-endpoint ... --s3-bucket ... Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Add README.md for scripts directory Comprehensive documentation for import_paperless.py script: - Overview and features - Requirements and installation - Usage examples (env vars + CLI args) - Storage backends (S3 vs file) - Metadata mapping table - Output structure - Resumability notes - Exit codes - Example output - Troubleshooting guide - Development/testing notes - Architecture overview Helps users understand how to use the import script for User Story 0. Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Fix Paperless API field mapping The Paperless-ngx API returns IDs for correspondent, document_type, and tags, not the actual string values. This commit fixes the import script to: - Use 'archived_file_name' instead of 'filename' for the actual filename - Fetch correspondent name from /api/correspondents/{id}/ endpoint - Fetch document type name from /api/document_types/{id}/ endpoint - Fetch tag names from /api/tags/{id}/ endpoint - Add caching for fetched entities to avoid duplicate API requests This ensures the sidecar metadata contains actual human-readable strings instead of numeric IDs. Changes: - PaperlessClient: Added get_correspondent_name(), get_document_type_name(), get_tag_name() - PaperlessClient: Added caches for fetched entities - get_all_documents(): Now resolves IDs to names for all fields - Updated module docstring to document this behavior Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * Refactor PaperlessClient: change labels to list, add storage path caching, and improve document fetching logic --------- Co-authored-by: Vibe Nuage Agent <vibe@mistral.ai> Co-authored-by: napnap75 <napnap75@users.noreply.github.com>
280 lines
7.1 KiB
Markdown
280 lines
7.1 KiB
Markdown
# Scripts
|
|
|
|
Standalone Python scripts for the Automatic PDF Renamer project.
|
|
|
|
## Available Scripts
|
|
|
|
| Script | Purpose | Dependencies |
|
|
|--------|---------|--------------|
|
|
| [`import_paperless.py`](import_paperless.py) | Import documents from Paperless-ngx to storage | requests, boto3, pymupdf, python-dateutil |
|
|
|
|
---
|
|
|
|
## `import_paperless.py` - Paperless-ngx Initial Import
|
|
|
|
**User Story 0**: Standalone script that imports existing paperless-ngx documents into a Garage S3 bucket (or local filesystem) with their metadata as sidecars.
|
|
|
|
### Features
|
|
|
|
- ✅ Connects to Paperless-ngx REST API
|
|
- ✅ Connects to Garage S3 or local filesystem
|
|
- ✅ Lists all documents via paginated API
|
|
- ✅ Maps Paperless metadata to sidecar fields
|
|
- ✅ Downloads PDFs and extracts first-page text
|
|
- ✅ Atomic sidecar writes (temp-key + copy pattern)
|
|
- ✅ Resumability (skip if already imported with matching SHA256)
|
|
- ✅ Dry-run mode for testing
|
|
- ✅ Summary reporting
|
|
- ✅ Proper exit codes
|
|
|
|
### Requirements
|
|
|
|
Install dependencies:
|
|
|
|
```bash
|
|
pip install -r scripts/requirements.txt
|
|
```
|
|
|
|
Or individually:
|
|
|
|
```bash
|
|
pip install requests boto3 pymupdf python-dateutil PyYAML
|
|
```
|
|
|
|
### Usage
|
|
|
|
#### Environment Variables (Recommended)
|
|
|
|
Set these before running:
|
|
|
|
```bash
|
|
# Paperless-ngx
|
|
export PAPERLESS_URL="http://paperless:8000"
|
|
export PAPERLESS_TOKEN="your-api-token"
|
|
|
|
# S3 Storage (Garage)
|
|
export S3_ENDPOINT_URL="http://garage:7777"
|
|
export S3_ACCESS_KEY_ID="your-access-key"
|
|
export S3_SECRET_ACCESS_KEY="your-secret-key"
|
|
export S3_BUCKET_NAME="pdf-renamer"
|
|
|
|
# File Storage (for testing)
|
|
export FILE_BASE_DIR="./import_storage"
|
|
```
|
|
|
|
#### Command Line
|
|
|
|
```bash
|
|
# S3 storage (default)
|
|
python scripts/import_paperless.py
|
|
|
|
# File storage for testing
|
|
python scripts/import_paperless.py \
|
|
--storage-backend file \
|
|
--file-base-dir ./test_storage
|
|
|
|
# Dry run (list what would be imported)
|
|
python scripts/import_paperless.py --dry-run
|
|
|
|
# Limited import (test with 5 documents)
|
|
python scripts/import_paperless.py --limit 5
|
|
|
|
# Verbose mode
|
|
python scripts/import_paperless.py --verbose
|
|
|
|
# Full options
|
|
python scripts/import_paperless.py \
|
|
--paperless-url http://paperless:8000 \
|
|
--paperless-token your-token \
|
|
--storage-backend s3 \
|
|
--s3-endpoint http://garage:7777 \
|
|
--s3-access-key your-key \
|
|
--s3-secret-key your-secret \
|
|
--s3-bucket pdf-renamer \
|
|
--dry-run \
|
|
--limit 10 \
|
|
--verbose
|
|
```
|
|
|
|
### Storage Backends
|
|
|
|
The import script supports two storage backends:
|
|
|
|
| Backend | Description | Use Case |
|
|
|---------|-------------|----------|
|
|
| `s3` | Garage S3 via boto3 | Production |
|
|
| `file` | Local filesystem | Testing, development |
|
|
|
|
#### S3 Backend
|
|
|
|
Requires:
|
|
- `--s3-endpoint` or `S3_ENDPOINT_URL`
|
|
- `--s3-access-key` or `S3_ACCESS_KEY_ID`
|
|
- `--s3-secret-key` or `S3_SECRET_ACCESS_KEY`
|
|
- `--s3-bucket` or `S3_BUCKET_NAME`
|
|
|
|
The script will refuse to run if the `files/` prefix is not empty (to prevent accidental overwrites).
|
|
|
|
#### File Backend
|
|
|
|
Requires:
|
|
- `--file-base-dir` or `FILE_BASE_DIR` (default: `./import_storage`)
|
|
|
|
Creates a directory structure matching the S3 layout:
|
|
```
|
|
file_base_dir/
|
|
files/
|
|
Template1/
|
|
document1.pdf
|
|
document1.pdf.meta.json
|
|
Template2/
|
|
document2.pdf
|
|
document2.pdf.meta.json
|
|
tmp/
|
|
(temporary files for atomic writes)
|
|
```
|
|
|
|
### Metadata Mapping
|
|
|
|
Paperless-ngx fields are mapped to sidecar fields as follows:
|
|
|
|
| Sidecar Field | Paperless Source | Notes |
|
|
|---------------|------------------|-------|
|
|
| `template_name` | `storage_path` (first segment) | Document family |
|
|
| `emission_date` | `created` | Document date |
|
|
| `emitting_company` | `correspondent.name` | Correspondent |
|
|
| `document_type` | `document_type.name` | Document type |
|
|
| `period` | Filename (parsed) or `created` (fallback) | Month-year, quarter, year |
|
|
| `extracted_text` | PDF first page | Via PyMuPDF |
|
|
| `optional_fields` | `labels` | Each label becomes an optional field |
|
|
| `sha256` | PDF binary | Computed from downloaded PDF |
|
|
| `status` | Hardcoded | `validated` |
|
|
| `confidence` | Hardcoded | `1.0` |
|
|
|
|
### Output Structure
|
|
|
|
Documents are stored at:
|
|
```
|
|
files/{template}/{original_filename}
|
|
files/{template}/{original_filename}.meta.json
|
|
```
|
|
|
|
Example:
|
|
```
|
|
files/Banque/Relevé de compte août 2025.pdf
|
|
files/Banque/Relevé de compte août 2025.pdf.meta.json
|
|
```
|
|
|
|
### Resumability
|
|
|
|
The script can be interrupted and rerun safely:
|
|
- Documents already imported with matching SHA256 are skipped
|
|
- Atomic sidecar writes ensure no partial/corrupted sidecars
|
|
- Summary shows imported, skipped, and failed counts
|
|
|
|
### Exit Codes
|
|
|
|
| Code | Meaning |
|
|
|------|---------|
|
|
| 0 | Success (all documents imported) |
|
|
| 1 | Failure (one or more documents failed) |
|
|
| 130 | Interrupted by user (SIGINT) |
|
|
|
|
### Example Output
|
|
|
|
```
|
|
2026-10-02 12:00:00,000 - INFO - Starting Paperless-ngx import...
|
|
2026-10-02 12:00:00,001 - INFO - Using S3 storage backend: http://garage:7777/pdf-renamer
|
|
2026-10-02 12:00:00,002 - INFO - Fetching documents from Paperless...
|
|
2026-10-02 12:00:05,123 - INFO - Retrieved 150 documents from Paperless
|
|
2026-10-02 12:00:05,124 - INFO - Processing document 1: Relevé août 2025.pdf
|
|
2026-10-02 12:00:05,456 - INFO - Successfully imported document 1
|
|
2026-10-02 12:00:05,457 - INFO - Processing document 2: Facture Q3 2025.pdf
|
|
...
|
|
|
|
============================================================
|
|
IMPORT SUMMARY
|
|
============================================================
|
|
Storage backend: s3
|
|
Imported: 150
|
|
Skipped: 0
|
|
Failed: 0
|
|
Period fallbacks: 0
|
|
|
|
Per-template counts:
|
|
Banque: 75
|
|
Factures: 50
|
|
Contrats: 25
|
|
============================================================
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
#### "Missing required values" error
|
|
|
|
Make sure all required environment variables are set or passed as CLI arguments:
|
|
|
|
```bash
|
|
# Check what's set
|
|
echo $PAPERLESS_URL
|
|
echo $S3_ENDPOINT_URL
|
|
```
|
|
|
|
#### "Bucket is not empty" error
|
|
|
|
The script refuses to run if `files/` prefix already has objects. Options:
|
|
|
|
1. **Use a different bucket**
|
|
2. **Use file storage for testing**: `--storage-backend file --file-base-dir ./test`
|
|
3. **Empty the bucket first** (if you're sure it's safe)
|
|
|
|
#### "PyMuPDF not available" warning
|
|
|
|
Text extraction will be skipped. Install it for full functionality:
|
|
|
|
```bash
|
|
pip install pymupdf
|
|
```
|
|
|
|
#### Connection errors
|
|
|
|
Check that:
|
|
- Paperless-ngx is running and accessible
|
|
- S3/Garage endpoint is correct
|
|
- Credentials are valid
|
|
- Network connectivity is available
|
|
|
|
### Development / Testing
|
|
|
|
For testing without Paperless-ngx or S3:
|
|
|
|
```bash
|
|
# Use file storage
|
|
python scripts/import_paperless.py \
|
|
--storage-backend file \
|
|
--file-base-dir ./test_storage \
|
|
--dry-run
|
|
|
|
# Or create a mock Paperless client (see tests/ directory)
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
The import script uses the same reusable components as the main service:
|
|
|
|
- `app/sidecar.py` - Sidecar data model and I/O
|
|
- `app/storage/` - Storage backend abstraction
|
|
- `app/period.py` - Period parsing and computation
|
|
- `app/extractor.py` - PDF text extraction
|
|
- `app/config.py` - Configuration utilities
|
|
|
|
This ensures consistency between the import script and the main service.
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
Same as the main project. See [../LICENSE](../LICENSE).
|