From 934b01a89a65e32e20093fd81969ec14eb360daa Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:12:48 +0300 Subject: [PATCH 1/8] BS-120 Add CooperationNetworkStatus model --- .../study_right_cooperation_network_status.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 funidata_utils/schemas/sisu/study_right_cooperation_network_status.py diff --git a/funidata_utils/schemas/sisu/study_right_cooperation_network_status.py b/funidata_utils/schemas/sisu/study_right_cooperation_network_status.py new file mode 100644 index 0000000..4f580d9 --- /dev/null +++ b/funidata_utils/schemas/sisu/study_right_cooperation_network_status.py @@ -0,0 +1,18 @@ +from typing import Literal + +from pydantic import BaseModel + +from .common import LocalDateRange, LocalizedString + + +class CooperationNetworkStatus(BaseModel): + direction: Literal["INBOUND", "OUTBOUND", "NONE"] + organisationTkCode: str | None = None + outboundStatus: Literal["NOT_VALID", "FORWARDED", "RECORDED", "REJECTED"] | None = None + rejectionReason: LocalizedString | None = None + outboundStatusMessage: str | None = None + cooperationNetworkId: str | None = None + universityOrgId: str | None = None + homeStudyRightId: str | None = None + inboundStatus: Literal["ACCEPTED", "PROCESSING", "REJECTED"] | None = None + homeStudyRightValidity: LocalDateRange | None = None From 94d67f2386d96d678d3218d4660470b3c5be1c7e Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:20:43 +0300 Subject: [PATCH 2/8] BS-120 Add InternationalContractualDegree model --- funidata_utils/schemas/sisu/study_right.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/funidata_utils/schemas/sisu/study_right.py b/funidata_utils/schemas/sisu/study_right.py index e2cfe80..e2036d4 100644 --- a/funidata_utils/schemas/sisu/study_right.py +++ b/funidata_utils/schemas/sisu/study_right.py @@ -133,6 +133,17 @@ def serialize_date_fields(self, _date: datetime.date | None, _info): return _date.isoformat() +class AttainableDegree(BaseModel): + degreeName: str | None = None + internationalInstitutionUrn: Annotated[STRIPPED_STR, Field(pattern=sis_code_urn_pattern('international-institution'))] + + +class InternationalContractualDegree(BaseModel): + localId: str + internationalContractualDegreeAgreementId: str + attainableDegrees: list[AttainableDegree] + + class StudyRight(BaseModel): id: OTM_ID_REGEX_VALIDATED_STR documentState: Literal['ACTIVE', 'DRAFT', 'DELETED'] @@ -176,8 +187,8 @@ class StudyRight(BaseModel): phase2QualificationUrns: Optional[list] = None phase1EducationLocationUrn: Annotated[STRIPPED_STR, Field(..., pattern=sis_code_urn_pattern('municipality'))] phase2EducationLocationUrn: Annotated[STRIPPED_STR | None, Field(pattern=sis_code_urn_pattern('municipality'))] = None - phase1InternationalContractualDegree: Optional[dict] = None - phase2InternationalContractualDegree: Optional[dict] = None + phase1InternationalContractualDegree: InternationalContractualDegree | None = None + phase2InternationalContractualDegree: InternationalContractualDegree | None = None admissionTypeUrn: Annotated[STRIPPED_STR | None, Field(pattern=sis_code_urn_pattern('admission-type'))] = None codeUrns: list[CodeUrnsStr] additionalInformation: Optional[dict] = None From 611cf0f6ae6e3888bfb929893d0051aa465d50ad Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:27:02 +0300 Subject: [PATCH 3/8] BS-120 Add type field to AssessmentItemAttainment --- funidata_utils/schemas/sisu/attainment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/funidata_utils/schemas/sisu/attainment.py b/funidata_utils/schemas/sisu/attainment.py index 0e0b741..a9a770f 100644 --- a/funidata_utils/schemas/sisu/attainment.py +++ b/funidata_utils/schemas/sisu/attainment.py @@ -223,6 +223,7 @@ class DegreeProgrammeAttainment(Attainment): class AssessmentItemAttainment(Attainment): + type: Literal['AssessmentItemAttainment'] = 'AssessmentItemAttainment' courseUnitId: str courseUnitGroupId: str assessmentItemId: str From 7d135c0ca0f528f416c812ec287aee0c1bea97e0 Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:27:23 +0300 Subject: [PATCH 4/8] BS-120 Add s2r2Classification field to Attainment --- funidata_utils/schemas/sisu/attainment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/funidata_utils/schemas/sisu/attainment.py b/funidata_utils/schemas/sisu/attainment.py index a9a770f..7a7bfad 100644 --- a/funidata_utils/schemas/sisu/attainment.py +++ b/funidata_utils/schemas/sisu/attainment.py @@ -112,6 +112,7 @@ class Attainment(ObjectWithDocumentState): 'DegreeProgrammeAttainment' ] attainmentDate: datetime.date + s2r2Classification: str | None = None @field_serializer('organisations') def organisations_as_list(self, v, _info) -> list[dict]: From 619e6981acd60f595cf1baa795587dff3d4215fd Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:27:54 +0300 Subject: [PATCH 5/8] BS-120 Add StudyModuleAttainment model --- funidata_utils/schemas/sisu/attainment.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/funidata_utils/schemas/sisu/attainment.py b/funidata_utils/schemas/sisu/attainment.py index 7a7bfad..1f4ab22 100644 --- a/funidata_utils/schemas/sisu/attainment.py +++ b/funidata_utils/schemas/sisu/attainment.py @@ -228,4 +228,12 @@ class AssessmentItemAttainment(Attainment): courseUnitId: str courseUnitGroupId: str assessmentItemId: str - courseUnitRealisationId: str | None \ No newline at end of file + courseUnitRealisationId: str | None + + +class StudyModuleAttainment(Attainment): + type: Literal['ModuleAttainment'] = 'ModuleAttainment' + moduleId: str + moduleGroupId: str + nodes: list[Union[AttainmentGroupNode, AttainmentReferenceNode]] | None = None + embeddedModules: list[dict] | None = None From cc018802b727a29175822732942ba4ebec4f0dce Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:52:10 +0300 Subject: [PATCH 6/8] BS-120 Edit imports --- funidata_utils/schemas/sisu/course_unit_realisation.py | 6 +++--- funidata_utils/schemas/sisu/enrolment_calculation_config.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/funidata_utils/schemas/sisu/course_unit_realisation.py b/funidata_utils/schemas/sisu/course_unit_realisation.py index d2c711c..e1c85d4 100644 --- a/funidata_utils/schemas/sisu/course_unit_realisation.py +++ b/funidata_utils/schemas/sisu/course_unit_realisation.py @@ -5,9 +5,9 @@ from pydantic import BaseModel, Field, conset, conlist, field_serializer, field_validator -from funidata_utils.schemas.common_serializers import serialize_as_list -from funidata_utils.schemas.sisu.base import SisBase, HashableBaseModel -from funidata_utils.schemas.sisu.common import ( +from ..common_serializers import serialize_as_list +from .base import SisBase, HashableBaseModel +from .common import ( LocalDateRange, LocalDateTimeRange, OTM_ID_REGEX_VALIDATED_STR, sis_code_urn_pattern, STRIPPED_STR, IntRange, SIS_MAX_MEDIUM_SET_SIZE, OrganisationRoleShare, SIS_MAX_SMALL_SET_SIZE, LocalizedString, SIS_MAX_TWEET_LENGTH, diff --git a/funidata_utils/schemas/sisu/enrolment_calculation_config.py b/funidata_utils/schemas/sisu/enrolment_calculation_config.py index a700650..3a6b7cb 100644 --- a/funidata_utils/schemas/sisu/enrolment_calculation_config.py +++ b/funidata_utils/schemas/sisu/enrolment_calculation_config.py @@ -5,9 +5,9 @@ from pydantic import BaseModel, Field, conset, field_serializer -from funidata_utils.schemas.common_serializers import serialize_as_list -from funidata_utils.schemas.sisu.base import SisBase -from funidata_utils.schemas.sisu.common import ( +from ..common_serializers import serialize_as_list +from .base import SisBase +from .common import ( OTM_ID_REGEX_VALIDATED_STR, LocalizedString, STRIPPED_STR, sis_code_urn_pattern, CreditRange, SIS_MAX_SMALL_SET_SIZE, ) From f83dcf46b01a1827e006b77c0ff6bd2f0d4d3c2c Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 10:53:49 +0300 Subject: [PATCH 7/8] BS-120 Add AttainedQualification model --- .../schemas/sisu/attained_qualification.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 funidata_utils/schemas/sisu/attained_qualification.py diff --git a/funidata_utils/schemas/sisu/attained_qualification.py b/funidata_utils/schemas/sisu/attained_qualification.py new file mode 100644 index 0000000..f543a2a --- /dev/null +++ b/funidata_utils/schemas/sisu/attained_qualification.py @@ -0,0 +1,39 @@ +import datetime +from pydantic import BaseModel, Field, field_serializer, conset +from typing import Annotated, Literal + +from .base import SisBase +from .common import OTM_ID_REGEX_VALIDATED_STR, STRIPPED_STR, SIS_MAX_MEDIUM_SET_SIZE, sis_code_urn_pattern +from ..common_serializers import serialize_as_list + + +class AssociatedStudy(BaseModel): + description: str + credits: int + attainmentLocation: Annotated[STRIPPED_STR, Field(pattern=sis_code_urn_pattern('educational-institution'))] + associatedDegree: str + attainmentDate: datetime.date + + +class AttainedQualification(SisBase): + id: OTM_ID_REGEX_VALIDATED_STR + documentState: Literal['ACTIVE', 'DRAFT', 'DELETED'] + qualificationId: OTM_ID_REGEX_VALIDATED_STR + personId: str = Field(description='PrivatePersonId') + studyRightId: str + moduleGroupId: OTM_ID_REGEX_VALIDATED_STR | None = None + associatedStudies: list[AssociatedStudy] | None = None + additionalInformation: str | None = None + attainmentDate: datetime.date | None = None + studyFieldUrn: Annotated[STRIPPED_STR | None, Field(pattern=sis_code_urn_pattern('study-field'))] = None + attainmentMethod: Literal['AUTOMATIC', 'MANUAL'] = 'MANUAL' + credits: int | None = None + attainmentIds: conset(OTM_ID_REGEX_VALIDATED_STR, max_length=SIS_MAX_MEDIUM_SET_SIZE) | None = None # noqa + childAttainedQualificationIds: conset(OTM_ID_REGEX_VALIDATED_STR, max_length=SIS_MAX_MEDIUM_SET_SIZE) | None = None # noqa + registrationDate: datetime.date | None = None + verifierPersonId: OTM_ID_REGEX_VALIDATED_STR | None = None + + @field_serializer("attainmentIds", "childAttainedQualificationIds") + def serialize_set_as_list_str(self, v, _info) -> list[str] | None: + serialized_list = serialize_as_list(v) + return serialized_list From 727d4f6182acd06843bd94f809b4d8b3951f68ca Mon Sep 17 00:00:00 2001 From: rrimpila Date: Fri, 7 Aug 2026 11:51:05 +0300 Subject: [PATCH 8/8] BS-120 Update init --- funidata_utils/schemas/sisu/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/funidata_utils/schemas/sisu/__init__.py b/funidata_utils/schemas/sisu/__init__.py index bf34afa..3961bc1 100644 --- a/funidata_utils/schemas/sisu/__init__.py +++ b/funidata_utils/schemas/sisu/__init__.py @@ -8,6 +8,8 @@ StudyRightCancellation, StudyRightCourseUnitSelection, StudyRightMinorSelection, + InternationalContractualDegree, + AttainableDegree, ) from .common import LocalizedString from .attainment import ( @@ -15,6 +17,7 @@ CustomCourseUnitAttainment, CustomModuleAttainment, DegreeProgrammeAttainment, + StudyModuleAttainment, ) from .mobility_period import MobilityPeriod from .thesis import Thesis @@ -46,3 +49,5 @@ from .term_registration import (TermRegistration, StudyRightTermRegistrations) from .enrolment_calculation_config import EnrolmentCalculationConfig from .code_book import CodeBook +from .study_right_cooperation_network_status import CooperationNetworkStatus +from .attained_qualification import AttainedQualification