Files
govoplan-records/src/govoplan_records/backend/schemas.py
T

311 lines
12 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field, model_validator
class StrictModel(BaseModel):
model_config = ConfigDict(extra="forbid")
class FilePlanNodeWriteRequest(StrictModel):
node_id: str = Field(min_length=1, max_length=255)
code: str = Field(min_length=1, max_length=120)
label: str = Field(min_length=1, max_length=500)
parent_node_id: str | None = Field(default=None, max_length=255)
description: str | None = Field(default=None, max_length=10_000)
active: bool = True
valid_from: datetime | None = None
valid_to: datetime | None = None
recorded_at: datetime
expected_revision: int | None = Field(default=None, ge=1)
idempotency_key: str = Field(min_length=1, max_length=255)
institutional_context: dict[str, Any] = Field(default_factory=dict)
@model_validator(mode="after")
def validate_interval(self):
if self.valid_from and self.valid_to and self.valid_to <= self.valid_from:
raise ValueError("valid_to must be after valid_from")
return self
class RecordClassWriteRequest(StrictModel):
class_id: str = Field(min_length=1, max_length=255)
file_plan_node_id: str = Field(min_length=1, max_length=255)
key: str = Field(min_length=1, max_length=120)
label: str = Field(min_length=1, max_length=500)
description: str | None = Field(default=None, max_length=10_000)
metadata_requirements: list[str] = Field(default_factory=list, max_length=100)
allowed_source_types: list[str] = Field(default_factory=list, max_length=100)
retention_period_days: int | None = Field(default=None, ge=0, le=365_000)
closure_trigger: str | None = Field(default=None, max_length=255)
access_mode: Literal["tenant", "restricted"] = "tenant"
active: bool = True
valid_from: datetime | None = None
valid_to: datetime | None = None
recorded_at: datetime
expected_revision: int | None = Field(default=None, ge=1)
idempotency_key: str = Field(min_length=1, max_length=255)
institutional_context: dict[str, Any] = Field(default_factory=dict)
@model_validator(mode="after")
def validate_interval(self):
if self.valid_from and self.valid_to and self.valid_to <= self.valid_from:
raise ValueError("valid_to must be after valid_from")
return self
class RecordCreateRequest(StrictModel):
record_id: str | None = Field(default=None, max_length=255)
record_number: str = Field(min_length=1, max_length=255)
class_id: str = Field(min_length=1, max_length=255)
file_plan_node_id: str = Field(min_length=1, max_length=255)
title: str = Field(min_length=1, max_length=500)
description: str | None = Field(default=None, max_length=20_000)
state: Literal["planned", "open"] = "open"
source_authority_mode: Literal[
"native_authoritative",
"external_authoritative",
"external_mirror",
"governed_sync",
"governance_overlay",
"linked_reference",
] = "native_authoritative"
access_mode: Literal["tenant", "restricted"] = "tenant"
purpose: str = Field(min_length=1, max_length=255)
classification: str | None = Field(default=None, max_length=120)
responsible_unit_id: str | None = Field(default=None, max_length=255)
responsible_function_id: str | None = Field(default=None, max_length=255)
external_reference: dict[str, Any] = Field(default_factory=dict)
institutional_context: dict[str, Any] = Field(default_factory=dict)
valid_from: datetime | None = None
valid_to: datetime | None = None
recorded_at: datetime
change_reason: str = Field(min_length=1, max_length=2_000)
idempotency_key: str = Field(min_length=1, max_length=255)
@model_validator(mode="after")
def validate_interval(self):
if self.valid_from and self.valid_to and self.valid_to <= self.valid_from:
raise ValueError("valid_to must be after valid_from")
return self
class RecordUpdateRequest(StrictModel):
expected_revision: int = Field(ge=1)
title: str | None = Field(default=None, min_length=1, max_length=500)
description: str | None = Field(default=None, max_length=20_000)
class_id: str | None = Field(default=None, min_length=1, max_length=255)
file_plan_node_id: str | None = Field(default=None, min_length=1, max_length=255)
state: Literal["planned", "open"] | None = None
access_mode: Literal["tenant", "restricted"] | None = None
purpose: str = Field(min_length=1, max_length=255)
classification: str | None = Field(default=None, max_length=120)
responsible_unit_id: str | None = Field(default=None, max_length=255)
responsible_function_id: str | None = Field(default=None, max_length=255)
institutional_context: dict[str, Any] | None = None
valid_from: datetime | None = None
valid_to: datetime | None = None
recorded_at: datetime
change_reason: str = Field(min_length=1, max_length=2_000)
idempotency_key: str = Field(min_length=1, max_length=255)
@model_validator(mode="after")
def validate_interval(self):
if self.valid_from and self.valid_to and self.valid_to <= self.valid_from:
raise ValueError("valid_to must be after valid_from")
return self
class RecordVolumeCreateRequest(StrictModel):
volume_id: str | None = Field(default=None, max_length=255)
label: str = Field(min_length=1, max_length=500)
valid_from: datetime | None = None
valid_to: datetime | None = None
recorded_at: datetime
purpose: str = Field(min_length=1, max_length=255)
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordSourceLocatorRequest(StrictModel):
source_module: str = Field(min_length=1, max_length=100)
resource_type: str = Field(min_length=1, max_length=120)
resource_id: str = Field(min_length=1, max_length=500)
source_revision: str = Field(min_length=1, max_length=255)
metadata: dict[str, Any] = Field(default_factory=dict)
class RecordItemCreateRequest(StrictModel):
source: RecordSourceLocatorRequest
purpose: str = Field(min_length=1, max_length=255)
filing_reason: str = Field(min_length=1, max_length=2_000)
idempotency_key: str = Field(min_length=1, max_length=255)
volume_id: str | None = Field(default=None, max_length=255)
relationship: str = Field(default="contains", min_length=1, max_length=120)
institutional_context: dict[str, Any] = Field(default_factory=dict)
metadata: dict[str, Any] = Field(default_factory=dict)
class RecordLifecycleActionRequest(StrictModel):
expected_revision: int = Field(ge=1)
purpose: str = Field(min_length=1, max_length=255)
reason: str = Field(min_length=1, max_length=2_000)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordCloseRequest(RecordLifecycleActionRequest):
retention_trigger_at: datetime | None = None
restart_retention: bool = False
class RecordAppraisalRequest(RecordLifecycleActionRequest):
outcome: Literal["retain", "transfer", "destroy", "reclassify"]
policy_refs: list[str] = Field(default_factory=list, max_length=100)
override_retention_not_due: bool = False
class RecordHoldCreateRequest(StrictModel):
hold_id: str | None = Field(default=None, max_length=255)
expected_record_revision: int = Field(ge=1)
reason: str = Field(min_length=1, max_length=10_000)
authority: str = Field(min_length=1, max_length=500)
purpose: str = Field(min_length=1, max_length=255)
scope: dict[str, Any] = Field(default_factory=dict)
effective_from: datetime | None = None
effective_to: datetime | None = None
policy_refs: list[str] = Field(default_factory=list, max_length=100)
institutional_context: dict[str, Any] = Field(default_factory=dict)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
@model_validator(mode="after")
def validate_interval(self):
if (
self.effective_from
and self.effective_to
and self.effective_to <= self.effective_from
):
raise ValueError("effective_to must be after effective_from")
return self
class RecordHoldReleaseRequest(StrictModel):
expected_hold_revision: int = Field(ge=1)
reason: str = Field(min_length=1, max_length=2_000)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordDispositionCreateRequest(StrictModel):
disposition_id: str | None = Field(default=None, max_length=255)
expected_record_revision: int = Field(ge=1)
action: Literal["retain", "transfer", "destroy", "reclassify"]
reason: str = Field(min_length=1, max_length=10_000)
purpose: str = Field(min_length=1, max_length=255)
policy_refs: list[str] = Field(default_factory=list, max_length=100)
institutional_context: dict[str, Any] = Field(default_factory=dict)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordDispositionFinalizeRequest(StrictModel):
expected_disposition_revision: int = Field(ge=1)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordDispositionWithdrawRequest(RecordDispositionFinalizeRequest):
reason: str = Field(min_length=1, max_length=2_000)
class RecordTransferPackageCreateRequest(StrictModel):
package_id: str | None = Field(default=None, max_length=255)
disposition_id: str = Field(min_length=1, max_length=255)
expected_record_revision: int = Field(ge=1)
provider_id: str = Field(min_length=1, max_length=100)
profile: str = Field(min_length=1, max_length=255)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordTransferDispatchRequest(StrictModel):
expected_package_revision: int = Field(ge=1)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordListResponse(StrictModel):
records: list[dict[str, Any]]
total: int
offset: int
limit: int
class RecordCatalogResponse(StrictModel):
file_plan: list[dict[str, Any]]
classes: list[dict[str, Any]]
class RecordDetailResponse(StrictModel):
record: dict[str, Any]
volumes: list[dict[str, Any]]
items: list[dict[str, Any]]
chronology: list[dict[str, Any]]
holds: list[dict[str, Any]]
dispositions: list[dict[str, Any]]
transfer_packages: list[dict[str, Any]]
access_explanation: dict[str, Any]
class RecordSourceProviderResponse(StrictModel):
providers: list[dict[str, Any]]
class RecordArchiveProviderResponse(StrictModel):
providers: list[dict[str, Any]]
class RecordRecoveryStatusResponse(StrictModel):
record_id: str
record_revision: int
evidence_sha256: str
healthy: bool
source_checks: list[dict[str, Any]]
package_checks: list[dict[str, Any]]
recovery_operations: list[dict[str, Any]]
failure_count: int
limitations: list[str]
__all__ = [
"FilePlanNodeWriteRequest",
"RecordAppraisalRequest",
"RecordArchiveProviderResponse",
"RecordCatalogResponse",
"RecordClassWriteRequest",
"RecordCloseRequest",
"RecordCreateRequest",
"RecordDetailResponse",
"RecordDispositionCreateRequest",
"RecordDispositionFinalizeRequest",
"RecordDispositionWithdrawRequest",
"RecordHoldCreateRequest",
"RecordHoldReleaseRequest",
"RecordItemCreateRequest",
"RecordLifecycleActionRequest",
"RecordListResponse",
"RecordRecoveryStatusResponse",
"RecordSourceProviderResponse",
"RecordTransferDispatchRequest",
"RecordTransferPackageCreateRequest",
"RecordUpdateRequest",
"RecordVolumeCreateRequest",
]