Skip to content

Zenodo

Zenodo plugin provides Package and Resource models and converters between Zenodo and Data Package notations

Installation

Not extra dependencies are required

Usage

Converting a Zenodo descriptor to the Data Package notation:

from dplib.plugins.zenodo.models import ZenodoPackage

package = ZenodoPackage.from_path("data/plugins/zenodo/package.json").to_dp()
print(package.to_text(format='json'))
{
  "resources": [
    {
      "name": "fishway_obstruction_data_v1",
      "path": "Fishway_Obstruction_Data_v1.csv",
      "format": "csv",
      "mediatype": "text/csv",
      "bytes": 1377,
      "hash": "7bdef6756c84c3aea749f8211c557684"
    },
    {
      "name": "readme",
      "path": "readme.md",
      "format": "md",
      "mediatype": "application/octet-stream",
      "bytes": 1577,
      "hash": "a23a3c99befca45e706c9343e39f5926"
    }
  ],
  "id": "https://doi.org/10.5281/zenodo.5770714",
  "title": "Fishway_Obstruction_Data_v1.csv",
  "description": "<p>This dataset contains pool-weir type fishway (sumerged notch and orifice) hydraulic scenarios with and without obstruction events in accordance with&nbsp;the publication:&nbsp;</p>\n\n<p>Fuentes-P&eacute;rez, J.F., Garc&iacute;a-Vega, A., Bravo-C&oacute;rdoba, F.J., Sanz-Ronda, F.J. 2021. A Step to Smart Fishways: An Autonomous Obstruction Detection System Using Hydraulic Modeling and Sensor Networks. Sensors 2021, 21(20), 6909.</p>",
  "homepage": "https://zenodo.org/api/records/5770714",
  "version": "v1",
  "contributors": [
    {
      "title": "Fuentes-P\u00e9rez, Juan Francisco",
      "role": "personal",
      "organization": "Department of Hydraulics and Hydrology, ETSIIAA, University of Valladolid, 34004 Palencia, Spain"
    }
  ],
  "keywords": [
    "fishways",
    "hydraulics",
    "smart fishways",
    "pool-weir",
    "hydrological variability",
    "nonuniformity",
    "clogging",
    "water-level sensors"
  ],
  "created": "2021-12-10T05:47:07.709885+00:00"
}

Converting a Data Package to Zenodo notation:

from dplib.models import Package
from dplib.plugins.zenodo.models import ZenodoPackage

package = ZenodoPackage.from_dp(Package.from_path("data/package.json"))
print(package.to_text(format="xml"))

Reference

dplib.plugins.zenodo.models.ZenodoPackage

Bases: Model

Zenodo Package model

Source code in dplib/plugins/zenodo/models/package.py
class ZenodoPackage(Model):
    """Zenodo Package model"""

    files: ZenodoFiles = pydantic.Field(default_factory=ZenodoFiles)
    metadata: ZenodoMetadata = pydantic.Field(default_factory=ZenodoMetadata)

    id: Optional[str] = None
    pids: Dict[str, ZenodoPid] = {}
    created: Optional[str] = None
    updated: Optional[str] = None
    links: Dict[str, str] = {}

    # Converters

    def to_dp(self) -> Package:
        """Convert to Data Package

        Returns:
           Data Package
        """
        package = Package()

        # Id
        if self.links.get("doi"):
            package.id = self.links.get("doi")

        # Name
        if self.id:
            package.name = self.id

        # Title
        if self.metadata.title:
            package.title = self.metadata.title

        # Description
        if self.metadata.description:
            package.description = self.metadata.description

        # Version
        if self.metadata.version:
            package.version = self.metadata.version

        # Created
        if self.created:
            package.created = self.created

        # Homepage
        if self.links.get("self_html"):
            package.homepage = self.links.get("self_html")

        # Keywords
        for subject in self.metadata.subjects:
            package.keywords.append(subject.subject)

        # Resources
        for entry in self.files.entries.values():
            if self.id:
                resource = entry.to_dp(package_id=self.id)
                package.resources.append(resource)

        # Licenses
        for right in self.metadata.rights:
            if right.id:
                license = License(
                    name=right.id,
                    title=right.title.en,
                    path=right.link or right.props.url,
                )
                package.licenses.append(license)

        # Contributors
        for type, items in [
            ("creator", self.metadata.creators),
            ("contributor", self.metadata.contributors),
        ]:
            for item in items:
                if item.person_or_org.name:
                    contributor = Contributor(
                        title=item.person_or_org.name,
                        givenName=item.person_or_org.given_name,
                        familyName=item.person_or_org.family_name,
                        roles=[item.role.id or type],
                    )
                    if item.affiliations:
                        contributor.organization = item.affiliations[0].name
                    package.contributors.append(contributor)

        # Custom
        if self.id:
            package.custom["zenodo:id"] = self.id

        return package

    @classmethod
    def from_dp(cls, package: Package) -> ZenodoPackage:
        """Create a Zenodo Package from Data Package

        Parameters:
            package: Data Package

        Returns:
            Zenodo Package
        """
        zenodo = ZenodoPackage()

        # Title
        if package.title:
            zenodo.metadata.title = package.title

        # Description
        if package.description:
            zenodo.metadata.description = package.description

        # Version
        if package.version:
            zenodo.metadata.version = package.version

        # Keywords
        for keyword in package.keywords:
            subject = ZenodoSubject(subject=keyword)
            zenodo.metadata.subjects.append(subject)

        # Resources
        for resource in package.resources:
            entry = ZenodoResource.from_dp(resource)
            if entry:
                zenodo.files.entries[entry.key] = entry

        # Licenses
        for license in package.licenses:
            right = ZenodoRight()
            right.id = license.name
            right.link = license.path
            right.title.en = license.title
            zenodo.metadata.rights.append(right)

        # Contributors
        for contributor in package.contributors:
            item = ZenodoContributor()
            item.person_or_org.name = contributor.title
            item.person_or_org.given_name = contributor.givenName
            item.person_or_org.family_name = contributor.familyName
            if contributor.roles:
                item.role.id = contributor.roles[0]
            if contributor.organization:
                affiliation = ZenodoContributorAffiliation(name=contributor.organization)
                item.affiliations.append(affiliation)
            if contributor.roles and contributor.roles[0] == "creator":
                zenodo.metadata.creators.append(item)
            else:
                zenodo.metadata.contributors.append(item)

        return zenodo

