Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .claude/temp.md

This file was deleted.

14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,20 @@ works/management/commands/goas_v01_simplified_0.1-90.geojson

works/management/commands/goas_v01_simplified-0.05-80.geojson

# Zenodo data artifacts (rendered per-environment; never commit sandbox state)
data/optimap-main.zip
data/*.gpkg
data/*.geojson
data/*.geojson.gz
data/*.csv
data/*.csv.gz
data/README.md
data/zenodo_dynamic.json
data/last_version.txt

# Test environment files (may contain secrets)
tests/.env

works/management/commands/goas_v01_simplified.geojson

works/management/commands/goas_v01.gpkg
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Zenodo data archival groundwork** (issue #63) — `python manage.py render_zenodo` builds `README.md`, a versioned `optimap-main.zip` (current git `HEAD`), and a `zenodo_dynamic.json` payload under `data/`; `deposit_zenodo` (or the combined `zenodo_deposit`) updates an existing Zenodo draft via [`zenodo-client`](https://pypi.org/project/zenodo-client/) and never publishes automatically. Each run records a `ZenodoDepositionLog` row (status, file list, total size, DOI, draft URL) and emails all `is_staff` users the outcome with a direct link to the draft. An admin action *Trigger Zenodo Deposition* runs the full render+deposit cycle. The `/data/` page now shows the latest successful deposition (sandbox-aware in `DEBUG`, production-only otherwise). Settings: `ZENODO_API_TOKEN`, `ZENODO_SANDBOX_DEPOSITION_ID`, `ZENODO_API_BASE`. Sources, related-identifier URLs, funding metadata, and the codebook are wired up incrementally in follow-up commits.
- **Tag works with EO4GEO Body of Knowledge concepts** (closes #245). New `bok_concepts` field on `Work` plus an autosuggest combobox on the work landing page (≥3-character query, full keyboard, multi-select) backed by `GET /api/v1/bok/search/`. Tagged concepts render as chips that link to the canonical concept page on `bok.eo4geo.eu`, surface in the public Work API as `bok_concepts` / `bok_concepts_resolved`, and emit JSON-LD `about: [DefinedTerm,…]` on the landing page. Adding the first concept on a harvested work flips its status from Harvested to Contributed for admin review; Recognition Board credit is recorded under a new generic *Ontology contributions* kind (so the same bucket can later cover other controlled vocabularies) and deduped per (user, work) so the same user adding more concepts later does not double-count. The cached BoK snapshot is refreshed by `python manage.py refresh_bok_snapshot` (pinned to `v3` by default; configurable via `OPTIMAP_BOK_VERSION`). The editor is **opt-in**: set `OPTIMAP_BOK_ENABLED_COLLECTIONS` to a comma-separated list of `Collection.identifier` slugs to enable it on works in those collections — empty (default) disables the editor site-wide. Read-only chips on already-tagged works remain visible regardless.

### Changed
Expand Down
152 changes: 152 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,158 @@ The app is deployed in the TUD Enterprise Cloud at <https://optimap.geo.tu-dresd

Day-to-day operation of a running OPTIMAP — managing harvesting sources and events, curating collections, blocking abusive users, running the Django-Q cluster, and the rest of the Django-admin surface — is documented in the operator handbook at **[docs/manage.md](docs/manage.md)**.

### Zenodo Data Archival

OPTIMAP can automatically archive datasets to Zenodo for long-term preservation and citability.

#### Initial Setup

Before running depositions, you must create a **draft deposition** on Zenodo with a **reserved DOI**:

1. **Create a Zenodo Account**
- Register at [Zenodo Sandbox](https://sandbox.zenodo.org/) (for testing) or [Zenodo Production](https://zenodo.org/)

2. **Generate API Token**
- Go to Account Settings → Applications → Personal access tokens
- Create a new token with `deposit:write` and `deposit:actions` scopes
- Save the token securely

3. **Create Draft Deposition with Reserved DOI**
- Go to "New upload" on Zenodo
- **Important:** Click "Reserve DOI" button to get a DOI assigned before publishing
- Fill in minimal required metadata (title, creators, upload type)
- **Do NOT publish** - keep it as a draft
- Files and metadata will be overwritten on the first deposition
- Note the deposition ID from the URL (e.g., `https://zenodo.org/deposit/123456`)

4. **Configure Environment Variables**

Add to your `.env` file:

```bash
# For testing (sandbox)
ZENODO_API_BASE=https://sandbox.zenodo.org/api
ZENODO_API_TOKEN=your_sandbox_token_here
ZENODO_SANDBOX_DEPOSITION_ID=123456

# For production
ZENODO_API_BASE=https://zenodo.org/api
ZENODO_API_TOKEN=your_production_token_here
ZENODO_SANDBOX_DEPOSITION_ID=123456 # Use the same setting name
```

Or set in `optimap/settings.py`:

```python
ZENODO_API_BASE = "https://sandbox.zenodo.org/api"
ZENODO_API_TOKEN = "your_token_here"
ZENODO_SANDBOX_DEPOSITION_ID = "123456"
```

#### Running a Deposition

**Manual deposition (recommended):**

```bash
# Full deposition cycle (render + upload)
python manage.py zenodo_deposit

# Skip render if files already exist
python manage.py zenodo_deposit --skip-render

# Use custom deposition ID
python manage.py zenodo_deposit --deposition-id 789012
```

**Individual steps:**

```bash
# Step 1: Generate data files and metadata
python manage.py render_zenodo

# Step 2: Upload to Zenodo
python manage.py deposit_zenodo
```

#### What Gets Uploaded

Each deposition includes:

- **README.md** - Human-readable dataset description with statistics
- **optimap-main.zip** - Complete source code snapshot
- **optimap_data_dump_YYYYMMDD.geojson** - All publication data as GeoJSON
- **optimap_data_dump_YYYYMMDD.gpkg** - All publication data as GeoPackage

**Metadata Management:**

The following fields are **automatically preserved** from the existing Zenodo deposition and never overwritten:

- `doi` - **Reserved DOI** (never modified - must remain constant across versions)
- `prereserve_doi` - Pre-reserved DOI information

All other metadata fields are **updated from the rendered data** on each deposition:

- `title` - Deposition title (default: "OPTIMAP FAIR Data Package")
- `upload_type` - Resource type (default: "dataset")
- `publication_date` - Set to current date
- `creators` - Authors/contributors (default: "OPTIMAP Contributors")
- `description` - Generated from README.md
- `version` - Auto-incremented version number
- `keywords` - Default: ["Open Access", "Open Science", "ORI", "Open Data", "FAIR"]
- `related_identifiers` - Links to related resources

**File Management:**

All existing files are **deleted** from the deposition before uploading new files. This ensures:

- No accumulation of old versions of files
- Clean replacement of all content
- Consistent file sets across versions

#### After Deposition

1. **Review the Draft**
- Check files and metadata at the Zenodo URL (provided in command output)
- Admin users will receive an email notification with the deposition link

2. **Publish When Ready**
- Publishing **cannot be undone**
- Published records are permanently archived and receive a DOI
- New versions can be created for updates

#### Monitoring

- View deposition history in Django Admin: `/admin/works/zenododepositionlog/`
- Check the `/data` page for the latest successful deposition
- In **DEBUG mode** (`OPTIMAP_DEBUG=True`): Shows latest sandbox deposition
- In **production mode**: Shows only production depositions (sandbox excluded)
- All depositions are logged with status, files, errors, and timing information

#### Troubleshooting

**"No deposition ID" error:**

- Set `ZENODO_SANDBOX_DEPOSITION_ID` in environment or use `--deposition-id` flag

**"401 Unauthorized" or "403 Permission Denied" error:**

- Check that `ZENODO_API_TOKEN` is set correctly (no extra whitespace)
- Verify token has `deposit:write` and `deposit:actions` scopes
- Ensure the token is for the correct Zenodo instance (sandbox vs production)
- Verify the deposition was created with the same account as the token
- Token must have access to the specific deposition ID

**"404 Not Found" error:**

- Verify deposition ID exists and is a draft (not published)
- Check that `ZENODO_API_BASE` matches where the deposition was created
- Ensure you're using the correct deposition ID (not record ID)

**Files not uploading:**

- Run `python manage.py render_zenodo` first to generate files
- Check that `data/` directory exists and contains files

## License

This software is published under the GNU General Public License v3.0 (see file [`LICENSE`](LICENSE)).
Expand Down
5 changes: 5 additions & 0 deletions optimap/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@
# Contact email for API user agents (OpenAlex, Wikidata, etc.)
CONTACT_EMAIL = "login@optimap.science"

# Zenodo configuration
ZENODO_API_TOKEN = env("ZENODO_API_TOKEN", default=None)
ZENODO_SANDBOX_DEPOSITION_ID = env("ZENODO_SANDBOX_DEPOSITION_ID", default=None)
ZENODO_API_BASE = env("ZENODO_API_BASE", default="https://sandbox.zenodo.org/api")

# Wikibase/Wikidata configuration
WIKIBASE_API_URL = env("WIKIBASE_API_URL", default="")

Expand Down
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ Pillow>=10.0
# SVG → PNG for the OPTIMAP logo on the og:image preview
cairosvg>=2.7

# Zenodo data deposition (issue #63)
zenodo-client==0.3.6
markdown>=3.7
jinja2>=3.1.4


# Geoextent library for spatial/temporal extent extraction
git+https://github.com/nuest/geoextent.git@main#egg=geoextent
13 changes: 13 additions & 0 deletions tests/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Zenodo API Configuration for Testing
# Copy this file to tests/.env and fill in your actual values

# Zenodo Sandbox API Token
# Get from: https://sandbox.zenodo.org/account/settings/applications/tokens/new/
ZENODO_API_TOKEN=your_sandbox_token_here

# Zenodo Sandbox Deposition ID
# Create a draft deposit first, then get its ID from the URL or API response
ZENODO_SANDBOX_DEPOSITION_ID=your_deposition_id_here

# Zenodo API Base URL (sandbox for testing, production for real deposits)
ZENODO_API_BASE=https://sandbox.zenodo.org/api
Loading
Loading