plexus-python-pulse-demeter 1.1.126__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.
- plexus/pulse/demeter/__init__.py +6 -0
- plexus/pulse/demeter/app/__init__.py +0 -0
- plexus/pulse/demeter/app/api/__init__.py +44 -0
- plexus/pulse/demeter/app/api/archive_internal_endpoints.py +316 -0
- plexus/pulse/demeter/app/api/archive_public_endpoints.py +255 -0
- plexus/pulse/demeter/app/api/dataset_internal_endpoints.py +1043 -0
- plexus/pulse/demeter/app/api/dataset_public_endpoints.py +2161 -0
- plexus/pulse/demeter/app/api/dataset_workflow_endpoints.py +532 -0
- plexus/pulse/demeter/app/api/tagging_endpoints.py +323 -0
- plexus/pulse/demeter/app/api/user_endpoints.py +78 -0
- plexus/pulse/demeter/app/core/__init__.py +0 -0
- plexus/pulse/demeter/app/core/database.py +43 -0
- plexus/pulse/demeter/app/core/vault.py +29 -0
- plexus/pulse/demeter/app/main.py +16 -0
- plexus/pulse/demeter/app/models/__init__.py +33 -0
- plexus/pulse/demeter/app/models/local.py +249 -0
- plexus/pulse/demeter/app/models/registry/__init__.py +112 -0
- plexus/pulse/demeter/app/models/registry/archive.py +529 -0
- plexus/pulse/demeter/app/models/registry/dataset.py +1531 -0
- plexus/pulse/demeter/app/models/registry/tagging.py +478 -0
- plexus/pulse/demeter/app/models/registry/user.py +79 -0
- plexus/pulse/demeter/tools/__init__.py +0 -0
- plexus/pulse/demeter/tools/main.py +306 -0
- plexus/pulse/demeter/utils/__init__.py +0 -0
- plexus/pulse/demeter/utils/config.py +25 -0
- plexus/pulse/demeter/utils/datautils.py +436 -0
- plexus_python_pulse_demeter-1.1.126.dist-info/METADATA +36 -0
- plexus_python_pulse_demeter-1.1.126.dist-info/RECORD +31 -0
- plexus_python_pulse_demeter-1.1.126.dist-info/WHEEL +5 -0
- plexus_python_pulse_demeter-1.1.126.dist-info/entry_points.txt +2 -0
- plexus_python_pulse_demeter-1.1.126.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import uuid as py_uuid
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
import pydantic as pdt
|
|
6
|
+
from plexus.common.utils.jsonutils import JsonType
|
|
7
|
+
from plexus.pulse.common.utils.datautils import (
|
|
8
|
+
validate_bag_name,
|
|
9
|
+
validate_colon_tag,
|
|
10
|
+
validate_dt_timezone,
|
|
11
|
+
validate_json_type_dump_size,
|
|
12
|
+
validate_slash_tag,
|
|
13
|
+
validate_snake_case,
|
|
14
|
+
validate_strict_relpath,
|
|
15
|
+
validate_user_name,
|
|
16
|
+
validate_vehicle_name,
|
|
17
|
+
)
|
|
18
|
+
from plexus.pulse.common.utils.jsonutils import json_datetime_encoder
|
|
19
|
+
from plexus.pulse.common.utils.ormutils import make_base_model
|
|
20
|
+
from sqlmodel import Field
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"LocalBase",
|
|
24
|
+
"DatasetMetafile",
|
|
25
|
+
"SampleMetafile",
|
|
26
|
+
"StandaloneDataset",
|
|
27
|
+
"StandaloneSample",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
LocalBase = make_base_model()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DatasetMetafile(LocalBase):
|
|
34
|
+
name: str
|
|
35
|
+
description: str
|
|
36
|
+
contributor: str
|
|
37
|
+
tags: list[str] | None = Field(default=None)
|
|
38
|
+
props: JsonType
|
|
39
|
+
|
|
40
|
+
@pdt.field_validator("name", mode="after")
|
|
41
|
+
@classmethod
|
|
42
|
+
def validate_name(cls, v: str) -> str:
|
|
43
|
+
validate_snake_case(v)
|
|
44
|
+
return v
|
|
45
|
+
|
|
46
|
+
@pdt.field_validator("contributor", mode="after")
|
|
47
|
+
@classmethod
|
|
48
|
+
def validate_contributor(cls, v: str) -> str:
|
|
49
|
+
validate_user_name(v)
|
|
50
|
+
return v
|
|
51
|
+
|
|
52
|
+
@pdt.field_validator("tags", mode="after")
|
|
53
|
+
@classmethod
|
|
54
|
+
def validate_tags(cls, v: list[str] | None) -> list[str] | None:
|
|
55
|
+
for tag in (v or []):
|
|
56
|
+
validate_colon_tag(tag)
|
|
57
|
+
return v
|
|
58
|
+
|
|
59
|
+
@pdt.field_validator("props", mode="after")
|
|
60
|
+
@classmethod
|
|
61
|
+
def validate_props(cls, v: JsonType) -> JsonType:
|
|
62
|
+
validate_json_type_dump_size(v, 10000)
|
|
63
|
+
return v
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SampleMetafile(LocalBase):
|
|
67
|
+
layout_uuid: py_uuid.UUID | None = Field(default=None)
|
|
68
|
+
layout_name: str | None = Field(default=None)
|
|
69
|
+
bag_name: str
|
|
70
|
+
vehicle_name: str
|
|
71
|
+
begin_frame_dt: datetime.datetime
|
|
72
|
+
end_frame_dt: datetime.datetime
|
|
73
|
+
tags: list[str] | None = Field(default=None)
|
|
74
|
+
props: JsonType
|
|
75
|
+
|
|
76
|
+
@pdt.field_validator("layout_name", mode="after")
|
|
77
|
+
@classmethod
|
|
78
|
+
def validate_layout_name(cls, v: str | None) -> str | None:
|
|
79
|
+
if v is not None:
|
|
80
|
+
validate_slash_tag(v)
|
|
81
|
+
return v
|
|
82
|
+
|
|
83
|
+
@pdt.model_validator(mode="after")
|
|
84
|
+
def validate_layout_uuid_layout_name(self) -> Self:
|
|
85
|
+
if (self.layout_uuid is None) and (self.layout_name is None):
|
|
86
|
+
raise ValueError("either layout_uuid or layout_name must be provided")
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
@pdt.field_validator("bag_name", mode="after")
|
|
90
|
+
@classmethod
|
|
91
|
+
def validate_bag_name(cls, v: str) -> str:
|
|
92
|
+
validate_bag_name(v)
|
|
93
|
+
return v
|
|
94
|
+
|
|
95
|
+
@pdt.field_validator("vehicle_name", mode="after")
|
|
96
|
+
@classmethod
|
|
97
|
+
def validate_vehicle_name(cls, v: str) -> str:
|
|
98
|
+
validate_vehicle_name(v)
|
|
99
|
+
return v
|
|
100
|
+
|
|
101
|
+
@pdt.field_validator("begin_frame_dt", mode="after")
|
|
102
|
+
@classmethod
|
|
103
|
+
def validate_begin_frame_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
104
|
+
validate_dt_timezone(v)
|
|
105
|
+
return v
|
|
106
|
+
|
|
107
|
+
@pdt.field_validator("end_frame_dt", mode="after")
|
|
108
|
+
@classmethod
|
|
109
|
+
def validate_end_frame_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
110
|
+
validate_dt_timezone(v)
|
|
111
|
+
return v
|
|
112
|
+
|
|
113
|
+
@pdt.model_validator(mode="after")
|
|
114
|
+
def validate_begin_frame_dt_end_frame_dt(self) -> Self:
|
|
115
|
+
if self.begin_frame_dt > self.end_frame_dt:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"begin_frame_dt '{self.begin_frame_dt}' is greater than end_frame_dt '{self.end_frame_dt}'")
|
|
118
|
+
return self
|
|
119
|
+
|
|
120
|
+
@pdt.field_validator("tags", mode="after")
|
|
121
|
+
@classmethod
|
|
122
|
+
def validate_tags(cls, v: list[str] | None) -> list[str] | None:
|
|
123
|
+
for tag in (v or []):
|
|
124
|
+
validate_colon_tag(tag)
|
|
125
|
+
return v
|
|
126
|
+
|
|
127
|
+
@pdt.field_validator("props", mode="after")
|
|
128
|
+
@classmethod
|
|
129
|
+
def validate_props(cls, v: JsonType) -> JsonType:
|
|
130
|
+
validate_json_type_dump_size(v, 10000)
|
|
131
|
+
return v
|
|
132
|
+
|
|
133
|
+
@pdt.field_serializer("begin_frame_dt", mode="plain")
|
|
134
|
+
def serialize_begin_frame_dt(self, v: datetime.datetime) -> str:
|
|
135
|
+
return json_datetime_encoder(v)
|
|
136
|
+
|
|
137
|
+
@pdt.field_serializer("end_frame_dt", mode="plain")
|
|
138
|
+
def serialize_end_frame_dt(self, v: datetime.datetime) -> str:
|
|
139
|
+
return json_datetime_encoder(v)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class StandaloneDataset(LocalBase):
|
|
143
|
+
name: str
|
|
144
|
+
description: str
|
|
145
|
+
contributor: str
|
|
146
|
+
tags: list[str] | None = Field(default=None)
|
|
147
|
+
props: JsonType
|
|
148
|
+
|
|
149
|
+
@pdt.field_validator("name", mode="after")
|
|
150
|
+
@classmethod
|
|
151
|
+
def validate_name(cls, v: str) -> str:
|
|
152
|
+
validate_snake_case(v)
|
|
153
|
+
return v
|
|
154
|
+
|
|
155
|
+
@pdt.field_validator("contributor", mode="after")
|
|
156
|
+
@classmethod
|
|
157
|
+
def validate_contributor(cls, v: str) -> str:
|
|
158
|
+
validate_user_name(v)
|
|
159
|
+
return v
|
|
160
|
+
|
|
161
|
+
@pdt.field_validator("tags", mode="after")
|
|
162
|
+
@classmethod
|
|
163
|
+
def validate_tags(cls, v: list[str] | None) -> list[str] | None:
|
|
164
|
+
for tag in (v or []):
|
|
165
|
+
validate_colon_tag(tag)
|
|
166
|
+
return v
|
|
167
|
+
|
|
168
|
+
@pdt.field_validator("props", mode="after")
|
|
169
|
+
@classmethod
|
|
170
|
+
def validate_props(cls, v: JsonType) -> JsonType:
|
|
171
|
+
validate_json_type_dump_size(v, 10000)
|
|
172
|
+
return v
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class StandaloneSample(LocalBase):
|
|
176
|
+
path: str
|
|
177
|
+
layout_uuid: py_uuid.UUID
|
|
178
|
+
bag_name: str
|
|
179
|
+
vehicle_name: str
|
|
180
|
+
begin_frame_dt: datetime.datetime
|
|
181
|
+
end_frame_dt: datetime.datetime
|
|
182
|
+
child_sample_paths: list[str] | None = Field(default=None)
|
|
183
|
+
tags: list[str] | None = Field(default=None)
|
|
184
|
+
props: JsonType
|
|
185
|
+
|
|
186
|
+
@pdt.field_validator("path", mode="after")
|
|
187
|
+
@classmethod
|
|
188
|
+
def validate_path(cls, v: str) -> str:
|
|
189
|
+
validate_strict_relpath(v)
|
|
190
|
+
return v
|
|
191
|
+
|
|
192
|
+
@pdt.field_validator("bag_name", mode="after")
|
|
193
|
+
@classmethod
|
|
194
|
+
def validate_bag_name(cls, v: str) -> str:
|
|
195
|
+
validate_bag_name(v)
|
|
196
|
+
return v
|
|
197
|
+
|
|
198
|
+
@pdt.field_validator("vehicle_name", mode="after")
|
|
199
|
+
@classmethod
|
|
200
|
+
def validate_vehicle_name(cls, v: str) -> str:
|
|
201
|
+
validate_vehicle_name(v)
|
|
202
|
+
return v
|
|
203
|
+
|
|
204
|
+
@pdt.field_validator("begin_frame_dt", mode="after")
|
|
205
|
+
@classmethod
|
|
206
|
+
def validate_begin_frame_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
207
|
+
validate_dt_timezone(v)
|
|
208
|
+
return v
|
|
209
|
+
|
|
210
|
+
@pdt.field_validator("end_frame_dt", mode="after")
|
|
211
|
+
@classmethod
|
|
212
|
+
def validate_end_frame_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
213
|
+
validate_dt_timezone(v)
|
|
214
|
+
return v
|
|
215
|
+
|
|
216
|
+
@pdt.model_validator(mode="after")
|
|
217
|
+
def validate_begin_frame_dt_end_frame_dt(self) -> Self:
|
|
218
|
+
if self.begin_frame_dt > self.end_frame_dt:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"begin_frame_dt '{self.begin_frame_dt}' is greater than end_frame_dt '{self.end_frame_dt}'")
|
|
221
|
+
return self
|
|
222
|
+
|
|
223
|
+
@pdt.field_validator("child_sample_paths", mode="after")
|
|
224
|
+
@classmethod
|
|
225
|
+
def validate_child_sample_paths(cls, v: list[str] | None) -> list[str] | None:
|
|
226
|
+
for path in (v or []):
|
|
227
|
+
validate_strict_relpath(path)
|
|
228
|
+
return v
|
|
229
|
+
|
|
230
|
+
@pdt.field_validator("tags", mode="after")
|
|
231
|
+
@classmethod
|
|
232
|
+
def validate_tags(cls, v: list[str] | None) -> list[str] | None:
|
|
233
|
+
for tag in (v or []):
|
|
234
|
+
validate_colon_tag(tag)
|
|
235
|
+
return v
|
|
236
|
+
|
|
237
|
+
@pdt.field_validator("props", mode="after")
|
|
238
|
+
@classmethod
|
|
239
|
+
def validate_props(cls, v: JsonType) -> JsonType:
|
|
240
|
+
validate_json_type_dump_size(v, 10000)
|
|
241
|
+
return v
|
|
242
|
+
|
|
243
|
+
@pdt.field_serializer("begin_frame_dt", mode="plain")
|
|
244
|
+
def serialize_begin_frame_dt(self, v: datetime.datetime) -> str:
|
|
245
|
+
return json_datetime_encoder(v)
|
|
246
|
+
|
|
247
|
+
@pdt.field_serializer("end_frame_dt", mode="plain")
|
|
248
|
+
def serialize_end_frame_dt(self, v: datetime.datetime) -> str:
|
|
249
|
+
return json_datetime_encoder(v)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from typing import Self
|
|
3
|
+
|
|
4
|
+
import textcase
|
|
5
|
+
from plexus.common.utils.funcutils import singleton
|
|
6
|
+
from plexus.pulse.common.utils.ormutils import make_base_model
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"RegistryBase",
|
|
10
|
+
"JobStatusFlag",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
RegistryBase = make_base_model()
|
|
14
|
+
RegistryBase.metadata.schema = "pulse_demeter"
|
|
15
|
+
RegistryBase.model_config.update(dict(alias_generator=textcase.camel, populate_by_name=True))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class JobStatusFlag(enum.Flag):
|
|
19
|
+
"""
|
|
20
|
+
Comprehensive job status bitflags.
|
|
21
|
+
|
|
22
|
+
Flags are grouped by functional categories with distinct bit ranges:
|
|
23
|
+
- progression: job lifecycle states (0x01 - 0x80)
|
|
24
|
+
- control: control/administrative requests (0x0100 - 0x8000)
|
|
25
|
+
- outcome: terminal outcomes (0x010000 - 0x800000)
|
|
26
|
+
"""
|
|
27
|
+
# Progression / lifecycle (ongoing states)
|
|
28
|
+
Submitted = 0x01
|
|
29
|
+
Queued = 0x02
|
|
30
|
+
Scheduled = 0x04
|
|
31
|
+
Running = 0x08
|
|
32
|
+
Paused = 0x10
|
|
33
|
+
Retrying = 0x20
|
|
34
|
+
|
|
35
|
+
# Control / administrative
|
|
36
|
+
EarlyStopRequested = 0x02_00
|
|
37
|
+
CancelRequested = 0x04_00
|
|
38
|
+
|
|
39
|
+
# Terminal / outcome
|
|
40
|
+
Completed = 0x01_00_00
|
|
41
|
+
EarlyStopped = 0x02_00_00
|
|
42
|
+
Cancelled = 0x04_00_00
|
|
43
|
+
Timeout = 0x08_00_00
|
|
44
|
+
Failed = 0x10_00_00
|
|
45
|
+
|
|
46
|
+
# Composite helpers
|
|
47
|
+
Progressing = Submitted | Queued | Scheduled | Running | Paused | Retrying
|
|
48
|
+
Terminal = Completed | EarlyStopped | Cancelled | Timeout | Failed
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
@singleton
|
|
52
|
+
def progressing_states(cls) -> list[Self]:
|
|
53
|
+
return [
|
|
54
|
+
cls.Submitted,
|
|
55
|
+
cls.Queued,
|
|
56
|
+
cls.Scheduled,
|
|
57
|
+
cls.Running,
|
|
58
|
+
cls.Paused,
|
|
59
|
+
cls.Retrying,
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
@singleton
|
|
64
|
+
def terminal_states(cls) -> list[Self]:
|
|
65
|
+
return [
|
|
66
|
+
cls.Completed,
|
|
67
|
+
cls.EarlyStopped,
|
|
68
|
+
cls.Cancelled,
|
|
69
|
+
cls.Timeout,
|
|
70
|
+
cls.Failed,
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
@singleton
|
|
75
|
+
def transitions(cls) -> dict[Self, set[Self]]:
|
|
76
|
+
return {
|
|
77
|
+
cls.Submitted: {
|
|
78
|
+
cls.Queued,
|
|
79
|
+
cls.CancelRequested,
|
|
80
|
+
},
|
|
81
|
+
cls.Queued: {
|
|
82
|
+
cls.Scheduled,
|
|
83
|
+
cls.CancelRequested,
|
|
84
|
+
},
|
|
85
|
+
cls.Scheduled: {
|
|
86
|
+
cls.Running,
|
|
87
|
+
cls.CancelRequested,
|
|
88
|
+
},
|
|
89
|
+
cls.Running: {
|
|
90
|
+
cls.Paused,
|
|
91
|
+
cls.EarlyStopRequested,
|
|
92
|
+
cls.CancelRequested,
|
|
93
|
+
cls.Completed,
|
|
94
|
+
cls.Timeout,
|
|
95
|
+
cls.Failed,
|
|
96
|
+
},
|
|
97
|
+
cls.Paused: {
|
|
98
|
+
cls.Running,
|
|
99
|
+
cls.EarlyStopRequested,
|
|
100
|
+
cls.CancelRequested,
|
|
101
|
+
},
|
|
102
|
+
cls.Retrying: {
|
|
103
|
+
cls.Queued,
|
|
104
|
+
cls.CancelRequested,
|
|
105
|
+
},
|
|
106
|
+
cls.EarlyStopRequested: {
|
|
107
|
+
cls.EarlyStopped,
|
|
108
|
+
},
|
|
109
|
+
cls.CancelRequested: {
|
|
110
|
+
cls.Cancelled,
|
|
111
|
+
},
|
|
112
|
+
}
|