maleo-identity 0.0.96__py3-none-any.whl → 0.0.99__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.

Potentially problematic release.


This version of maleo-identity might be problematic. Click here for more details.

Files changed (57) hide show
  1. maleo/identity/constants/api_key.py +13 -0
  2. maleo/identity/constants/organization.py +0 -14
  3. maleo/identity/constants/organization_registration_code.py +0 -15
  4. maleo/identity/constants/organization_relation.py +13 -0
  5. maleo/identity/constants/patient.py +7 -0
  6. maleo/identity/constants/user.py +0 -15
  7. maleo/identity/constants/user_medical_role.py +13 -0
  8. maleo/identity/constants/user_organization_role.py +13 -0
  9. maleo/identity/constants/user_profile.py +0 -15
  10. maleo/identity/constants/user_system_role.py +13 -0
  11. maleo/identity/dtos.py +295 -0
  12. maleo/identity/enums/api_key.py +13 -0
  13. maleo/identity/enums/organization.py +5 -6
  14. maleo/identity/enums/organization_relation.py +12 -0
  15. maleo/identity/enums/patient.py +22 -0
  16. maleo/identity/enums/user.py +10 -6
  17. maleo/identity/enums/user_medical_role.py +12 -0
  18. maleo/identity/enums/user_organization_role.py +12 -0
  19. maleo/identity/enums/user_profile.py +9 -0
  20. maleo/identity/enums/user_system_role.py +12 -0
  21. maleo/identity/mixins/api_key.py +6 -0
  22. maleo/identity/mixins/common.py +29 -0
  23. maleo/identity/mixins/organization_relation.py +14 -0
  24. maleo/identity/mixins/patient.py +8 -0
  25. maleo/identity/mixins/user_profile.py +4 -21
  26. maleo/identity/models.py +89 -95
  27. maleo/identity/schemas/api_key.py +128 -0
  28. maleo/identity/schemas/common.py +152 -120
  29. maleo/identity/schemas/organization.py +39 -6
  30. maleo/identity/schemas/organization_registration_code.py +14 -5
  31. maleo/identity/schemas/organization_relation.py +178 -0
  32. maleo/identity/schemas/patient.py +234 -0
  33. maleo/identity/schemas/user.py +19 -4
  34. maleo/identity/schemas/user_medical_role.py +181 -0
  35. maleo/identity/schemas/user_organization_role.py +181 -0
  36. maleo/identity/schemas/user_profile.py +68 -10
  37. maleo/identity/schemas/user_system_role.py +174 -0
  38. maleo/identity/types/api_key.py +7 -0
  39. maleo/identity/types/organization.py +1 -2
  40. maleo/identity/types/organization_registration_code.py +1 -2
  41. maleo/identity/types/organization_relation.py +7 -0
  42. maleo/identity/types/patient.py +4 -0
  43. maleo/identity/types/user.py +1 -2
  44. maleo/identity/types/user_medical_role.py +7 -0
  45. maleo/identity/types/user_organization_role.py +7 -0
  46. maleo/identity/types/user_profile.py +1 -2
  47. maleo/identity/types/user_system_role.py +7 -0
  48. maleo/identity/utils/organization.py +33 -27
  49. maleo/identity/utils/user.py +33 -27
  50. maleo/identity/validators/__init__.py +0 -0
  51. maleo/identity/validators/patient.py +6 -0
  52. {maleo_identity-0.0.96.dist-info → maleo_identity-0.0.99.dist-info}/METADATA +2 -1
  53. maleo_identity-0.0.99.dist-info/RECORD +67 -0
  54. maleo_identity-0.0.96.dist-info/RECORD +0 -36
  55. {maleo_identity-0.0.96.dist-info → maleo_identity-0.0.99.dist-info}/WHEEL +0 -0
  56. {maleo_identity-0.0.96.dist-info → maleo_identity-0.0.99.dist-info}/licenses/LICENSE +0 -0
  57. {maleo_identity-0.0.96.dist-info → maleo_identity-0.0.99.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,8 @@
1
1
  from pydantic import BaseModel, Field
2
2
  from typing import Annotated, Generic
3
- from maleo.types.datetime import OptDateT
4
3
  from maleo.types.string import OptStrT
5
4
 
6
5
 
7
- class IdCard(BaseModel, Generic[OptStrT]):
8
- id_card: Annotated[OptStrT, Field(..., description="User's Id Card", max_length=16)]
9
-
10
-
11
6
  class LeadingTitle(BaseModel, Generic[OptStrT]):
12
7
  leading_title: Annotated[
13
8
  OptStrT, Field(..., description="User's Leading Title", max_length=25)
@@ -38,21 +33,9 @@ class EndingTitle(BaseModel, Generic[OptStrT]):
38
33
  ]
39
34
 
40
35
 
41
- class FullName(BaseModel, Generic[OptStrT]):
42
- full_name: Annotated[
43
- OptStrT, Field(..., description="User's Full Name", max_length=200)
44
- ]
45
-
46
-
47
- class BirthPlace(BaseModel, Generic[OptStrT]):
48
- birth_place: Annotated[
49
- OptStrT, Field(..., description="User's Birth Place", max_length=50)
50
- ]
51
-
52
-
53
- class BirthDate(BaseModel, Generic[OptDateT]):
54
- birth_date: Annotated[OptDateT, Field(..., description="User's birth date")]
55
-
56
-
57
36
  class AvatarName(BaseModel, Generic[OptStrT]):
58
37
  avatar_name: Annotated[OptStrT, Field(..., description="User's Avatar Name")]
38
+
39
+
40
+ class AvatarUrl(BaseModel, Generic[OptStrT]):
41
+ avatar_url: Annotated[OptStrT, Field(..., description="User's Avatar URL")]
maleo/identity/models.py CHANGED
@@ -1,16 +1,28 @@
1
1
  from datetime import date
2
- from sqlalchemy import ForeignKey, UniqueConstraint, and_
3
- from sqlalchemy.dialects.postgresql import UUID as PostgreSQLUUID
4
- from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship
5
- from sqlalchemy.types import Date, Enum, Integer, String, Text
2
+ from sqlalchemy import CheckConstraint, ForeignKey, UniqueConstraint
3
+ from sqlalchemy.dialects.postgresql import JSONB, UUID as PostgreSQLUUID
4
+ from sqlalchemy.orm import Mapped, mapped_column
5
+ from sqlalchemy.types import Boolean, Date, Enum, Integer, String, Text
6
6
  from uuid import UUID, uuid4
7
- from maleo.enums.identity import BloodType, OptBloodType, Gender, OptGender
8
- from maleo.enums.organization import OrganizationType, OrganizationRole
7
+ from maleo.enums.identity import (
8
+ BloodType,
9
+ OptBloodType,
10
+ Rhesus,
11
+ OptRhesus,
12
+ Gender,
13
+ OptGender,
14
+ )
15
+ from maleo.enums.organization import (
16
+ OrganizationType,
17
+ OrganizationRelation as OrganizationRelationEnum,
18
+ OrganizationRole,
19
+ )
9
20
  from maleo.enums.medical import MedicalRole
10
21
  from maleo.enums.system import SystemRole
11
22
  from maleo.enums.user import UserType
12
23
  from maleo.schemas.model import DataIdentifier, DataStatus, DataTimestamp
13
24
  from maleo.types.integer import OptInt
25
+ from maleo.types.misc import OptListOfAnyOrStrToAnyDict
14
26
  from maleo.types.string import OptStr
15
27
 
16
28
 
@@ -40,6 +52,77 @@ class APIKey(
40
52
  )
41
53
 
42
54
 
55
+ class Patient(
56
+ DataStatus,
57
+ DataTimestamp,
58
+ DataIdentifier,
59
+ ):
60
+ __tablename__ = "patients"
61
+ organization_id: Mapped[int] = mapped_column(
62
+ Integer,
63
+ ForeignKey("organizations.id", ondelete="CASCADE", onupdate="CASCADE"),
64
+ nullable=False,
65
+ )
66
+ id_card: Mapped[OptStr] = mapped_column(
67
+ name="id_card", type_=String(16), unique=True
68
+ )
69
+ passport: Mapped[OptStr] = mapped_column(
70
+ name="passport", type_=String(9), unique=True
71
+ )
72
+ name: Mapped[str] = mapped_column(name="name", type_=String(200), nullable=False)
73
+ place_of_birth: Mapped[OptStr] = mapped_column(
74
+ name="place_of_birth", type_=String(50)
75
+ )
76
+ date_of_birth: Mapped[date] = mapped_column(
77
+ name="date_of_birth", type_=Date, nullable=False
78
+ )
79
+ gender: Mapped[Gender] = mapped_column(
80
+ name="gender", type_=Enum(Gender, name="gender"), nullable=False
81
+ )
82
+ blood_type: Mapped[OptBloodType] = mapped_column(
83
+ name="blood_type", type_=Enum(BloodType, name="blood_type")
84
+ )
85
+ rhesus: Mapped[OptRhesus] = mapped_column(
86
+ name="rhesus", type_=Enum(Rhesus, name="rhesus")
87
+ )
88
+
89
+ __table_args__ = (
90
+ CheckConstraint(
91
+ "id_card IS NOT NULL OR passport IS NOT NULL",
92
+ name="chk_id_card_or_passport",
93
+ ),
94
+ )
95
+
96
+
97
+ class OrganizationRelation(
98
+ DataStatus,
99
+ DataTimestamp,
100
+ DataIdentifier,
101
+ ):
102
+ __tablename__ = "organization_relations"
103
+ source_id: Mapped[int] = mapped_column(
104
+ Integer,
105
+ ForeignKey("organizations.id", ondelete="CASCADE", onupdate="CASCADE"),
106
+ nullable=False,
107
+ )
108
+ target_id: Mapped[int] = mapped_column(
109
+ Integer,
110
+ ForeignKey("organizations.id", ondelete="CASCADE", onupdate="CASCADE"),
111
+ nullable=False,
112
+ )
113
+ relation: Mapped[OrganizationRelationEnum] = mapped_column(
114
+ name="relation",
115
+ type_=Enum(OrganizationRelationEnum, name="organization_relation"),
116
+ nullable=False,
117
+ )
118
+ is_bidirectional: Mapped[bool] = mapped_column(
119
+ name="is_bidirectional", type_=Boolean, default=False, nullable=False
120
+ )
121
+ metadata: Mapped[OptListOfAnyOrStrToAnyDict] = mapped_column(
122
+ name="metadata", type_=JSONB, default=None
123
+ )
124
+
125
+
43
126
  class OrganizationRegistrationCode(
44
127
  DataStatus,
45
128
  DataTimestamp,
@@ -62,15 +145,6 @@ class OrganizationRegistrationCode(
62
145
  name="current_uses", type_=Integer, nullable=False, default=0
63
146
  )
64
147
 
65
- # Relationship
66
- @declared_attr
67
- def organization(cls) -> Mapped["Organization"]:
68
- return relationship(
69
- "Organization",
70
- back_populates="registration_code",
71
- uselist=False, # 👈 ensures one-to-one, not one-to-many
72
- )
73
-
74
148
 
75
149
  class Organization(
76
150
  DataStatus,
@@ -96,24 +170,6 @@ class Organization(
96
170
  nullable=False,
97
171
  )
98
172
 
99
- # Relationship
100
- @declared_attr
101
- def registration_code(cls) -> Mapped["OrganizationRegistrationCode | None"]:
102
- return relationship(
103
- "OrganizationRegistrationCode",
104
- back_populates="organization",
105
- uselist=False,
106
- cascade="all, delete-orphan",
107
- )
108
-
109
- @declared_attr
110
- def users(cls) -> Mapped[list["UserOrganization"]]:
111
- return relationship(
112
- "UserOrganization",
113
- back_populates="organization",
114
- cascade="all, delete-orphan",
115
- )
116
-
117
173
 
118
174
  class UserMedicalRole(DataStatus, DataTimestamp, DataIdentifier):
119
175
  __tablename__ = "user_medical_roles"
@@ -206,11 +262,6 @@ class UserProfile(
206
262
  name="avatar_name", type_=Text, nullable=False
207
263
  )
208
264
 
209
- # Relationships
210
- @declared_attr
211
- def user(cls) -> Mapped["User"]:
212
- return relationship("User", back_populates="profile")
213
-
214
265
 
215
266
  class UserSystemRole(
216
267
  DataStatus,
@@ -234,11 +285,6 @@ class UserSystemRole(
234
285
  UniqueConstraint("user_id", "system_role", name="uq_user_system_role"),
235
286
  )
236
287
 
237
- # Relationships
238
- @declared_attr
239
- def user(cls) -> Mapped["User"]:
240
- return relationship("User", back_populates="system_roles")
241
-
242
288
 
243
289
  class User(
244
290
  DataStatus,
@@ -263,24 +309,6 @@ class User(
263
309
  name="password", type_=String(255), nullable=False
264
310
  )
265
311
 
266
- @declared_attr
267
- def organizations(cls) -> Mapped[list["UserOrganization"]]:
268
- return relationship(
269
- "UserOrganization", back_populates="user", cascade="all, delete-orphan"
270
- )
271
-
272
- @declared_attr
273
- def profile(cls) -> Mapped["UserProfile | None"]:
274
- return relationship(
275
- "UserProfile", back_populates="user", cascade="all, delete-orphan"
276
- )
277
-
278
- @declared_attr
279
- def system_roles(cls) -> Mapped["UserSystemRole | None"]:
280
- return relationship(
281
- "UserSystemRole", back_populates="user", cascade="all, delete-orphan"
282
- )
283
-
284
312
 
285
313
  class UserOrganization(
286
314
  DataStatus,
@@ -303,37 +331,3 @@ class UserOrganization(
303
331
  __table_args__ = (
304
332
  UniqueConstraint("user_id", "organization_id", name="uq_user_organization"),
305
333
  )
306
-
307
- # Relationships
308
- @declared_attr
309
- def user(cls) -> Mapped["User"]:
310
- return relationship("User", back_populates="organizations")
311
-
312
- @declared_attr
313
- def organization(cls) -> Mapped["Organization"]:
314
- return relationship("Organization", back_populates="users")
315
-
316
- @declared_attr
317
- def organization_roles(cls) -> Mapped[list["UserOrganizationRole"]]:
318
- return relationship(
319
- "UserOrganizationRole",
320
- primaryjoin=and_(
321
- UserOrganization.user_id == UserOrganizationRole.user_id,
322
- UserOrganization.organization_id
323
- == UserOrganizationRole.organization_id,
324
- ),
325
- viewonly=True,
326
- lazy="selectin",
327
- )
328
-
329
- @declared_attr
330
- def medical_roles(cls) -> Mapped[list["UserMedicalRole"]]:
331
- return relationship(
332
- "UserMedicalRole",
333
- primaryjoin=and_(
334
- UserOrganization.user_id == UserMedicalRole.user_id,
335
- UserOrganization.organization_id == UserMedicalRole.organization_id,
336
- ),
337
- viewonly=True,
338
- lazy="selectin",
339
- )
@@ -0,0 +1,128 @@
1
+ from typing import Literal, Tuple, overload
2
+ from uuid import UUID
3
+ from maleo.enums.status import (
4
+ ListOfDataStatuses,
5
+ FULL_DATA_STATUSES,
6
+ )
7
+ from maleo.schemas.mixins.filter import convert as convert_filter
8
+ from maleo.schemas.mixins.identity import (
9
+ Ids,
10
+ UUIDs,
11
+ IntUserId,
12
+ IntUserIds,
13
+ IntOrganizationId,
14
+ IntOrganizationIds,
15
+ )
16
+ from maleo.schemas.mixins.sort import convert as convert_sort
17
+ from maleo.schemas.parameter import (
18
+ ReadSingleParameter as BaseReadSingleParameter,
19
+ ReadPaginatedMultipleParameter,
20
+ DeleteSingleParameter as BaseDeleteSingleParameter,
21
+ )
22
+ from maleo.types.dict import StrToAnyDict
23
+ from maleo.types.integer import OptInt, OptListOfInts
24
+ from maleo.types.uuid import OptListOfUUIDs
25
+ from ..enums.api_key import IdentifierType
26
+ from ..types.api_key import IdentifierValueType
27
+
28
+
29
+ class CreateParameter(
30
+ IntOrganizationId[OptInt],
31
+ IntUserId[int],
32
+ ):
33
+ pass
34
+
35
+
36
+ class ReadMultipleParameter(
37
+ ReadPaginatedMultipleParameter,
38
+ IntOrganizationIds[OptListOfInts],
39
+ IntUserIds[OptListOfInts],
40
+ UUIDs[OptListOfUUIDs],
41
+ Ids[OptListOfInts],
42
+ ):
43
+ @property
44
+ def _query_param_fields(self) -> set[str]:
45
+ return {
46
+ "ids",
47
+ "uuids",
48
+ "statuses",
49
+ "user_ids",
50
+ "organization_ids",
51
+ "search",
52
+ "page",
53
+ "limit",
54
+ "use_cache",
55
+ }
56
+
57
+ def to_query_params(self) -> StrToAnyDict:
58
+ params = self.model_dump(
59
+ mode="json", include=self._query_param_fields, exclude_none=True
60
+ )
61
+ params["filters"] = convert_filter(self.date_filters)
62
+ params["sorts"] = convert_sort(self.sort_columns)
63
+ params = {k: v for k, v in params.items()}
64
+ return params
65
+
66
+
67
+ class ReadSingleParameter(BaseReadSingleParameter[IdentifierType, IdentifierValueType]):
68
+ @overload
69
+ @classmethod
70
+ def new(
71
+ cls,
72
+ identifier_type: Literal[IdentifierType.ID],
73
+ identifier_value: int,
74
+ statuses: ListOfDataStatuses = list(FULL_DATA_STATUSES),
75
+ use_cache: bool = True,
76
+ ) -> "ReadSingleParameter": ...
77
+ @overload
78
+ @classmethod
79
+ def new(
80
+ cls,
81
+ identifier_type: Literal[IdentifierType.UUID],
82
+ identifier_value: UUID,
83
+ statuses: ListOfDataStatuses = list(FULL_DATA_STATUSES),
84
+ use_cache: bool = True,
85
+ ) -> "ReadSingleParameter": ...
86
+ @overload
87
+ @classmethod
88
+ def new(
89
+ cls,
90
+ identifier_type: Literal[IdentifierType.COMPOSITE],
91
+ identifier_value: Tuple[int, OptInt],
92
+ statuses: ListOfDataStatuses = list(FULL_DATA_STATUSES),
93
+ use_cache: bool = True,
94
+ ) -> "ReadSingleParameter": ...
95
+ @overload
96
+ @classmethod
97
+ def new(
98
+ cls,
99
+ identifier_type: IdentifierType,
100
+ identifier_value: IdentifierValueType,
101
+ statuses: ListOfDataStatuses = list(FULL_DATA_STATUSES),
102
+ use_cache: bool = True,
103
+ ) -> "ReadSingleParameter": ...
104
+ @classmethod
105
+ def new(
106
+ cls,
107
+ identifier_type: IdentifierType,
108
+ identifier_value: IdentifierValueType,
109
+ statuses: ListOfDataStatuses = list(FULL_DATA_STATUSES),
110
+ use_cache: bool = True,
111
+ ) -> "ReadSingleParameter":
112
+ return cls(
113
+ identifier_type=identifier_type,
114
+ identifier_value=identifier_value,
115
+ statuses=statuses,
116
+ use_cache=use_cache,
117
+ )
118
+
119
+ def to_query_params(self) -> StrToAnyDict:
120
+ return self.model_dump(
121
+ mode="json", include={"statuses", "use_cache"}, exclude_none=True
122
+ )
123
+
124
+
125
+ class DeleteSingleParameter(
126
+ BaseDeleteSingleParameter[IdentifierType, IdentifierValueType]
127
+ ):
128
+ pass