f3-data-models 0.4.2__py3-none-any.whl → 0.4.4__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.
- f3_data_models/models.py +138 -150
- f3_data_models/testing.py +4 -25
- f3_data_models/utils.py +26 -69
- {f3_data_models-0.4.2.dist-info → f3_data_models-0.4.4.dist-info}/METADATA +2 -1
- f3_data_models-0.4.4.dist-info/RECORD +7 -0
- {f3_data_models-0.4.2.dist-info → f3_data_models-0.4.4.dist-info}/WHEEL +1 -1
- f3_data_models-0.4.2.dist-info/RECORD +0 -7
f3_data_models/models.py
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
1
|
+
import enum
|
2
|
+
from datetime import date, datetime, time
|
2
3
|
from typing import Any, Dict, List, Optional
|
4
|
+
|
5
|
+
from citext import CIText
|
3
6
|
from sqlalchemy import (
|
4
7
|
ARRAY,
|
5
8
|
JSON,
|
@@ -9,25 +12,24 @@ from sqlalchemy import (
|
|
9
12
|
VARCHAR,
|
10
13
|
Boolean,
|
11
14
|
DateTime,
|
15
|
+
Enum,
|
12
16
|
Float,
|
13
17
|
ForeignKey,
|
14
18
|
Index,
|
15
19
|
Integer,
|
16
|
-
func,
|
17
20
|
UniqueConstraint,
|
18
|
-
Enum,
|
19
21
|
Uuid,
|
22
|
+
func,
|
20
23
|
inspect,
|
21
24
|
)
|
22
|
-
from typing_extensions import Annotated
|
23
25
|
from sqlalchemy.orm import (
|
24
26
|
DeclarativeBase,
|
25
|
-
mapped_column,
|
26
27
|
Mapped,
|
28
|
+
mapped_column,
|
27
29
|
relationship,
|
28
30
|
)
|
29
31
|
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
30
|
-
import
|
32
|
+
from typing_extensions import Annotated
|
31
33
|
|
32
34
|
# Custom Annotations
|
33
35
|
time_notz = Annotated[time, TIME(timezone=False)]
|
@@ -35,9 +37,7 @@ time_with_tz = Annotated[time, TIME(timezone=True)]
|
|
35
37
|
ts_notz = Annotated[datetime, DateTime(timezone=False)]
|
36
38
|
text = Annotated[str, TEXT]
|
37
39
|
intpk = Annotated[int, mapped_column(Integer, primary_key=True, autoincrement=True)]
|
38
|
-
dt_create = Annotated[
|
39
|
-
datetime, mapped_column(DateTime, server_default=func.timezone("utc", func.now()))
|
40
|
-
]
|
40
|
+
dt_create = Annotated[datetime, mapped_column(DateTime, server_default=func.timezone("utc", func.now()))]
|
41
41
|
dt_update = Annotated[
|
42
42
|
datetime,
|
43
43
|
mapped_column(
|
@@ -49,30 +49,70 @@ dt_update = Annotated[
|
|
49
49
|
|
50
50
|
|
51
51
|
class User_Status(enum.Enum):
|
52
|
+
"""
|
53
|
+
Enum representing the status of a user.
|
54
|
+
|
55
|
+
Attributes:
|
56
|
+
active
|
57
|
+
inactive
|
58
|
+
deleted
|
59
|
+
"""
|
60
|
+
|
52
61
|
active = 1
|
53
62
|
inactive = 2
|
54
63
|
deleted = 3
|
55
64
|
|
56
65
|
|
57
66
|
class Region_Role(enum.Enum):
|
67
|
+
"""
|
68
|
+
Enum representing the roles within a region.
|
69
|
+
|
70
|
+
Attributes:
|
71
|
+
user
|
72
|
+
editor
|
73
|
+
admin
|
74
|
+
"""
|
75
|
+
|
58
76
|
user = 1
|
59
77
|
editor = 2
|
60
78
|
admin = 3
|
61
79
|
|
62
80
|
|
63
81
|
class User_Role(enum.Enum):
|
82
|
+
"""
|
83
|
+
Enum representing the roles of a user.
|
84
|
+
|
85
|
+
Attributes:
|
86
|
+
user
|
87
|
+
editor
|
88
|
+
admin
|
89
|
+
"""
|
90
|
+
|
64
91
|
user = 1
|
65
92
|
editor = 2
|
66
93
|
admin = 3
|
67
94
|
|
68
95
|
|
69
96
|
class Update_Request_Status(enum.Enum):
|
97
|
+
"""
|
98
|
+
Enum representing the status of an update request.
|
99
|
+
|
100
|
+
Attributes:
|
101
|
+
pending
|
102
|
+
approved
|
103
|
+
rejected
|
104
|
+
"""
|
105
|
+
|
70
106
|
pending = 1
|
71
107
|
approved = 2
|
72
108
|
rejected = 3
|
73
109
|
|
74
110
|
|
75
111
|
class Day_Of_Week(enum.Enum):
|
112
|
+
"""
|
113
|
+
Enum representing the days of the week.
|
114
|
+
"""
|
115
|
+
|
76
116
|
monday = 0
|
77
117
|
tuesday = 1
|
78
118
|
wednesday = 2
|
@@ -83,11 +123,30 @@ class Day_Of_Week(enum.Enum):
|
|
83
123
|
|
84
124
|
|
85
125
|
class Event_Cadence(enum.Enum):
|
126
|
+
"""
|
127
|
+
Enum representing the cadence of an event.
|
128
|
+
|
129
|
+
Attributes:
|
130
|
+
weekly
|
131
|
+
monthly
|
132
|
+
"""
|
133
|
+
|
86
134
|
weekly = 1
|
87
135
|
monthly = 2
|
88
136
|
|
89
137
|
|
90
138
|
class Org_Type(enum.Enum):
|
139
|
+
"""
|
140
|
+
Enum representing the type of organization.
|
141
|
+
|
142
|
+
Attributes:
|
143
|
+
ao
|
144
|
+
region
|
145
|
+
area
|
146
|
+
sector
|
147
|
+
nation
|
148
|
+
"""
|
149
|
+
|
91
150
|
ao = 1
|
92
151
|
region = 2
|
93
152
|
area = 3
|
@@ -96,12 +155,31 @@ class Org_Type(enum.Enum):
|
|
96
155
|
|
97
156
|
|
98
157
|
class Event_Category(enum.Enum):
|
158
|
+
"""
|
159
|
+
Enum representing the category of an event.
|
160
|
+
|
161
|
+
Attributes:
|
162
|
+
first_f
|
163
|
+
second_f
|
164
|
+
third_f
|
165
|
+
"""
|
166
|
+
|
99
167
|
first_f = 1
|
100
168
|
second_f = 2
|
101
169
|
third_f = 3
|
102
170
|
|
103
171
|
|
104
172
|
class Request_Type(enum.Enum):
|
173
|
+
"""
|
174
|
+
Enum representing the type of request.
|
175
|
+
|
176
|
+
Attributes:
|
177
|
+
create_location
|
178
|
+
create_event
|
179
|
+
edit
|
180
|
+
delete_event
|
181
|
+
"""
|
182
|
+
|
105
183
|
create_location = 1
|
106
184
|
create_event = 2
|
107
185
|
edit = 3
|
@@ -154,11 +232,7 @@ class Base(DeclarativeBase):
|
|
154
232
|
Returns:
|
155
233
|
dict: A dictionary representation of the model instance.
|
156
234
|
"""
|
157
|
-
return {
|
158
|
-
c.key: self.get(c.key)
|
159
|
-
for c in self.__table__.columns
|
160
|
-
if c.key not in ["created", "updated"]
|
161
|
-
}
|
235
|
+
return {c.key: self.get(c.key) for c in self.__table__.columns if c.key not in ["created", "updated"]}
|
162
236
|
|
163
237
|
def to_update_dict(self) -> Dict[InstrumentedAttribute, Any]:
|
164
238
|
update_dict = {}
|
@@ -174,7 +248,7 @@ class Base(DeclarativeBase):
|
|
174
248
|
related_value = getattr(self, rel.key)
|
175
249
|
if related_value is not None:
|
176
250
|
if rel.uselist:
|
177
|
-
update_dict[rel] =
|
251
|
+
update_dict[rel] = list(related_value)
|
178
252
|
print(rel, update_dict[rel])
|
179
253
|
else:
|
180
254
|
update_dict[rel] = related_value
|
@@ -284,9 +358,7 @@ class Role_x_Permission(Base):
|
|
284
358
|
__tablename__ = "roles_x_permissions"
|
285
359
|
|
286
360
|
role_id: Mapped[int] = mapped_column(ForeignKey("roles.id"), primary_key=True)
|
287
|
-
permission_id: Mapped[int] = mapped_column(
|
288
|
-
ForeignKey("permissions.id"), primary_key=True
|
289
|
-
)
|
361
|
+
permission_id: Mapped[int] = mapped_column(ForeignKey("permissions.id"), primary_key=True)
|
290
362
|
|
291
363
|
|
292
364
|
class Role_x_User_x_Org(Base):
|
@@ -335,7 +407,7 @@ class Org(Base):
|
|
335
407
|
achievements (Optional[List[Achievement]]): The achievements available within the organization.
|
336
408
|
parent_org (Optional[Org]): The parent organization.
|
337
409
|
slack_space (Optional[SlackSpace]): The associated Slack workspace.
|
338
|
-
"""
|
410
|
+
""" # noqa: E501
|
339
411
|
|
340
412
|
__tablename__ = "orgs"
|
341
413
|
|
@@ -363,9 +435,7 @@ class Org(Base):
|
|
363
435
|
Index("idx_orgs_is_active", "is_active"),
|
364
436
|
)
|
365
437
|
|
366
|
-
locations: Mapped[Optional[List["Location"]]] = relationship(
|
367
|
-
"Location", cascade="expunge"
|
368
|
-
)
|
438
|
+
locations: Mapped[Optional[List["Location"]]] = relationship("Location", cascade="expunge")
|
369
439
|
event_types: Mapped[Optional[List["EventType"]]] = relationship(
|
370
440
|
"EventType",
|
371
441
|
primaryjoin="or_(EventType.specific_org_id == Org.id, EventType.specific_org_id.is_(None))",
|
@@ -383,9 +453,7 @@ class Org(Base):
|
|
383
453
|
cascade="expunge",
|
384
454
|
primaryjoin="or_(Achievement.specific_org_id == Org.id, Achievement.specific_org_id.is_(None))",
|
385
455
|
)
|
386
|
-
parent_org: Mapped[Optional["Org"]] = relationship(
|
387
|
-
"Org", remote_side=[id], cascade="expunge"
|
388
|
-
)
|
456
|
+
parent_org: Mapped[Optional["Org"]] = relationship("Org", remote_side=[id], cascade="expunge")
|
389
457
|
slack_space: Mapped[Optional["SlackSpace"]] = relationship(
|
390
458
|
"SlackSpace", secondary="orgs_x_slack_spaces", cascade="expunge"
|
391
459
|
)
|
@@ -404,7 +472,7 @@ class EventType(Base):
|
|
404
472
|
specific_org_id (Optional[int]): The ID of the specific organization.
|
405
473
|
created (datetime): The timestamp when the record was created.
|
406
474
|
updated (datetime): The timestamp when the record was last updated.
|
407
|
-
"""
|
475
|
+
""" # noqa: E501
|
408
476
|
|
409
477
|
__tablename__ = "event_types"
|
410
478
|
|
@@ -427,14 +495,12 @@ class EventType_x_Event(Base):
|
|
427
495
|
event_type_id (int): The ID of the associated event type.
|
428
496
|
|
429
497
|
event (Event): The associated event.
|
430
|
-
"""
|
498
|
+
""" # noqa: E501
|
431
499
|
|
432
500
|
__tablename__ = "events_x_event_types"
|
433
501
|
|
434
502
|
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), primary_key=True)
|
435
|
-
event_type_id: Mapped[int] = mapped_column(
|
436
|
-
ForeignKey("event_types.id"), primary_key=True
|
437
|
-
)
|
503
|
+
event_type_id: Mapped[int] = mapped_column(ForeignKey("event_types.id"), primary_key=True)
|
438
504
|
__table_args__ = (
|
439
505
|
Index("idx_events_x_event_types_event_id", "event_id"),
|
440
506
|
Index("idx_events_x_event_types_event_type_id", "event_type_id"),
|
@@ -443,25 +509,6 @@ class EventType_x_Event(Base):
|
|
443
509
|
event: Mapped["Event"] = relationship(back_populates="event_x_event_types")
|
444
510
|
|
445
511
|
|
446
|
-
# class EventType_x_Org(Base):
|
447
|
-
# """
|
448
|
-
# Model representing the association between event types and organizations. This controls which event types are available for selection at the region level, as well as default types for each AO.
|
449
|
-
|
450
|
-
# Attributes:
|
451
|
-
# event_type_id (int): The ID of the associated event type.
|
452
|
-
# org_id (int): The ID of the associated organization.
|
453
|
-
# is_default (bool): Whether this is the default event type for the organization. Default is False.
|
454
|
-
# """
|
455
|
-
|
456
|
-
# __tablename__ = "event_types_x_org"
|
457
|
-
|
458
|
-
# event_type_id: Mapped[int] = mapped_column(
|
459
|
-
# ForeignKey("event_types.id"), primary_key=True
|
460
|
-
# )
|
461
|
-
# org_id: Mapped[int] = mapped_column(ForeignKey("orgs.id"), primary_key=True)
|
462
|
-
# is_default: Mapped[bool] = mapped_column(Boolean, default=False)
|
463
|
-
|
464
|
-
|
465
512
|
class EventTag(Base):
|
466
513
|
"""
|
467
514
|
Model representing an event tag. These are used to mark special events, such as anniversaries or special workouts.
|
@@ -496,37 +543,16 @@ class EventTag_x_Event(Base):
|
|
496
543
|
event_tag_id (int): The ID of the associated event tag.
|
497
544
|
|
498
545
|
event (Event): The associated event.
|
499
|
-
"""
|
546
|
+
""" # noqa: E501
|
500
547
|
|
501
548
|
__tablename__ = "event_tags_x_events"
|
502
549
|
|
503
550
|
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), primary_key=True)
|
504
|
-
event_tag_id: Mapped[int] = mapped_column(
|
505
|
-
ForeignKey("event_tags.id"), primary_key=True
|
506
|
-
)
|
551
|
+
event_tag_id: Mapped[int] = mapped_column(ForeignKey("event_tags.id"), primary_key=True)
|
507
552
|
|
508
553
|
event: Mapped["Event"] = relationship(back_populates="event_x_event_tags")
|
509
554
|
|
510
555
|
|
511
|
-
# class EventTag_x_Org(Base):
|
512
|
-
# """
|
513
|
-
# Model representing the association between event tags and organizations. Controls which event tags are available for selection at the region level.
|
514
|
-
|
515
|
-
# Attributes:
|
516
|
-
# event_tag_id (int): The ID of the associated event tag.
|
517
|
-
# org_id (int): The ID of the associated organization.
|
518
|
-
# color_override (Optional[str]): The calendar color override for the event tag (if the region wants to use something other than the default).
|
519
|
-
# """
|
520
|
-
|
521
|
-
# __tablename__ = "event_tags_x_org"
|
522
|
-
|
523
|
-
# event_tag_id: Mapped[int] = mapped_column(
|
524
|
-
# ForeignKey("event_tags.id"), primary_key=True
|
525
|
-
# )
|
526
|
-
# org_id: Mapped[int] = mapped_column(ForeignKey("orgs.id"), primary_key=True)
|
527
|
-
# color_override: Mapped[Optional[str]]
|
528
|
-
|
529
|
-
|
530
556
|
class Org_x_SlackSpace(Base):
|
531
557
|
"""
|
532
558
|
Model representing the association between organizations and Slack workspaces. This is currently meant to be one to one, but theoretically could support multiple workspaces per organization.
|
@@ -534,14 +560,12 @@ class Org_x_SlackSpace(Base):
|
|
534
560
|
Attributes:
|
535
561
|
org_id (int): The ID of the associated organization.
|
536
562
|
slack_space_id (str): The ID of the associated Slack workspace.
|
537
|
-
"""
|
563
|
+
""" # noqa: E501
|
538
564
|
|
539
565
|
__tablename__ = "orgs_x_slack_spaces"
|
540
566
|
|
541
567
|
org_id: Mapped[int] = mapped_column(ForeignKey("orgs.id"), primary_key=True)
|
542
|
-
slack_space_id: Mapped[int] = mapped_column(
|
543
|
-
ForeignKey("slack_spaces.id"), primary_key=True
|
544
|
-
)
|
568
|
+
slack_space_id: Mapped[int] = mapped_column(ForeignKey("slack_spaces.id"), primary_key=True)
|
545
569
|
|
546
570
|
|
547
571
|
class Location(Base):
|
@@ -565,7 +589,7 @@ class Location(Base):
|
|
565
589
|
meta (Optional[Dict[str, Any]]): Additional metadata for the location.
|
566
590
|
created (datetime): The timestamp when the record was created.
|
567
591
|
updated (datetime): The timestamp when the record was last updated.
|
568
|
-
"""
|
592
|
+
""" # noqa: E501
|
569
593
|
|
570
594
|
__tablename__ = "locations"
|
571
595
|
|
@@ -575,12 +599,8 @@ class Location(Base):
|
|
575
599
|
description: Mapped[Optional[text]]
|
576
600
|
is_active: Mapped[bool]
|
577
601
|
email: Mapped[Optional[str]]
|
578
|
-
latitude: Mapped[Optional[float]] = mapped_column(
|
579
|
-
|
580
|
-
)
|
581
|
-
longitude: Mapped[Optional[float]] = mapped_column(
|
582
|
-
Float(precision=8, decimal_return_scale=5)
|
583
|
-
)
|
602
|
+
latitude: Mapped[Optional[float]] = mapped_column(Float(precision=8, decimal_return_scale=5))
|
603
|
+
longitude: Mapped[Optional[float]] = mapped_column(Float(precision=8, decimal_return_scale=5))
|
584
604
|
address_street: Mapped[Optional[str]]
|
585
605
|
address_street2: Mapped[Optional[str]]
|
586
606
|
address_city: Mapped[Optional[str]]
|
@@ -639,7 +659,7 @@ class Event(Base):
|
|
639
659
|
event_tags (Optional[List[EventTag]]): The associated event tags.
|
640
660
|
event_x_event_types (List[EventType_x_Event]): The association between the event and event types.
|
641
661
|
event_x_event_tags (Optional[List[EventTag_x_Event]]): The association between the event and event tags.
|
642
|
-
"""
|
662
|
+
""" # noqa: E501
|
643
663
|
|
644
664
|
__tablename__ = "events"
|
645
665
|
|
@@ -680,9 +700,7 @@ class Event(Base):
|
|
680
700
|
)
|
681
701
|
|
682
702
|
org: Mapped[Org] = relationship(innerjoin=True, cascade="expunge", viewonly=True)
|
683
|
-
location: Mapped[Location] = relationship(
|
684
|
-
innerjoin=True, cascade="expunge", viewonly=True
|
685
|
-
)
|
703
|
+
location: Mapped[Location] = relationship(innerjoin=True, cascade="expunge", viewonly=True)
|
686
704
|
event_types: Mapped[List[EventType]] = relationship(
|
687
705
|
secondary="events_x_event_types",
|
688
706
|
innerjoin=True,
|
@@ -698,9 +716,7 @@ class Event(Base):
|
|
698
716
|
event_x_event_tags: Mapped[Optional[List[EventTag_x_Event]]] = relationship(
|
699
717
|
back_populates="event", cascade="save-update, merge, delete"
|
700
718
|
)
|
701
|
-
attendance: Mapped[List["Attendance"]] = relationship(
|
702
|
-
back_populates="event", cascade="expunge, delete"
|
703
|
-
)
|
719
|
+
attendance: Mapped[List["Attendance"]] = relationship(back_populates="event", cascade="expunge, delete")
|
704
720
|
|
705
721
|
|
706
722
|
class AttendanceType(Base):
|
@@ -710,7 +726,7 @@ class AttendanceType(Base):
|
|
710
726
|
Attributes:
|
711
727
|
type (str): The type of attendance.
|
712
728
|
description (Optional[str]): A description of the attendance type.
|
713
|
-
"""
|
729
|
+
""" # noqa: E501
|
714
730
|
|
715
731
|
__tablename__ = "attendance_types"
|
716
732
|
|
@@ -730,20 +746,14 @@ class Attendance_x_AttendanceType(Base):
|
|
730
746
|
attendance_type_id (int): The ID of the associated attendance type.
|
731
747
|
|
732
748
|
attendance (Attendance): The associated attendance.
|
733
|
-
"""
|
749
|
+
""" # noqa: E501
|
734
750
|
|
735
751
|
__tablename__ = "attendance_x_attendance_types"
|
736
752
|
|
737
|
-
attendance_id: Mapped[int] = mapped_column(
|
738
|
-
|
739
|
-
)
|
740
|
-
attendance_type_id: Mapped[int] = mapped_column(
|
741
|
-
ForeignKey("attendance_types.id"), primary_key=True
|
742
|
-
)
|
753
|
+
attendance_id: Mapped[int] = mapped_column(ForeignKey("attendance.id"), primary_key=True)
|
754
|
+
attendance_type_id: Mapped[int] = mapped_column(ForeignKey("attendance_types.id"), primary_key=True)
|
743
755
|
|
744
|
-
attendance: Mapped["Attendance"] = relationship(
|
745
|
-
back_populates="attendance_x_attendance_types"
|
746
|
-
)
|
756
|
+
attendance: Mapped["Attendance"] = relationship(back_populates="attendance_x_attendance_types")
|
747
757
|
|
748
758
|
|
749
759
|
class User(Base):
|
@@ -764,7 +774,7 @@ class User(Base):
|
|
764
774
|
status (UserStatus): The status of the user. Default is 'active'.
|
765
775
|
created (datetime): The timestamp when the record was created.
|
766
776
|
updated (datetime): The timestamp when the record was last updated.
|
767
|
-
"""
|
777
|
+
""" # noqa: E501
|
768
778
|
|
769
779
|
__tablename__ = "users"
|
770
780
|
|
@@ -772,7 +782,7 @@ class User(Base):
|
|
772
782
|
f3_name: Mapped[Optional[str]]
|
773
783
|
first_name: Mapped[Optional[str]]
|
774
784
|
last_name: Mapped[Optional[str]]
|
775
|
-
email: Mapped[str] = mapped_column(
|
785
|
+
email: Mapped[str] = mapped_column(CIText, unique=True)
|
776
786
|
phone: Mapped[Optional[str]]
|
777
787
|
emergency_contact: Mapped[Optional[str]]
|
778
788
|
emergency_phone: Mapped[Optional[str]]
|
@@ -781,9 +791,7 @@ class User(Base):
|
|
781
791
|
avatar_url: Mapped[Optional[str]]
|
782
792
|
meta: Mapped[Optional[Dict[str, Any]]]
|
783
793
|
email_verified: Mapped[Optional[datetime]]
|
784
|
-
status: Mapped[User_Status] = mapped_column(
|
785
|
-
Enum(User_Status), default=User_Status.active
|
786
|
-
)
|
794
|
+
status: Mapped[User_Status] = mapped_column(Enum(User_Status), default=User_Status.active)
|
787
795
|
created: Mapped[dt_create]
|
788
796
|
updated: Mapped[dt_update]
|
789
797
|
|
@@ -811,7 +819,7 @@ class SlackUser(Base):
|
|
811
819
|
slack_updated (Optional[datetime]): The last update time of the Slack user.
|
812
820
|
created (datetime): The timestamp when the record was created.
|
813
821
|
updated (datetime): The timestamp when the record was last updated.
|
814
|
-
"""
|
822
|
+
""" # noqa: E501
|
815
823
|
|
816
824
|
__tablename__ = "slack_users"
|
817
825
|
|
@@ -853,7 +861,7 @@ class Attendance(Base):
|
|
853
861
|
slack_user (Optional[SlackUser]): The associated Slack user.
|
854
862
|
attendance_x_attendance_types (List[Attendance_x_AttendanceType]): The association between the attendance and attendance types.
|
855
863
|
attendance_types (List[AttendanceType]): The associated attendance types.
|
856
|
-
"""
|
864
|
+
""" # noqa: E501
|
857
865
|
|
858
866
|
__tablename__ = "attendance"
|
859
867
|
__table_args__ = (UniqueConstraint("event_id", "user_id", "is_planned"),)
|
@@ -866,15 +874,13 @@ class Attendance(Base):
|
|
866
874
|
created: Mapped[dt_create]
|
867
875
|
updated: Mapped[dt_update]
|
868
876
|
|
869
|
-
event: Mapped[Event] = relationship(
|
870
|
-
innerjoin=True, cascade="expunge", viewonly=True
|
871
|
-
)
|
877
|
+
event: Mapped[Event] = relationship(innerjoin=True, cascade="expunge", viewonly=True)
|
872
878
|
user: Mapped[User] = relationship(innerjoin=True, cascade="expunge", viewonly=True)
|
873
879
|
slack_user: Mapped[Optional[SlackUser]] = relationship(
|
874
880
|
innerjoin=False, cascade="expunge", secondary="users", viewonly=True
|
875
881
|
)
|
876
|
-
attendance_x_attendance_types: Mapped[List[Attendance_x_AttendanceType]] = (
|
877
|
-
|
882
|
+
attendance_x_attendance_types: Mapped[List[Attendance_x_AttendanceType]] = relationship(
|
883
|
+
back_populates="attendance", cascade="save-update, merge, delete"
|
878
884
|
)
|
879
885
|
attendance_types: Mapped[List[AttendanceType]] = relationship(
|
880
886
|
secondary="attendance_x_attendance_types",
|
@@ -897,7 +903,7 @@ class Achievement(Base):
|
|
897
903
|
specific_org_id (Optional[int]): The ID of the specific region if a custom achievement. If null, the achievement is available to all regions.
|
898
904
|
created (datetime): The timestamp when the record was created.
|
899
905
|
updated (datetime): The timestamp when the record was last updated.
|
900
|
-
"""
|
906
|
+
""" # noqa: E501
|
901
907
|
|
902
908
|
__tablename__ = "achievements"
|
903
909
|
|
@@ -919,17 +925,13 @@ class Achievement_x_User(Base):
|
|
919
925
|
achievement_id (int): The ID of the associated achievement.
|
920
926
|
user_id (int): The ID of the associated user.
|
921
927
|
date_awarded (date): The date the achievement was awarded. Default is the current date.
|
922
|
-
"""
|
928
|
+
""" # noqa: E501
|
923
929
|
|
924
930
|
__tablename__ = "achievements_x_users"
|
925
931
|
|
926
|
-
achievement_id: Mapped[int] = mapped_column(
|
927
|
-
ForeignKey("achievements.id"), primary_key=True
|
928
|
-
)
|
932
|
+
achievement_id: Mapped[int] = mapped_column(ForeignKey("achievements.id"), primary_key=True)
|
929
933
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True)
|
930
|
-
date_awarded: Mapped[date] = mapped_column(
|
931
|
-
DateTime, server_default=func.timezone("utc", func.now())
|
932
|
-
)
|
934
|
+
date_awarded: Mapped[date] = mapped_column(DateTime, server_default=func.timezone("utc", func.now()))
|
933
935
|
|
934
936
|
|
935
937
|
class Position(Base):
|
@@ -941,7 +943,7 @@ class Position(Base):
|
|
941
943
|
description (Optional[str]): A description of the position.
|
942
944
|
org_type (Optional[Org_Type]): The associated organization type. This is used to limit the positions available to certain types of organizations. If null, the position is available to all organization types.
|
943
945
|
org_id (Optional[int]): The ID of the associated organization. This is used to limit the positions available to certain organizations. If null, the position is available to all organizations.
|
944
|
-
"""
|
946
|
+
""" # noqa: E501
|
945
947
|
|
946
948
|
__tablename__ = "positions"
|
947
949
|
|
@@ -962,13 +964,11 @@ class Position_x_Org_x_User(Base):
|
|
962
964
|
position_id (int): The ID of the associated position.
|
963
965
|
org_id (int): The ID of the associated organization.
|
964
966
|
user_id (int): The ID of the associated user.
|
965
|
-
"""
|
967
|
+
""" # noqa: E501
|
966
968
|
|
967
969
|
__tablename__ = "positions_x_orgs_x_users"
|
968
970
|
|
969
|
-
position_id: Mapped[int] = mapped_column(
|
970
|
-
ForeignKey("positions.id"), primary_key=True
|
971
|
-
)
|
971
|
+
position_id: Mapped[int] = mapped_column(ForeignKey("positions.id"), primary_key=True)
|
972
972
|
org_id: Mapped[int] = mapped_column(ForeignKey("orgs.id"), primary_key=True)
|
973
973
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True)
|
974
974
|
|
@@ -987,7 +987,7 @@ class Expansion(Base):
|
|
987
987
|
interested_in_organizing (bool): Whether the user is interested in organizing.
|
988
988
|
created (datetime): The timestamp when the record was created.
|
989
989
|
updated (datetime): The timestamp when the record was last updated.
|
990
|
-
"""
|
990
|
+
""" # noqa: E501
|
991
991
|
|
992
992
|
__tablename__ = "expansions"
|
993
993
|
|
@@ -1011,17 +1011,13 @@ class Expansion_x_User(Base):
|
|
1011
1011
|
user_id (int): The ID of the associated user.
|
1012
1012
|
requst_date (date): The date of the request. Default is the current date.
|
1013
1013
|
notes (Optional[text]): Additional notes for the association.
|
1014
|
-
"""
|
1014
|
+
""" # noqa: E501
|
1015
1015
|
|
1016
1016
|
__tablename__ = "expansions_x_users"
|
1017
1017
|
|
1018
|
-
expansion_id: Mapped[int] = mapped_column(
|
1019
|
-
ForeignKey("expansions.id"), primary_key=True
|
1020
|
-
)
|
1018
|
+
expansion_id: Mapped[int] = mapped_column(ForeignKey("expansions.id"), primary_key=True)
|
1021
1019
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True)
|
1022
|
-
request_date: Mapped[date] = mapped_column(
|
1023
|
-
DateTime, server_default=func.timezone("utc", func.now())
|
1024
|
-
)
|
1020
|
+
request_date: Mapped[date] = mapped_column(DateTime, server_default=func.timezone("utc", func.now()))
|
1025
1021
|
notes: Mapped[Optional[text]]
|
1026
1022
|
|
1027
1023
|
|
@@ -1043,7 +1039,7 @@ class NextAuthAccount(Base):
|
|
1043
1039
|
session_state (Optional[text]): The session state.
|
1044
1040
|
created (datetime): The timestamp when the record was created.
|
1045
1041
|
updated (datetime): The timestamp when the record was last updated.
|
1046
|
-
"""
|
1042
|
+
""" # noqa: E501
|
1047
1043
|
|
1048
1044
|
__tablename__ = "auth_accounts"
|
1049
1045
|
|
@@ -1072,7 +1068,7 @@ class NextAuthSession(Base):
|
|
1072
1068
|
expires (ts_notz): The expiration time of the session.
|
1073
1069
|
created (datetime): The timestamp when the record was created.
|
1074
1070
|
updated (datetime): The timestamp when the record was last updated.
|
1075
|
-
"""
|
1071
|
+
""" # noqa: E501
|
1076
1072
|
|
1077
1073
|
__tablename__ = "auth_sessions"
|
1078
1074
|
|
@@ -1093,7 +1089,7 @@ class NextAuthVerificationToken(Base):
|
|
1093
1089
|
expires (ts_notz): The expiration time of the token.
|
1094
1090
|
created (datetime): The timestamp when the record was created.
|
1095
1091
|
updated (datetime): The timestamp when the record was last updated.
|
1096
|
-
"""
|
1092
|
+
""" # noqa: E501
|
1097
1093
|
|
1098
1094
|
__tablename__ = "auth_verification_tokens"
|
1099
1095
|
|
@@ -1154,16 +1150,12 @@ class UpdateRequest(Base):
|
|
1154
1150
|
request_type (Request_Type): The type of the request.
|
1155
1151
|
created (datetime): The timestamp when the record was created.
|
1156
1152
|
updated (datetime): The timestamp when the record was last updated.
|
1157
|
-
"""
|
1153
|
+
""" # noqa: E501
|
1158
1154
|
|
1159
1155
|
__tablename__ = "update_requests"
|
1160
1156
|
|
1161
|
-
id: Mapped[Uuid] = mapped_column(
|
1162
|
-
|
1163
|
-
)
|
1164
|
-
token: Mapped[Uuid] = mapped_column(
|
1165
|
-
UUID(as_uuid=True), server_default=func.gen_random_uuid()
|
1166
|
-
)
|
1157
|
+
id: Mapped[Uuid] = mapped_column(UUID(as_uuid=True), primary_key=True, server_default=func.gen_random_uuid())
|
1158
|
+
token: Mapped[Uuid] = mapped_column(UUID(as_uuid=True), server_default=func.gen_random_uuid())
|
1167
1159
|
region_id: Mapped[int] = mapped_column(ForeignKey("orgs.id"))
|
1168
1160
|
event_id: Mapped[Optional[int]] = mapped_column(ForeignKey("events.id"))
|
1169
1161
|
event_type_ids: Mapped[Optional[List[int]]] = mapped_column(ARRAY(Integer))
|
@@ -1193,12 +1185,8 @@ class UpdateRequest(Base):
|
|
1193
1185
|
location_state: Mapped[Optional[str]]
|
1194
1186
|
location_zip: Mapped[Optional[str]]
|
1195
1187
|
location_country: Mapped[Optional[str]]
|
1196
|
-
location_lat: Mapped[Optional[float]] = mapped_column(
|
1197
|
-
|
1198
|
-
)
|
1199
|
-
location_lng: Mapped[Optional[float]] = mapped_column(
|
1200
|
-
Float(precision=8, decimal_return_scale=5)
|
1201
|
-
)
|
1188
|
+
location_lat: Mapped[Optional[float]] = mapped_column(Float(precision=8, decimal_return_scale=5))
|
1189
|
+
location_lng: Mapped[Optional[float]] = mapped_column(Float(precision=8, decimal_return_scale=5))
|
1202
1190
|
location_id: Mapped[Optional[int]] = mapped_column(ForeignKey("locations.id"))
|
1203
1191
|
location_contact_email: Mapped[Optional[str]]
|
1204
1192
|
|
f3_data_models/testing.py
CHANGED
@@ -1,33 +1,12 @@
|
|
1
|
-
from f3_data_models.models import
|
2
|
-
import datetime
|
1
|
+
from f3_data_models.models import User
|
3
2
|
from f3_data_models.utils import DbManager
|
4
3
|
|
5
4
|
|
6
5
|
def test_update_event():
|
7
|
-
|
8
|
-
|
9
|
-
location_id=2,
|
10
|
-
is_series=True,
|
11
|
-
is_active=True,
|
12
|
-
highlight=True,
|
13
|
-
start_date=datetime.date(2025, 2, 17),
|
14
|
-
end_date=datetime.date(2026, 2, 17),
|
15
|
-
start_time="0400",
|
16
|
-
end_time="0600",
|
17
|
-
event_x_event_types=[
|
18
|
-
EventType_x_Event(event_type_id=3),
|
19
|
-
],
|
20
|
-
recurrence_pattern=Event_Cadence.weekly,
|
21
|
-
day_of_week=Day_Of_Week.monday,
|
22
|
-
recurrence_interval=1,
|
23
|
-
index_within_interval=1,
|
24
|
-
name="Test Event",
|
6
|
+
user = User(
|
7
|
+
email="evan.Petzoldt@protonmail.com",
|
25
8
|
)
|
26
|
-
|
27
|
-
DbManager.update_record(Event, 3, update_dict)
|
28
|
-
|
29
|
-
# event = DbManager.get(Event, 3)
|
30
|
-
DbManager.delete_records(Event, [Event.series_id == 3])
|
9
|
+
DbManager.create_record(user)
|
31
10
|
|
32
11
|
|
33
12
|
if __name__ == "__main__":
|
f3_data_models/utils.py
CHANGED
@@ -1,24 +1,21 @@
|
|
1
1
|
import os
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List, Optional, Tuple,
|
3
|
+
from typing import Generic, List, Optional, Tuple, Type, TypeVar # noqa
|
4
4
|
|
5
|
+
import pg8000
|
5
6
|
import sqlalchemy
|
6
|
-
from
|
7
|
-
|
7
|
+
from google.cloud.sql.connector import Connector, IPTypes
|
8
|
+
from pydot import Dot
|
9
|
+
from sqlalchemy import Select, and_, inspect, select
|
8
10
|
from sqlalchemy.dialects.postgresql import insert
|
9
11
|
from sqlalchemy.engine import Engine
|
10
|
-
from sqlalchemy.orm import
|
12
|
+
from sqlalchemy.orm import class_mapper, joinedload, sessionmaker
|
13
|
+
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
11
14
|
from sqlalchemy.orm.collections import InstrumentedList
|
15
|
+
from sqlalchemy_schemadisplay import create_schema_graph
|
12
16
|
|
13
17
|
from f3_data_models.models import Base
|
14
18
|
|
15
|
-
from pydot import Dot
|
16
|
-
from sqlalchemy_schemadisplay import create_schema_graph
|
17
|
-
from google.cloud.sql.connector import Connector, IPTypes
|
18
|
-
import pg8000
|
19
|
-
from sqlalchemy.orm import class_mapper
|
20
|
-
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
21
|
-
|
22
19
|
|
23
20
|
@dataclass
|
24
21
|
class DatabaseField:
|
@@ -53,9 +50,7 @@ def get_engine(echo=False) -> Engine:
|
|
53
50
|
)
|
54
51
|
return conn
|
55
52
|
|
56
|
-
engine = sqlalchemy.create_engine(
|
57
|
-
"postgresql+pg8000://", creator=get_connection, echo=echo
|
58
|
-
)
|
53
|
+
engine = sqlalchemy.create_engine("postgresql+pg8000://", creator=get_connection, echo=echo)
|
59
54
|
return engine
|
60
55
|
|
61
56
|
|
@@ -81,10 +76,7 @@ T = TypeVar("T")
|
|
81
76
|
|
82
77
|
def _joinedloads(cls: T, query: Select, joinedloads: list | str) -> Select:
|
83
78
|
if joinedloads == "all":
|
84
|
-
joinedloads = [
|
85
|
-
getattr(cls, relationship.key)
|
86
|
-
for relationship in cls.__mapper__.relationships
|
87
|
-
]
|
79
|
+
joinedloads = [getattr(cls, relationship.key) for relationship in cls.__mapper__.relationships]
|
88
80
|
return query.options(*[joinedload(load) for load in joinedloads])
|
89
81
|
|
90
82
|
|
@@ -101,9 +93,7 @@ class DbManager:
|
|
101
93
|
session.rollback()
|
102
94
|
close_session(session)
|
103
95
|
|
104
|
-
def find_records(
|
105
|
-
cls: T, filters: Optional[List], joinedloads: List | str = []
|
106
|
-
) -> List[T]:
|
96
|
+
def find_records(cls: T, filters: Optional[List], joinedloads: List | str = []) -> List[T]:
|
107
97
|
session = get_session()
|
108
98
|
try:
|
109
99
|
query = select(cls)
|
@@ -117,9 +107,7 @@ class DbManager:
|
|
117
107
|
session.rollback()
|
118
108
|
close_session(session)
|
119
109
|
|
120
|
-
def find_first_record(
|
121
|
-
cls: T, filters: Optional[List], joinedloads: List | str = []
|
122
|
-
) -> T:
|
110
|
+
def find_first_record(cls: T, filters: Optional[List], joinedloads: List | str = []) -> T:
|
123
111
|
session = get_session()
|
124
112
|
try:
|
125
113
|
query = select(cls)
|
@@ -136,21 +124,14 @@ class DbManager:
|
|
136
124
|
def find_join_records2(left_cls: T, right_cls: T, filters) -> List[Tuple[T]]:
|
137
125
|
session = get_session()
|
138
126
|
try:
|
139
|
-
records = (
|
140
|
-
session.query(left_cls, right_cls)
|
141
|
-
.join(right_cls)
|
142
|
-
.filter(and_(*filters))
|
143
|
-
.all()
|
144
|
-
)
|
127
|
+
records = session.query(left_cls, right_cls).join(right_cls).filter(and_(*filters)).all()
|
145
128
|
session.expunge_all()
|
146
129
|
return records
|
147
130
|
finally:
|
148
131
|
session.rollback()
|
149
132
|
close_session(session)
|
150
133
|
|
151
|
-
def find_join_records3(
|
152
|
-
left_cls: T, right_cls1: T, right_cls2: T, filters, left_join=False
|
153
|
-
) -> List[Tuple[T]]:
|
134
|
+
def find_join_records3(left_cls: T, right_cls1: T, right_cls2: T, filters, left_join=False) -> List[Tuple[T]]:
|
154
135
|
session = get_session()
|
155
136
|
try:
|
156
137
|
records = (
|
@@ -176,7 +157,7 @@ class DbManager:
|
|
176
157
|
mapper = class_mapper(cls)
|
177
158
|
relationships = mapper.relationships.keys()
|
178
159
|
for attr, value in fields.items():
|
179
|
-
key = attr.key
|
160
|
+
key = attr if isinstance(attr, str) else attr.key
|
180
161
|
if hasattr(cls, key) and key not in relationships:
|
181
162
|
if isinstance(attr, InstrumentedAttribute):
|
182
163
|
setattr(record, key, value)
|
@@ -194,24 +175,17 @@ class DbManager:
|
|
194
175
|
if isinstance(value, list) and og_primary_key:
|
195
176
|
# Delete existing related records
|
196
177
|
related_class = relationship.mapper.class_
|
197
|
-
related_relationships = class_mapper(
|
198
|
-
|
199
|
-
).relationships.keys()
|
200
|
-
session.query(related_class).filter(
|
201
|
-
getattr(related_class, og_primary_key) == id
|
202
|
-
).delete()
|
178
|
+
related_relationships = class_mapper(related_class).relationships.keys()
|
179
|
+
session.query(related_class).filter(getattr(related_class, og_primary_key) == id).delete()
|
203
180
|
# Add new related records
|
204
181
|
items = [item.__dict__ for item in value]
|
205
182
|
for related_item in items:
|
206
183
|
update_dict = {
|
207
184
|
k: v
|
208
185
|
for k, v in related_item.items()
|
209
|
-
if hasattr(related_class, k)
|
210
|
-
and k not in related_relationships
|
186
|
+
if hasattr(related_class, k) and k not in related_relationships
|
211
187
|
}
|
212
|
-
related_record = related_class(
|
213
|
-
**{og_primary_key: id, **update_dict}
|
214
|
-
)
|
188
|
+
related_record = related_class(**{og_primary_key: id, **update_dict})
|
215
189
|
session.add(related_record)
|
216
190
|
|
217
191
|
try:
|
@@ -234,9 +208,7 @@ class DbManager:
|
|
234
208
|
# Update simple fields
|
235
209
|
for attr, value in fields.items():
|
236
210
|
key = attr.key
|
237
|
-
if key in valid_attributes and not isinstance(
|
238
|
-
value, InstrumentedList
|
239
|
-
):
|
211
|
+
if key in valid_attributes and not isinstance(value, InstrumentedList):
|
240
212
|
setattr(obj, key, value)
|
241
213
|
|
242
214
|
# Update relationships separately
|
@@ -256,9 +228,7 @@ class DbManager:
|
|
256
228
|
if isinstance(value, list) and og_primary_key:
|
257
229
|
# Delete existing related records
|
258
230
|
related_class = relationship.mapper.class_
|
259
|
-
related_relationships = class_mapper(
|
260
|
-
related_class
|
261
|
-
).relationships.keys()
|
231
|
+
related_relationships = class_mapper(related_class).relationships.keys()
|
262
232
|
session.query(related_class).filter(
|
263
233
|
getattr(related_class, og_primary_key) == obj.id
|
264
234
|
).delete()
|
@@ -268,12 +238,9 @@ class DbManager:
|
|
268
238
|
update_dict = {
|
269
239
|
k: v
|
270
240
|
for k, v in related_item.items()
|
271
|
-
if hasattr(related_class, k)
|
272
|
-
and k not in related_relationships
|
241
|
+
if hasattr(related_class, k) and k not in related_relationships
|
273
242
|
}
|
274
|
-
related_record = related_class(
|
275
|
-
**{og_primary_key: obj.id, **update_dict}
|
276
|
-
)
|
243
|
+
related_record = related_class(**{og_primary_key: obj.id, **update_dict})
|
277
244
|
session.add(related_record)
|
278
245
|
|
279
246
|
session.flush()
|
@@ -307,11 +274,7 @@ class DbManager:
|
|
307
274
|
session = get_session()
|
308
275
|
try:
|
309
276
|
for record in records:
|
310
|
-
record_dict = {
|
311
|
-
k: v
|
312
|
-
for k, v in record.__dict__.items()
|
313
|
-
if k != "_sa_instance_state"
|
314
|
-
}
|
277
|
+
record_dict = {k: v for k, v in record.__dict__.items() if k != "_sa_instance_state"}
|
315
278
|
stmt = insert(cls).values(record_dict).on_conflict_do_nothing()
|
316
279
|
session.execute(stmt)
|
317
280
|
session.flush()
|
@@ -323,15 +286,9 @@ class DbManager:
|
|
323
286
|
session = get_session()
|
324
287
|
try:
|
325
288
|
for record in records:
|
326
|
-
record_dict = {
|
327
|
-
k: v
|
328
|
-
for k, v in record.__dict__.items()
|
329
|
-
if k != "_sa_instance_state"
|
330
|
-
}
|
289
|
+
record_dict = {k: v for k, v in record.__dict__.items() if k != "_sa_instance_state"}
|
331
290
|
stmt = insert(cls).values(record_dict)
|
332
|
-
update_dict = {
|
333
|
-
c.name: getattr(record, c.name) for c in cls.__table__.columns
|
334
|
-
}
|
291
|
+
update_dict = {c.name: getattr(record, c.name) for c in cls.__table__.columns}
|
335
292
|
stmt = stmt.on_conflict_do_update(
|
336
293
|
index_elements=[cls.__table__.primary_key.columns.keys()],
|
337
294
|
set_=update_dict,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: f3-data-models
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.4
|
4
4
|
Summary: The data schema and models for F3 Nation applications.
|
5
5
|
License: MIT
|
6
6
|
Author: Evan Petzoldt
|
@@ -21,6 +21,7 @@ Requires-Dist: sphinx-autodoc-typehints (>=2.5.0,<3.0.0)
|
|
21
21
|
Requires-Dist: sphinx-multiversion (>=0.2.4,<0.3.0)
|
22
22
|
Requires-Dist: sphinx-rtd-theme (>=3.0.2,<4.0.0)
|
23
23
|
Requires-Dist: sqlalchemy (>=2.0.36,<3.0.0)
|
24
|
+
Requires-Dist: sqlalchemy-citext (>=1.8.0,<2.0.0)
|
24
25
|
Requires-Dist: sqlalchemy-schemadisplay (>=2.0,<3.0)
|
25
26
|
Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
|
26
27
|
Project-URL: Documentation, https://github.io/F3-Nation/f3-data-models
|
@@ -0,0 +1,7 @@
|
|
1
|
+
f3_data_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
f3_data_models/models.py,sha256=q_oQEe2Ccvcnl5l4Zf4fYNoogiT_1SSa8vMNmbW_QH8,45973
|
3
|
+
f3_data_models/testing.py,sha256=fdiwiSy0ZoYbsXAKhukVHMJr7WvBLwD20Tg949wshmk,265
|
4
|
+
f3_data_models/utils.py,sha256=l9x2nEcIrnjDLkIAATDsZ5I-0Y2sg1u_sfkBcRKrTu4,13051
|
5
|
+
f3_data_models-0.4.4.dist-info/METADATA,sha256=fTfrpyeuWFQKRRM79NlpO4U2j6CVboBmBKD8sxCYOv8,2766
|
6
|
+
f3_data_models-0.4.4.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
7
|
+
f3_data_models-0.4.4.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
f3_data_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
f3_data_models/models.py,sha256=gz14Lybp1QpLUMdCMgXbO5tazx_dcTc6laoyvbPIFqE,46533
|
3
|
-
f3_data_models/testing.py,sha256=KmTjMe345-NZCdHNaNnOyL33J8D5oVdqdIhYIKbM8XM,968
|
4
|
-
f3_data_models/utils.py,sha256=UpNx1E_kmt8_1vN4fdFSy55yPCoDA2algzof_5EAJ2k,13835
|
5
|
-
f3_data_models-0.4.2.dist-info/METADATA,sha256=W8Ze7HlIc4UiZHQaYY4w1fbe_jHG8ISecN-QVk-4JRs,2716
|
6
|
-
f3_data_models-0.4.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
7
|
-
f3_data_models-0.4.2.dist-info/RECORD,,
|