diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index 508d74c7..c96fbbda 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -35,7 +35,7 @@ jobs:
python-version: 3.11
- name: Install requirements
- run: make setup
+ run: make install
- name: Run linters
run: make linter
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3f2ed16a..4ff025aa 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@ default_language_version:
python: python3.11
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.1.6
+ rev: v0.1.8
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
@@ -20,26 +20,16 @@ repos:
- id: check-yaml
name: yaml
- repo: https://github.com/python-poetry/poetry
- rev: 1.7.0
+ rev: 1.7.1
hooks:
- id: poetry-check
name: poetry
- - repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.7.0
- hooks: # configured according to mypy maintainer: https://github.com/python/mypy/issues/13916
- - id: mypy
- name: mypy
- files: ^mex/
- pass_filenames: false
- args: [mex]
- additional_dependencies:
- - "backoff>=2.2.1,<3"
- - "click>=8.1.7,<9"
- - "pandas-stubs>=2.1.1,<3"
- - "pydantic-settings>=2.1.0,<3"
- - "pydantic>=2.5.1,<3"
- - "pytest>=7.4.3,<8"
- - "types-ldap3>=2.9.13.15,<3"
- - "types-pytz>=2023.3.1.1,<2024"
- - "types-requests>=2.31.0.10,<3"
- - "types-setuptools>=68.2.0.1,<69"
+ - repo: local
+ hooks:
+ - id: mypy
+ name: mypy
+ entry: poetry run dmypy run --timeout 7200 -- mex
+ files: ^mex/
+ language: system
+ pass_filenames: false
+ types: [python]
diff --git a/README.md b/README.md
index b318435d..7ff9ffc4 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,6 @@ MEx project will be released under the same license in the future.
- update global dependencies in `requirements.txt` manually
- update git hooks with `pre-commit autoupdate`
-- update git hook additional dependencies manually
- show outdated dependencies with `poetry show --outdated`
- update dependencies in poetry using `poetry update --lock`
- update github actions manually in `.github\workflows\default.yml`
diff --git a/mex/common/ldap/models/person.py b/mex/common/ldap/models/person.py
index 12784e73..ecef8e1b 100644
--- a/mex/common/ldap/models/person.py
+++ b/mex/common/ldap/models/person.py
@@ -7,14 +7,14 @@
class LDAPPerson(LDAPActor):
"""Model class for LDAP persons."""
- company: str | None = Field(None)
- department: str | None = Field(None)
- departmentNumber: str | None = Field(None)
- displayName: str | None = Field(None)
- employeeID: str = Field(...)
- givenName: list[str] = Field(..., min_length=1)
- ou: list[str] = Field([])
- sn: str = Field(...)
+ company: str | None = None
+ department: str | None = None
+ departmentNumber: str | None = None
+ displayName: str | None = None
+ employeeID: str
+ givenName: list[str] = Field(min_length=1)
+ ou: list[str] = []
+ sn: str
@classmethod
def get_ldap_fields(cls) -> tuple[str, ...]:
diff --git a/mex/common/models/access_platform.py b/mex/common/models/access_platform.py
index fbc24e91..339f51f9 100644
--- a/mex/common/models/access_platform.py
+++ b/mex/common/models/access_platform.py
@@ -1,3 +1,5 @@
+from typing import Annotated
+
from pydantic import Field
from mex.common.models.base import BaseModel
@@ -5,27 +7,16 @@
from mex.common.models.merged_item import MergedItem
from mex.common.types import (
AccessPlatformID,
+ APIType,
ContactPointID,
Link,
OrganizationalUnitID,
PersonID,
+ TechnicalAccessibility,
Text,
- VocabularyEnum,
)
-class TechnicalAccessibility(VocabularyEnum):
- """Technical accessibility within RKI and outside of RKI."""
-
- __vocabulary__ = "technical-accessibility"
-
-
-class APIType(VocabularyEnum):
- """Technical standard or style of a network API."""
-
- __vocabulary__ = "api-type"
-
-
class BaseAccessPlatform(BaseModel):
"""A way of physically accessing the Resource for re-use."""
@@ -34,14 +25,15 @@ class BaseAccessPlatform(BaseModel):
contact: list[OrganizationalUnitID | PersonID | ContactPointID] = []
description: list[Text] = []
endpointDescription: Link | None = None
- endpointType: APIType | None = Field(
- None, examples=["https://mex.rki.de/item/api-type-1"]
- )
+ endpointType: Annotated[
+ APIType, Field(examples=["https://mex.rki.de/item/api-type-1"])
+ ] | None = None
endpointURL: Link | None = None
landingPage: list[Link] = []
- technicalAccessibility: TechnicalAccessibility = Field(
- ..., examples=["https://mex.rki.de/item/technical-accessibility-1"]
- )
+ technicalAccessibility: Annotated[
+ TechnicalAccessibility,
+ Field(examples=["https://mex.rki.de/item/technical-accessibility-1"]),
+ ]
title: list[Text] = []
unitInCharge: list[OrganizationalUnitID] = []
diff --git a/mex/common/models/activity.py b/mex/common/models/activity.py
index 3aa9316f..f095aeeb 100644
--- a/mex/common/models/activity.py
+++ b/mex/common/models/activity.py
@@ -7,8 +7,8 @@
from mex.common.models.merged_item import MergedItem
from mex.common.types import (
ActivityID,
+ ActivityType,
ContactPointID,
- Identifier,
Link,
OrganizationalUnitID,
OrganizationID,
@@ -16,16 +16,9 @@
Text,
Theme,
Timestamp,
- VocabularyEnum,
)
-class ActivityType(VocabularyEnum):
- """The activity type."""
-
- __vocabulary__ = "activity-type"
-
-
class BaseActivity(BaseModel):
"""The context a resource was generated in.
@@ -40,35 +33,21 @@ class BaseActivity(BaseModel):
]
] = []
alternativeTitle: list[Text] = []
- contact: list[
- Annotated[
- OrganizationalUnitID | PersonID | ContactPointID,
- Field(examples=[Identifier.generate(seed=42)]),
- ]
- ] = Field(
- ...,
- min_length=1,
- )
+ contact: Annotated[
+ list[OrganizationalUnitID | PersonID | ContactPointID,], Field(min_length=1)
+ ]
documentation: list[Link] = []
end: list[
- Annotated[
- Timestamp,
- Field(
- examples=["2024-01-17", "2024", "2024-01"],
- ),
- ]
+ Annotated[Timestamp, Field(examples=["2024-01-17", "2024", "2024-01"])]
] = []
- externalAssociate: list[OrganizationalUnitID | PersonID] = []
+ externalAssociate: list[OrganizationID | PersonID] = []
funderOrCommissioner: list[OrganizationID] = []
fundingProgram: list[str] = []
involvedPerson: list[PersonID] = []
involvedUnit: list[OrganizationalUnitID] = []
isPartOfActivity: list[ActivityID] = []
publication: list[Link] = []
- responsibleUnit: list[OrganizationalUnitID] = Field(
- ...,
- min_length=1,
- )
+ responsibleUnit: Annotated[list[OrganizationalUnitID], Field(min_length=1)]
shortName: list[Text] = []
start: list[
Annotated[Timestamp, Field(examples=["2023-01-16", "2023", "2023-02"])]
@@ -77,7 +56,7 @@ class BaseActivity(BaseModel):
theme: list[
Annotated[Theme, Field(examples=["https://mex.rki.de/item/theme-1"])]
] = []
- title: list[Text] = Field(..., min_length=1)
+ title: Annotated[list[Text], Field(min_length=1)]
website: list[Link] = []
diff --git a/mex/common/models/base.py b/mex/common/models/base.py
index 4fa408e8..c68faf2f 100644
--- a/mex/common/models/base.py
+++ b/mex/common/models/base.py
@@ -2,17 +2,39 @@
import pickle # nosec
from collections.abc import MutableMapping
from functools import cache
-from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union, get_args, get_origin
+from typing import TYPE_CHECKING, Annotated, Any, TypeVar, Union, get_args, get_origin
-from pydantic import BaseModel as PydanticBaseModel
+from pydantic import (
+ BaseModel as PydanticBaseModel,
+)
from pydantic import ConfigDict, Field, TypeAdapter, ValidationError, model_validator
from pydantic.fields import FieldInfo
+from pydantic.json_schema import DEFAULT_REF_TEMPLATE, JsonSchemaMode, JsonSchemaValue
+from pydantic.json_schema import (
+ GenerateJsonSchema as PydanticJsonSchemaGenerator,
+)
from mex.common.types import Identifier
RawModelDataT = TypeVar("RawModelDataT")
+class JsonSchemaGenerator(PydanticJsonSchemaGenerator):
+ """Customization of the pydantic class for generating JSON schemas."""
+
+ def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue:
+ """Disable pydantic behavior to wrap top-level `$ref` keys in an `allOf`.
+
+ For example, pydantic would convert
+ {"$ref": "#/$defs/APIType", "examples": ["api-type-1"]}
+ into
+ {"allOf": {"$ref": "#/$defs/APIType"}, "examples": ["api-type-1"]}
+ which is in fact recommended by JSON schema, but we need to disable this
+ to stay compatible with mex-editor and mex-model.
+ """
+ return json_schema
+
+
class BaseModel(PydanticBaseModel):
"""Common base class for all MEx model classes."""
@@ -27,6 +49,32 @@ class BaseModel(PydanticBaseModel):
validate_assignment=True,
)
+ @classmethod
+ def model_json_schema(
+ cls,
+ by_alias: bool = True,
+ ref_template: str = DEFAULT_REF_TEMPLATE,
+ schema_generator: type[PydanticJsonSchemaGenerator] = JsonSchemaGenerator,
+ mode: JsonSchemaMode = "validation",
+ ) -> dict[str, Any]:
+ """Generates a JSON schema for a model class.
+
+ Args:
+ by_alias: Whether to use attribute aliases or not.
+ ref_template: The reference template.
+ schema_generator: Overriding the logic used to generate the JSON schema
+ mode: The mode in which to generate the schema.
+
+ Returns:
+ The JSON schema for the given model class.
+ """
+ return super().model_json_schema(
+ by_alias=by_alias,
+ ref_template=ref_template,
+ schema_generator=schema_generator,
+ mode=mode,
+ )
+
@classmethod
@cache
def _get_alias_lookup(cls) -> dict[str, str]:
@@ -73,7 +121,7 @@ def _get_field_names_allowing_none(cls) -> list[str]:
@classmethod
def _convert_non_list_to_list(
cls, name: str, field: FieldInfo, value: Any
- ) -> Optional[list[Any]]:
+ ) -> list[Any] | None:
"""Convert a non-list value to a list value by wrapping it in a list."""
if value is None:
if name in cls._get_field_names_allowing_none():
@@ -171,12 +219,13 @@ class MExModel(BaseModel):
# also used as the foreign key for all fields containing references.
stableTargetId: Any
- identifier: Identifier = Field(
- ...,
- description=(
- "A globally unique identifier for this item. Regardless of the entity-type "
- "or whether this item was extracted, merged, etc. identifiers will be "
- "assigned just once."
+ identifier: Annotated[
+ Identifier,
+ Field(
+ description=(
+ "A globally unique identifier for this item. Regardless of the "
+ "entity-type or whether this item was extracted, merged, etc. "
+ "identifiers will be assigned just once."
+ ),
),
- examples=[Identifier.generate(seed=42)],
- )
+ ]
diff --git a/mex/common/models/contact_point.py b/mex/common/models/contact_point.py
index de61d98c..5b315bd3 100644
--- a/mex/common/models/contact_point.py
+++ b/mex/common/models/contact_point.py
@@ -12,17 +12,7 @@ class BaseContactPoint(BaseModel):
"""A contact point - for example, an interdepartmental project."""
stableTargetId: ContactPointID
- email: list[
- Annotated[
- Email,
- Field(
- examples=["info@rki.de"],
- ),
- ]
- ] = Field(
- ...,
- min_length=1,
- )
+ email: Annotated[list[Email], Field(min_length=1)]
class ExtractedContactPoint(BaseContactPoint, ExtractedData):
diff --git a/mex/common/models/distribution.py b/mex/common/models/distribution.py
index 91ff6010..15b32943 100644
--- a/mex/common/models/distribution.py
+++ b/mex/common/models/distribution.py
@@ -1,3 +1,5 @@
+from typing import Annotated
+
from pydantic import Field
from mex.common.models.base import BaseModel
@@ -7,28 +9,24 @@
AccessPlatformID,
AccessRestriction,
DistributionID,
+ License,
Link,
+ MIMEType,
+ OrganizationID,
PersonID,
Timestamp,
- VocabularyEnum,
)
-class MIMEType(VocabularyEnum):
- """The mime type."""
-
- __vocabulary__ = "mime-type"
-
-
class BaseDistribution(BaseModel):
"""A specific representation of a dataset."""
stableTargetId: DistributionID
accessService: AccessPlatformID | None = None
- accessRestriction: AccessRestriction = Field(
- ...,
- examples=["https://mex.rki.de/item/access-restriction-1"],
- )
+ accessRestriction: Annotated[
+ AccessRestriction,
+ Field(examples=["https://mex.rki.de/item/access-restriction-1"]),
+ ]
accessURL: Link | None = None
author: list[PersonID] = []
contactPerson: list[PersonID] = []
@@ -36,22 +34,28 @@ class BaseDistribution(BaseModel):
dataManager: list[PersonID] = []
downloadURL: Link | None = None
issued: Timestamp
- license: list[Link] = []
- mediaType: MIMEType | None = Field(
- None,
- examples=["https://mex.rki.de/item/mime-type-1"],
- )
- modified: list[Timestamp] = []
+ license: Annotated[
+ License, Field(examples=["https://mex.rki.de/item/license-1"])
+ ] | None = None
+ mediaType: Annotated[
+ MIMEType,
+ Field(
+ examples=["https://mex.rki.de/item/mime-type-1"],
+ ),
+ ] | None = None
+ modified: Timestamp | None = None
otherContributor: list[PersonID] = []
projectLeader: list[PersonID] = []
projectManager: list[PersonID] = []
- publisher: list[PersonID] = Field(..., min_length=1)
+ publisher: Annotated[list[OrganizationID], Field(min_length=1)]
researcher: list[PersonID] = []
- title: str = Field(
- ...,
- examples=["theNameOfTheFile"],
- min_length=1,
- )
+ title: Annotated[
+ str,
+ Field(
+ examples=["theNameOfTheFile"],
+ min_length=1,
+ ),
+ ]
class ExtractedDistribution(BaseDistribution, ExtractedData):
diff --git a/mex/common/models/extracted_data.py b/mex/common/models/extracted_data.py
index 5abaa8b9..485508c0 100644
--- a/mex/common/models/extracted_data.py
+++ b/mex/common/models/extracted_data.py
@@ -1,4 +1,4 @@
-from typing import Any
+from typing import Annotated, Any
from pydantic import Field, model_validator
@@ -19,31 +19,34 @@ class BaseExtractedData(MExModel):
correct type, e.g. `PersonID`.
"""
- hadPrimarySource: PrimarySourceID = Field(
- ...,
- description=(
- "The stableTargetID of the primary source, that this item was extracted "
- "from. This field is mandatory for all extracted items to aid with data "
- "provenance. Extracted primary sources also have this field and are all "
- "extracted from a primary source called MEx, which is its own primary "
- "source and has the static stableTargetID: "
- f"{MEX_PRIMARY_SOURCE_STABLE_TARGET_ID}"
+ hadPrimarySource: Annotated[
+ PrimarySourceID,
+ Field(
+ description=(
+ "The stableTargetID of the primary source, that this item was "
+ "extracted from. This field is mandatory for all extracted items to "
+ "aid with data provenance. Extracted primary sources also have this "
+ "field and are all extracted from a primary source called MEx, which "
+ "is its own primary source and has the static stableTargetID: "
+ f"{MEX_PRIMARY_SOURCE_STABLE_TARGET_ID}"
+ ),
),
- examples=[PrimarySourceID.generate(seed=42)],
- )
- identifierInPrimarySource: str = Field(
- ...,
- description=(
- "This is the identifier the original item had in its source system. "
- "It is only unique amongst items coming from the same system, because "
- "identifier formats are likely to overlap between systems. "
- "The value for `identifierInPrimarySource` is therefore only unique in "
- "composition with `hadPrimarySource`. MEx uses this composite key "
- "to assign a stable and globally unique `identifier` to each item."
+ ]
+ identifierInPrimarySource: Annotated[
+ str,
+ Field(
+ description=(
+ "This is the identifier the original item had in its source system. "
+ "It is only unique amongst items coming from the same system, because "
+ "identifier formats are likely to overlap between systems. "
+ "The value for `identifierInPrimarySource` is therefore only unique in "
+ "composition with `hadPrimarySource`. MEx uses this composite key "
+ "to assign a stable and globally unique `identifier` to each item."
+ ),
+ examples=["123456", "item-501", "D7/x4/zz.final3"],
+ min_length=1,
),
- examples=["123456", "item-501", "D7/x4/zz.final3"],
- min_length=1,
- )
+ ]
def __str__(self) -> str:
"""Format this extracted data instance as a string for logging."""
diff --git a/mex/common/models/organization.py b/mex/common/models/organization.py
index 1a4d64b1..3b522e84 100644
--- a/mex/common/models/organization.py
+++ b/mex/common/models/organization.py
@@ -22,6 +22,7 @@ class BaseOrganization(BaseModel):
Field(
pattern=r"^https://gepris\.dfg\.de/gepris/institution/[0-9]{1,64}$",
examples=["https://gepris.dfg.de/gepris/institution/10179"],
+ json_schema_extra={"format": "uri"},
),
]
] = []
@@ -31,6 +32,7 @@ class BaseOrganization(BaseModel):
Field(
pattern=r"^https://d\-nb\.info/gnd/[-X0-9]{3,10}$",
examples=["https://d-nb.info/gnd/17690-4"],
+ json_schema_extra={"format": "uri"},
),
]
] = []
@@ -40,16 +42,18 @@ class BaseOrganization(BaseModel):
Field(
pattern=r"^https://isni\.org/isni/[X0-9]{16}$",
examples=["https://isni.org/isni/0000000109403744"],
+ json_schema_extra={"format": "uri"},
),
]
] = []
- officialName: list[Text] = Field(..., min_length=1)
+ officialName: Annotated[list[Text], Field(min_length=1)]
rorId: list[
Annotated[
str,
Field(
pattern=r"^https://ror\.org/[a-z0-9]{9}$",
examples=["https://ror.org/01k5qnb77"],
+ json_schema_extra={"format": "uri"},
),
]
] = []
@@ -60,16 +64,20 @@ class BaseOrganization(BaseModel):
Field(
pattern=r"^https://viaf\.org/viaf/[0-9]{2,22}$",
examples=["https://viaf.org/viaf/123556639"],
+ json_schema_extra={"format": "uri"},
+ ),
+ ]
+ ] = []
+ wikidataId: list[
+ Annotated[
+ str,
+ Field(
+ examples=["http://www.wikidata.org/entity/Q679041"],
+ pattern=r"^https://www\.wikidata\.org/entity/[PQ0-9]{2,64}$",
+ json_schema_extra={"format": "uri"},
),
]
] = []
- wikidataId: Annotated[
- str,
- Field(
- examples=["http://www.wikidata.org/entity/Q679041"],
- pattern=r"^https://www\.wikidata\.org/entity/[PQ0-9]{2,64}$",
- ),
- ] | None = None
class ExtractedOrganization(BaseOrganization, ExtractedData):
diff --git a/mex/common/models/organizational_unit.py b/mex/common/models/organizational_unit.py
index 8db28c5c..6c24269d 100644
--- a/mex/common/models/organizational_unit.py
+++ b/mex/common/models/organizational_unit.py
@@ -13,15 +13,8 @@ class BaseOrganizationalUnit(BaseModel):
stableTargetId: OrganizationalUnitID
alternativeName: list[Text] = []
- email: list[
- Annotated[
- Email,
- Field(
- examples=["info@rki.de"],
- ),
- ]
- ] = []
- name: list[Text] = Field(..., min_length=1)
+ email: list[Email] = []
+ name: Annotated[list[Text], Field(min_length=1)]
parentUnit: OrganizationalUnitID | None = None
shortName: list[Text] = []
unitOf: list[OrganizationID] = []
diff --git a/mex/common/models/person.py b/mex/common/models/person.py
index ff24e8ae..4203c27b 100644
--- a/mex/common/models/person.py
+++ b/mex/common/models/person.py
@@ -13,14 +13,7 @@ class BasePerson(BaseModel):
stableTargetId: PersonID
affiliation: list[OrganizationID] = []
- email: list[
- Annotated[
- Email,
- Field(
- examples=["info@rki.de"],
- ),
- ]
- ] = []
+ email: list[Email] = []
familyName: list[
Annotated[
str,
@@ -33,15 +26,15 @@ class BasePerson(BaseModel):
Annotated[
str,
Field(
- examples=["Anna Schmidt", "P. Meier", "Wolf Maria Hermann"],
+ examples=["Juturna Felicitás", "M. Berhanu", "Keone Seong-Hyeon"],
),
]
- ]
+ ] = []
givenName: list[
Annotated[
str,
Field(
- examples=["Wangari", "Marie Salomea", "May-Britt"],
+ examples=["Romāns", "Marie Salomea", "May-Britt"],
),
]
] = []
@@ -51,6 +44,7 @@ class BasePerson(BaseModel):
Field(
pattern=r"^https://isni\.org/isni/[X0-9]{16}$",
examples=["https://isni.org/isni/0000000109403744"],
+ json_schema_extra={"format": "uri"},
),
]
] = []
@@ -61,6 +55,7 @@ class BasePerson(BaseModel):
Field(
pattern=r"^https://orcid\.org/[-X0-9]{9,21}$",
examples=["https://orcid.org/0000-0002-9079-593X"],
+ json_schema_extra={"format": "uri"},
),
],
] = []
diff --git a/mex/common/models/primary_source.py b/mex/common/models/primary_source.py
index d8491757..288a9be8 100644
--- a/mex/common/models/primary_source.py
+++ b/mex/common/models/primary_source.py
@@ -1,3 +1,5 @@
+from typing import Annotated
+
from pydantic import Field
from mex.common.models.base import BaseModel
@@ -24,10 +26,12 @@ class BasePrimarySource(BaseModel):
locatedAt: list[Link] = []
title: list[Text] = []
unitInCharge: list[OrganizationalUnitID] = []
- version: str | None = Field(
- None,
- examples=["v1", "2023-01-16", "Schema 9"],
- )
+ version: Annotated[
+ str,
+ Field(
+ examples=["v1", "2023-01-16", "Schema 9"],
+ ),
+ ] | None = None
class ExtractedPrimarySource(BasePrimarySource, ExtractedData):
diff --git a/mex/common/models/resource.py b/mex/common/models/resource.py
index 2984ca26..7ac1e7e5 100644
--- a/mex/common/models/resource.py
+++ b/mex/common/models/resource.py
@@ -9,68 +9,39 @@
AccessPlatformID,
AccessRestriction,
ActivityID,
+ AnonymizationPseudonymization,
ContactPointID,
+ DataProcessingState,
DistributionID,
+ Frequency,
+ Language,
+ License,
Link,
OrganizationalUnitID,
OrganizationID,
PersonID,
ResourceID,
+ ResourceTypeGeneral,
Text,
Theme,
Timestamp,
- VocabularyEnum,
)
-class ResourceTypeGeneral(VocabularyEnum):
- """The general type of a resource."""
-
- __vocabulary__ = "resource-type-general"
-
-
-class AnonymizationPseudonymization(VocabularyEnum):
- """Whether the resource is anonymized/pseudonymized."""
-
- __vocabulary__ = "anonymization-pseudonymization"
-
-
-class DataProcessingState(VocabularyEnum):
- """Type for state of data processing."""
-
- __vocabulary__ = "data-processing-state"
-
-
-class Frequency(VocabularyEnum):
- """Frequency type."""
-
- __vocabulary__ = "frequency"
-
-
-class Language(VocabularyEnum):
- """Language type."""
-
- __vocabulary__ = "language"
-
-
-class License(VocabularyEnum):
- """License type."""
-
- __vocabulary__ = "license"
-
-
class BaseResource(BaseModel):
"""A defined piece or collection of information."""
stableTargetId: ResourceID
accessPlatform: list[AccessPlatformID] = []
- accessRestriction: AccessRestriction = Field(
- ...,
- examples=["https://mex.rki.de/item/access-restriction-1"],
- )
- accrualPeriodicity: Frequency | None = Field(
- None, examples=["https://mex.rki.de/item/frequency-1"]
- )
+ accessRestriction: Annotated[
+ AccessRestriction,
+ Field(
+ examples=["https://mex.rki.de/item/access-restriction-1"],
+ ),
+ ]
+ accrualPeriodicity: Annotated[
+ Frequency, Field(examples=["https://mex.rki.de/item/frequency-1"])
+ ] | None = None
alternativeTitle: list[Text] = []
anonymizationPseudonymization: list[
Annotated[
@@ -80,12 +51,12 @@ class BaseResource(BaseModel):
),
]
] = []
- contact: list[OrganizationalUnitID | PersonID | ContactPointID] = Field(
- ..., min_length=1
- )
+ contact: Annotated[
+ list[OrganizationalUnitID | PersonID | ContactPointID], Field(min_length=1)
+ ]
contributingUnit: list[OrganizationalUnitID] = []
contributor: list[PersonID] = []
- created: list[Timestamp] = []
+ created: Timestamp | None = None
creator: list[PersonID] = []
description: list[Text] = []
distribution: list[DistributionID] = []
@@ -98,21 +69,23 @@ class BaseResource(BaseModel):
language: list[
Annotated[Language, Field(examples=["https://mex.rki.de/item/language-1"])]
] = []
- license: list[
+ license: Annotated[
+ License, Field(examples=["https://mex.rki.de/item/license-1"])
+ ] | None = None
+ loincId: list[str] = []
+ meshId: list[
Annotated[
- License,
+ str,
Field(
- examples=["https://mex.rki.de/item/license-1"],
+ pattern=r"^http://id\.nlm\.nih\.gov/mesh/[A-Z0-9]{2,64}$",
+ examples=["http://id.nlm.nih.gov/mesh/D001604"],
+ json_schema_extra={"format": "uri"},
),
]
] = []
- loincId: list[str] = []
- meshId: list[
- Annotated[str, Field(pattern=r"^http://id\.nlm\.nih\.gov/mesh/[A-Z0-9]{2,64}$")]
- ] = []
method: list[Text] = []
methodDescription: list[Text] = []
- modified: list[Timestamp] = []
+ modified: Timestamp | None = None
publication: list[Link] = []
publisher: list[OrganizationID] = []
qualityInformation: list[Text] = []
@@ -128,15 +101,26 @@ class BaseResource(BaseModel):
rights: list[Text] = []
sizeOfDataBasis: str | None = None
spatial: list[Text] = []
- stateOfDataProcessing: DataProcessingState | None = Field(
- None, examples=["https://mex.rki.de/item/data-processing-state-1"]
- )
- temporal: list[Timestamp | str] = []
- theme: list[
- Annotated[Theme, Field(examples=["https://mex.rki.de/item/theme-1"])]
- ] = Field(..., min_length=1)
- title: list[Text] = Field(..., min_length=1)
- unitInCharge: list[OrganizationalUnitID] = Field(..., min_length=1)
+ stateOfDataProcessing: list[
+ Annotated[
+ DataProcessingState,
+ Field(
+ examples=["https://mex.rki.de/item/data-processing-state-1"],
+ ),
+ ]
+ ] = []
+ temporal: Timestamp | Annotated[
+ str,
+ Field(
+ examples=["2022-01 bis 2022-03", "Sommer 2023", "nach 2013", "1998-2008"]
+ ),
+ ] | None = None
+ theme: Annotated[
+ list[Annotated[Theme, Field(examples=["https://mex.rki.de/item/theme-1"])]],
+ Field(min_length=1),
+ ]
+ title: Annotated[list[Text], Field(min_length=1)]
+ unitInCharge: Annotated[list[OrganizationalUnitID], Field(min_length=1)]
wasGeneratedBy: ActivityID | None = None
diff --git a/mex/common/models/variable.py b/mex/common/models/variable.py
index 2b7e1594..ce16a3c2 100644
--- a/mex/common/models/variable.py
+++ b/mex/common/models/variable.py
@@ -6,48 +6,46 @@
from mex.common.models.extracted_data import ExtractedData
from mex.common.models.merged_item import MergedItem
from mex.common.types import (
+ DataType,
ResourceID,
Text,
VariableGroupID,
VariableID,
- VocabularyEnum,
)
-class DataType(VocabularyEnum):
- """The type of the single piece of information within a datum."""
-
- __vocabulary__ = "data-type"
-
-
class BaseVariable(BaseModel):
"""A single piece of information within a resource."""
stableTargetId: VariableID
belongsTo: list[VariableGroupID] = []
- codingSystem: str | None = Field(
- None,
- examples=["SF-36 Version 1"],
- )
- dataType: DataType | None = Field(
- None,
- examples=["https://mex.rki.de/item/data-type-1"],
- )
+ codingSystem: Annotated[
+ str,
+ Field(
+ examples=["SF-36 Version 1"],
+ ),
+ ] | None = None
+ dataType: Annotated[
+ DataType,
+ Field(
+ examples=["https://mex.rki.de/item/data-type-1"],
+ ),
+ ] | None = None
description: list[Text] = []
- label: list[
- Annotated[
- Text,
- Field(
- examples=[
- {"language": "de", "value": "Mehrere Treppenabsätze steigen"}
- ],
- ),
- ]
- ] = Field(
- ...,
- min_length=1,
- )
- usedIn: list[ResourceID] = Field(..., min_length=1)
+ label: Annotated[
+ list[
+ Annotated[
+ Text,
+ Field(
+ examples=[
+ {"language": "de", "value": "Mehrere Treppenabsätze steigen"}
+ ],
+ ),
+ ]
+ ],
+ Field(min_length=1),
+ ]
+ usedIn: Annotated[list[ResourceID], Field(min_length=1)]
valueSet: list[
Annotated[
str,
@@ -59,9 +57,7 @@ class BaseVariable(BaseModel):
],
),
]
- ] = Field(
- [],
- )
+ ] = []
class ExtractedVariable(BaseVariable, ExtractedData):
diff --git a/mex/common/models/variable_group.py b/mex/common/models/variable_group.py
index 7fb25fe6..d5a2f09e 100644
--- a/mex/common/models/variable_group.py
+++ b/mex/common/models/variable_group.py
@@ -1,3 +1,5 @@
+from typing import Annotated
+
from pydantic import Field
from mex.common.models.base import BaseModel
@@ -10,8 +12,8 @@ class BaseVariableGroup(BaseModel):
"""The grouping of variables according to a certain aspect."""
stableTargetId: VariableGroupID
- containedBy: list[ResourceID] = Field(..., min_length=1)
- label: list[Text] = Field(..., min_length=1)
+ containedBy: Annotated[list[ResourceID], Field(min_length=1)]
+ label: Annotated[list[Text], Field(min_length=1)]
class ExtractedVariableGroup(BaseVariableGroup, ExtractedData):
diff --git a/mex/common/public_api/models.py b/mex/common/public_api/models.py
index b665f6c4..2668b2b8 100644
--- a/mex/common/public_api/models.py
+++ b/mex/common/public_api/models.py
@@ -66,7 +66,7 @@ class PublicApiItem(PublicApiBaseModel):
entityType: str
itemId: UUID | None = Field(None, exclude=True)
- businessId: str = Field(..., exclude=True)
+ businessId: str = Field(exclude=True)
values: list[PublicApiField]
@property
@@ -96,7 +96,7 @@ class PublicApiItemWithoutValues(PublicApiBaseModel):
entityType: str
itemId: UUID | None = Field(None, exclude=True)
- businessId: str = Field(..., exclude=True)
+ businessId: str = Field(exclude=True)
@property
def stableTargetId(self) -> Identifier: # noqa: N802
diff --git a/mex/common/testing/joker.py b/mex/common/testing/joker.py
index 1536ead3..cd7be534 100644
--- a/mex/common/testing/joker.py
+++ b/mex/common/testing/joker.py
@@ -10,7 +10,7 @@ class Joker:
assert value == {"predictable": 42, "timestamp": Joker()}
"""
- _repr_cache = None
+ _repr_cache: str | None = None
def __eq__(self, obj: Any) -> bool:
"""Pretend to be equal to any other object."""
diff --git a/mex/common/transform.py b/mex/common/transform.py
index a443b3b9..e4a59b1a 100644
--- a/mex/common/transform.py
+++ b/mex/common/transform.py
@@ -55,3 +55,21 @@ def dromedary_to_snake(string: str) -> str:
for word in re.split(r"([A-Z]+(?![a-z])|[a-z]+|[A-Z][a-z]+)", string)
if word.strip("_")
)
+
+
+@cache
+def dromedary_to_kebab(string: str) -> str:
+ """Convert the given string from `dromedaryCase` into `kebab-case`."""
+ return "-".join(
+ word.lower()
+ for word in re.split(r"([A-Z]+(?![a-z])|[a-z]+|[A-Z][a-z]+)", string)
+ if word.strip("-")
+ )
+
+
+@cache
+def kebab_to_camel(string: str) -> str:
+ """Convert the given string from `kebab-case` into `CamelCase`."""
+ if len(tokens := re.split(r"\-+", string)) > 1:
+ return "".join(word.title() for word in tokens)
+ return string[:1].upper() + string[1:]
diff --git a/mex/common/types/__init__.py b/mex/common/types/__init__.py
index 9f8be4ed..2dbc6286 100644
--- a/mex/common/types/__init__.py
+++ b/mex/common/types/__init__.py
@@ -28,6 +28,17 @@
)
from mex.common.types.vocabulary import (
AccessRestriction,
+ ActivityType,
+ AnonymizationPseudonymization,
+ APIType,
+ DataProcessingState,
+ DataType,
+ Frequency,
+ Language,
+ License,
+ MIMEType,
+ ResourceTypeGeneral,
+ TechnicalAccessibility,
Theme,
VocabularyEnum,
VocabularyLoader,
@@ -38,23 +49,34 @@
"AccessPlatformID",
"AccessRestriction",
"ActivityID",
+ "ActivityType",
+ "AnonymizationPseudonymization",
+ "APIType",
"AssetsPath",
"CET",
"ContactPointID",
+ "DataProcessingState",
+ "DataType",
"DistributionID",
"Email",
+ "Frequency",
"Identifier",
"IdentityProvider",
+ "Language",
+ "License",
"Link",
"LinkLanguage",
+ "MIMEType",
"OrganizationalUnitID",
"OrganizationID",
"PathWrapper",
"PersonID",
"PrimarySourceID",
"ResourceID",
+ "ResourceTypeGeneral",
"Sink",
"split_to_caps",
+ "TechnicalAccessibility",
"Text",
"TextLanguage",
"Theme",
diff --git a/mex/common/types/email.py b/mex/common/types/email.py
index b21020a5..fd4416bd 100644
--- a/mex/common/types/email.py
+++ b/mex/common/types/email.py
@@ -4,7 +4,7 @@
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import core_schema
-EMAIL_PATTERN = r".+@.+"
+EMAIL_PATTERN = r"^[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+$"
class Email(str):
@@ -12,7 +12,7 @@ class Email(str):
@classmethod
def __get_pydantic_core_schema__(cls, _source: Type[Any]) -> core_schema.CoreSchema:
- """Get pydanctic core schema."""
+ """Get pydantic core schema."""
return core_schema.str_schema(pattern=EMAIL_PATTERN)
@classmethod
@@ -21,5 +21,7 @@ def __get_pydantic_json_schema__(
) -> JsonSchemaValue:
"""Add title and format."""
field_schema = handler(core_schema_)
- field_schema.update(title=cls.__name__, format="email")
+ field_schema["title"] = cls.__name__
+ field_schema["format"] = "email"
+ field_schema["examples"] = ["info@rki.de"]
return field_schema
diff --git a/mex/common/types/link.py b/mex/common/types/link.py
index 7ef3d902..7f08c7bd 100644
--- a/mex/common/types/link.py
+++ b/mex/common/types/link.py
@@ -41,7 +41,6 @@ class Link(BaseModel):
language: LinkLanguage | None = None
title: str | None = None
url: str = Field(
- ...,
pattern=r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?",
min_length=1,
examples=["https://hello-world.org", "file://S:/OE/MF4/Projekte/MEx"],
@@ -56,14 +55,11 @@ def convert_markdown_to_link(cls, values: Any) -> dict[str, Any]:
return values
if isinstance(values, str):
if match := re.match(r"\[(?P
.*)\]\((?P.*)\)", values):
- url_dict = {
+ return {
key: markdown_unescape(value)
for key, value in match.groupdict().items()
}
- else:
- url_dict = {"url": values}
-
- return url_dict
+ return {"url": values}
raise ValueError(f"Allowed input types are dict and str, got {type(values)}")
def __str__(self) -> str:
diff --git a/mex/common/types/text.py b/mex/common/types/text.py
index 3d55a0d6..99927521 100644
--- a/mex/common/types/text.py
+++ b/mex/common/types/text.py
@@ -26,7 +26,7 @@ class Text(BaseModel):
Text(value="foo") == Text.model_validate("foo")
"""
- value: str = Field(..., min_length=1)
+ value: str = Field(min_length=1)
language: TextLanguage | None = None
@model_validator(mode="before")
diff --git a/mex/common/types/timestamp.py b/mex/common/types/timestamp.py
index e7c25d78..441994ac 100644
--- a/mex/common/types/timestamp.py
+++ b/mex/common/types/timestamp.py
@@ -167,6 +167,7 @@ def __get_pydantic_json_schema__(
"""Modify the schema to add the class name as title and examples."""
json_schema = handler(core_schema_)
json_schema["title"] = cls.__name__
+ json_schema["format"] = "date-time"
json_schema["examples"] = [
"2011",
"2019-03",
@@ -232,7 +233,7 @@ def __eq__(self, other: Any) -> bool:
try:
other = self.validate(other)
except TypeError:
- return NotImplemented
+ return False
return bool(
self.date_time == other.date_time and self.precision == other.precision
)
@@ -242,7 +243,7 @@ def __gt__(self, other: Any) -> bool:
try:
other = self.validate(other)
except TypeError:
- return NotImplemented
+ raise NotImplementedError()
return bool(self.date_time > other.date_time)
def __str__(self) -> str:
diff --git a/mex/common/types/vocabulary.py b/mex/common/types/vocabulary.py
index 13c4f8e9..f519e2f7 100644
--- a/mex/common/types/vocabulary.py
+++ b/mex/common/types/vocabulary.py
@@ -114,6 +114,72 @@ class AccessRestriction(VocabularyEnum):
__vocabulary__ = "access-restriction"
+class ActivityType(VocabularyEnum):
+ """The activity type."""
+
+ __vocabulary__ = "activity-type"
+
+
+class AnonymizationPseudonymization(VocabularyEnum):
+ """Whether the resource is anonymized/pseudonymized."""
+
+ __vocabulary__ = "anonymization-pseudonymization"
+
+
+class APIType(VocabularyEnum):
+ """Technical standard or style of a network API."""
+
+ __vocabulary__ = "api-type"
+
+
+class DataProcessingState(VocabularyEnum):
+ """Type for state of data processing."""
+
+ __vocabulary__ = "data-processing-state"
+
+
+class DataType(VocabularyEnum):
+ """The type of the single piece of information within a datum."""
+
+ __vocabulary__ = "data-type"
+
+
+class Frequency(VocabularyEnum):
+ """Frequency type."""
+
+ __vocabulary__ = "frequency"
+
+
+class Language(VocabularyEnum):
+ """Language type."""
+
+ __vocabulary__ = "language"
+
+
+class License(VocabularyEnum):
+ """License type."""
+
+ __vocabulary__ = "license"
+
+
+class MIMEType(VocabularyEnum):
+ """The mime type."""
+
+ __vocabulary__ = "mime-type"
+
+
+class ResourceTypeGeneral(VocabularyEnum):
+ """The general type of a resource."""
+
+ __vocabulary__ = "resource-type-general"
+
+
+class TechnicalAccessibility(VocabularyEnum):
+ """Technical accessibility within RKI and outside of RKI."""
+
+ __vocabulary__ = "technical-accessibility"
+
+
class Theme(VocabularyEnum):
"""The theme type."""
diff --git a/poetry.lock b/poetry.lock
index e1d7605f..52328b32 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "accessible-pygments"
@@ -56,18 +56,15 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
[[package]]
name = "babel"
-version = "2.13.1"
+version = "2.14.0"
description = "Internationalization utilities"
optional = false
python-versions = ">=3.7"
files = [
- {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"},
- {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"},
+ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
+ {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
]
-[package.dependencies]
-setuptools = {version = "*", markers = "python_version >= \"3.12\""}
-
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
@@ -102,29 +99,33 @@ lxml = ["lxml"]
[[package]]
name = "black"
-version = "23.11.0"
+version = "23.12.0"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
files = [
- {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"},
- {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"},
- {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"},
- {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"},
- {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"},
- {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"},
- {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"},
- {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"},
- {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"},
- {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"},
- {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"},
- {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"},
- {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"},
- {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"},
- {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"},
- {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"},
- {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"},
- {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"},
+ {file = "black-23.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67f19562d367468ab59bd6c36a72b2c84bc2f16b59788690e02bbcb140a77175"},
+ {file = "black-23.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbd75d9f28a7283b7426160ca21c5bd640ca7cd8ef6630b4754b6df9e2da8462"},
+ {file = "black-23.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:593596f699ca2dcbbbdfa59fcda7d8ad6604370c10228223cd6cf6ce1ce7ed7e"},
+ {file = "black-23.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:12d5f10cce8dc27202e9a252acd1c9a426c83f95496c959406c96b785a92bb7d"},
+ {file = "black-23.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e73c5e3d37e5a3513d16b33305713237a234396ae56769b839d7c40759b8a41c"},
+ {file = "black-23.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba09cae1657c4f8a8c9ff6cfd4a6baaf915bb4ef7d03acffe6a2f6585fa1bd01"},
+ {file = "black-23.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace64c1a349c162d6da3cef91e3b0e78c4fc596ffde9413efa0525456148873d"},
+ {file = "black-23.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:72db37a2266b16d256b3ea88b9affcdd5c41a74db551ec3dd4609a59c17d25bf"},
+ {file = "black-23.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fdf6f23c83078a6c8da2442f4d4eeb19c28ac2a6416da7671b72f0295c4a697b"},
+ {file = "black-23.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39dda060b9b395a6b7bf9c5db28ac87b3c3f48d4fdff470fa8a94ab8271da47e"},
+ {file = "black-23.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7231670266ca5191a76cb838185d9be59cfa4f5dd401b7c1c70b993c58f6b1b5"},
+ {file = "black-23.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:193946e634e80bfb3aec41830f5d7431f8dd5b20d11d89be14b84a97c6b8bc75"},
+ {file = "black-23.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcf91b01ddd91a2fed9a8006d7baa94ccefe7e518556470cf40213bd3d44bbbc"},
+ {file = "black-23.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:996650a89fe5892714ea4ea87bc45e41a59a1e01675c42c433a35b490e5aa3f0"},
+ {file = "black-23.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdbff34c487239a63d86db0c9385b27cdd68b1bfa4e706aa74bb94a435403672"},
+ {file = "black-23.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:97af22278043a6a1272daca10a6f4d36c04dfa77e61cbaaf4482e08f3640e9f0"},
+ {file = "black-23.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ead25c273adfad1095a8ad32afdb8304933efba56e3c1d31b0fee4143a1e424a"},
+ {file = "black-23.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c71048345bdbced456cddf1622832276d98a710196b842407840ae8055ade6ee"},
+ {file = "black-23.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a832b6e00eef2c13b3239d514ea3b7d5cc3eaa03d0474eedcbbda59441ba5d"},
+ {file = "black-23.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:6a82a711d13e61840fb11a6dfecc7287f2424f1ca34765e70c909a35ffa7fb95"},
+ {file = "black-23.12.0-py3-none-any.whl", hash = "sha256:a7c07db8200b5315dc07e331dda4d889a56f6bf4db6a9c2a526fa3166a81614f"},
+ {file = "black-23.12.0.tar.gz", hash = "sha256:330a327b422aca0634ecd115985c1c7fd7bdb5b5a2ef8aa9888a82e2ebe9437a"},
]
[package.dependencies]
@@ -136,7 +137,7 @@ platformdirs = ">=2"
[package.extras]
colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4)"]
+d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
@@ -277,63 +278,63 @@ files = [
[[package]]
name = "coverage"
-version = "7.3.2"
+version = "7.3.3"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"},
- {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"},
- {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"},
- {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"},
- {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"},
- {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"},
- {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"},
- {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"},
- {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"},
- {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"},
- {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"},
- {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"},
- {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"},
- {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"},
- {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"},
- {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"},
- {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"},
- {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"},
- {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"},
- {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"},
- {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"},
- {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"},
- {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"},
- {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"},
- {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"},
- {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"},
- {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"},
- {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"},
- {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"},
- {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"},
- {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"},
- {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"},
- {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"},
- {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"},
- {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"},
- {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"},
- {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"},
- {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"},
- {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"},
- {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"},
- {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"},
- {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"},
- {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"},
- {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"},
- {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"},
- {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"},
- {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"},
- {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"},
- {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"},
- {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"},
- {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"},
- {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"},
+ {file = "coverage-7.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d874434e0cb7b90f7af2b6e3309b0733cde8ec1476eb47db148ed7deeb2a9494"},
+ {file = "coverage-7.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee6621dccce8af666b8c4651f9f43467bfbf409607c604b840b78f4ff3619aeb"},
+ {file = "coverage-7.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1367aa411afb4431ab58fd7ee102adb2665894d047c490649e86219327183134"},
+ {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f0f8f0c497eb9c9f18f21de0750c8d8b4b9c7000b43996a094290b59d0e7523"},
+ {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0338c4b0951d93d547e0ff8d8ea340fecf5885f5b00b23be5aa99549e14cfd"},
+ {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d31650d313bd90d027f4be7663dfa2241079edd780b56ac416b56eebe0a21aab"},
+ {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9437a4074b43c177c92c96d051957592afd85ba00d3e92002c8ef45ee75df438"},
+ {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e17d9cb06c13b4f2ef570355fa45797d10f19ca71395910b249e3f77942a837"},
+ {file = "coverage-7.3.3-cp310-cp310-win32.whl", hash = "sha256:eee5e741b43ea1b49d98ab6e40f7e299e97715af2488d1c77a90de4a663a86e2"},
+ {file = "coverage-7.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:593efa42160c15c59ee9b66c5f27a453ed3968718e6e58431cdfb2d50d5ad284"},
+ {file = "coverage-7.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c944cf1775235c0857829c275c777a2c3e33032e544bcef614036f337ac37bb"},
+ {file = "coverage-7.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eda7f6e92358ac9e1717ce1f0377ed2b9320cea070906ece4e5c11d172a45a39"},
+ {file = "coverage-7.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c854c1d2c7d3e47f7120b560d1a30c1ca221e207439608d27bc4d08fd4aeae8"},
+ {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:222b038f08a7ebed1e4e78ccf3c09a1ca4ac3da16de983e66520973443b546bc"},
+ {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff4800783d85bff132f2cc7d007426ec698cdce08c3062c8d501ad3f4ea3d16c"},
+ {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fc200cec654311ca2c3f5ab3ce2220521b3d4732f68e1b1e79bef8fcfc1f2b97"},
+ {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:307aecb65bb77cbfebf2eb6e12009e9034d050c6c69d8a5f3f737b329f4f15fb"},
+ {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ffb0eacbadb705c0a6969b0adf468f126b064f3362411df95f6d4f31c40d31c1"},
+ {file = "coverage-7.3.3-cp311-cp311-win32.whl", hash = "sha256:79c32f875fd7c0ed8d642b221cf81feba98183d2ff14d1f37a1bbce6b0347d9f"},
+ {file = "coverage-7.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:243576944f7c1a1205e5cd658533a50eba662c74f9be4c050d51c69bd4532936"},
+ {file = "coverage-7.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a2ac4245f18057dfec3b0074c4eb366953bca6787f1ec397c004c78176a23d56"},
+ {file = "coverage-7.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f9191be7af41f0b54324ded600e8ddbcabea23e1e8ba419d9a53b241dece821d"},
+ {file = "coverage-7.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c0b1b8b5a4aebf8fcd227237fc4263aa7fa0ddcd4d288d42f50eff18b0bac4"},
+ {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee453085279df1bac0996bc97004771a4a052b1f1e23f6101213e3796ff3cb85"},
+ {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1191270b06ecd68b1d00897b2daddb98e1719f63750969614ceb3438228c088e"},
+ {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:007a7e49831cfe387473e92e9ff07377f6121120669ddc39674e7244350a6a29"},
+ {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:af75cf83c2d57717a8493ed2246d34b1f3398cb8a92b10fd7a1858cad8e78f59"},
+ {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:811ca7373da32f1ccee2927dc27dc523462fd30674a80102f86c6753d6681bc6"},
+ {file = "coverage-7.3.3-cp312-cp312-win32.whl", hash = "sha256:733537a182b5d62184f2a72796eb6901299898231a8e4f84c858c68684b25a70"},
+ {file = "coverage-7.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:e995efb191f04b01ced307dbd7407ebf6e6dc209b528d75583277b10fd1800ee"},
+ {file = "coverage-7.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbd8a5fe6c893de21a3c6835071ec116d79334fbdf641743332e442a3466f7ea"},
+ {file = "coverage-7.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:50c472c1916540f8b2deef10cdc736cd2b3d1464d3945e4da0333862270dcb15"},
+ {file = "coverage-7.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e9223a18f51d00d3ce239c39fc41410489ec7a248a84fab443fbb39c943616c"},
+ {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f501e36ac428c1b334c41e196ff6bd550c0353c7314716e80055b1f0a32ba394"},
+ {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475de8213ed95a6b6283056d180b2442eee38d5948d735cd3d3b52b86dd65b92"},
+ {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afdcc10c01d0db217fc0a64f58c7edd635b8f27787fea0a3054b856a6dff8717"},
+ {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fff0b2f249ac642fd735f009b8363c2b46cf406d3caec00e4deeb79b5ff39b40"},
+ {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a1f76cfc122c9e0f62dbe0460ec9cc7696fc9a0293931a33b8870f78cf83a327"},
+ {file = "coverage-7.3.3-cp38-cp38-win32.whl", hash = "sha256:757453848c18d7ab5d5b5f1827293d580f156f1c2c8cef45bfc21f37d8681069"},
+ {file = "coverage-7.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad2453b852a1316c8a103c9c970db8fbc262f4f6b930aa6c606df9b2766eee06"},
+ {file = "coverage-7.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b15e03b8ee6a908db48eccf4e4e42397f146ab1e91c6324da44197a45cb9132"},
+ {file = "coverage-7.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89400aa1752e09f666cc48708eaa171eef0ebe3d5f74044b614729231763ae69"},
+ {file = "coverage-7.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c59a3e59fb95e6d72e71dc915e6d7fa568863fad0a80b33bc7b82d6e9f844973"},
+ {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ede881c7618f9cf93e2df0421ee127afdfd267d1b5d0c59bcea771cf160ea4a"},
+ {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3bfd2c2f0e5384276e12b14882bf2c7621f97c35320c3e7132c156ce18436a1"},
+ {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f3bad1a9313401ff2964e411ab7d57fb700a2d5478b727e13f156c8f89774a0"},
+ {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:65d716b736f16e250435473c5ca01285d73c29f20097decdbb12571d5dfb2c94"},
+ {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a702e66483b1fe602717020a0e90506e759c84a71dbc1616dd55d29d86a9b91f"},
+ {file = "coverage-7.3.3-cp39-cp39-win32.whl", hash = "sha256:7fbf3f5756e7955174a31fb579307d69ffca91ad163467ed123858ce0f3fd4aa"},
+ {file = "coverage-7.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cad9afc1644b979211989ec3ff7d82110b2ed52995c2f7263e7841c846a75348"},
+ {file = "coverage-7.3.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:d299d379b676812e142fb57662a8d0d810b859421412b4d7af996154c00c31bb"},
+ {file = "coverage-7.3.3.tar.gz", hash = "sha256:df04c64e58df96b4427db8d0559e95e2df3138c9916c96f9f6a4dd220db2fdb7"},
]
[package.extras]
@@ -595,6 +596,21 @@ files = [
[package.dependencies]
traitlets = "*"
+[[package]]
+name = "mex-model"
+version = "2.2.0"
+description = "RKI MEx metadata model."
+optional = false
+python-versions = "*"
+files = []
+develop = false
+
+[package.source]
+type = "git"
+url = "https://github.com/robert-koch-institut/mex-model.git"
+reference = "2.2.0"
+resolved_reference = "d8ef77aa5a5f3a696e5698969ffa0dae46d5b785"
+
[[package]]
name = "mypy"
version = "1.7.1"
@@ -710,36 +726,36 @@ files = [
[[package]]
name = "pandas"
-version = "2.1.3"
+version = "2.1.4"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.9"
files = [
- {file = "pandas-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acf08a73b5022b479c1be155d4988b72f3020f308f7a87c527702c5f8966d34f"},
- {file = "pandas-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3cc4469ff0cf9aa3a005870cb49ab8969942b7156e0a46cc3f5abd6b11051dfb"},
- {file = "pandas-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35172bff95f598cc5866c047f43c7f4df2c893acd8e10e6653a4b792ed7f19bb"},
- {file = "pandas-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59dfe0e65a2f3988e940224e2a70932edc964df79f3356e5f2997c7d63e758b4"},
- {file = "pandas-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0296a66200dee556850d99b24c54c7dfa53a3264b1ca6f440e42bad424caea03"},
- {file = "pandas-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:465571472267a2d6e00657900afadbe6097c8e1dc43746917db4dfc862e8863e"},
- {file = "pandas-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04d4c58e1f112a74689da707be31cf689db086949c71828ef5da86727cfe3f82"},
- {file = "pandas-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fa2ad4ff196768ae63a33f8062e6838efed3a319cf938fdf8b95e956c813042"},
- {file = "pandas-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4441ac94a2a2613e3982e502ccec3bdedefe871e8cea54b8775992485c5660ef"},
- {file = "pandas-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ded6ff28abbf0ea7689f251754d3789e1edb0c4d0d91028f0b980598418a58"},
- {file = "pandas-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca5680368a5139d4920ae3dc993eb5106d49f814ff24018b64d8850a52c6ed2"},
- {file = "pandas-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:de21e12bf1511190fc1e9ebc067f14ca09fccfb189a813b38d63211d54832f5f"},
- {file = "pandas-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a5d53c725832e5f1645e7674989f4c106e4b7249c1d57549023ed5462d73b140"},
- {file = "pandas-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7cf4cf26042476e39394f1f86868d25b265ff787c9b2f0d367280f11afbdee6d"},
- {file = "pandas-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72c84ec1b1d8e5efcbff5312abe92bfb9d5b558f11e0cf077f5496c4f4a3c99e"},
- {file = "pandas-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f539e113739a3e0cc15176bf1231a553db0239bfa47a2c870283fd93ba4f683"},
- {file = "pandas-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc77309da3b55732059e484a1efc0897f6149183c522390772d3561f9bf96c00"},
- {file = "pandas-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:08637041279b8981a062899da0ef47828df52a1838204d2b3761fbd3e9fcb549"},
- {file = "pandas-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b99c4e51ef2ed98f69099c72c75ec904dd610eb41a32847c4fcbc1a975f2d2b8"},
- {file = "pandas-2.1.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7ea8ae8004de0381a2376662c0505bb0a4f679f4c61fbfd122aa3d1b0e5f09d"},
- {file = "pandas-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd76d67ca2d48f56e2db45833cf9d58f548f97f61eecd3fdc74268417632b8a"},
- {file = "pandas-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1329dbe93a880a3d7893149979caa82d6ba64a25e471682637f846d9dbc10dd2"},
- {file = "pandas-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:321ecdb117bf0f16c339cc6d5c9a06063854f12d4d9bc422a84bb2ed3207380a"},
- {file = "pandas-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:11a771450f36cebf2a4c9dbd3a19dfa8c46c4b905a3ea09dc8e556626060fe71"},
- {file = "pandas-2.1.3.tar.gz", hash = "sha256:22929f84bca106921917eb73c1521317ddd0a4c71b395bcf767a106e3494209f"},
+ {file = "pandas-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bdec823dc6ec53f7a6339a0e34c68b144a7a1fd28d80c260534c39c62c5bf8c9"},
+ {file = "pandas-2.1.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:294d96cfaf28d688f30c918a765ea2ae2e0e71d3536754f4b6de0ea4a496d034"},
+ {file = "pandas-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b728fb8deba8905b319f96447a27033969f3ea1fea09d07d296c9030ab2ed1d"},
+ {file = "pandas-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00028e6737c594feac3c2df15636d73ace46b8314d236100b57ed7e4b9ebe8d9"},
+ {file = "pandas-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:426dc0f1b187523c4db06f96fb5c8d1a845e259c99bda74f7de97bd8a3bb3139"},
+ {file = "pandas-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:f237e6ca6421265643608813ce9793610ad09b40154a3344a088159590469e46"},
+ {file = "pandas-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b7d852d16c270e4331f6f59b3e9aa23f935f5c4b0ed2d0bc77637a8890a5d092"},
+ {file = "pandas-2.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7d5f2f54f78164b3d7a40f33bf79a74cdee72c31affec86bfcabe7e0789821"},
+ {file = "pandas-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0aa6e92e639da0d6e2017d9ccff563222f4eb31e4b2c3cf32a2a392fc3103c0d"},
+ {file = "pandas-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d797591b6846b9db79e65dc2d0d48e61f7db8d10b2a9480b4e3faaddc421a171"},
+ {file = "pandas-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2d3e7b00f703aea3945995ee63375c61b2e6aa5aa7871c5d622870e5e137623"},
+ {file = "pandas-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:dc9bf7ade01143cddc0074aa6995edd05323974e6e40d9dbde081021ded8510e"},
+ {file = "pandas-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:482d5076e1791777e1571f2e2d789e940dedd927325cc3cb6d0800c6304082f6"},
+ {file = "pandas-2.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8a706cfe7955c4ca59af8c7a0517370eafbd98593155b48f10f9811da440248b"},
+ {file = "pandas-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0513a132a15977b4a5b89aabd304647919bc2169eac4c8536afb29c07c23540"},
+ {file = "pandas-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9f17f2b6fc076b2a0078862547595d66244db0f41bf79fc5f64a5c4d635bead"},
+ {file = "pandas-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:45d63d2a9b1b37fa6c84a68ba2422dc9ed018bdaa668c7f47566a01188ceeec1"},
+ {file = "pandas-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:f69b0c9bb174a2342818d3e2778584e18c740d56857fc5cdb944ec8bbe4082cf"},
+ {file = "pandas-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3f06bda01a143020bad20f7a85dd5f4a1600112145f126bc9e3e42077c24ef34"},
+ {file = "pandas-2.1.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab5796839eb1fd62a39eec2916d3e979ec3130509930fea17fe6f81e18108f6a"},
+ {file = "pandas-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbaf9e8d3a63a9276d707b4d25930a262341bca9874fcb22eff5e3da5394732"},
+ {file = "pandas-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ebfd771110b50055712b3b711b51bee5d50135429364d0498e1213a7adc2be8"},
+ {file = "pandas-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ea107e0be2aba1da619cc6ba3f999b2bfc9669a83554b1904ce3dd9507f0860"},
+ {file = "pandas-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:d65148b14788b3758daf57bf42725caa536575da2b64df9964c563b015230984"},
+ {file = "pandas-2.1.4.tar.gz", hash = "sha256:fcb68203c833cc735321512e13861358079a96c174a61f5116a1de89c58c0ef7"},
]
[package.dependencies]
@@ -777,13 +793,13 @@ xml = ["lxml (>=4.8.0)"]
[[package]]
name = "pandas-stubs"
-version = "2.1.1.230928"
+version = "2.1.4.231218"
description = "Type annotations for pandas"
optional = false
python-versions = ">=3.9"
files = [
- {file = "pandas_stubs-2.1.1.230928-py3-none-any.whl", hash = "sha256:992d97159e054ca3175ebe8321ac5616cf6502dd8218b03bb0eaf3c4f6939037"},
- {file = "pandas_stubs-2.1.1.230928.tar.gz", hash = "sha256:ce1691c71c5d67b8f332da87763f7f54650f46895d99964d588c3a5d79e2cacc"},
+ {file = "pandas_stubs-2.1.4.231218-py3-none-any.whl", hash = "sha256:9c9a8db37b83ff4ff9f672644099abc624ed407aa46d9dcb5f305de9925b3d29"},
+ {file = "pandas_stubs-2.1.4.231218.tar.gz", hash = "sha256:f0dd07b3bb2935ddcff9c7b7ba9076cf3529b968a0dee96fab53f5f8747974f7"},
]
[package.dependencies]
@@ -807,13 +823,13 @@ testing = ["docopt", "pytest (<6.0.0)"]
[[package]]
name = "pathspec"
-version = "0.11.2"
+version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
- {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
+ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
[[package]]
@@ -862,13 +878,13 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "prompt-toolkit"
-version = "3.0.41"
+version = "3.0.43"
description = "Library for building powerful interactive command lines in Python"
optional = false
python-versions = ">=3.7.0"
files = [
- {file = "prompt_toolkit-3.0.41-py3-none-any.whl", hash = "sha256:f36fe301fafb7470e86aaf90f036eef600a3210be4decf461a5b1ca8403d3cb2"},
- {file = "prompt_toolkit-3.0.41.tar.gz", hash = "sha256:941367d97fc815548822aa26c2a269fdc4eb21e9ec05fc5d447cf09bad5d75f0"},
+ {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"},
+ {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"},
]
[package.dependencies]
@@ -1203,46 +1219,30 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "ruff"
-version = "0.1.7"
+version = "0.1.8"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.1.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7f80496854fdc65b6659c271d2c26e90d4d401e6a4a31908e7e334fab4645aac"},
- {file = "ruff-0.1.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1ea109bdb23c2a4413f397ebd8ac32cb498bee234d4191ae1a310af760e5d287"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0c2de9dd9daf5e07624c24add25c3a490dbf74b0e9bca4145c632457b3b42a"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:69a4bed13bc1d5dabf3902522b5a2aadfebe28226c6269694283c3b0cecb45fd"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de02ca331f2143195a712983a57137c5ec0f10acc4aa81f7c1f86519e52b92a1"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45b38c3f8788a65e6a2cab02e0f7adfa88872696839d9882c13b7e2f35d64c5f"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c64cb67b2025b1ac6d58e5ffca8f7b3f7fd921f35e78198411237e4f0db8e73"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dcc6bb2f4df59cb5b4b40ff14be7d57012179d69c6565c1da0d1f013d29951b"},
- {file = "ruff-0.1.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2bb4bb6bbe921f6b4f5b6fdd8d8468c940731cb9406f274ae8c5ed7a78c478"},
- {file = "ruff-0.1.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:276a89bcb149b3d8c1b11d91aa81898fe698900ed553a08129b38d9d6570e717"},
- {file = "ruff-0.1.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:90c958fe950735041f1c80d21b42184f1072cc3975d05e736e8d66fc377119ea"},
- {file = "ruff-0.1.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6b05e3b123f93bb4146a761b7a7d57af8cb7384ccb2502d29d736eaade0db519"},
- {file = "ruff-0.1.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:290ecab680dce94affebefe0bbca2322a6277e83d4f29234627e0f8f6b4fa9ce"},
- {file = "ruff-0.1.7-py3-none-win32.whl", hash = "sha256:416dfd0bd45d1a2baa3b1b07b1b9758e7d993c256d3e51dc6e03a5e7901c7d80"},
- {file = "ruff-0.1.7-py3-none-win_amd64.whl", hash = "sha256:4af95fd1d3b001fc41325064336db36e3d27d2004cdb6d21fd617d45a172dd96"},
- {file = "ruff-0.1.7-py3-none-win_arm64.whl", hash = "sha256:0683b7bfbb95e6df3c7c04fe9d78f631f8e8ba4868dfc932d43d690698057e2e"},
- {file = "ruff-0.1.7.tar.gz", hash = "sha256:dffd699d07abf54833e5f6cc50b85a6ff043715da8788c4a79bcd4ab4734d306"},
-]
-
-[[package]]
-name = "setuptools"
-version = "69.0.2"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"},
- {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"},
+ {file = "ruff-0.1.8-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7de792582f6e490ae6aef36a58d85df9f7a0cfd1b0d4fe6b4fb51803a3ac96fa"},
+ {file = "ruff-0.1.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c8e3255afd186c142eef4ec400d7826134f028a85da2146102a1172ecc7c3696"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff78a7583020da124dd0deb835ece1d87bb91762d40c514ee9b67a087940528b"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd8ee69b02e7bdefe1e5da2d5b6eaaddcf4f90859f00281b2333c0e3a0cc9cd6"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a05b0ddd7ea25495e4115a43125e8a7ebed0aa043c3d432de7e7d6e8e8cd6448"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e6f08ca730f4dc1b76b473bdf30b1b37d42da379202a059eae54ec7fc1fbcfed"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f35960b02df6b827c1b903091bb14f4b003f6cf102705efc4ce78132a0aa5af3"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d076717c67b34c162da7c1a5bda16ffc205e0e0072c03745275e7eab888719f"},
+ {file = "ruff-0.1.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a21ab023124eafb7cef6d038f835cb1155cd5ea798edd8d9eb2f8b84be07d9"},
+ {file = "ruff-0.1.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ce697c463458555027dfb194cb96d26608abab920fa85213deb5edf26e026664"},
+ {file = "ruff-0.1.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db6cedd9ffed55548ab313ad718bc34582d394e27a7875b4b952c2d29c001b26"},
+ {file = "ruff-0.1.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:05ffe9dbd278965271252704eddb97b4384bf58b971054d517decfbf8c523f05"},
+ {file = "ruff-0.1.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5daaeaf00ae3c1efec9742ff294b06c3a2a9db8d3db51ee4851c12ad385cda30"},
+ {file = "ruff-0.1.8-py3-none-win32.whl", hash = "sha256:e49fbdfe257fa41e5c9e13c79b9e79a23a79bd0e40b9314bc53840f520c2c0b3"},
+ {file = "ruff-0.1.8-py3-none-win_amd64.whl", hash = "sha256:f41f692f1691ad87f51708b823af4bb2c5c87c9248ddd3191c8f088e66ce590a"},
+ {file = "ruff-0.1.8-py3-none-win_arm64.whl", hash = "sha256:aa8ee4f8440023b0a6c3707f76cadce8657553655dcbb5fc9b2f9bb9bee389f6"},
+ {file = "ruff-0.1.8.tar.gz", hash = "sha256:f7ee467677467526cfe135eab86a40a0e8db43117936ac4f9b469ce9cdb3fb62"},
]
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
[[package]]
name = "six"
version = "1.16.0"
@@ -1312,22 +1312,22 @@ test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"]
[[package]]
name = "sphinx-book-theme"
-version = "1.0.1"
+version = "1.1.0"
description = "A clean book theme for scientific explanations and documentation with Sphinx"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
files = [
- {file = "sphinx_book_theme-1.0.1-py3-none-any.whl", hash = "sha256:d15f8248b3718a9a6be0ba617a32d1591f9fa39c614469bface777ba06a73b75"},
- {file = "sphinx_book_theme-1.0.1.tar.gz", hash = "sha256:927b399a6906be067e49c11ef1a87472f1b1964075c9eea30fb82c64b20aedee"},
+ {file = "sphinx_book_theme-1.1.0-py3-none-any.whl", hash = "sha256:088bc69d65fab8446adb8691ed61687f71bf7504c9740af68bc78cf936a26112"},
+ {file = "sphinx_book_theme-1.1.0.tar.gz", hash = "sha256:ad4f92998e53e24751ecd0978d3eb79fdaa59692f005b1b286ecdd6146ebc9c1"},
]
[package.dependencies]
-pydata-sphinx-theme = ">=0.13.3"
-sphinx = ">=4,<7"
+pydata-sphinx-theme = ">=0.14"
+sphinx = ">=5"
[package.extras]
code-style = ["pre-commit"]
-doc = ["ablog", "docutils (==0.17.1)", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs (<=3.4.0)", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"]
+doc = ["ablog", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"]
test = ["beautifulsoup4", "coverage", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"]
[[package]]
@@ -1520,24 +1520,24 @@ urllib3 = ">=2"
[[package]]
name = "types-setuptools"
-version = "68.2.0.2"
+version = "69.0.0.0"
description = "Typing stubs for setuptools"
optional = false
python-versions = ">=3.7"
files = [
- {file = "types-setuptools-68.2.0.2.tar.gz", hash = "sha256:09efc380ad5c7f78e30bca1546f706469568cf26084cfab73ecf83dea1d28446"},
- {file = "types_setuptools-68.2.0.2-py3-none-any.whl", hash = "sha256:d5b5ff568ea2474eb573dcb783def7dadfd9b1ff638bb653b3c7051ce5aeb6d1"},
+ {file = "types-setuptools-69.0.0.0.tar.gz", hash = "sha256:b0a06219f628c6527b2f8ce770a4f47550e00d3e8c3ad83e2dc31bc6e6eda95d"},
+ {file = "types_setuptools-69.0.0.0-py3-none-any.whl", hash = "sha256:8c86195bae2ad81e6dea900a570fe9d64a59dbce2b11cc63c046b03246ea77bf"},
]
[[package]]
name = "typing-extensions"
-version = "4.8.0"
+version = "4.9.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
- {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
+ {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
+ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
]
[[package]]
@@ -1581,4 +1581,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
-content-hash = "849ce044de49aa58ccbeba1a7724435ba32658dbe5bf41274a3f518162072098"
+content-hash = "a63b14dd514e25b7d0cd96e9a812a870ec4e26bc15d4831761cb46ab3b5a61b5"
diff --git a/pyproject.toml b/pyproject.toml
index 938789ab..7c389691 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mex-common"
-version = "0.17.0"
+version = "0.18.0"
description = "RKI MEx common library."
authors = ["RKI MEx Team "]
readme = "README.md"
@@ -15,25 +15,26 @@ click = "^8.1.7"
langdetect = "^1.0.9"
ldap3 = "^2.9.1"
numpy = "^1.26.2"
-pandas = "^2.1.3"
-pydantic = "^2.5.1"
+pandas = "^2.1.4"
+pydantic = "^2.5.2"
pydantic-settings = "^2.1.0"
requests = "^2.31.0"
[tool.poetry.group.dev.dependencies]
black = "^23.11.0"
ipdb = "^0.13.13"
-mypy = "^1.7.0"
-pandas-stubs = "^2.1.1"
+mex-model = { git = "https://github.com/robert-koch-institut/mex-model.git", rev = "2.2.0"}
+mypy = "^1.7.1"
+pandas-stubs = "^2.1.4"
pytest = "^7.4.3"
pytest-cov = "^4.1.0"
-ruff = "^0.1.6"
+ruff = "^0.1.8"
sphinx = "^6.2.1"
-sphinx-book-theme = "^1.0.1"
-types-ldap3 = "^2.9.13.15"
-types-pytz = "^2023.3.1.1"
-types-requests = "^2.31.0.10"
-types-setuptools = "^68.2.0.1"
+sphinx-book-theme = "^1.1.0"
+types-ldap3 = "^2.9.13"
+types-pytz = "^2023.3.1"
+types-requests = "^2.31.0"
+types-setuptools = "^69.0.0"
[tool.ipdb]
context = 5
@@ -59,7 +60,7 @@ addopts = [
"--cov",
"--no-cov-on-fail",
"--cov-report=term-missing:skip-covered",
- "--cov-fail-under=90",
+ "--cov-fail-under=95",
"--cov-branch",
"--pdbcls=IPython.terminal.debugger:TerminalPdb"
]
diff --git a/requirements.txt b/requirements.txt
index 0413bde6..2e89804d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
-poetry==1.7.0
+poetry==1.7.1
pre-commit==3.5.0
-wheel==0.41.3
+wheel==0.42.0
diff --git a/tests/models/test_schema.py b/tests/models/test_schema.py
new file mode 100644
index 00000000..0410cf5a
--- /dev/null
+++ b/tests/models/test_schema.py
@@ -0,0 +1,170 @@
+import json
+import re
+from copy import deepcopy
+from importlib.resources import files
+from itertools import zip_longest
+from typing import Any, Callable
+
+import pytest
+
+from mex.common.models import EXTRACTED_MODEL_CLASSES_BY_NAME
+from mex.common.transform import dromedary_to_kebab
+from mex.common.types.identifier import MEX_ID_PATTERN
+
+SPECIFIED_SCHEMA_PATH = files("mex.model").joinpath("entities")
+
+GENERATED_SCHEMAS = dict(
+ sorted(
+ {
+ name.removeprefix("Extracted"): model.model_json_schema(
+ ref_template="/schema/fields/{model}"
+ )
+ for name, model in EXTRACTED_MODEL_CLASSES_BY_NAME.items()
+ }.items()
+ )
+)
+SPECIFIED_SCHEMAS = dict(
+ sorted(
+ {
+ schema["title"].replace(" ", ""): schema
+ for file_name in SPECIFIED_SCHEMA_PATH.glob("*.json")
+ if (schema := json.load(open(file_name, encoding="utf-8")))
+ and not schema["title"].startswith("Concept")
+ }.items()
+ )
+)
+ENTITY_TYPES_AND_FIELD_NAMES_BY_FQN = {
+ f"{entity_type}.{field_name}": (entity_type, field_name)
+ for entity_type, schema in SPECIFIED_SCHEMAS.items()
+ for field_name in schema["properties"]
+}
+
+
+def test_entity_types_match_spec() -> None:
+ assert list(GENERATED_SCHEMAS) == list(SPECIFIED_SCHEMAS)
+
+
+@pytest.mark.parametrize(
+ ("generated", "specified"),
+ zip_longest(GENERATED_SCHEMAS.values(), SPECIFIED_SCHEMAS.values()),
+ ids=GENERATED_SCHEMAS,
+)
+def test_field_names_match_spec(
+ generated: dict[str, Any], specified: dict[str, Any]
+) -> None:
+ assert set(generated["properties"]) == set(specified["properties"])
+
+
+@pytest.mark.parametrize(
+ ("generated", "specified"),
+ zip_longest(GENERATED_SCHEMAS.values(), SPECIFIED_SCHEMAS.values()),
+ ids=GENERATED_SCHEMAS,
+)
+def test_required_fields_match_spec(
+ generated: dict[str, Any], specified: dict[str, Any]
+) -> None:
+ assert set(generated["required"]) == set(specified["required"])
+
+
+def deduplicate_dicts(dct: dict[str, Any], key: str) -> None:
+ # take a set of dicts and deduplicate them by dumping/loading to json
+ dct[key] = [json.loads(s) for s in dict.fromkeys(json.dumps(d) for d in dct[key])]
+
+
+def dissolve_single_item_lists(dct: dict[str, Any], key: str) -> None:
+ # if a list in a dict value has just one item, dissolve it into the parent dict
+ if len(dct[key]) == 1 and isinstance(dct[key][0], dict):
+ dct.update(dct.pop(key)[0])
+
+
+def sub_only_text(repl: Callable[[str], str], string: str) -> str:
+ # substitute only the textual parts of a string, e.g. leave slashes alone
+ return re.sub(r"([a-zA-Z_-]+)", lambda m: repl(m.group(0)), string)
+
+
+def prepare_field(field: str, obj: list[Any] | dict[str, Any]) -> None:
+ # prepare each item in a list (in-place)
+ if isinstance(obj, list):
+ for item in obj:
+ prepare_field(field, item)
+ obj[:] = [item for item in obj if item]
+ return
+
+ # discard annotations that we can safely ignore
+ # (these have no use-case and no implementation plans yet)
+ obj.pop("sameAs", None) # only in spec
+ obj.pop("subPropertyOf", None) # only in spec
+ obj.pop("description", None) # only in model (mostly implementation hints)
+
+ # pop annotations that we don't compare directly but use for other comparisons
+ title = obj.pop("title", "") # only in model (autogenerated by pydantic)
+ use_scheme = obj.pop("useScheme", "") # only in spec (needed to select vocabulary)
+ vocabulary = use_scheme.removeprefix("https://mex.rki.de/item/") # vocabulary name
+
+ # ignore differences between dates and datetimes
+ # (we only have `Timestamp` as a date-time implementation, but no type for `date`,
+ # but we might/should add that in the future)
+ if obj.get("format") in ("date", "date-time"):
+ obj.pop("examples", None)
+ obj.pop("pattern", None)
+ obj["format"] = "date-time"
+
+ # align reference paths
+ # (the paths to referenced vocabularies and types differ between the models
+ # and the specification, so we need to make sure they match before comparing)
+ if obj.get("pattern") == MEX_ID_PATTERN:
+ obj.pop("pattern")
+ obj.pop("type")
+ if field in ("identifier", "stableTargetId"):
+ obj["$ref"] = "/schema/fields/identifier"
+ else:
+ obj["$ref"] = f"/schema/entities/{title.removesuffix('ID')}#/identifier"
+
+ # align concept/enum annotations
+ # (spec uses `useScheme` to specify vocabularies and models use enums)
+ if obj.get("$ref") == "/schema/entities/concept#/identifier":
+ obj["$ref"] = f"/schema/fields/{vocabulary}"
+
+ # make sure all refs have paths in kebab-case
+ # (the models use the class names, whereas the spec uses kebab-case URLs)
+ if "$ref" in obj:
+ obj["$ref"] = sub_only_text(dromedary_to_kebab, obj["$ref"])
+
+ # recurse into the field definitions for array items
+ if obj.get("type") == "array":
+ prepare_field(field, obj["items"])
+
+ for quantifier in {"anyOf", "allOf"} & set(obj):
+ # prepare choices
+ prepare_field(field, obj[quantifier])
+ # deduplicate items, used for date/times
+ deduplicate_dicts(obj, quantifier)
+ # collapse non-choices
+ dissolve_single_item_lists(obj, quantifier)
+
+
+@pytest.mark.parametrize(
+ ("entity_type", "field_name"),
+ ENTITY_TYPES_AND_FIELD_NAMES_BY_FQN.values(),
+ ids=ENTITY_TYPES_AND_FIELD_NAMES_BY_FQN.keys(),
+)
+def test_field_defs_match_spec(entity_type: str, field_name: str) -> None:
+ specified_properties = SPECIFIED_SCHEMAS[entity_type]["properties"]
+ generated_properties = GENERATED_SCHEMAS[entity_type]["properties"]
+ specified = deepcopy(specified_properties[field_name])
+ generated = deepcopy(generated_properties[field_name])
+
+ prepare_field(field_name, specified)
+ prepare_field(field_name, generated)
+
+ assert (
+ generated == specified
+ ), f"""
+{entity_type}.{field_name}
+
+specified:
+{json.dumps(specified_properties[field_name], indent=4, sort_keys=True)}
+
+generated:
+{json.dumps(generated_properties[field_name], indent=4, sort_keys=True)}
+"""
diff --git a/tests/public_api/test_connector.py b/tests/public_api/test_connector.py
index bab905ae..45d5e8eb 100644
--- a/tests/public_api/test_connector.py
+++ b/tests/public_api/test_connector.py
@@ -133,6 +133,7 @@ def test_post_models_mocked(
assert payload == expected_payload
+@pytest.mark.skip(reason="public api is being deprecated")
@pytest.mark.integration
def test_search_model_that_does_not_exist() -> None:
random_id = Identifier.generate()
@@ -318,6 +319,7 @@ def test_get_all_items_mocked(
assert items == mex_metadata_items_response
+@pytest.mark.skip(reason="public api is being deprecated")
@pytest.mark.integration
def test_get_all_items() -> None:
connector = PublicApiConnector.get()
diff --git a/tests/public_api/test_extract.py b/tests/public_api/test_extract.py
index 00f3d9bc..39b134b5 100644
--- a/tests/public_api/test_extract.py
+++ b/tests/public_api/test_extract.py
@@ -10,6 +10,7 @@
from mex.common.public_api.models import PublicApiMetadataItemsResponse
+@pytest.mark.skip(reason="public api is being deprecated")
@pytest.mark.integration
def test_extract_mex_person_items() -> None:
mex_persons = list(extract_mex_person_items())
@@ -38,8 +39,7 @@ def __init__(self: PublicApiConnector) -> None:
assert mex_persons == mex_metadata_items_response.items[2:4] * 2
-@pytest.mark.integration
-def test_extract_mex_person_items_limit_reached(
+def test_extract_mex_person_items_mocked_limit_reached(
mex_metadata_items_response: PublicApiMetadataItemsResponse,
monkeypatch: MonkeyPatch,
) -> None:
diff --git a/tests/sinks/test_public_api.py b/tests/sinks/test_public_api.py
index 698f22c6..ceac5787 100644
--- a/tests/sinks/test_public_api.py
+++ b/tests/sinks/test_public_api.py
@@ -48,6 +48,7 @@ def __init__(self: PublicApiConnector) -> None:
delete_model.assert_called_once_with(extracted_person)
+@pytest.mark.skip(reason="public api is being deprecated")
@pytest.mark.integration
def test_public_api_post_and_purge_roundtrip(
extracted_primary_sources: dict[str, ExtractedPrimarySource]
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 6db462a4..7cc443d0 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -9,7 +9,13 @@
from pydantic import BaseModel as PydanticModel
from pydantic import Field, SecretStr
-from mex.common.transform import MExEncoder, dromedary_to_snake, snake_to_dromedary
+from mex.common.transform import (
+ MExEncoder,
+ dromedary_to_kebab,
+ dromedary_to_snake,
+ kebab_to_camel,
+ snake_to_dromedary,
+)
from mex.common.types import Identifier, Timestamp
@@ -93,3 +99,49 @@ def test_snake_to_dromedary(string: str, expected: str) -> None:
def test_dromedary_to_snake(string: str, expected: str) -> None:
result = dromedary_to_snake(string)
assert result == expected
+
+
+@pytest.mark.parametrize(
+ ("string", "expected"),
+ [
+ ("", ""),
+ ("word", "word"),
+ ("already-kebab", "already-kebab"),
+ ("Such-AWeird-MIXEDCase", "such-a-weird-mixed-case"),
+ ("ABWordCDEWordFG", "ab-word-cde-word-fg"),
+ ("multipleWordsInAString", "multiple-words-in-a-string"),
+ ],
+ ids=[
+ "empty",
+ "single word",
+ "already kebab",
+ "mixed case",
+ "caps words",
+ "multiple words",
+ ],
+)
+def test_dromedary_to_kebab(string: str, expected: str) -> None:
+ result = dromedary_to_kebab(string)
+ assert result == expected
+
+
+@pytest.mark.parametrize(
+ ("string", "expected"),
+ [
+ ("", ""),
+ ("word", "Word"),
+ ("AlreadyCamel", "AlreadyCamel"),
+ ("Mixed-CASE", "MixedCase"),
+ ("multiple-words-in-a-string", "MultipleWordsInAString"),
+ ],
+ ids=[
+ "empty",
+ "single word",
+ "already camel",
+ "mixed case",
+ "multiple words",
+ ],
+)
+def test_kebab_to_camel(string: str, expected: str) -> None:
+ result = kebab_to_camel(string)
+ assert result == expected
diff --git a/tests/types/test_timestamp.py b/tests/types/test_timestamp.py
index 0dbfa21f..56c29ea9 100644
--- a/tests/types/test_timestamp.py
+++ b/tests/types/test_timestamp.py
@@ -1,7 +1,8 @@
-from datetime import datetime
+from datetime import date, datetime
from typing import Any
import pytest
+from pytz import timezone
from mex.common.types import CET, UTC, Timestamp
@@ -60,15 +61,29 @@ def test_timestamp_validation_errors(value: Any, message: str) -> None:
"1999-01-20T21:00:00Z",
),
(
- (datetime(2020, 3, 22),),
+ ("2016-06-10T21:42:24.76073899Z",),
{},
- "2020-03-21T23:00:00Z",
+ "2016-06-10T21:42:24Z",
+ ),
+ (
+ (date(2020, 3, 22),),
+ {},
+ "2020-03-22",
),
(
(datetime(2020, 3, 22, 14, 30, 58),),
{},
"2020-03-22T13:30:58Z",
),
+ (
+ (
+ datetime(
+ 2020, 3, 22, 14, 30, 58, tzinfo=timezone("America/Los_Angeles")
+ ),
+ ),
+ {},
+ "2020-03-22T22:23:58Z",
+ ),
(
(Timestamp(2004, 11),),
{},
@@ -86,8 +101,10 @@ def test_timestamp_validation_errors(value: Any, message: str) -> None:
"date string",
"time string",
"padded time",
+ "nano seconds",
"date",
"datetime",
+ "pacific time",
"timestamp",
],
)
@@ -96,3 +113,29 @@ def test_timestamp_parsing(
) -> None:
timestamp = Timestamp(*args, **kwargs)
assert str(timestamp) == expected
+
+
+def test_timestamp_eq() -> None:
+ assert Timestamp(2004) == Timestamp("2004")
+ assert Timestamp(2004, 11) == Timestamp(2004, 11)
+ assert Timestamp(2004, 11, 2) == "2004-11-02"
+ assert Timestamp(2020, 3, 22, 14, 30, 58, 0) == datetime(2020, 3, 22, 14, 30, 58, 0)
+ assert Timestamp(2005) != object()
+
+
+def test_timestamp_gt() -> None:
+ assert Timestamp(2004) > Timestamp("2003")
+ assert Timestamp(2004, 11) < "2013-10-02"
+ assert Timestamp(2004, 11) <= Timestamp(2004, 12)
+ assert Timestamp(2020, 3, 22, 14, 30, 58) >= datetime(2020, 3, 22, 14, 29)
+
+ with pytest.raises(NotImplementedError):
+ assert Timestamp(2005) > object()
+
+
+def test_timestamp_str() -> None:
+ assert str(Timestamp(2004, 11, 26)) == "2004-11-26"
+
+
+def test_timestamp_repr() -> None:
+ assert repr(Timestamp(2018, 3, 2, 13, 0, 1)) == 'Timestamp("2018-03-02T12:00:01Z")'
diff --git a/tests/wikidata/test_transform.py b/tests/wikidata/test_transform.py
index e5d176dd..7b34aa77 100644
--- a/tests/wikidata/test_transform.py
+++ b/tests/wikidata/test_transform.py
@@ -48,7 +48,7 @@ def test_transform_wikidata_organization_to_organization(
"rorId": ["https://ror.org/05vs9tj88", "https://ror.org/044kkbh92"],
"shortName": [],
"viafId": ["https://viaf.org/viaf/129013645"],
- "wikidataId": "https://www.wikidata.org/entity/Q26678",
+ "wikidataId": ["https://www.wikidata.org/entity/Q26678"],
}
with open(TESTDATA_DIR / "items_details.json", "r", encoding="utf-8") as f: