Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/add consent models and fix temporal type #288

Merged
merged 4 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion mex/common/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- AccessPlatform
- Activity
- BibliographicResource
- Consent
- ContactPoint
- Distribution
- Organization
Expand Down Expand Up @@ -106,6 +107,16 @@
PreventiveBibliographicResource,
SubtractiveBibliographicResource,
)
from mex.common.models.consent import (
AdditiveConsent,
BaseConsent,
ConsentRuleSetRequest,
ConsentRuleSetResponse,
ExtractedConsent,
MergedConsent,
PreventiveConsent,
SubtractiveConsent,
)
from mex.common.models.contact_point import (
AdditiveContactPoint,
BaseContactPoint,
Expand Down Expand Up @@ -211,6 +222,7 @@
"AdditiveAccessPlatform",
"AdditiveActivity",
"AdditiveBibliographicResource",
"AdditiveConsent",
"AdditiveContactPoint",
"AdditiveDistribution",
"AdditiveOrganization",
Expand All @@ -230,11 +242,12 @@
"AnyRuleSetRequest",
"AnyRuleSetResponse",
"AnySubtractiveModel",
"BaseBibliographicResource",
"BASE_MODEL_CLASSES_BY_NAME",
"BASE_MODEL_CLASSES",
"BaseAccessPlatform",
"BaseActivity",
"BaseBibliographicResource",
"BaseConsent",
"BaseContactPoint",
"BaseDistribution",
"BaseModel",
Expand All @@ -245,6 +258,8 @@
"BaseResource",
"BaseVariable",
"BaseVariableGroup",
"ConsentRuleSetRequest",
"ConsentRuleSetResponse",
"ContactPointRuleSetRequest",
"ContactPointRuleSetResponse",
"DistributionRuleSetRequest",
Expand All @@ -254,6 +269,7 @@
"ExtractedAccessPlatform",
"ExtractedActivity",
"ExtractedBibliographicResource",
"ExtractedConsent",
"ExtractedContactPoint",
"ExtractedData",
"ExtractedDistribution",
Expand All @@ -275,6 +291,7 @@
"MergedAccessPlatform",
"MergedActivity",
"MergedBibliographicResource",
"MergedConsent",
"MergedContactPoint",
"MergedDistribution",
"MergedItem",
Expand All @@ -300,6 +317,7 @@
"PreventiveAccessPlatform",
"PreventiveActivity",
"PreventiveBibliographicResource",
"PreventiveConsent",
"PreventiveContactPoint",
"PreventiveDistribution",
"PreventiveOrganization",
Expand All @@ -325,6 +343,7 @@
"SubtractiveAccessPlatform",
"SubtractiveActivity",
"SubtractiveBibliographicResource",
"SubtractiveConsent",
"SubtractiveContactPoint",
"SubtractiveDistribution",
"SubtractiveOrganization",
Expand All @@ -349,6 +368,7 @@
BaseAccessPlatform
| BaseActivity
| BaseBibliographicResource
| BaseConsent
| BaseContactPoint
| BaseDistribution
| BaseOrganization
Expand All @@ -368,6 +388,7 @@
ExtractedAccessPlatform
| ExtractedActivity
| ExtractedBibliographicResource
| ExtractedConsent
| ExtractedContactPoint
| ExtractedDistribution
| ExtractedOrganization
Expand All @@ -389,6 +410,7 @@
MergedAccessPlatform
| MergedActivity
| MergedBibliographicResource
| MergedConsent
| MergedContactPoint
| MergedDistribution
| MergedOrganization
Expand All @@ -408,6 +430,7 @@
AdditiveAccessPlatform
| AdditiveActivity
| AdditiveBibliographicResource
| AdditiveConsent
| AdditiveContactPoint
| AdditiveDistribution
| AdditiveOrganization
Expand All @@ -429,6 +452,7 @@
SubtractiveAccessPlatform
| SubtractiveActivity
| SubtractiveBibliographicResource
| SubtractiveConsent
| SubtractiveContactPoint
| SubtractiveDistribution
| SubtractiveOrganization
Expand All @@ -450,6 +474,7 @@
PreventiveAccessPlatform
| PreventiveActivity
| PreventiveBibliographicResource
| PreventiveConsent
| PreventiveContactPoint
| PreventiveDistribution
| PreventiveOrganization
Expand Down Expand Up @@ -477,6 +502,7 @@
AccessPlatformRuleSetRequest
| ActivityRuleSetRequest
| BibliographicResourceRuleSetRequest
| ConsentRuleSetRequest
| ContactPointRuleSetRequest
| DistributionRuleSetRequest
| OrganizationRuleSetRequest
Expand All @@ -498,6 +524,7 @@
AccessPlatformRuleSetResponse
| ActivityRuleSetResponse
| BibliographicResourceRuleSetResponse
| ConsentRuleSetResponse
| ContactPointRuleSetResponse
| DistributionRuleSetResponse
| OrganizationRuleSetResponse
Expand Down
10 changes: 10 additions & 0 deletions mex/common/models/base/schema.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from typing import Any

from pydantic.json_schema import (
GenerateJsonSchema as PydanticJsonSchemaGenerator,
)
from pydantic.json_schema import JsonSchemaValue
from pydantic_core.core_schema import ComplexSchema


class JsonSchemaGenerator(PydanticJsonSchemaGenerator):
Expand All @@ -18,3 +21,10 @@ def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue:
to stay compatible with mex-editor and mex-model.
"""
return json_schema

def complex_schema(self, schema: ComplexSchema) -> dict[str, Any]:
"""Returns a schema that matches a complex value."""
# TODO: clean this up in MX-1704 (stop-gap)
erichesse marked this conversation as resolved.
Show resolved Hide resolved
raise NotImplementedError(
"Method for generating JsonSchema for 'complex' schemas is not implemented."
)
155 changes: 155 additions & 0 deletions mex/common/models/consent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
"""A consent - for example, an interdepartmental project."""

from typing import Annotated, ClassVar, Literal

from pydantic import Field, computed_field

from mex.common.models.base.extracted_data import ExtractedData
from mex.common.models.base.merged_item import MergedItem
from mex.common.models.base.model import BaseModel
from mex.common.models.base.rules import (
AdditiveRule,
PreventiveRule,
RuleSet,
SubtractiveRule,
)
from mex.common.types import (
ConsentStatus,
ConsentType,
ExtractedConsentIdentifier,
MergedConsentIdentifier,
MergedPersonIdentifier,
MergedPrimarySourceIdentifier,
YearMonthDayTime,
)


class _Stem(BaseModel):
stemType: ClassVar[Annotated[Literal["Consent"], Field(frozen=True)]] = "Consent"


class _OptionalValues(_Stem):
hasConsentType: (
Annotated[
ConsentType, Field(examples=["https://mex.rki.de/item/consent-type-1"])
]
| None
) = None


class _RequiredValues(_Stem):
hasConsentStatus: Annotated[
ConsentStatus, Field(examples=["https://mex.rki.de/item/consent-status-1"])
]
hasDataSubject: MergedPersonIdentifier
isIndicatedAtTime: YearMonthDayTime


class _SparseValues(_Stem):
hasConsentStatus: (
Annotated[
ConsentStatus, Field(examples=["https://mex.rki.de/item/consent-status-1"])
]
| None
) = None
hasDataSubject: MergedPersonIdentifier | None = None
isIndicatedAtTime: YearMonthDayTime | None = None


class _VariadicValues(_Stem):
hasConsentType: list[
Annotated[
ConsentType, Field(examples=["https://mex.rki.de/item/consent-type-1"])
]
] = []
hasConsentStatus: list[
Annotated[
ConsentStatus, Field(examples=["https://mex.rki.de/item/consent-status-1"])
]
] = []
hasDataSubject: list[MergedPersonIdentifier] = []
isIndicatedAtTime: list[YearMonthDayTime] = []


class BaseConsent(_OptionalValues, _RequiredValues):
"""All fields for a valid consent except for provenance."""


class ExtractedConsent(BaseConsent, ExtractedData):
"""An automatically extracted metadata set describing a consent."""

entityType: Annotated[
Literal["ExtractedConsent"], Field(alias="$type", frozen=True)
] = "ExtractedConsent"

@computed_field # type: ignore[prop-decorator]
@property
def identifier(self) -> ExtractedConsentIdentifier:
"""Return the computed identifier for this extracted data item."""
return self._get_identifier(ExtractedConsentIdentifier)

@computed_field # type: ignore[prop-decorator]
@property
def stableTargetId(self) -> MergedConsentIdentifier: # noqa: N802
"""Return the computed stableTargetId for this extracted data item."""
return self._get_stable_target_id(MergedConsentIdentifier)


class MergedConsent(BaseConsent, MergedItem):
"""The result of merging all extracted data and rules for a consent."""

entityType: Annotated[
Literal["MergedConsent"], Field(alias="$type", frozen=True)
] = "MergedConsent"
identifier: Annotated[MergedConsentIdentifier, Field(frozen=True)]


class AdditiveConsent(_OptionalValues, _SparseValues, AdditiveRule):
"""Rule to add values to merged consent items."""

entityType: Annotated[
Literal["AdditiveConsent"], Field(alias="$type", frozen=True)
] = "AdditiveConsent"


class SubtractiveConsent(_VariadicValues, SubtractiveRule):
"""Rule to subtract values from merged consent items."""

entityType: Annotated[
Literal["SubtractiveConsent"], Field(alias="$type", frozen=True)
] = "SubtractiveConsent"


class PreventiveConsent(_Stem, PreventiveRule):
"""Rule to prevent primary sources for fields of merged consent items."""

entityType: Annotated[
Literal["PreventiveConsent"], Field(alias="$type", frozen=True)
] = "PreventiveConsent"
hasConsentType: list[MergedPrimarySourceIdentifier] = []
hasConsentStatus: list[MergedPrimarySourceIdentifier] = []
hasDataSubject: list[MergedPrimarySourceIdentifier] = []
isIndicatedAtTime: list[MergedPrimarySourceIdentifier] = []


class _BaseRuleSet(_Stem, RuleSet):
additive: AdditiveConsent
subtractive: SubtractiveConsent
preventive: PreventiveConsent


class ConsentRuleSetRequest(_BaseRuleSet):
"""Set of rules to create or update a consent item."""

entityType: Annotated[
Literal["ConsentRuleSetRequest"], Field(alias="$type", frozen=True)
] = "ConsentRuleSetRequest"


class ConsentRuleSetResponse(_BaseRuleSet):
"""Set of rules to retrieve a consent item."""

entityType: Annotated[
Literal["ConsentRuleSetResponse"], Field(alias="$type", frozen=True)
] = "ConsentRuleSetResponse"
stableTargetId: MergedConsentIdentifier
18 changes: 17 additions & 1 deletion mex/common/models/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,24 @@
YearMonthDayTime,
)

DoiStr = Annotated[
str,
Field(
examples=[
"https://doi.org/10.1007/978-1-0716-2441-8_7",
"https://doi.org/10.2807/1560-7917.ES.2022.27.46.2200849",
"https://doi.org/10.3389/fmicb.2022.868887",
"http://dx.doi.org/10.25646/5147",
"https://doi.org/10.1016/j.vaccine.2022.11.065",
],
pattern=r"^(((http)|(https))://(dx.)?doi.org/)(10.\d{4,9}/[-._;()/:A-Z0-9]+)$",
),
]
LoincIdStr = Annotated[
str,
Field(
examples=["https://loinc.org/95209-3", "https://loinc.org/LA26211-5"],
pattern=r"^https://loinc.org/([a-zA-z]*)|(([0-9]{5}-[0-9]*))$",
pattern=r"^https://loinc.org/([a-zA-z]*)|(([0-9]*(-[0-9])*))$",
json_schema_extra={"format": "uri"},
),
]
Expand Down Expand Up @@ -168,6 +181,7 @@ class _OptionalValues(_Stem):
| None
) = None
created: YearMonthDayTime | YearMonthDay | YearMonth | Year | None = None
doi: DoiStr | None = None
hasPersonalData: (
Annotated[
PersonalData,
Expand Down Expand Up @@ -237,6 +251,7 @@ class _VariadicValues(_Stem):
Annotated[Frequency, Field(examples=["https://mex.rki.de/item/frequency-1"])]
] = []
created: list[YearMonthDayTime | YearMonthDay | YearMonth | Year] = []
doi: list[DoiStr] = []
hasPersonalData: list[
Annotated[
PersonalData,
Expand Down Expand Up @@ -339,6 +354,7 @@ class PreventiveResource(_Stem, PreventiveRule):
contributingUnit: list[MergedPrimarySourceIdentifier] = []
contributor: list[MergedPrimarySourceIdentifier] = []
created: list[MergedPrimarySourceIdentifier] = []
doi: list[MergedPrimarySourceIdentifier] = []
creator: list[MergedPrimarySourceIdentifier] = []
description: list[MergedPrimarySourceIdentifier] = []
distribution: list[MergedPrimarySourceIdentifier] = []
Expand Down
Loading