created: Optional[str] = None class-attribute instance-attribute

files: ZenodoFiles = pydantic.Field(default_factory=ZenodoFiles) class-attribute instance-attribute

id: Optional[str] = None class-attribute instance-attribute

metadata: ZenodoMetadata = pydantic.Field(default_factory=ZenodoMetadata) class-attribute instance-attribute

pids: Dict[str, ZenodoPid] = {} class-attribute instance-attribute

updated: Optional[str] = None class-attribute instance-attribute

from_dp(package) classmethod

Create a Zenodo Package from Data Package

Parameters:

Name Type Description Default
package Package

Data Package

required

Returns:

Type Description
ZenodoPackage

Zenodo Package

Source code in dplib/plugins/zenodo/models/package.py
@classmethod
def from_dp(cls, package: Package) -> ZenodoPackage:
    """Create a Zenodo Package from Data Package

    Parameters:
        package: Data Package

    Returns:
        Zenodo Package
    """
    zenodo = ZenodoPackage()

    # Title
    if package.title:
        zenodo.metadata.title = package.title

    # Description
    if package.description:
        zenodo.metadata.description = package.description

    # Version
    if package.version:
        zenodo.metadata.version = package.version

    # Keywords
    for keyword in package.keywords:
        subject = ZenodoSubject(subject=keyword)
        zenodo.metadata.subjects.append(subject)

    # Resources
    for resource in package.resources:
        entry = ZenodoResource.from_dp(resource)
        if entry:
            zenodo.files.entries[entry.key] = entry

    # Licenses
    for license in package.licenses:
        right = ZenodoRight()
        right.id = license.name
        right.link = license.path
        right.title.en = license.title
        zenodo.metadata.rights.append(right)

    # Contributors
    for contributor in package.contributors:
        item = ZenodoContributor()
        item.person_or_org.name = contributor.title
        item.person_or_org.given_name = contributor.givenName
        item.person_or_org.family_name = contributor.familyName
        if contributor.roles:
            item.role.id = contributor.roles[0]
        if contributor.organization:
            affiliation = ZenodoContributorAffiliation(name=contributor.organization)
            item.affiliations.append(affiliation)
        if contributor.roles and contributor.roles[0] == "creator":
            zenodo.metadata.creators.append(item)
        else:
            zenodo.metadata.contributors.append(item)

    return zenodo

to_dp()

Convert to Data Package

Returns:

Type Description
Package

Data Package

Source code in dplib/plugins/zenodo/models/package.py
def to_dp(self) -> Package:
    """Convert to Data Package

    Returns:
       Data Package
    """
    package = Package()

    # Id
    if self.links.get("doi"):
        package.id = self.links.get("doi")

    # Name
    if self.id:
        package.name = self.id

    # Title
    if self.metadata.title:
        package.title = self.metadata.title

    # Description
    if self.metadata.description:
        package.description = self.metadata.description

    # Version
    if self.metadata.version:
        package.version = self.metadata.version

    # Created
    if self.created:
        package.created = self.created

    # Homepage
    if self.links.get("self_html"):
        package.homepage = self.links.get("self_html")

    # Keywords
    for subject in self.metadata.subjects:
        package.keywords.append(subject.subject)

    # Resources
    for entry in self.files.entries.values():
        if self.id:
            resource = entry.to_dp(package_id=self.id)
            package.resources.append(resource)

    # Licenses
    for right in self.metadata.rights:
        if right.id:
            license = License(
                name=right.id,
                title=right.title.en,
                path=right.link or right.props.url,
            )
            package.licenses.append(license)

    # Contributors
    for type, items in [
        ("creator", self.metadata.creators),
        ("contributor", self.metadata.contributors),
    ]:
        for item in items:
            if item.person_or_org.name:
                contributor = Contributor(
                    title=item.person_or_org.name,
                    givenName=item.person_or_org.given_name,
                    familyName=item.person_or_org.family_name,
                    roles=[item.role.id or type],
                )
                if item.affiliations:
                    contributor.organization = item.affiliations[0].name
                package.contributors.append(contributor)

    # Custom
    if self.id:
        package.custom["zenodo:id"] = self.id

    return package

dplib.plugins.zenodo.models.ZenodoResource

Bases: Model

Zenodo Resource model

Source code in dplib/plugins/zenodo/models/resource.py
class ZenodoResource(Model):
    """Zenodo Resource model"""

    key: str
    id: Optional[str] = None
    checksum: Optional[str] = None
    ext: Optional[str] = None
    mimetype: Optional[str] = None
    size: Optional[int] = None

    # Converters

    def to_dp(self, *, package_id: str) -> Resource:
        """Convert to Data Package resource

        Returns:
           Data Resource
        """
        resource = Resource(
            path=f"https://zenodo.org/records/{package_id}/files/{self.key}",
            name=slugify_name(self.key),
        )

        # Format
        if self.ext:
            resource.format = self.ext.lower()

        # Mediatype
        if self.mimetype:
            resource.mediatype = self.mimetype

        # Bytes
        if self.size:
            resource.bytes = self.size

        # Hash
        if self.checksum:
            resource.hash = self.checksum.replace("md5:", "")

        # Custom
        if self.id:
            resource.custom["zenodo:id"] = self.id

        return resource

    @classmethod
    def from_dp(cls, resource: Resource) -> Optional[ZenodoResource]:
        """Create Zenodo Resource from Data Resource

        Parameters:
            resource: Data Resource

        Returns:
            Zenodo Resource
        """
        if not resource.path or not isinstance(resource.path, str):
            return

        # Path
        zenodo = ZenodoResource(key=os.path.basename(resource.path))

        # Format
        if resource.format:
            zenodo.ext = resource.format

        # Mediatype
        if resource.mediatype:
            zenodo.mimetype = resource.mediatype

        # Bytes
        if resource.bytes:
            zenodo.size = resource.bytes

        # Hash
        hash = resource.get_hash()
        if hash:
            if hash.type == "md5":
                zenodo.checksum = hash.long

        return zenodo

checksum: Optional[str] = None class-attribute instance-attribute

ext: Optional[str] = None class-attribute instance-attribute

id: Optional[str] = None class-attribute instance-attribute

key: str instance-attribute

mimetype: Optional[str] = None class-attribute instance-attribute

size: Optional[int] = None class-attribute instance-attribute

from_dp(resource) classmethod

Create Zenodo Resource from Data Resource

Parameters:

Name Type Description Default
resource Resource

Data Resource

required

Returns:

Type Description
Optional[ZenodoResource]

Zenodo Resource

Source code in dplib/plugins/zenodo/models/resource.py
@classmethod
def from_dp(cls, resource: Resource) -> Optional[ZenodoResource]:
    """Create Zenodo Resource from Data Resource

    Parameters:
        resource: Data Resource

    Returns:
        Zenodo Resource
    """
    if not resource.path or not isinstance(resource.path, str):
        return

    # Path
    zenodo = ZenodoResource(key=os.path.basename(resource.path))

    # Format
    if resource.format:
        zenodo.ext = resource.format

    # Mediatype
    if resource.mediatype:
        zenodo.mimetype = resource.mediatype

    # Bytes
    if resource.bytes:
        zenodo.size = resource.bytes

    # Hash
    hash = resource.get_hash()
    if hash:
        if hash.type == "md5":
            zenodo.checksum = hash.long

    return zenodo

to_dp(*, package_id)

Convert to Data Package resource

Returns:

Type Description
Resource

Data Resource

Source code in dplib/plugins/zenodo/models/resource.py
def to_dp(self, *, package_id: str) -> Resource:
    """Convert to Data Package resource

    Returns:
       Data Resource
    """
    resource = Resource(
        path=f"https://zenodo.org/records/{package_id}/files/{self.key}",
        name=slugify_name(self.key),
    )

    # Format
    if self.ext:
        resource.format = self.ext.lower()

    # Mediatype
    if self.mimetype:
        resource.mediatype = self.mimetype

    # Bytes
    if self.size:
        resource.bytes = self.size

    # Hash
    if self.checksum:
        resource.hash = self.checksum.replace("md5:", "")

    # Custom
    if self.id:
        resource.custom["zenodo:id"] = self.id

    return resource