Skip to content

Refactor/zenodo metadata adapter#67

Open
mbruns91 wants to merge 2 commits into
mainfrom
refactor/zenodo-metadata-adapter
Open

Refactor/zenodo metadata adapter#67
mbruns91 wants to merge 2 commits into
mainfrom
refactor/zenodo-metadata-adapter

Conversation

@mbruns91
Copy link
Copy Markdown
Contributor

This PR adds an adapter path from the service-independent PublicationMetadata model to Zenodo deposition metadata. ZenodoMetadata can now wrap a PublicationMetadata instance while keeping Zenodo-specific fields and payload generation inside the Zenodo service layer.

Summary

  • Added optional metadata: PublicationMetadata composition to ZenodoMetadata.
  • Kept existing legacy ZenodoMetadata field-based construction working.
  • Updated Zenodo convenience constructors to accept common metadata, e.g. ZenodoMetadata.software(publication).
  • Mapped common metadata concepts into Zenodo API payloads:
    • Person creators -> Zenodo creators
    • Contributor.role -> Zenodo contributor type
    • RelatedIdentifier -> Zenodo related identifiers
    • common title, description, publication date, license, DOI, version, language, and keywords
  • Kept Zenodo-specific fields on ZenodoMetadata, including upload type, publication/image type, access control, communities, grants, notes, and DOI reservation.
  • Rejected mixed duplicate common fields when metadata is provided, to avoid ambiguous payload generation.
  • Did not change deposition resource behavior yet; clients still pass ZenodoMetadata or raw mappings.

Unit Tests

Added focused Zenodo metadata tests covering:

  • serialization of PublicationMetadata through ZenodoMetadata
  • convenience constructor usage
  • mapping common creators, contributors, and related identifiers into Zenodo payload shape
  • keeping Zenodo-specific fields on the adapter
  • rejecting duplicate legacy common fields when wrapped metadata is used
  • defensive validation for invalid adapted person identity

Existing Zenodo metadata tests continue to cover the legacy field-based path.

Validation

Validated with:

ruff check --fix --diff .
python -m mypy courier
pytest -q
coverage run -m pytest -q
coverage combine
coverage report -m

Examples

Legacy Zenodo Metadata

from courier.services.zenodo import Creator, ZenodoMetadata

metadata = ZenodoMetadata.software()
metadata.title = "courier"
metadata.description = "Python client."
metadata.publication_date = "2026-04-21"
metadata.license = "Apache-2.0"
metadata.creators.append(Creator(name="Doe, Jane"))
metadata.add_keyword("python")

payload = metadata.to_payload()

This still works. Common publication fields and Zenodo-specific fields are all set directly on ZenodoMetadata.

New Adapter Style

from courier.metadata import Person, PublicationMetadata
from courier.services.zenodo import ZenodoMetadata

publication = PublicationMetadata(
  title="courier",
  description="Python client.",
  publication_date="2026-04-21",
  creators=[Person(name="Doe, Jane")],
  keywords=["python"],
  license="Apache-2.0",
)

metadata = ZenodoMetadata.software(publication)

payload = metadata.to_payload()

Here, reusable publication fields live in PublicationMetadata. ZenodoMetadata supplies the Zenodo-specific wrapper, especially upload_type="software".

New Adapter With Zenodo-Specific Fields

from courier.metadata import Person, PublicationMetadata
from courier.services.zenodo import ZenodoMetadata

publication = PublicationMetadata(
  title="Example paper",
  description="Paper metadata.",
  publication_date="2026-04-21",
  creators=[Person(name="Doe, Jane")],
  license="cc-by-4.0",
)

metadata = ZenodoMetadata.publication("article", publication)
metadata.add_community("pyiron")
metadata.add_grant("grant-1")
metadata.prereserve_doi = True
metadata.notes = "Submitted through courier."

payload = metadata.to_payload()

This is valid because publication_type, communities, grants, DOI reservation, and notes are Zenodo-specific adapter fields.

Invalid Mixed Usage

from courier.metadata import Person, PublicationMetadata
from courier.services.zenodo import ZenodoMetadata

publication = PublicationMetadata(
  title="courier",
  description="Python client.",
  creators=[Person(name="Doe, Jane")],
  license="Apache-2.0",
)

metadata = ZenodoMetadata(
  metadata=publication,
  upload_type="software",
  title="Different title",
)

metadata.to_payload()

This is invalid because title is a common publication field. When metadata=publication is provided, common fields must come from PublicationMetadata, not also from legacy ZenodoMetadata fields.

Also invalid:

ZenodoMetadata(
  metadata=publication,
  upload_type="software",
  creators=[Creator(name="Legacy, Creator")],
)

and:

ZenodoMetadata(
  metadata=publication,
  upload_type="software",
  keywords=["legacy"],
)

These are rejected to avoid ambiguous payload generation.

@github-actions
Copy link
Copy Markdown

Binder 👈 Launch a binder notebook on branch pyiron/courier/refactor/zenodo-metadata-adapter

@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e53f9fa) to head (0428c36).

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #67   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           27        27           
  Lines         1070      1164   +94     
=========================================
+ Hits          1070      1164   +94     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an adapter path from service-independent PublicationMetadata into Zenodo deposition metadata while preserving the existing legacy ZenodoMetadata field-based workflow.

Changes:

  • Added PublicationMetadata wrapping support to ZenodoMetadata.
  • Mapped common creators, contributors, related identifiers, and publication fields into Zenodo payloads.
  • Added unit tests for adapter serialization, duplicate common-field rejection, and validation edge cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
courier/services/zenodo/metadata.py Adds adapter logic, conflict validation, and updated convenience constructors.
tests/unit/services/zenodo/test_metadata.py Adds focused tests covering the new PublicationMetadata adapter behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mbruns91 mbruns91 requested a review from samwaseda May 28, 2026 19:58
@mbruns91 mbruns91 force-pushed the refactor/zenodo-metadata-adapter branch from 1d0dbe8 to 0428c36 Compare May 29, 2026 08:47
@mbruns91 mbruns91 changed the base branch from refactor/metadata-model to main May 29, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants