diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee8e3f2a..8d6330c7 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.7.3 + rev: v0.8.0 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d7a1f1e..f20b8610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- add vocabulary and temporal unions and lookups to `mex.common.types` +- add `mex.common.fields` with field type by class name lookups + ### Changes + - wikidata helper now optionally accepts wikidata primary source +- set default empty rules to all of the rule-set models +- pin pydantic to sub 2.10 (for now) because of breaking changes ### Deprecated @@ -18,6 +24,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- switch HTTP method for preview endpoint to `POST` +- add optional values to variadic values for distribution models +- make `endpointDescription` optional for variadic access platform models + ### Security ## [0.41.0] - 2024-11-18 diff --git a/mex/common/backend_api/connector.py b/mex/common/backend_api/connector.py index 2912d0ae..2be5ebe0 100644 --- a/mex/common/backend_api/connector.py +++ b/mex/common/backend_api/connector.py @@ -179,7 +179,7 @@ def preview_merged_item( A single merged item """ response = self.request( - method="GET", + method="POST", endpoint=f"preview-item/{stable_target_id}", payload=rule_set, ) diff --git a/mex/common/connector/__init__.py b/mex/common/connector/__init__.py index d129844f..328a5c13 100644 --- a/mex/common/connector/__init__.py +++ b/mex/common/connector/__init__.py @@ -2,7 +2,7 @@ from mex.common.connector.http import HTTPConnector __all__ = ( + "CONNECTOR_STORE", "BaseConnector", "HTTPConnector", - "CONNECTOR_STORE", ) diff --git a/mex/common/fields.py b/mex/common/fields.py new file mode 100644 index 00000000..55abf0f2 --- /dev/null +++ b/mex/common/fields.py @@ -0,0 +1,137 @@ +from mex.common.models import ( + ADDITIVE_MODEL_CLASSES_BY_NAME, + EXTRACTED_MODEL_CLASSES_BY_NAME, + MERGED_MODEL_CLASSES_BY_NAME, + PREVENTIVE_MODEL_CLASSES_BY_NAME, + SUBTRACTIVE_MODEL_CLASSES_BY_NAME, +) +from mex.common.types import ( + MERGED_IDENTIFIER_CLASSES, + TEMPORAL_ENTITIES, + VOCABULARY_ENUMS, + Email, + Link, + LiteralStringType, + Text, +) +from mex.common.utils import contains_only_types, group_fields_by_class_name + +# all models classes +ALL_MODEL_CLASSES_BY_NAME = { + **ADDITIVE_MODEL_CLASSES_BY_NAME, + **EXTRACTED_MODEL_CLASSES_BY_NAME, + **MERGED_MODEL_CLASSES_BY_NAME, + **PREVENTIVE_MODEL_CLASSES_BY_NAME, + **SUBTRACTIVE_MODEL_CLASSES_BY_NAME, +} + +# fields that are immutable and can only be set once +FROZEN_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: field_info.frozen is True, +) + +# static fields that are set once on class-level to a literal type +LITERAL_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: isinstance(field_info.annotation, LiteralStringType), +) + +# fields typed as merged identifiers containing references to merged items +REFERENCE_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, *MERGED_IDENTIFIER_CLASSES), +) + +# nested fields that contain `Text` objects +TEXT_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, Text), +) + +# nested fields that contain `Link` objects +LINK_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, Link), +) + +# fields annotated as `Email` type +EMAIL_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, Email), +) + +# fields annotated as `int` type +INTEGER_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, int), +) + +# fields annotated as `str` type +STRING_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, str), +) + +# fields annotated as any temporal type +TEMPORAL_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, *TEMPORAL_ENTITIES), +) + +# fields annotated as any vocabulary enum +VOCABULARY_FIELDS_BY_CLASS_NAME = group_fields_by_class_name( + ALL_MODEL_CLASSES_BY_NAME, + lambda field_info: contains_only_types(field_info, *VOCABULARY_ENUMS), +) + +# fields with changeable values that are not nested objects or merged item references +MUTABLE_FIELDS_BY_CLASS_NAME = { + name: sorted( + { + field_name + for field_name in cls.get_all_fields() + if field_name + not in ( + *FROZEN_FIELDS_BY_CLASS_NAME[name], + *REFERENCE_FIELDS_BY_CLASS_NAME[name], + *TEXT_FIELDS_BY_CLASS_NAME[name], + *LINK_FIELDS_BY_CLASS_NAME[name], + ) + } + ) + for name, cls in ALL_MODEL_CLASSES_BY_NAME.items() +} + +# fields with mergeable values that are neither literal nor frozen +MERGEABLE_FIELDS_BY_CLASS_NAME = { + name: sorted( + { + field_name + for field_name in cls.model_fields + if field_name + not in ( + *FROZEN_FIELDS_BY_CLASS_NAME[name], + *LITERAL_FIELDS_BY_CLASS_NAME[name], + ) + } + ) + for name, cls in ALL_MODEL_CLASSES_BY_NAME.items() +} + +# fields with values that should be set once but are neither literal nor references +FINAL_FIELDS_BY_CLASS_NAME = { + name: sorted( + { + field_name + for field_name in cls.get_all_fields() + if field_name in FROZEN_FIELDS_BY_CLASS_NAME[name] + and field_name + not in ( + *LITERAL_FIELDS_BY_CLASS_NAME[name], + *REFERENCE_FIELDS_BY_CLASS_NAME[name], + ) + } + ) + for name, cls in ALL_MODEL_CLASSES_BY_NAME.items() +} diff --git a/mex/common/identity/__init__.py b/mex/common/identity/__init__.py index a50ba584..0a100510 100644 --- a/mex/common/identity/__init__.py +++ b/mex/common/identity/__init__.py @@ -4,7 +4,7 @@ __all__ = ( "BaseProvider", - "get_provider", "Identity", + "get_provider", "register_provider", ) diff --git a/mex/common/models/__init__.py b/mex/common/models/__init__.py index c4de1ced..9b5159c8 100644 --- a/mex/common/models/__init__.py +++ b/mex/common/models/__init__.py @@ -213,12 +213,33 @@ ) __all__ = ( + "ADDITIVE_MODEL_CLASSES", + "ADDITIVE_MODEL_CLASSES_BY_NAME", + "BASE_MODEL_CLASSES", + "BASE_MODEL_CLASSES_BY_NAME", + "EXTRACTED_MODEL_CLASSES", + "EXTRACTED_MODEL_CLASSES_BY_NAME", + "FILTER_MODEL_BY_EXTRACTED_CLASS_NAME", + "MAPPING_MODEL_BY_EXTRACTED_CLASS_NAME", + "MERGED_MODEL_CLASSES", + "MERGED_MODEL_CLASSES_BY_NAME", + "MEX_PRIMARY_SOURCE_IDENTIFIER", + "MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE", + "MEX_PRIMARY_SOURCE_STABLE_TARGET_ID", + "PREVENTIVE_MODEL_CLASSES", + "PREVENTIVE_MODEL_CLASSES_BY_NAME", + "RULE_MODEL_CLASSES", + "RULE_MODEL_CLASSES_BY_NAME", + "RULE_SET_REQUEST_CLASSES", + "RULE_SET_REQUEST_CLASSES_BY_NAME", + "RULE_SET_RESPONSE_CLASSES", + "RULE_SET_RESPONSE_CLASSES_BY_NAME", + "SUBTRACTIVE_MODEL_CLASSES", + "SUBTRACTIVE_MODEL_CLASSES_BY_NAME", "AccessPlatformRuleSetRequest", "AccessPlatformRuleSetResponse", "ActivityRuleSetRequest", "ActivityRuleSetResponse", - "ADDITIVE_MODEL_CLASSES_BY_NAME", - "ADDITIVE_MODEL_CLASSES", "AdditiveAccessPlatform", "AdditiveActivity", "AdditiveBibliographicResource", @@ -242,8 +263,6 @@ "AnyRuleSetRequest", "AnyRuleSetResponse", "AnySubtractiveModel", - "BASE_MODEL_CLASSES_BY_NAME", - "BASE_MODEL_CLASSES", "BaseAccessPlatform", "BaseActivity", "BaseBibliographicResource", @@ -264,8 +283,6 @@ "ContactPointRuleSetResponse", "DistributionRuleSetRequest", "DistributionRuleSetResponse", - "EXTRACTED_MODEL_CLASSES_BY_NAME", - "EXTRACTED_MODEL_CLASSES", "ExtractedAccessPlatform", "ExtractedActivity", "ExtractedBibliographicResource", @@ -281,13 +298,7 @@ "ExtractedResource", "ExtractedVariable", "ExtractedVariableGroup", - "FILTER_MODEL_BY_EXTRACTED_CLASS_NAME", - "generate_entity_filter_schema", - "generate_mapping_schema", "GenericFieldInfo", - "MAPPING_MODEL_BY_EXTRACTED_CLASS_NAME", - "MERGED_MODEL_CLASSES_BY_NAME", - "MERGED_MODEL_CLASSES", "MergedAccessPlatform", "MergedActivity", "MergedBibliographicResource", @@ -303,17 +314,12 @@ "MergedResource", "MergedVariable", "MergedVariableGroup", - "MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE", - "MEX_PRIMARY_SOURCE_IDENTIFIER", - "MEX_PRIMARY_SOURCE_STABLE_TARGET_ID", - "OrganizationalUnitRuleSetRequest", - "OrganizationalUnitRuleSetResponse", "OrganizationRuleSetRequest", "OrganizationRuleSetResponse", + "OrganizationalUnitRuleSetRequest", + "OrganizationalUnitRuleSetResponse", "PersonRuleSetRequest", "PersonRuleSetResponse", - "PREVENTIVE_MODEL_CLASSES_BY_NAME", - "PREVENTIVE_MODEL_CLASSES", "PreventiveAccessPlatform", "PreventiveActivity", "PreventiveBibliographicResource", @@ -332,14 +338,6 @@ "PrimarySourceRuleSetResponse", "ResourceRuleSetRequest", "ResourceRuleSetResponse", - "RULE_MODEL_CLASSES_BY_NAME", - "RULE_MODEL_CLASSES", - "RULE_SET_REQUEST_CLASSES_BY_NAME", - "RULE_SET_REQUEST_CLASSES", - "RULE_SET_RESPONSE_CLASSES_BY_NAME", - "RULE_SET_RESPONSE_CLASSES", - "SUBTRACTIVE_MODEL_CLASSES_BY_NAME", - "SUBTRACTIVE_MODEL_CLASSES", "SubtractiveAccessPlatform", "SubtractiveActivity", "SubtractiveBibliographicResource", @@ -358,6 +356,8 @@ "VariableGroupRuleSetResponse", "VariableRuleSetRequest", "VariableRuleSetResponse", + "generate_entity_filter_schema", + "generate_mapping_schema", ) MEX_PRIMARY_SOURCE_IDENTIFIER = ExtractedPrimarySourceIdentifier("00000000000001") diff --git a/mex/common/models/access_platform.py b/mex/common/models/access_platform.py index b20cb732..12c46e47 100644 --- a/mex/common/models/access_platform.py +++ b/mex/common/models/access_platform.py @@ -65,7 +65,7 @@ class _SparseValues(_Stem): class _VariadicValues(_Stem): - endpointDescription: list[Link] + endpointDescription: list[Link] = [] endpointType: list[APIType] = [] endpointURL: list[Link] = [] technicalAccessibility: list[TechnicalAccessibility] = [] @@ -141,9 +141,9 @@ class PreventiveAccessPlatform(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveAccessPlatform - subtractive: SubtractiveAccessPlatform - preventive: PreventiveAccessPlatform + additive: AdditiveAccessPlatform = AdditiveAccessPlatform() + subtractive: SubtractiveAccessPlatform = SubtractiveAccessPlatform() + preventive: PreventiveAccessPlatform = PreventiveAccessPlatform() class AccessPlatformRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/activity.py b/mex/common/models/activity.py index 4dfb8397..83db3ead 100644 --- a/mex/common/models/activity.py +++ b/mex/common/models/activity.py @@ -174,9 +174,9 @@ class PreventiveActivity(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveActivity - subtractive: SubtractiveActivity - preventive: PreventiveActivity + additive: AdditiveActivity = AdditiveActivity() + subtractive: SubtractiveActivity = SubtractiveActivity() + preventive: PreventiveActivity = PreventiveActivity() class ActivityRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/base/rules.py b/mex/common/models/base/rules.py index a7135ba3..be48a554 100644 --- a/mex/common/models/base/rules.py +++ b/mex/common/models/base/rules.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + from mex.common.models.base.entity import BaseEntity @@ -15,3 +17,8 @@ class PreventiveRule(BaseEntity): class RuleSet(BaseEntity): """Base class for a set of an additive, subtractive and preventive rule.""" + + if TYPE_CHECKING: # pragma: no cover + additive: AdditiveRule + subtractive: SubtractiveRule + preventive: PreventiveRule diff --git a/mex/common/models/bibliographic_resource.py b/mex/common/models/bibliographic_resource.py index 59f6db4d..795cba71 100644 --- a/mex/common/models/bibliographic_resource.py +++ b/mex/common/models/bibliographic_resource.py @@ -285,9 +285,9 @@ class PreventiveBibliographicResource(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveBibliographicResource - subtractive: SubtractiveBibliographicResource - preventive: PreventiveBibliographicResource + additive: AdditiveBibliographicResource = AdditiveBibliographicResource() + subtractive: SubtractiveBibliographicResource = SubtractiveBibliographicResource() + preventive: PreventiveBibliographicResource = PreventiveBibliographicResource() class BibliographicResourceRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/consent.py b/mex/common/models/consent.py index 8724664a..36d0b5d7 100644 --- a/mex/common/models/consent.py +++ b/mex/common/models/consent.py @@ -113,9 +113,9 @@ class PreventiveConsent(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveConsent - subtractive: SubtractiveConsent - preventive: PreventiveConsent + additive: AdditiveConsent = AdditiveConsent() + subtractive: SubtractiveConsent = SubtractiveConsent() + preventive: PreventiveConsent = PreventiveConsent() class ConsentRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/contact_point.py b/mex/common/models/contact_point.py index 24ba8d8a..ea6ba0ab 100644 --- a/mex/common/models/contact_point.py +++ b/mex/common/models/contact_point.py @@ -94,9 +94,9 @@ class PreventiveContactPoint(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveContactPoint - subtractive: SubtractiveContactPoint - preventive: PreventiveContactPoint + additive: AdditiveContactPoint = AdditiveContactPoint() + subtractive: SubtractiveContactPoint = SubtractiveContactPoint() + preventive: PreventiveContactPoint = PreventiveContactPoint() class ContactPointRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/distribution.py b/mex/common/models/distribution.py index ae4c68cb..c83f2b8e 100644 --- a/mex/common/models/distribution.py +++ b/mex/common/models/distribution.py @@ -76,7 +76,11 @@ class _SparseValues(_Stem): class _VariadicValues(_Stem): accessRestriction: list[AccessRestriction] = [] + accessService: list[MergedAccessPlatformIdentifier] = [] issued: list[YearMonthDayTime | YearMonthDay | YearMonth | Year] = [] + license: list[License] = [] + mediaType: list[MIMEType] = [] + modified: list[YearMonthDayTime | YearMonthDay | YearMonth | Year] = [] title: list[ Annotated[ str, @@ -157,9 +161,9 @@ class PreventiveDistribution(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveDistribution - subtractive: SubtractiveDistribution - preventive: PreventiveDistribution + additive: AdditiveDistribution = AdditiveDistribution() + subtractive: SubtractiveDistribution = SubtractiveDistribution() + preventive: PreventiveDistribution = PreventiveDistribution() class DistributionRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/organization.py b/mex/common/models/organization.py index e453c689..2b5e9462 100644 --- a/mex/common/models/organization.py +++ b/mex/common/models/organization.py @@ -170,9 +170,9 @@ class PreventiveOrganization(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveOrganization - subtractive: SubtractiveOrganization - preventive: PreventiveOrganization + additive: AdditiveOrganization = AdditiveOrganization() + subtractive: SubtractiveOrganization = SubtractiveOrganization() + preventive: PreventiveOrganization = PreventiveOrganization() class OrganizationRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/organizational_unit.py b/mex/common/models/organizational_unit.py index bed0f756..6ca4a08d 100644 --- a/mex/common/models/organizational_unit.py +++ b/mex/common/models/organizational_unit.py @@ -123,9 +123,9 @@ class PreventiveOrganizationalUnit(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveOrganizationalUnit - subtractive: SubtractiveOrganizationalUnit - preventive: PreventiveOrganizationalUnit + additive: AdditiveOrganizationalUnit = AdditiveOrganizationalUnit() + subtractive: SubtractiveOrganizationalUnit = SubtractiveOrganizationalUnit() + preventive: PreventiveOrganizationalUnit = PreventiveOrganizationalUnit() class OrganizationalUnitRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/person.py b/mex/common/models/person.py index 0db93af5..1d8b7c56 100644 --- a/mex/common/models/person.py +++ b/mex/common/models/person.py @@ -143,9 +143,9 @@ class PreventivePerson(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditivePerson - subtractive: SubtractivePerson - preventive: PreventivePerson + additive: AdditivePerson = AdditivePerson() + subtractive: SubtractivePerson = SubtractivePerson() + preventive: PreventivePerson = PreventivePerson() class PersonRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/primary_source.py b/mex/common/models/primary_source.py index 0cee5692..f129c2a6 100644 --- a/mex/common/models/primary_source.py +++ b/mex/common/models/primary_source.py @@ -137,9 +137,9 @@ class PreventivePrimarySource(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditivePrimarySource - subtractive: SubtractivePrimarySource - preventive: PreventivePrimarySource + additive: AdditivePrimarySource = AdditivePrimarySource() + subtractive: SubtractivePrimarySource = SubtractivePrimarySource() + preventive: PreventivePrimarySource = PreventivePrimarySource() class PrimarySourceRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/resource.py b/mex/common/models/resource.py index a9913fc3..84c96805 100644 --- a/mex/common/models/resource.py +++ b/mex/common/models/resource.py @@ -328,9 +328,9 @@ class PreventiveResource(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveResource - subtractive: SubtractiveResource - preventive: PreventiveResource + additive: AdditiveResource = AdditiveResource() + subtractive: SubtractiveResource = SubtractiveResource() + preventive: PreventiveResource = PreventiveResource() class ResourceRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/variable.py b/mex/common/models/variable.py index 21bb7873..34ad31ac 100644 --- a/mex/common/models/variable.py +++ b/mex/common/models/variable.py @@ -161,9 +161,9 @@ class PreventiveVariable(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveVariable - subtractive: SubtractiveVariable - preventive: PreventiveVariable + additive: AdditiveVariable = AdditiveVariable() + subtractive: SubtractiveVariable = SubtractiveVariable() + preventive: PreventiveVariable = PreventiveVariable() class VariableRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/models/variable_group.py b/mex/common/models/variable_group.py index b7ea1ee1..d4e6f2eb 100644 --- a/mex/common/models/variable_group.py +++ b/mex/common/models/variable_group.py @@ -98,9 +98,9 @@ class PreventiveVariableGroup(_Stem, PreventiveRule): class _BaseRuleSet(_Stem, RuleSet): - additive: AdditiveVariableGroup - subtractive: SubtractiveVariableGroup - preventive: PreventiveVariableGroup + additive: AdditiveVariableGroup = AdditiveVariableGroup() + subtractive: SubtractiveVariableGroup = SubtractiveVariableGroup() + preventive: PreventiveVariableGroup = PreventiveVariableGroup() class VariableGroupRuleSetRequest(_BaseRuleSet): diff --git a/mex/common/types/__init__.py b/mex/common/types/__init__.py index ddebce95..3b3c904e 100644 --- a/mex/common/types/__init__.py +++ b/mex/common/types/__init__.py @@ -75,6 +75,23 @@ ) __all__ = ( + "CET", + "EMAIL_PATTERN", + "EXTRACTED_IDENTIFIER_CLASSES", + "EXTRACTED_IDENTIFIER_CLASSES_BY_NAME", + "IDENTIFIER_PATTERN", + "MERGED_IDENTIFIER_CLASSES", + "MERGED_IDENTIFIER_CLASSES_BY_NAME", + "NESTED_MODEL_CLASSES", + "NESTED_MODEL_CLASSES_BY_NAME", + "TEMPORAL_ENTITY_CLASSES_BY_PRECISION", + "TEMPORAL_ENTITY_FORMATS_BY_PRECISION", + "URL_PATTERN", + "UTC", + "VOCABULARY_ENUMS", + "VOCABULARY_ENUMS_BY_NAME", + "VOCABULARY_PATTERN", + "APIType", "AccessRestriction", "ActivityType", "AnonymizationPseudonymization", @@ -82,18 +99,14 @@ "AnyMergedIdentifier", "AnyNestedModel", "AnyPrimitiveType", - "APIType", + "AnyVocabularyEnum", "AssetsPath", "BibliographicResourceType", - "CET", "ConsentStatus", "ConsentType", "DataProcessingState", "DataType", - "EMAIL_PATTERN", "Email", - "EXTRACTED_IDENTIFIER_CLASSES_BY_NAME", - "EXTRACTED_IDENTIFIER_CLASSES", "ExtractedAccessPlatformIdentifier", "ExtractedActivityIdentifier", "ExtractedBibliographicResourceIdentifier", @@ -101,15 +114,14 @@ "ExtractedContactPointIdentifier", "ExtractedDistributionIdentifier", "ExtractedIdentifier", - "ExtractedOrganizationalUnitIdentifier", "ExtractedOrganizationIdentifier", + "ExtractedOrganizationalUnitIdentifier", "ExtractedPersonIdentifier", "ExtractedPrimarySourceIdentifier", "ExtractedResourceIdentifier", "ExtractedVariableGroupIdentifier", "ExtractedVariableIdentifier", "Frequency", - "IDENTIFIER_PATTERN", "Identifier", "IdentityProvider", "Language", @@ -117,8 +129,7 @@ "Link", "LinkLanguage", "LiteralStringType", - "MERGED_IDENTIFIER_CLASSES_BY_NAME", - "MERGED_IDENTIFIER_CLASSES", + "MIMEType", "MergedAccessPlatformIdentifier", "MergedActivityIdentifier", "MergedBibliographicResourceIdentifier", @@ -126,33 +137,24 @@ "MergedContactPointIdentifier", "MergedDistributionIdentifier", "MergedIdentifier", - "MergedOrganizationalUnitIdentifier", "MergedOrganizationIdentifier", + "MergedOrganizationalUnitIdentifier", "MergedPersonIdentifier", "MergedPrimarySourceIdentifier", "MergedResourceIdentifier", "MergedVariableGroupIdentifier", "MergedVariableIdentifier", - "MIMEType", - "NESTED_MODEL_CLASSES_BY_NAME", - "NESTED_MODEL_CLASSES", "PathWrapper", "PersonalData", "ResourceCreationMethod", "ResourceTypeGeneral", "Sink", - "split_to_caps", "TechnicalAccessibility", - "TEMPORAL_ENTITY_CLASSES_BY_PRECISION", - "TEMPORAL_ENTITY_FORMATS_BY_PRECISION", "TemporalEntity", "TemporalEntityPrecision", "Text", "TextLanguage", "Theme", - "URL_PATTERN", - "UTC", - "VOCABULARY_PATTERN", "VocabularyEnum", "VocabularyLoader", "WorkPath", @@ -160,7 +162,42 @@ "YearMonth", "YearMonthDay", "YearMonthDayTime", + "split_to_caps", +) + +AnyVocabularyEnum = ( + AccessRestriction + | ActivityType + | AnonymizationPseudonymization + | APIType + | BibliographicResourceType + | ConsentStatus + | ConsentType + | DataProcessingState + | Frequency + | Language + | License + | MIMEType + | PersonalData + | ResourceCreationMethod + | ResourceTypeGeneral + | TechnicalAccessibility + | Theme ) +VOCABULARY_ENUMS: Final[list[type[AnyVocabularyEnum]]] = list( + get_args(AnyVocabularyEnum) +) +VOCABULARY_ENUMS_BY_NAME: Final[dict[str, type[AnyVocabularyEnum]]] = { + cls.__name__: cls for cls in VOCABULARY_ENUMS +} + +AnyTemporalEntity = Year | YearMonth | YearMonthDay | YearMonthDayTime +TEMPORAL_ENTITIES: Final[list[type[AnyTemporalEntity]]] = list( + get_args(AnyTemporalEntity) +) +TEMPORAL_ENTITIES_BY_NAME: Final[dict[str, type[AnyTemporalEntity]]] = { + cls.__name__: cls for cls in TEMPORAL_ENTITIES +} AnyNestedModel = Link | Text NESTED_MODEL_CLASSES: Final[list[type[AnyNestedModel]]] = list(get_args(AnyNestedModel)) diff --git a/mex/common/types/temporal_entity.py b/mex/common/types/temporal_entity.py index 37345f52..be899682 100644 --- a/mex/common/types/temporal_entity.py +++ b/mex/common/types/temporal_entity.py @@ -64,7 +64,7 @@ class TemporalEntityPrecision(Enum): class TemporalEntity: """Custom temporal entity with precision detection and timezone normalization.""" - __slots__ = ("precision", "date_time") + __slots__ = ("date_time", "precision") precision: TemporalEntityPrecision date_time: datetime @@ -266,9 +266,10 @@ def _parse_date( value: date, ) -> tuple[datetime, TemporalEntityPrecision]: """Parse a date and assume the precision is days.""" - return datetime( - value.year, value.month, value.day, tzinfo=CET - ), TemporalEntityPrecision.DAY + return ( + datetime(value.year, value.month, value.day, tzinfo=CET), + TemporalEntityPrecision.DAY, + ) def __eq__(self, other: object) -> bool: """Return whether the given other value is the same as this one.""" diff --git a/pdm.lock b/pdm.lock index 2f19fcf6..9e5fb90d 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:95b57bdc31c43e5f2fa75b308b5f4cf77d7dcab09203816b1785556b49669207" +content_hash = "sha256:94f30388b0b5a02bb7db250d607b9328308c8d5b846e168ea40b74da0542abb2" [[metadata.targets]] requires_python = "==3.11.*" @@ -148,49 +148,49 @@ files = [ [[package]] name = "coverage" -version = "7.6.7" +version = "7.6.8" requires_python = ">=3.9" summary = "Code coverage measurement for Python" groups = ["dev"] marker = "python_version == \"3.11\"" files = [ - {file = "coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469"}, - {file = "coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99"}, - {file = "coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec"}, - {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b"}, - {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a"}, - {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b"}, - {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d"}, - {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4"}, - {file = "coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2"}, - {file = "coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f"}, - {file = "coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, + {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, + {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, + {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, ] [[package]] name = "coverage" -version = "7.6.7" +version = "7.6.8" extras = ["toml"] requires_python = ">=3.9" summary = "Code coverage measurement for Python" groups = ["dev"] marker = "python_version == \"3.11\"" dependencies = [ - "coverage==7.6.7", + "coverage==7.6.8", "tomli; python_full_version <= \"3.11.0a6\"", ] files = [ - {file = "coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469"}, - {file = "coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99"}, - {file = "coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec"}, - {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b"}, - {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a"}, - {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b"}, - {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d"}, - {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4"}, - {file = "coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2"}, - {file = "coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f"}, - {file = "coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, + {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, + {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, + {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, ] [[package]] @@ -429,11 +429,11 @@ files = [ [[package]] name = "mex-model" -version = "3.2.0" -requires_python = "<3.13,>=3.11" +version = "3.3.2" +requires_python = ">=3.11,<3.13" git = "https://github.com/robert-koch-institut/mex-model.git" -ref = "3.2.0" -revision = "4a622e20bd9d912c9ac677a309d7c1b8adbf715f" +ref = "3.3.2" +revision = "5e7c3dd6ee904e6727402cc1bf4663ea014cccbc" summary = "JSON schema files defining the MEx metadata model." groups = ["default"] marker = "python_version == \"3.11\"" @@ -849,30 +849,30 @@ files = [ [[package]] name = "ruff" -version = "0.7.4" +version = "0.8.0" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] marker = "python_version == \"3.11\"" files = [ - {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, - {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, - {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, - {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, - {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, - {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, - {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, + {file = "ruff-0.8.0-py3-none-linux_armv6l.whl", hash = "sha256:fcb1bf2cc6706adae9d79c8d86478677e3bbd4ced796ccad106fd4776d395fea"}, + {file = "ruff-0.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:295bb4c02d58ff2ef4378a1870c20af30723013f441c9d1637a008baaf928c8b"}, + {file = "ruff-0.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b1f1c76b47c18fa92ee78b60d2d20d7e866c55ee603e7d19c1e991fad933a9a"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb0d4f250a7711b67ad513fde67e8870109e5ce590a801c3722580fe98c33a99"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e55cce9aa93c5d0d4e3937e47b169035c7e91c8655b0974e61bb79cf398d49c"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f4cd64916d8e732ce6b87f3f5296a8942d285bbbc161acee7fe561134af64f9"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c5c1466be2a2ebdf7c5450dd5d980cc87c8ba6976fb82582fea18823da6fa362"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dabfd05b96b7b8f2da00d53c514eea842bff83e41e1cceb08ae1966254a51df"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:facebdfe5a5af6b1588a1d26d170635ead6892d0e314477e80256ef4a8470cf3"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85e654f0ded7befe2d61eeaf3d3b1e4ef3894469cd664ffa85006c7720f1e4a2"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:83a55679c4cb449fa527b8497cadf54f076603cc36779b2170b24f704171ce70"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:812e2052121634cf13cd6fddf0c1871d0ead1aad40a1a258753c04c18bb71bbd"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:780d5d8523c04202184405e60c98d7595bdb498c3c6abba3b6d4cdf2ca2af426"}, + {file = "ruff-0.8.0-py3-none-win32.whl", hash = "sha256:5fdb6efecc3eb60bba5819679466471fd7d13c53487df7248d6e27146e985468"}, + {file = "ruff-0.8.0-py3-none-win_amd64.whl", hash = "sha256:582891c57b96228d146725975fbb942e1f30a0c4ba19722e692ca3eb25cc9b4f"}, + {file = "ruff-0.8.0-py3-none-win_arm64.whl", hash = "sha256:ba93e6294e9a737cd726b74b09a6972e36bb511f9a102f1d9a7e1ce94dd206a6"}, + {file = "ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 4fadcd4a..44840c4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,12 +12,12 @@ dependencies = [ "click>=8,<9", "langdetect>=1,<2", "ldap3>=2,<3", - "mex-model @ git+https://github.com/robert-koch-institut/mex-model.git@3.2.0", + "mex-model @ git+https://github.com/robert-koch-institut/mex-model.git@3.3.2", "numpy>=2,<3", "pandas>=2,<3", "pyarrow>=18,<19", "pydantic-settings>=2,<3", - "pydantic>=2,<3", + "pydantic>=2,<2.10", "pytz>=2024,<2024.2", "requests>=2,<3", ] @@ -29,7 +29,7 @@ optional-dependencies.dev = [ "pytest-random-order>=1,<2", "pytest-xdist>=3,<4", "pytest>=8,<9", - "ruff>=0.7,<1", + "ruff>=0.8,<1", "sphinx>=8,<9", "types-ldap3>=2,<3", "types-pytz>=2024,<2025", diff --git a/tests/backend_api/test_connector.py b/tests/backend_api/test_connector.py index 083c0f38..a3f52e67 100644 --- a/tests/backend_api/test_connector.py +++ b/tests/backend_api/test_connector.py @@ -181,7 +181,7 @@ def test_preview_merged_item_mocked( assert response == merged_person assert mocked_backend.call_args == call( - "GET", + "POST", "http://localhost:8080/v0/preview-item/NGwfzG8ROsrvIiQIVDVy", None, headers={ diff --git a/tests/models/test_rules.py b/tests/models/test_rules.py index 97c493a2..7dc4ff0a 100644 --- a/tests/models/test_rules.py +++ b/tests/models/test_rules.py @@ -1,5 +1,40 @@ -from mex.common.models import BASE_MODEL_CLASSES_BY_NAME, PREVENTIVE_MODEL_CLASSES -from mex.common.types import MergedPrimarySourceIdentifier +from mex.common.models import ( + ADDITIVE_MODEL_CLASSES, + BASE_MODEL_CLASSES_BY_NAME, + PREVENTIVE_MODEL_CLASSES, + RULE_SET_REQUEST_CLASSES, + RULE_SET_RESPONSE_CLASSES, + SUBTRACTIVE_MODEL_CLASSES, +) +from mex.common.types import Identifier, MergedPrimarySourceIdentifier + + +def test_additive_models_define_same_fields_as_base_model() -> None: + for additive_rule in ADDITIVE_MODEL_CLASSES: + base_model_name = "Base" + additive_rule.stemType + base_model = BASE_MODEL_CLASSES_BY_NAME[base_model_name] + expected_fields = {"entityType", *base_model.model_fields} + assert set(additive_rule.model_fields) == expected_fields + + +def test_additive_models_have_no_required_fields() -> None: + for additive_model in ADDITIVE_MODEL_CLASSES: + model = additive_model() + assert model.model_dump(exclude_unset=True) == {} + + +def test_subtractive_models_define_same_fields_as_base_model() -> None: + for subtractive_rule in SUBTRACTIVE_MODEL_CLASSES: + base_model_name = "Base" + subtractive_rule.stemType + base_model = BASE_MODEL_CLASSES_BY_NAME[base_model_name] + expected_fields = {"entityType", *base_model.model_fields} + assert set(subtractive_rule.model_fields) == expected_fields + + +def test_subtractive_models_have_no_required_fields() -> None: + for subtractive_model in SUBTRACTIVE_MODEL_CLASSES: + model = subtractive_model() + assert model.model_dump(exclude_unset=True) == {} def test_preventive_models_define_same_fields_as_base_model() -> None: @@ -17,3 +52,19 @@ def test_preventive_models_define_all_fields_as_correct_type() -> None: assert field_info.default == preventive_rule.__name__ else: assert field_info.annotation == list[MergedPrimarySourceIdentifier] + assert field_info.is_required() is False + assert field_info.default == [] + + +def test_rule_set_request_models_have_no_required_fields() -> None: + for rule_set_request in RULE_SET_REQUEST_CLASSES: + model = rule_set_request() + assert model.model_dump(exclude_unset=True) == {} + + +def test_rule_set_response_models_only_require_stable_target_id() -> None: + for rule_set_response in RULE_SET_RESPONSE_CLASSES: + model = rule_set_response(stableTargetId=Identifier.generate(seed=1)) + assert model.model_dump(exclude_unset=True) == { + "stableTargetId": "bFQoRhcVH5DHUr" + } diff --git a/tests/test_fields.py b/tests/test_fields.py new file mode 100644 index 00000000..bafde1ad --- /dev/null +++ b/tests/test_fields.py @@ -0,0 +1,37 @@ +from mex.common.fields import ( + ALL_MODEL_CLASSES_BY_NAME, + EMAIL_FIELDS_BY_CLASS_NAME, + FINAL_FIELDS_BY_CLASS_NAME, + FROZEN_FIELDS_BY_CLASS_NAME, + INTEGER_FIELDS_BY_CLASS_NAME, + LINK_FIELDS_BY_CLASS_NAME, + LITERAL_FIELDS_BY_CLASS_NAME, + MERGEABLE_FIELDS_BY_CLASS_NAME, + MUTABLE_FIELDS_BY_CLASS_NAME, + REFERENCE_FIELDS_BY_CLASS_NAME, + STRING_FIELDS_BY_CLASS_NAME, + TEMPORAL_FIELDS_BY_CLASS_NAME, + TEXT_FIELDS_BY_CLASS_NAME, + VOCABULARY_FIELDS_BY_CLASS_NAME, +) + + +def test_all_fields_by_class_names_include_all_classes() -> None: + assert { + len(lookup) + for lookup in ( + FROZEN_FIELDS_BY_CLASS_NAME, + LITERAL_FIELDS_BY_CLASS_NAME, + REFERENCE_FIELDS_BY_CLASS_NAME, + TEXT_FIELDS_BY_CLASS_NAME, + LINK_FIELDS_BY_CLASS_NAME, + EMAIL_FIELDS_BY_CLASS_NAME, + INTEGER_FIELDS_BY_CLASS_NAME, + STRING_FIELDS_BY_CLASS_NAME, + TEMPORAL_FIELDS_BY_CLASS_NAME, + VOCABULARY_FIELDS_BY_CLASS_NAME, + MUTABLE_FIELDS_BY_CLASS_NAME, + MERGEABLE_FIELDS_BY_CLASS_NAME, + FINAL_FIELDS_BY_CLASS_NAME, + ) + } == {len(ALL_MODEL_CLASSES_BY_NAME)}