ashmatics-datamodels 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ashmatics_datamodels/__init__.py +38 -0
- ashmatics_datamodels/common/__init__.py +80 -0
- ashmatics_datamodels/common/base.py +76 -0
- ashmatics_datamodels/common/enums.py +119 -0
- ashmatics_datamodels/common/frameworks.py +168 -0
- ashmatics_datamodels/common/regulators.py +152 -0
- ashmatics_datamodels/common/validators.py +237 -0
- ashmatics_datamodels/documents/__init__.py +272 -0
- ashmatics_datamodels/documents/base.py +438 -0
- ashmatics_datamodels/documents/evidence.py +236 -0
- ashmatics_datamodels/documents/governance.py +696 -0
- ashmatics_datamodels/documents/governance_enums.py +151 -0
- ashmatics_datamodels/documents/manufacturers.py +299 -0
- ashmatics_datamodels/documents/models.py +431 -0
- ashmatics_datamodels/documents/products.py +347 -0
- ashmatics_datamodels/documents/regulatory.py +981 -0
- ashmatics_datamodels/documents/use_cases.py +335 -0
- ashmatics_datamodels/fda/__init__.py +165 -0
- ashmatics_datamodels/fda/adverse_events.py +309 -0
- ashmatics_datamodels/fda/classifications.py +389 -0
- ashmatics_datamodels/fda/clearances.py +481 -0
- ashmatics_datamodels/fda/enums.py +186 -0
- ashmatics_datamodels/fda/manufacturers.py +180 -0
- ashmatics_datamodels/fda/products.py +228 -0
- ashmatics_datamodels/fda/recalls.py +213 -0
- ashmatics_datamodels/use_cases/__init__.py +57 -0
- ashmatics_datamodels/use_cases/categories.py +116 -0
- ashmatics_datamodels/use_cases/enums.py +137 -0
- ashmatics_datamodels/use_cases/use_cases.py +188 -0
- ashmatics_datamodels/utils/__init__.py +19 -0
- ashmatics_datamodels-0.3.2.dist-info/METADATA +134 -0
- ashmatics_datamodels-0.3.2.dist-info/RECORD +34 -0
- ashmatics_datamodels-0.3.2.dist-info/WHEEL +4 -0
- ashmatics_datamodels-0.3.2.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Copyright 2025 Asher Informatics PBC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
AshMatics Core DataModels
|
|
17
|
+
|
|
18
|
+
Canonical Pydantic data models for AshMatics healthcare applications.
|
|
19
|
+
Provides FDA vocabulary, regulatory schemas, clinical AI use case taxonomy,
|
|
20
|
+
and MongoDB document schemas with standardized three-tier structure.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
from ashmatics_datamodels.fda import FDA_ManufacturerBase, FDA_510kClearance
|
|
24
|
+
from ashmatics_datamodels.common import AshMaticsBaseModel
|
|
25
|
+
from ashmatics_datamodels.use_cases import UseCaseCategoryBase
|
|
26
|
+
from ashmatics_datamodels.documents import EvidenceDocument, RegulatoryDocument
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
__version__ = "0.3.2"
|
|
30
|
+
__author__ = "Asher Informatics PBC"
|
|
31
|
+
|
|
32
|
+
from ashmatics_datamodels.common import AshMaticsBaseModel, TimestampedModel
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"__version__",
|
|
36
|
+
"AshMaticsBaseModel",
|
|
37
|
+
"TimestampedModel",
|
|
38
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Copyright 2025 Asher Informatics PBC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Common base models and utilities shared across all jurisdictions.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from ashmatics_datamodels.common.base import (
|
|
20
|
+
AshMaticsBaseModel,
|
|
21
|
+
AuditedModel,
|
|
22
|
+
TimestampedModel,
|
|
23
|
+
)
|
|
24
|
+
from ashmatics_datamodels.common.enums import (
|
|
25
|
+
AuthorizationStatus,
|
|
26
|
+
ParsingStatus,
|
|
27
|
+
Region,
|
|
28
|
+
RegulatoryStatus,
|
|
29
|
+
RiskCategory,
|
|
30
|
+
)
|
|
31
|
+
from ashmatics_datamodels.common.frameworks import (
|
|
32
|
+
RegulatoryFrameworkBase,
|
|
33
|
+
RegulatoryFrameworkCreate,
|
|
34
|
+
RegulatoryFrameworkResponse,
|
|
35
|
+
RegulatoryFrameworkStats,
|
|
36
|
+
RegulatoryFrameworkSummary,
|
|
37
|
+
RegulatoryFrameworkUpdate,
|
|
38
|
+
)
|
|
39
|
+
from ashmatics_datamodels.common.regulators import (
|
|
40
|
+
RegulatorBase,
|
|
41
|
+
RegulatorCreate,
|
|
42
|
+
RegulatorResponse,
|
|
43
|
+
RegulatorStats,
|
|
44
|
+
RegulatorSummary,
|
|
45
|
+
RegulatorUpdate,
|
|
46
|
+
)
|
|
47
|
+
from ashmatics_datamodels.common.validators import (
|
|
48
|
+
validate_country_code,
|
|
49
|
+
validate_iso_date,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
# Base models
|
|
54
|
+
"AshMaticsBaseModel",
|
|
55
|
+
"TimestampedModel",
|
|
56
|
+
"AuditedModel",
|
|
57
|
+
# Enums
|
|
58
|
+
"AuthorizationStatus",
|
|
59
|
+
"RegulatoryStatus",
|
|
60
|
+
"RiskCategory",
|
|
61
|
+
"ParsingStatus",
|
|
62
|
+
"Region",
|
|
63
|
+
# Validators
|
|
64
|
+
"validate_country_code",
|
|
65
|
+
"validate_iso_date",
|
|
66
|
+
# Regulators
|
|
67
|
+
"RegulatorBase",
|
|
68
|
+
"RegulatorCreate",
|
|
69
|
+
"RegulatorUpdate",
|
|
70
|
+
"RegulatorResponse",
|
|
71
|
+
"RegulatorSummary",
|
|
72
|
+
"RegulatorStats",
|
|
73
|
+
# Regulatory Frameworks
|
|
74
|
+
"RegulatoryFrameworkBase",
|
|
75
|
+
"RegulatoryFrameworkCreate",
|
|
76
|
+
"RegulatoryFrameworkUpdate",
|
|
77
|
+
"RegulatoryFrameworkResponse",
|
|
78
|
+
"RegulatoryFrameworkSummary",
|
|
79
|
+
"RegulatoryFrameworkStats",
|
|
80
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Copyright 2025 Asher Informatics PBC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Base model classes for all AshMatics schemas.
|
|
17
|
+
|
|
18
|
+
These models are database-agnostic and provide common configuration
|
|
19
|
+
and functionality across all domain-specific schemas.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AshMaticsBaseModel(BaseModel):
|
|
29
|
+
"""
|
|
30
|
+
Base model for all AshMatics schemas.
|
|
31
|
+
|
|
32
|
+
Configuration:
|
|
33
|
+
- from_attributes: Enable ORM mode for SQLAlchemy integration in apps
|
|
34
|
+
- validate_assignment: Validate on field assignment
|
|
35
|
+
- use_enum_values: Use enum values, not names, in serialization
|
|
36
|
+
- extra: Forbid extra fields by default for strict validation
|
|
37
|
+
- str_strip_whitespace: Strip whitespace from string fields
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
model_config = ConfigDict(
|
|
41
|
+
from_attributes=True,
|
|
42
|
+
validate_assignment=True,
|
|
43
|
+
use_enum_values=True,
|
|
44
|
+
extra="forbid",
|
|
45
|
+
str_strip_whitespace=True,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TimestampedModel(AshMaticsBaseModel):
|
|
50
|
+
"""
|
|
51
|
+
Model with creation and update timestamps.
|
|
52
|
+
|
|
53
|
+
Use for entities that need temporal tracking but not full audit trails.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
created_at: Optional[datetime] = Field(
|
|
57
|
+
None, description="Timestamp when record was created"
|
|
58
|
+
)
|
|
59
|
+
updated_at: Optional[datetime] = Field(
|
|
60
|
+
None, description="Timestamp when record was last updated"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AuditedModel(TimestampedModel):
|
|
65
|
+
"""
|
|
66
|
+
Model with full audit trail support.
|
|
67
|
+
|
|
68
|
+
Use for entities requiring user attribution for changes.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
created_by: Optional[str] = Field(
|
|
72
|
+
None, description="User ID or system identifier who created the record"
|
|
73
|
+
)
|
|
74
|
+
updated_by: Optional[str] = Field(
|
|
75
|
+
None, description="User ID or system identifier who last updated the record"
|
|
76
|
+
)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Copyright 2025 Asher Informatics PBC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Common enumerations shared across jurisdictions.
|
|
17
|
+
|
|
18
|
+
These enums represent concepts that are jurisdiction-agnostic or
|
|
19
|
+
shared across multiple regulatory frameworks.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from enum import Enum
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AuthorizationStatus(str, Enum):
|
|
26
|
+
"""
|
|
27
|
+
Lifecycle status of regulatory authorizations.
|
|
28
|
+
|
|
29
|
+
Workflow: UNDER_REVIEW → ACTIVE → {EXPIRED | WITHDRAWN | SUSPENDED}
|
|
30
|
+
|
|
31
|
+
Used for tracking state of 510(k) clearances, PMA approvals,
|
|
32
|
+
CE Marks, and other regulatory authorizations.
|
|
33
|
+
|
|
34
|
+
Reference: ASHMATICS Workflow Ontology
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
ACTIVE = "active" # Authorization currently valid and in effect
|
|
38
|
+
EXPIRED = "expired" # Authorization past expiry date, no longer valid
|
|
39
|
+
WITHDRAWN = "withdrawn" # Authorization voluntarily withdrawn by holder
|
|
40
|
+
SUSPENDED = "suspended" # Authorization temporarily suspended by authority
|
|
41
|
+
UNDER_REVIEW = "under_review" # Application under regulatory review
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RegulatoryStatus(str, Enum):
|
|
45
|
+
"""
|
|
46
|
+
Overall regulatory standing of a product across jurisdictions.
|
|
47
|
+
|
|
48
|
+
Workflow: PENDING → {APPROVED | REJECTED}
|
|
49
|
+
Post-Market: APPROVED → {WITHDRAWN | SUSPENDED}
|
|
50
|
+
|
|
51
|
+
Represents product-level regulatory status, distinct from
|
|
52
|
+
individual authorization status.
|
|
53
|
+
|
|
54
|
+
Reference: ASHMATICS Workflow Ontology
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
APPROVED = "approved" # Product has received regulatory approval
|
|
58
|
+
PENDING = "pending" # Product submission under regulatory review
|
|
59
|
+
REJECTED = "rejected" # Product submission rejected by authority
|
|
60
|
+
WITHDRAWN = "withdrawn" # Product voluntarily withdrawn from market
|
|
61
|
+
SUSPENDED = "suspended" # Product marketing suspended by authority
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class RiskCategory(str, Enum):
|
|
65
|
+
"""
|
|
66
|
+
General risk categorization for medical devices.
|
|
67
|
+
|
|
68
|
+
Maps to FDA Device Class: Low (I), Moderate (II), High (III)
|
|
69
|
+
|
|
70
|
+
Used for risk-based filtering and analysis across regulatory
|
|
71
|
+
jurisdictions. Aligns with FDA device classification system.
|
|
72
|
+
|
|
73
|
+
Reference: ASHMATICS Risk Ontology
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
LOW = "low" # Minimal potential for harm (Class I devices)
|
|
77
|
+
MODERATE = "moderate" # Moderate potential requiring special controls (Class II)
|
|
78
|
+
HIGH = "high" # Significant potential for harm (Class III)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ParsingStatus(str, Enum):
|
|
82
|
+
"""
|
|
83
|
+
Document parsing workflow status.
|
|
84
|
+
|
|
85
|
+
Workflow: PENDING → IN_PROGRESS → {COMPLETED | FAILED}
|
|
86
|
+
Alternative: PENDING → SKIPPED
|
|
87
|
+
|
|
88
|
+
Used for tracking AI-powered document processing, chunking,
|
|
89
|
+
and knowledge graph extraction workflows.
|
|
90
|
+
|
|
91
|
+
Reference: ASHMATICS Internal Workflow Ontology
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
PENDING = "pending" # Document queued for parsing
|
|
95
|
+
IN_PROGRESS = "in_progress" # Document currently being processed
|
|
96
|
+
COMPLETED = "completed" # Document successfully parsed
|
|
97
|
+
FAILED = "failed" # Document parsing encountered errors
|
|
98
|
+
SKIPPED = "skipped" # Document intentionally not parsed
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Region(str, Enum):
|
|
102
|
+
"""
|
|
103
|
+
Geographic regions for manufacturer categorization.
|
|
104
|
+
|
|
105
|
+
Maps to ISO 3166-1 country codes where applicable, with custom
|
|
106
|
+
regional groupings (EU, APAC, LATAM).
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
USA = "USA"
|
|
110
|
+
EU = "EU"
|
|
111
|
+
DE = "DE"
|
|
112
|
+
UK = "UK"
|
|
113
|
+
CHINA = "CHINA"
|
|
114
|
+
APAC = "APAC"
|
|
115
|
+
LATAM = "LATAM"
|
|
116
|
+
AUS = "AUS"
|
|
117
|
+
JP = "JP"
|
|
118
|
+
KR = "KR"
|
|
119
|
+
OTHER = "OTHER"
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Copyright 2025 Asher Informatics PBC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Regulatory Framework schemas for multi-jurisdiction support.
|
|
17
|
+
|
|
18
|
+
Regulatory frameworks represent specific approval pathways within
|
|
19
|
+
each jurisdiction (510(k), PMA, CE Mark, ARTG, etc.).
|
|
20
|
+
|
|
21
|
+
Migrated from: KB src/app/schemas/regulatory_framework_schema.py
|
|
22
|
+
JIRA: ASHKBAPP-28 (Phase 2.1)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
from pydantic import Field
|
|
29
|
+
|
|
30
|
+
from ashmatics_datamodels.common.base import AshMaticsBaseModel, TimestampedModel
|
|
31
|
+
from ashmatics_datamodels.common.regulators import RegulatorSummary
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RegulatoryFrameworkBase(AshMaticsBaseModel):
|
|
35
|
+
"""
|
|
36
|
+
Base schema for regulatory frameworks.
|
|
37
|
+
|
|
38
|
+
A regulatory framework represents a specific pathway for device
|
|
39
|
+
authorization (e.g., FDA 510(k), FDA PMA, EU CE Mark, TGA ARTG).
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
framework_code: str = Field(
|
|
43
|
+
...,
|
|
44
|
+
max_length=50,
|
|
45
|
+
description="Unique framework code (e.g., '510K', 'PMA', 'CE_MARK', 'ARTG')",
|
|
46
|
+
)
|
|
47
|
+
framework_name: str = Field(
|
|
48
|
+
...,
|
|
49
|
+
max_length=255,
|
|
50
|
+
description="Full framework name (e.g., 'Premarket Notification 510(k)')",
|
|
51
|
+
)
|
|
52
|
+
authorization_type: str = Field(
|
|
53
|
+
...,
|
|
54
|
+
max_length=50,
|
|
55
|
+
description="Type: clearance, approval, registration, notification, self_certification",
|
|
56
|
+
)
|
|
57
|
+
requires_premarket_review: bool = Field(
|
|
58
|
+
...,
|
|
59
|
+
description="Whether premarket review by regulator is required",
|
|
60
|
+
)
|
|
61
|
+
requires_clinical_data: bool = Field(
|
|
62
|
+
False,
|
|
63
|
+
description="Whether clinical data submission is required",
|
|
64
|
+
)
|
|
65
|
+
description: Optional[str] = Field(
|
|
66
|
+
None,
|
|
67
|
+
description="Detailed framework description",
|
|
68
|
+
)
|
|
69
|
+
typical_review_time_days: Optional[int] = Field(
|
|
70
|
+
None,
|
|
71
|
+
ge=0,
|
|
72
|
+
description="Typical review time in days",
|
|
73
|
+
)
|
|
74
|
+
renewal_required: bool = Field(
|
|
75
|
+
False,
|
|
76
|
+
description="Whether periodic renewal is required",
|
|
77
|
+
)
|
|
78
|
+
renewal_frequency_years: Optional[int] = Field(
|
|
79
|
+
None,
|
|
80
|
+
ge=1,
|
|
81
|
+
description="Renewal frequency in years (if applicable)",
|
|
82
|
+
)
|
|
83
|
+
is_active: bool = Field(
|
|
84
|
+
True,
|
|
85
|
+
description="Whether framework is currently active",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class RegulatoryFrameworkCreate(RegulatoryFrameworkBase):
|
|
90
|
+
"""Schema for creating a new regulatory framework."""
|
|
91
|
+
|
|
92
|
+
regulator_id: int = Field(
|
|
93
|
+
...,
|
|
94
|
+
gt=0,
|
|
95
|
+
description="Foreign key to regulators",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class RegulatoryFrameworkUpdate(AshMaticsBaseModel):
|
|
100
|
+
"""Schema for updating an existing regulatory framework."""
|
|
101
|
+
|
|
102
|
+
model_config = {"extra": "ignore"}
|
|
103
|
+
|
|
104
|
+
framework_name: Optional[str] = Field(None, max_length=255)
|
|
105
|
+
authorization_type: Optional[str] = Field(None, max_length=50)
|
|
106
|
+
requires_premarket_review: Optional[bool] = None
|
|
107
|
+
requires_clinical_data: Optional[bool] = None
|
|
108
|
+
description: Optional[str] = None
|
|
109
|
+
typical_review_time_days: Optional[int] = None
|
|
110
|
+
renewal_required: Optional[bool] = None
|
|
111
|
+
renewal_frequency_years: Optional[int] = None
|
|
112
|
+
is_active: Optional[bool] = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class RegulatoryFrameworkResponse(RegulatoryFrameworkBase, TimestampedModel):
|
|
116
|
+
"""
|
|
117
|
+
Schema for regulatory framework responses.
|
|
118
|
+
|
|
119
|
+
Includes nested regulator information and computed fields.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
id: Optional[int] = Field(None, description="Framework ID")
|
|
123
|
+
regulator_id: int = Field(..., description="Foreign key to regulators")
|
|
124
|
+
|
|
125
|
+
# Nested regulator information
|
|
126
|
+
regulator: Optional[RegulatorSummary] = Field(
|
|
127
|
+
None, description="Regulator details"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Computed fields
|
|
131
|
+
authorization_count: int = Field(
|
|
132
|
+
0, description="Number of authorizations under this framework"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class RegulatoryFrameworkSummary(AshMaticsBaseModel):
|
|
137
|
+
"""Minimal framework information for nested responses."""
|
|
138
|
+
|
|
139
|
+
id: int
|
|
140
|
+
framework_code: str
|
|
141
|
+
framework_name: str
|
|
142
|
+
authorization_type: str
|
|
143
|
+
regulator_code: Optional[str] = None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class RegulatoryFrameworkStats(AshMaticsBaseModel):
|
|
147
|
+
"""Schema for regulatory framework statistics."""
|
|
148
|
+
|
|
149
|
+
total_frameworks: int = Field(..., description="Total number of frameworks")
|
|
150
|
+
active_frameworks: int = Field(..., description="Number of active frameworks")
|
|
151
|
+
by_authorization_type: dict[str, int] = Field(
|
|
152
|
+
default_factory=dict, description="Count by authorization type"
|
|
153
|
+
)
|
|
154
|
+
by_regulator: dict[str, int] = Field(
|
|
155
|
+
default_factory=dict, description="Count by regulator"
|
|
156
|
+
)
|
|
157
|
+
requiring_premarket_review: int = Field(
|
|
158
|
+
0, description="Frameworks requiring premarket review"
|
|
159
|
+
)
|
|
160
|
+
requiring_clinical_data: int = Field(
|
|
161
|
+
0, description="Frameworks requiring clinical data"
|
|
162
|
+
)
|
|
163
|
+
requiring_renewal: int = Field(
|
|
164
|
+
0, description="Frameworks requiring periodic renewal"
|
|
165
|
+
)
|
|
166
|
+
avg_review_time_days: Optional[float] = Field(
|
|
167
|
+
None, description="Average review time in days"
|
|
168
|
+
)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright 2025 Asher Informatics PBC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Regulator schemas for multi-jurisdiction support.
|
|
17
|
+
|
|
18
|
+
Regulators are the governing bodies that oversee medical device approval
|
|
19
|
+
in their respective jurisdictions (FDA, EMA, TGA, MHRA, etc.).
|
|
20
|
+
|
|
21
|
+
Migrated from: KB src/app/schemas/regulator_schema.py
|
|
22
|
+
JIRA: ASHKBAPP-28 (Phase 2.1)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
from pydantic import Field, HttpUrl, field_validator
|
|
29
|
+
|
|
30
|
+
from ashmatics_datamodels.common.base import AshMaticsBaseModel, TimestampedModel
|
|
31
|
+
from ashmatics_datamodels.common.validators import validate_country_code
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RegulatorBase(AshMaticsBaseModel):
|
|
35
|
+
"""
|
|
36
|
+
Base schema for regulatory authorities.
|
|
37
|
+
|
|
38
|
+
Represents governing bodies like FDA (US), EMA (EU), TGA (Australia),
|
|
39
|
+
MHRA (UK), Health Canada, PMDA (Japan), etc.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
code: str = Field(
|
|
43
|
+
...,
|
|
44
|
+
max_length=20,
|
|
45
|
+
description="Regulator code (e.g., FDA, EMA, TGA, MHRA, PMDA)",
|
|
46
|
+
)
|
|
47
|
+
name: str = Field(
|
|
48
|
+
...,
|
|
49
|
+
max_length=255,
|
|
50
|
+
description="Short official name (e.g., 'Food and Drug Administration')",
|
|
51
|
+
)
|
|
52
|
+
full_name: Optional[str] = Field(
|
|
53
|
+
None,
|
|
54
|
+
max_length=500,
|
|
55
|
+
description="Complete official name",
|
|
56
|
+
)
|
|
57
|
+
country_code: Optional[str] = Field(
|
|
58
|
+
None,
|
|
59
|
+
max_length=2,
|
|
60
|
+
description="ISO 3166-1 alpha-2 country code (e.g., 'US', 'DE')",
|
|
61
|
+
)
|
|
62
|
+
region: Optional[str] = Field(
|
|
63
|
+
None,
|
|
64
|
+
max_length=50,
|
|
65
|
+
description="Geographic region (e.g., 'North America', 'EU', 'Asia-Pacific')",
|
|
66
|
+
)
|
|
67
|
+
website: Optional[str] = Field(
|
|
68
|
+
None,
|
|
69
|
+
max_length=500,
|
|
70
|
+
description="Official website URL",
|
|
71
|
+
)
|
|
72
|
+
api_endpoint: Optional[str] = Field(
|
|
73
|
+
None,
|
|
74
|
+
max_length=500,
|
|
75
|
+
description="API endpoint for data integration (e.g., OpenFDA)",
|
|
76
|
+
)
|
|
77
|
+
is_active: bool = Field(
|
|
78
|
+
True,
|
|
79
|
+
description="Whether regulator is currently active",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@field_validator("country_code")
|
|
83
|
+
@classmethod
|
|
84
|
+
def validate_country(cls, v: Optional[str]) -> Optional[str]:
|
|
85
|
+
return validate_country_code(v)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class RegulatorCreate(RegulatorBase):
|
|
89
|
+
"""Schema for creating a new regulator."""
|
|
90
|
+
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class RegulatorUpdate(AshMaticsBaseModel):
|
|
95
|
+
"""Schema for updating an existing regulator (all fields optional)."""
|
|
96
|
+
|
|
97
|
+
model_config = {"extra": "ignore"} # Allow partial updates
|
|
98
|
+
|
|
99
|
+
code: Optional[str] = Field(None, max_length=20)
|
|
100
|
+
name: Optional[str] = Field(None, max_length=255)
|
|
101
|
+
full_name: Optional[str] = Field(None, max_length=500)
|
|
102
|
+
country_code: Optional[str] = Field(None, max_length=2)
|
|
103
|
+
region: Optional[str] = Field(None, max_length=50)
|
|
104
|
+
website: Optional[str] = None
|
|
105
|
+
api_endpoint: Optional[str] = None
|
|
106
|
+
is_active: Optional[bool] = None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class RegulatorResponse(RegulatorBase, TimestampedModel):
|
|
110
|
+
"""
|
|
111
|
+
Schema for regulator responses.
|
|
112
|
+
|
|
113
|
+
Includes computed fields for framework and classification counts.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
id: Optional[int] = Field(None, description="Regulator ID")
|
|
117
|
+
|
|
118
|
+
# Computed fields (populated by endpoint logic)
|
|
119
|
+
framework_count: int = Field(
|
|
120
|
+
0, description="Number of regulatory frameworks under this regulator"
|
|
121
|
+
)
|
|
122
|
+
classification_system_count: int = Field(
|
|
123
|
+
0, description="Number of classification systems managed"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class RegulatorSummary(AshMaticsBaseModel):
|
|
128
|
+
"""Minimal regulator information for nested responses."""
|
|
129
|
+
|
|
130
|
+
id: int
|
|
131
|
+
code: str
|
|
132
|
+
name: str
|
|
133
|
+
region: Optional[str] = None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class RegulatorStats(AshMaticsBaseModel):
|
|
137
|
+
"""Schema for regulator statistics."""
|
|
138
|
+
|
|
139
|
+
total_regulators: int = Field(..., description="Total number of regulators")
|
|
140
|
+
active_regulators: int = Field(..., description="Number of active regulators")
|
|
141
|
+
by_region: dict[str, int] = Field(
|
|
142
|
+
default_factory=dict, description="Count by region"
|
|
143
|
+
)
|
|
144
|
+
by_country: dict[str, int] = Field(
|
|
145
|
+
default_factory=dict, description="Count by country code"
|
|
146
|
+
)
|
|
147
|
+
with_frameworks: int = Field(
|
|
148
|
+
0, description="Regulators with at least one framework"
|
|
149
|
+
)
|
|
150
|
+
with_classification_systems: int = Field(
|
|
151
|
+
0, description="Regulators with at least one classification system"
|
|
152
|
+
)
|