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,2161 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import datetime
|
|
3
|
+
import uuid as py_uuid
|
|
4
|
+
from collections import Counter
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
|
|
8
|
+
import pydantic as pdt
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
import sqlalchemy.exc as sa_exc
|
|
11
|
+
import sqlalchemy.orm as sa_orm
|
|
12
|
+
from fastapi import APIRouter, Depends
|
|
13
|
+
from fastapi.params import Body, Path, Query
|
|
14
|
+
from plexus.common.utils.funcutils import singleton
|
|
15
|
+
from plexus.pulse.common.utils.apiutils import managed_db_session
|
|
16
|
+
from plexus.pulse.common.utils.ormutils import (
|
|
17
|
+
db_create_revision_model,
|
|
18
|
+
db_create_revision_models,
|
|
19
|
+
db_make_order_by_clause,
|
|
20
|
+
db_read_active_revision_models,
|
|
21
|
+
model_name_of,
|
|
22
|
+
)
|
|
23
|
+
from plexus.pulse.common.utils.sqlutils import escape_sql_like
|
|
24
|
+
from plexus.pulse.common.utils.tagutils import predefined_tagsets
|
|
25
|
+
|
|
26
|
+
from plexus.pulse.demeter.app.api import current_dt_clock
|
|
27
|
+
from plexus.pulse.demeter.app.core.database import make_session
|
|
28
|
+
from plexus.pulse.demeter.app.models import ColumnFormatter, ColumnOrdering, Pagination, TableColumn
|
|
29
|
+
from plexus.pulse.demeter.app.models.registry import JobStatusFlag
|
|
30
|
+
from plexus.pulse.demeter.app.models.registry.dataset import (
|
|
31
|
+
AnnotationGuidelineTable as GuidelineTable,
|
|
32
|
+
ClassifierTaxonomyTable as TaxonomyTable,
|
|
33
|
+
DatasetPromotionTable as DatasetPromotionTable,
|
|
34
|
+
DatasetRegistrationTable as RegistrationTable,
|
|
35
|
+
ModelArchitectureTable as ArchitectureTable,
|
|
36
|
+
ModelAssessmentPromotionTable as AssessmentPromotionTable,
|
|
37
|
+
ModelAssessmentTable as AssessmentTable,
|
|
38
|
+
ModelBenchmarkTable as BenchmarkTable,
|
|
39
|
+
ModelEvaluationTable as EvaluationTable,
|
|
40
|
+
ModelExperimentPromotionTable as ExperimentPromotionTable,
|
|
41
|
+
ModelExperimentTable as ExperimentTable,
|
|
42
|
+
ModelTrainingTable as TrainingTable,
|
|
43
|
+
RegisteredDatasetLayoutTable as DatasetLayoutTable,
|
|
44
|
+
RegisteredDatasetSampleTable as DatasetSampleTable,
|
|
45
|
+
RegisteredDatasetTable as DatasetTable,
|
|
46
|
+
RegisteredSampleTable as SampleTable,
|
|
47
|
+
SampleAnnotationTable as AnnotationTable,
|
|
48
|
+
SampleClassifierTable as ClassifierTable,
|
|
49
|
+
SampleLayoutTable as LayoutTable,
|
|
50
|
+
)
|
|
51
|
+
from plexus.pulse.demeter.app.models.registry.dataset import (
|
|
52
|
+
RegisteredDataset as Dataset,
|
|
53
|
+
RegisteredSample as Sample,
|
|
54
|
+
SampleAnnotation as Annotation,
|
|
55
|
+
SampleClassifier as Classifier,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
router = APIRouter(prefix="/pulse_demeter/dataset_public", tags=["dataset_public"])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# Composite model naming convention
|
|
62
|
+
# - Use 'And' to join one-to-one relationships. For a chain of one-to-one relations, create a single composite model
|
|
63
|
+
# that covers the whole chain.
|
|
64
|
+
# - Use 'With' to indicate one-to-many relationships.
|
|
65
|
+
# - Use plural form for the target segment of a one-to-many relationship.
|
|
66
|
+
# - If the target is a one-to-one relationship, pluralize the entire relationship segment.
|
|
67
|
+
# - If the target is also one-to-many, pluralize the source segment of that nested relationship.
|
|
68
|
+
# - For multiple chained one-to-many relationships, pluralize each relevant segment.
|
|
69
|
+
# - Preserve the order of segments following the hierarchy.
|
|
70
|
+
# Examples:
|
|
71
|
+
# - A one-to-one B one-to-one C
|
|
72
|
+
# AAndBAndC(a: A, b: B, c: C)
|
|
73
|
+
# - A one-to-many B one-to-one C
|
|
74
|
+
# BAndC(b: B, c: C)
|
|
75
|
+
# AWithBAndCs(a: A, b_and_cs: list[BAndC]) # pluralize the entire one-to-one segment
|
|
76
|
+
# - A one-to-one B one-to-many C
|
|
77
|
+
# BWithCs(b: B, cs: list[C])
|
|
78
|
+
# AAndBWithCs(a: A, b_with_cs: BWithCs)
|
|
79
|
+
# - A one-to-many B one-to-many C
|
|
80
|
+
# BWithCs(b: B, cs: list[C])
|
|
81
|
+
# AWithBsWithCs(a: A, bs_with_cs: list[BWithCs]) # pluralize source segments
|
|
82
|
+
# - A one-to-many B one-to-many C one-to-many D
|
|
83
|
+
# CWithDs(c: C, ds: list[D])
|
|
84
|
+
# BWithCsWithDs(b: B, cs_with_ds: list[CWithDs])
|
|
85
|
+
# AWithBsWithCsWithDs(a: A, bs_with_cs_with_ds: list[BWithCsWithDs])
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@router.get("/layouts", response_model=list[LayoutTable])
|
|
89
|
+
def layouts(db: sa_orm.Session = Depends(make_session)) -> list[LayoutTable]:
|
|
90
|
+
with managed_db_session(db):
|
|
91
|
+
return db_read_active_revision_models(db, LayoutTable)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@router.get("/layout", response_model=LayoutTable)
|
|
95
|
+
def get_layout(
|
|
96
|
+
uuid: py_uuid.UUID | None = Query(None),
|
|
97
|
+
name: str | None = Query(None),
|
|
98
|
+
db: sa_orm.Session = Depends(make_session),
|
|
99
|
+
) -> LayoutTable:
|
|
100
|
+
if uuid is None and name is None:
|
|
101
|
+
raise sa_exc.ArgumentError("either 'uuid' or 'name' must be provided")
|
|
102
|
+
with managed_db_session(db):
|
|
103
|
+
query_stmt = db.query(LayoutTable).where(LayoutTable.expired_at.is_(None))
|
|
104
|
+
if uuid is not None:
|
|
105
|
+
query_stmt = query_stmt.where(LayoutTable.uuid == uuid)
|
|
106
|
+
if name is not None:
|
|
107
|
+
query_stmt = query_stmt.where(LayoutTable.name == name)
|
|
108
|
+
db_layout: LayoutTable | None = query_stmt.one_or_none()
|
|
109
|
+
if db_layout is None:
|
|
110
|
+
raise sa_exc.NoResultFound(
|
|
111
|
+
f"'{model_name_of(LayoutTable)}' with uuid '{uuid}' and/or name '{name}' not found")
|
|
112
|
+
return db_layout
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@router.get("/taxonomies", response_model=list[TaxonomyTable])
|
|
116
|
+
def taxonomies(db: sa_orm.Session = Depends(make_session)) -> list[TaxonomyTable]:
|
|
117
|
+
with managed_db_session(db):
|
|
118
|
+
return db_read_active_revision_models(db, TaxonomyTable)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@router.get("/taxonomy", response_model=TaxonomyTable)
|
|
122
|
+
def get_taxonomy(
|
|
123
|
+
uuid: py_uuid.UUID | None = Query(None),
|
|
124
|
+
name: str | None = Query(None),
|
|
125
|
+
db: sa_orm.Session = Depends(make_session),
|
|
126
|
+
) -> TaxonomyTable:
|
|
127
|
+
if uuid is None and name is None:
|
|
128
|
+
raise sa_exc.ArgumentError("either 'uuid' or 'name' must be provided")
|
|
129
|
+
with managed_db_session(db):
|
|
130
|
+
query_stmt = db.query(TaxonomyTable).where(TaxonomyTable.expired_at.is_(None))
|
|
131
|
+
if uuid is not None:
|
|
132
|
+
query_stmt = query_stmt.where(TaxonomyTable.uuid == uuid)
|
|
133
|
+
if name is not None:
|
|
134
|
+
query_stmt = query_stmt.where(TaxonomyTable.name == name)
|
|
135
|
+
db_taxonomy: TaxonomyTable | None = query_stmt.one_or_none()
|
|
136
|
+
if db_taxonomy is None:
|
|
137
|
+
raise sa_exc.NoResultFound(
|
|
138
|
+
f"'{model_name_of(TaxonomyTable)}' with uuid '{uuid}' and/or name '{name}' not found")
|
|
139
|
+
return db_taxonomy
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@router.get("/guidelines", response_model=list[GuidelineTable])
|
|
143
|
+
def guidelines(db: sa_orm.Session = Depends(make_session)) -> list[GuidelineTable]:
|
|
144
|
+
with managed_db_session(db):
|
|
145
|
+
return db_read_active_revision_models(db, GuidelineTable)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@router.get("/guideline", response_model=GuidelineTable)
|
|
149
|
+
def get_guideline(
|
|
150
|
+
uuid: py_uuid.UUID | None = Query(None),
|
|
151
|
+
name: str | None = Query(None),
|
|
152
|
+
db: sa_orm.Session = Depends(make_session),
|
|
153
|
+
) -> GuidelineTable:
|
|
154
|
+
if uuid is None and name is None:
|
|
155
|
+
raise sa_exc.ArgumentError("either 'uuid' or 'name' must be provided")
|
|
156
|
+
with managed_db_session(db):
|
|
157
|
+
query_stmt = db.query(GuidelineTable).where(GuidelineTable.expired_at.is_(None))
|
|
158
|
+
if uuid is not None:
|
|
159
|
+
query_stmt = query_stmt.where(GuidelineTable.uuid == uuid)
|
|
160
|
+
if name is not None:
|
|
161
|
+
query_stmt = query_stmt.where(GuidelineTable.name == name)
|
|
162
|
+
db_guideline: GuidelineTable | None = query_stmt.one_or_none()
|
|
163
|
+
if db_guideline is None:
|
|
164
|
+
raise sa_exc.NoResultFound(
|
|
165
|
+
f"'{model_name_of(GuidelineTable)}' with uuid '{uuid}' and/or name '{name}' not found")
|
|
166
|
+
return db_guideline
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@router.get("/architectures", response_model=list[ArchitectureTable])
|
|
170
|
+
def architectures(db: sa_orm.Session = Depends(make_session)) -> list[ArchitectureTable]:
|
|
171
|
+
with managed_db_session(db):
|
|
172
|
+
return db_read_active_revision_models(db, ArchitectureTable)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@router.get("/architecture", response_model=ArchitectureTable)
|
|
176
|
+
def get_architecture(
|
|
177
|
+
uuid: py_uuid.UUID | None = Query(None),
|
|
178
|
+
name: str | None = Query(None),
|
|
179
|
+
db: sa_orm.Session = Depends(make_session),
|
|
180
|
+
) -> ArchitectureTable:
|
|
181
|
+
if uuid is None and name is None:
|
|
182
|
+
raise sa_exc.ArgumentError("either 'uuid' or 'name' must be provided")
|
|
183
|
+
with managed_db_session(db):
|
|
184
|
+
query_stmt = db.query(ArchitectureTable).where(ArchitectureTable.expired_at.is_(None))
|
|
185
|
+
if uuid is not None:
|
|
186
|
+
query_stmt = query_stmt.where(ArchitectureTable.uuid == uuid)
|
|
187
|
+
if name is not None:
|
|
188
|
+
query_stmt = query_stmt.where(ArchitectureTable.name == name)
|
|
189
|
+
db_architecture: ArchitectureTable | None = query_stmt.one_or_none()
|
|
190
|
+
if db_architecture is None:
|
|
191
|
+
raise sa_exc.NoResultFound(
|
|
192
|
+
f"'{model_name_of(ArchitectureTable)}' with uuid '{uuid}' and/or name '{name}' not found")
|
|
193
|
+
return db_architecture
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@router.get("/benchmarks", response_model=list[BenchmarkTable])
|
|
197
|
+
def benchmarks(db: sa_orm.Session = Depends(make_session)) -> list[BenchmarkTable]:
|
|
198
|
+
with managed_db_session(db):
|
|
199
|
+
return db_read_active_revision_models(db, BenchmarkTable)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@router.get("/benchmark", response_model=BenchmarkTable)
|
|
203
|
+
def get_benchmark(
|
|
204
|
+
uuid: py_uuid.UUID | None = Query(None),
|
|
205
|
+
name: str | None = Query(None),
|
|
206
|
+
db: sa_orm.Session = Depends(make_session),
|
|
207
|
+
) -> BenchmarkTable:
|
|
208
|
+
if uuid is None and name is None:
|
|
209
|
+
raise sa_exc.ArgumentError("either 'uuid' or 'name' must be provided")
|
|
210
|
+
with managed_db_session(db):
|
|
211
|
+
query_stmt = db.query(BenchmarkTable).where(BenchmarkTable.expired_at.is_(None))
|
|
212
|
+
if uuid is not None:
|
|
213
|
+
query_stmt = query_stmt.where(BenchmarkTable.uuid == uuid)
|
|
214
|
+
if name is not None:
|
|
215
|
+
query_stmt = query_stmt.where(BenchmarkTable.name == name)
|
|
216
|
+
db_benchmark: BenchmarkTable | None = query_stmt.one_or_none()
|
|
217
|
+
if db_benchmark is None:
|
|
218
|
+
raise sa_exc.NoResultFound(
|
|
219
|
+
f"'{model_name_of(BenchmarkTable)}' with uuid '{uuid}' and/or name '{name}' not found")
|
|
220
|
+
return db_benchmark
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class LayoutTableAndSamplesCount(pdt.BaseModel):
|
|
224
|
+
layout: LayoutTable
|
|
225
|
+
samples_count: int
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class DatasetTableDetail(pdt.BaseModel):
|
|
229
|
+
dataset: DatasetTable
|
|
230
|
+
layouts: list[LayoutTableAndSamplesCount]
|
|
231
|
+
samples_count: int
|
|
232
|
+
registration: RegistrationTable | None
|
|
233
|
+
promotion: DatasetPromotionTable | None
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class DatasetTableDetailsAndPagination(pdt.BaseModel):
|
|
237
|
+
dataset_details: list[DatasetTableDetail]
|
|
238
|
+
pagination: Pagination
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def query_layout_samples_counts(
|
|
242
|
+
dataset_uuids: list[py_uuid.UUID],
|
|
243
|
+
db: sa_orm.Session,
|
|
244
|
+
) -> dict[py_uuid.UUID, list[LayoutTableAndSamplesCount]]:
|
|
245
|
+
db_dataset_layouts = (
|
|
246
|
+
db
|
|
247
|
+
.query(DatasetLayoutTable)
|
|
248
|
+
.where(DatasetLayoutTable.dataset_uuid.in_(dataset_uuids))
|
|
249
|
+
.all()
|
|
250
|
+
)
|
|
251
|
+
db_dataset_layouts_dicts = {}
|
|
252
|
+
for db_dataset_layout in db_dataset_layouts: (
|
|
253
|
+
db_dataset_layouts_dicts
|
|
254
|
+
.setdefault(db_dataset_layout.dataset_uuid, {})
|
|
255
|
+
.setdefault(db_dataset_layout.layout_uuid, db_dataset_layout)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
layout_uuids = list(set(layout_uuid
|
|
259
|
+
for db_dataset_layouts_dict in db_dataset_layouts_dicts.values()
|
|
260
|
+
for layout_uuid in db_dataset_layouts_dict))
|
|
261
|
+
db_layouts = (
|
|
262
|
+
db
|
|
263
|
+
.query(LayoutTable)
|
|
264
|
+
.where(LayoutTable.expired_at.is_(None), LayoutTable.uuid.in_(layout_uuids))
|
|
265
|
+
.all()
|
|
266
|
+
)
|
|
267
|
+
db_layouts_dict = {db_layout.uuid: db_layout for db_layout in db_layouts}
|
|
268
|
+
for layout_uuid in layout_uuids:
|
|
269
|
+
if layout_uuid not in db_layouts_dict:
|
|
270
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(LayoutTable)}' with uuid '{layout_uuid}' not found")
|
|
271
|
+
|
|
272
|
+
# NOTE:
|
|
273
|
+
# - Layouts here are intentionally ordered by `samples_count` in descending order
|
|
274
|
+
# so that the most populated layouts appear first.
|
|
275
|
+
# - Other endpoints may order layouts differently (for example, by `LayoutTable.sqn`)
|
|
276
|
+
# to provide a stable schema-oriented ordering.
|
|
277
|
+
# This difference is intentional and reflects the distinct use-cases.
|
|
278
|
+
return {
|
|
279
|
+
dataset_uuid: [
|
|
280
|
+
LayoutTableAndSamplesCount(layout=db_layouts_dict.get(db_dataset_layout.layout_uuid),
|
|
281
|
+
samples_count=db_dataset_layout.samples_count)
|
|
282
|
+
for db_dataset_layout
|
|
283
|
+
in sorted(db_dataset_layouts_dict.values(), key=lambda x: x.samples_count, reverse=True)
|
|
284
|
+
]
|
|
285
|
+
for dataset_uuid, db_dataset_layouts_dict in db_dataset_layouts_dicts.items()
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@router.get("/list_datasets", response_model=DatasetTableDetailsAndPagination)
|
|
290
|
+
def list_datasets(
|
|
291
|
+
skip: int = Query(0),
|
|
292
|
+
limit: int = Query(100),
|
|
293
|
+
begin_at: datetime.datetime | None = Query(None),
|
|
294
|
+
end_at: datetime.datetime | None = Query(None),
|
|
295
|
+
major_layout: str | None = Query(None),
|
|
296
|
+
name_match: str | None = Query(None),
|
|
297
|
+
contributor_sso_sid: int | None = Query(None),
|
|
298
|
+
only_completed_jobs: bool = Query(True),
|
|
299
|
+
only_shared: bool = Query(True),
|
|
300
|
+
order_by: list[str] | None = Query(None),
|
|
301
|
+
db: sa_orm.Session = Depends(make_session),
|
|
302
|
+
) -> DatasetTableDetailsAndPagination:
|
|
303
|
+
with managed_db_session(db):
|
|
304
|
+
query_stmt = (
|
|
305
|
+
db
|
|
306
|
+
.query(DatasetTable, RegistrationTable, DatasetPromotionTable)
|
|
307
|
+
.outerjoin(RegistrationTable, DatasetTable.uuid == RegistrationTable.dataset_uuid)
|
|
308
|
+
.outerjoin(DatasetPromotionTable, DatasetTable.uuid == DatasetPromotionTable.dataset_uuid)
|
|
309
|
+
.where(DatasetTable.expired_at.is_(None),
|
|
310
|
+
RegistrationTable.expired_at.is_(None),
|
|
311
|
+
DatasetPromotionTable.expired_at.is_(None))
|
|
312
|
+
)
|
|
313
|
+
if only_completed_jobs:
|
|
314
|
+
query_stmt = query_stmt.where(sa.or_(RegistrationTable.sqn.is_(None),
|
|
315
|
+
RegistrationTable.flags.op("&")(JobStatusFlag.Completed.value) > 0))
|
|
316
|
+
if only_shared:
|
|
317
|
+
query_stmt = query_stmt.where(DatasetPromotionTable.flags.op("&")(JobStatusFlag.Completed.value) > 0)
|
|
318
|
+
if begin_at is not None:
|
|
319
|
+
query_stmt = query_stmt.where(DatasetTable.created_at >= begin_at)
|
|
320
|
+
if end_at is not None:
|
|
321
|
+
query_stmt = query_stmt.where(DatasetTable.created_at < end_at)
|
|
322
|
+
if name_match is not None:
|
|
323
|
+
query_stmt = query_stmt.where(DatasetTable.name.ilike(f"%{escape_sql_like(name_match)}%", escape="\\"))
|
|
324
|
+
if contributor_sso_sid is not None:
|
|
325
|
+
query_stmt = query_stmt.where(DatasetTable.contributor_sso_sid == contributor_sso_sid)
|
|
326
|
+
query_stmt = query_stmt.order_by(*db_make_order_by_clause(DatasetTable, order_by))
|
|
327
|
+
|
|
328
|
+
total = query_stmt.count()
|
|
329
|
+
if total == 0:
|
|
330
|
+
return DatasetTableDetailsAndPagination(
|
|
331
|
+
dataset_details=[],
|
|
332
|
+
pagination=Pagination(skip=skip, limit=limit, total=0),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
db_dataset_registration_promotions = query_stmt.offset(skip).limit(limit).all()
|
|
336
|
+
|
|
337
|
+
dataset_uuids = list(set(db_dataset.uuid for db_dataset, _, _ in db_dataset_registration_promotions))
|
|
338
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts(dataset_uuids, db)
|
|
339
|
+
|
|
340
|
+
return DatasetTableDetailsAndPagination(
|
|
341
|
+
dataset_details=[
|
|
342
|
+
DatasetTableDetail(
|
|
343
|
+
dataset=db_dataset,
|
|
344
|
+
layouts=layout_samples_counts_by_datasets.get(db_dataset.uuid, []),
|
|
345
|
+
samples_count=sum(layout_samples_count.samples_count
|
|
346
|
+
for layout_samples_count in
|
|
347
|
+
layout_samples_counts_by_datasets.get(db_dataset.uuid, [])),
|
|
348
|
+
registration=db_registration,
|
|
349
|
+
promotion=db_promotion,
|
|
350
|
+
)
|
|
351
|
+
for db_dataset, db_registration, db_promotion in db_dataset_registration_promotions
|
|
352
|
+
],
|
|
353
|
+
pagination=Pagination(skip=skip, limit=limit, total=total),
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@router.get("/get_dataset/{dataset_uuid}", response_model=DatasetTableDetail)
|
|
358
|
+
def get_dataset(
|
|
359
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
360
|
+
db: sa_orm.Session = Depends(make_session),
|
|
361
|
+
) -> DatasetTableDetail:
|
|
362
|
+
with managed_db_session(db):
|
|
363
|
+
db_dataset_registration_promotion = (
|
|
364
|
+
db
|
|
365
|
+
.query(DatasetTable, RegistrationTable, DatasetPromotionTable)
|
|
366
|
+
.outerjoin(RegistrationTable, DatasetTable.uuid == RegistrationTable.dataset_uuid)
|
|
367
|
+
.outerjoin(DatasetPromotionTable, DatasetTable.uuid == DatasetPromotionTable.dataset_uuid)
|
|
368
|
+
.where(DatasetTable.expired_at.is_(None),
|
|
369
|
+
RegistrationTable.expired_at.is_(None),
|
|
370
|
+
DatasetPromotionTable.expired_at.is_(None),
|
|
371
|
+
DatasetTable.uuid == dataset_uuid)
|
|
372
|
+
.one_or_none()
|
|
373
|
+
)
|
|
374
|
+
if db_dataset_registration_promotion is None:
|
|
375
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
376
|
+
|
|
377
|
+
db_dataset, db_registration, db_promotion = db_dataset_registration_promotion
|
|
378
|
+
|
|
379
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts([dataset_uuid], db)
|
|
380
|
+
layout_samples_counts = layout_samples_counts_by_datasets.get(dataset_uuid, [])
|
|
381
|
+
|
|
382
|
+
return DatasetTableDetail(
|
|
383
|
+
dataset=db_dataset,
|
|
384
|
+
layouts=layout_samples_counts,
|
|
385
|
+
samples_count=sum(layout_samples_count.samples_count for layout_samples_count in layout_samples_counts),
|
|
386
|
+
registration=db_registration,
|
|
387
|
+
promotion=db_promotion,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class ExperimentTableDetail(pdt.BaseModel):
|
|
392
|
+
experiment: ExperimentTable
|
|
393
|
+
training: TrainingTable | None
|
|
394
|
+
promotion: ExperimentPromotionTable | None
|
|
395
|
+
architecture: ArchitectureTable | None
|
|
396
|
+
dataset: DatasetTable | None
|
|
397
|
+
layouts: list[LayoutTableAndSamplesCount] | None
|
|
398
|
+
samples_count: int
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class ExperimentTableDetailsAndPagination(pdt.BaseModel):
|
|
402
|
+
experiment_details: list[ExperimentTableDetail]
|
|
403
|
+
pagination: Pagination
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@router.get("/list_model_experiments/", response_model=ExperimentTableDetailsAndPagination)
|
|
407
|
+
def list_model_experiments(
|
|
408
|
+
skip: int = Query(0),
|
|
409
|
+
limit: int = Query(100),
|
|
410
|
+
begin_at: datetime.datetime | None = Query(None),
|
|
411
|
+
end_at: datetime.datetime | None = Query(None),
|
|
412
|
+
architecture: str | None = Query(None),
|
|
413
|
+
name_match: str | None = Query(None),
|
|
414
|
+
contributor_sso_sid: int | None = Query(None),
|
|
415
|
+
only_completed_jobs: bool = Query(True),
|
|
416
|
+
only_shared: bool = Query(True),
|
|
417
|
+
order_by: list[str] | None = Query(None),
|
|
418
|
+
db: sa_orm.Session = Depends(make_session),
|
|
419
|
+
) -> ExperimentTableDetailsAndPagination:
|
|
420
|
+
with managed_db_session(db):
|
|
421
|
+
if architecture is not None:
|
|
422
|
+
db_architecture = (
|
|
423
|
+
db
|
|
424
|
+
.query(ArchitectureTable)
|
|
425
|
+
.where(ArchitectureTable.expired_at.is_(None), ArchitectureTable.name == architecture)
|
|
426
|
+
.one_or_none()
|
|
427
|
+
)
|
|
428
|
+
if db_architecture is None:
|
|
429
|
+
raise sa_exc.NoResultFound(
|
|
430
|
+
f"'{model_name_of(ArchitectureTable)}' with name '{architecture}' not found")
|
|
431
|
+
architecture_uuid = db_architecture.uuid
|
|
432
|
+
else:
|
|
433
|
+
architecture_uuid = None
|
|
434
|
+
|
|
435
|
+
query_stmt = (
|
|
436
|
+
db
|
|
437
|
+
.query(ExperimentTable, TrainingTable, ExperimentPromotionTable)
|
|
438
|
+
.outerjoin(TrainingTable, ExperimentTable.uuid == TrainingTable.experiment_uuid)
|
|
439
|
+
.outerjoin(ExperimentPromotionTable, ExperimentTable.uuid == ExperimentPromotionTable.experiment_uuid)
|
|
440
|
+
.where(ExperimentTable.expired_at.is_(None),
|
|
441
|
+
TrainingTable.expired_at.is_(None),
|
|
442
|
+
ExperimentPromotionTable.expired_at.is_(None))
|
|
443
|
+
)
|
|
444
|
+
if only_completed_jobs:
|
|
445
|
+
query_stmt = query_stmt.where(sa.or_(TrainingTable.sqn.is_(None),
|
|
446
|
+
TrainingTable.flags.op("&")(JobStatusFlag.Completed.value) > 0))
|
|
447
|
+
if only_shared:
|
|
448
|
+
query_stmt = query_stmt.where(ExperimentPromotionTable.flags.op("&")(JobStatusFlag.Completed.value) > 0)
|
|
449
|
+
if begin_at is not None:
|
|
450
|
+
query_stmt = query_stmt.where(ExperimentTable.created_at >= begin_at)
|
|
451
|
+
if end_at is not None:
|
|
452
|
+
query_stmt = query_stmt.where(ExperimentTable.created_at < end_at)
|
|
453
|
+
if architecture_uuid is not None:
|
|
454
|
+
query_stmt = query_stmt.where(ExperimentTable.architecture_uuid == architecture_uuid)
|
|
455
|
+
if name_match is not None:
|
|
456
|
+
query_stmt = query_stmt.where(ExperimentTable.name.ilike(f"%{escape_sql_like(name_match)}%", escape="\\"))
|
|
457
|
+
if contributor_sso_sid is not None:
|
|
458
|
+
query_stmt = query_stmt.where(ExperimentTable.contributor_sso_sid == contributor_sso_sid)
|
|
459
|
+
query_stmt = query_stmt.order_by(*db_make_order_by_clause(ExperimentTable, order_by))
|
|
460
|
+
|
|
461
|
+
total = query_stmt.count()
|
|
462
|
+
if total == 0:
|
|
463
|
+
return ExperimentTableDetailsAndPagination(
|
|
464
|
+
experiment_details=[],
|
|
465
|
+
pagination=Pagination(skip=skip, limit=limit, total=0),
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
db_experiment_training_promotions = query_stmt.offset(skip).limit(limit).all()
|
|
469
|
+
|
|
470
|
+
architecture_uuids = list(set(db_experiment.architecture_uuid
|
|
471
|
+
for db_experiment, _, _ in db_experiment_training_promotions))
|
|
472
|
+
db_architectures = (
|
|
473
|
+
db
|
|
474
|
+
.query(ArchitectureTable)
|
|
475
|
+
.where(ArchitectureTable.expired_at.is_(None), ArchitectureTable.uuid.in_(architecture_uuids))
|
|
476
|
+
.all()
|
|
477
|
+
)
|
|
478
|
+
db_architectures_dict = {db_architecture.uuid: db_architecture for db_architecture in db_architectures}
|
|
479
|
+
|
|
480
|
+
dataset_uuids = list(set(db_experiment.dataset_uuid
|
|
481
|
+
for db_experiment, _, _ in db_experiment_training_promotions))
|
|
482
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts(dataset_uuids, db)
|
|
483
|
+
|
|
484
|
+
db_datasets = (
|
|
485
|
+
db
|
|
486
|
+
.query(DatasetTable)
|
|
487
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(dataset_uuids))
|
|
488
|
+
.all()
|
|
489
|
+
)
|
|
490
|
+
db_datasets_dict = {db_dataset.uuid: db_dataset for db_dataset in db_datasets}
|
|
491
|
+
|
|
492
|
+
return ExperimentTableDetailsAndPagination(
|
|
493
|
+
experiment_details=[
|
|
494
|
+
ExperimentTableDetail(
|
|
495
|
+
experiment=db_experiment,
|
|
496
|
+
training=db_training,
|
|
497
|
+
promotion=db_promotion,
|
|
498
|
+
architecture=db_architectures_dict.get(db_experiment.architecture_uuid),
|
|
499
|
+
dataset=db_datasets_dict.get(db_experiment.dataset_uuid),
|
|
500
|
+
layouts=layout_samples_counts_by_datasets.get(db_experiment.dataset_uuid, []),
|
|
501
|
+
samples_count=sum(layout_samples_count.samples_count
|
|
502
|
+
for layout_samples_count in
|
|
503
|
+
layout_samples_counts_by_datasets.get(db_experiment.dataset_uuid, [])),
|
|
504
|
+
)
|
|
505
|
+
for db_experiment, db_training, db_promotion in db_experiment_training_promotions
|
|
506
|
+
],
|
|
507
|
+
pagination=Pagination(skip=skip, limit=limit, total=total),
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@router.get("/get_model_experiment/{experiment_uuid}", response_model=ExperimentTableDetail)
|
|
512
|
+
def get_model_experiment(
|
|
513
|
+
experiment_uuid: py_uuid.UUID = Path(),
|
|
514
|
+
db: sa_orm.Session = Depends(make_session),
|
|
515
|
+
) -> ExperimentTableDetail:
|
|
516
|
+
with managed_db_session(db):
|
|
517
|
+
db_experiment_training_promotion = (
|
|
518
|
+
db
|
|
519
|
+
.query(ExperimentTable, TrainingTable, ExperimentPromotionTable)
|
|
520
|
+
.outerjoin(TrainingTable, ExperimentTable.uuid == TrainingTable.experiment_uuid)
|
|
521
|
+
.outerjoin(ExperimentPromotionTable, ExperimentTable.uuid == ExperimentPromotionTable.experiment_uuid)
|
|
522
|
+
.where(ExperimentTable.expired_at.is_(None),
|
|
523
|
+
TrainingTable.expired_at.is_(None),
|
|
524
|
+
ExperimentPromotionTable.expired_at.is_(None),
|
|
525
|
+
ExperimentTable.uuid == experiment_uuid)
|
|
526
|
+
.one_or_none()
|
|
527
|
+
)
|
|
528
|
+
if db_experiment_training_promotion is None:
|
|
529
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(ExperimentTable)}' with uuid '{experiment_uuid}' not found")
|
|
530
|
+
|
|
531
|
+
db_experiment, db_training, db_promotion = db_experiment_training_promotion
|
|
532
|
+
|
|
533
|
+
db_architecture = (
|
|
534
|
+
db
|
|
535
|
+
.query(ArchitectureTable)
|
|
536
|
+
.where(ArchitectureTable.expired_at.is_(None), ArchitectureTable.uuid == db_experiment.architecture_uuid)
|
|
537
|
+
.one_or_none()
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts([db_experiment.dataset_uuid], db)
|
|
541
|
+
layout_samples_counts = layout_samples_counts_by_datasets.get(db_experiment.dataset_uuid, [])
|
|
542
|
+
|
|
543
|
+
db_dataset = (
|
|
544
|
+
db
|
|
545
|
+
.query(DatasetTable)
|
|
546
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == db_experiment.dataset_uuid)
|
|
547
|
+
.one_or_none()
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
return ExperimentTableDetail(
|
|
551
|
+
experiment=db_experiment,
|
|
552
|
+
training=db_training,
|
|
553
|
+
promotion=db_promotion,
|
|
554
|
+
architecture=db_architecture,
|
|
555
|
+
dataset=db_dataset,
|
|
556
|
+
layouts=layout_samples_counts,
|
|
557
|
+
samples_count=sum(layout_samples_count.samples_count for layout_samples_count in layout_samples_counts),
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
class AssessmentTableDetail(pdt.BaseModel):
|
|
562
|
+
assessment: AssessmentTable
|
|
563
|
+
evaluation: EvaluationTable | None
|
|
564
|
+
promotion: AssessmentPromotionTable | None
|
|
565
|
+
benchmark: BenchmarkTable | None
|
|
566
|
+
experiment: ExperimentTable | None
|
|
567
|
+
dataset: DatasetTable | None
|
|
568
|
+
layouts: list[LayoutTableAndSamplesCount] | None
|
|
569
|
+
samples_count: int
|
|
570
|
+
architecture: ArchitectureTable | None
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
class AssessmentTableDetailsAndPagination(pdt.BaseModel):
|
|
574
|
+
assessment_details: list[AssessmentTableDetail]
|
|
575
|
+
pagination: Pagination
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
@router.get("/list_model_assessments/", response_model=AssessmentTableDetailsAndPagination)
|
|
579
|
+
def list_model_assessments(
|
|
580
|
+
skip: int = Query(0),
|
|
581
|
+
limit: int = Query(100),
|
|
582
|
+
begin_at: datetime.datetime | None = Query(None),
|
|
583
|
+
end_at: datetime.datetime | None = Query(None),
|
|
584
|
+
benchmark: str | None = Query(None),
|
|
585
|
+
name_match: str | None = Query(None),
|
|
586
|
+
contributor_sso_sid: int | None = Query(None),
|
|
587
|
+
only_completed_jobs: bool = Query(True),
|
|
588
|
+
only_shared: bool = Query(True),
|
|
589
|
+
order_by: list[str] | None = Query(None),
|
|
590
|
+
db: sa_orm.Session = Depends(make_session),
|
|
591
|
+
) -> AssessmentTableDetailsAndPagination:
|
|
592
|
+
with managed_db_session(db):
|
|
593
|
+
if benchmark is not None:
|
|
594
|
+
db_benchmark = (
|
|
595
|
+
db
|
|
596
|
+
.query(BenchmarkTable)
|
|
597
|
+
.where(BenchmarkTable.expired_at.is_(None), BenchmarkTable.name == benchmark)
|
|
598
|
+
.one_or_none()
|
|
599
|
+
)
|
|
600
|
+
if db_benchmark is None:
|
|
601
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(BenchmarkTable)}' with name '{benchmark}' not found")
|
|
602
|
+
benchmark_uuid = db_benchmark.uuid
|
|
603
|
+
else:
|
|
604
|
+
benchmark_uuid = None
|
|
605
|
+
|
|
606
|
+
query_stmt = (
|
|
607
|
+
db
|
|
608
|
+
.query(AssessmentTable, EvaluationTable, AssessmentPromotionTable)
|
|
609
|
+
.outerjoin(EvaluationTable, AssessmentTable.uuid == EvaluationTable.assessment_uuid)
|
|
610
|
+
.outerjoin(AssessmentPromotionTable, AssessmentTable.uuid == AssessmentPromotionTable.assessment_uuid)
|
|
611
|
+
.where(AssessmentTable.expired_at.is_(None),
|
|
612
|
+
EvaluationTable.expired_at.is_(None),
|
|
613
|
+
AssessmentPromotionTable.expired_at.is_(None))
|
|
614
|
+
)
|
|
615
|
+
if only_completed_jobs:
|
|
616
|
+
query_stmt = query_stmt.where(sa.or_(EvaluationTable.sqn.is_(None),
|
|
617
|
+
EvaluationTable.flags.op("&")(JobStatusFlag.Completed.value) > 0))
|
|
618
|
+
if only_shared:
|
|
619
|
+
query_stmt = query_stmt.where(AssessmentPromotionTable.flags.op("&")(JobStatusFlag.Completed.value) > 0)
|
|
620
|
+
if begin_at is not None:
|
|
621
|
+
query_stmt = query_stmt.where(AssessmentTable.created_at >= begin_at)
|
|
622
|
+
if end_at is not None:
|
|
623
|
+
query_stmt = query_stmt.where(AssessmentTable.created_at < end_at)
|
|
624
|
+
if benchmark_uuid is not None:
|
|
625
|
+
query_stmt = query_stmt.where(AssessmentTable.benchmark_uuid == benchmark_uuid)
|
|
626
|
+
if name_match is not None:
|
|
627
|
+
query_stmt = query_stmt.where(AssessmentTable.name.ilike(f"%{escape_sql_like(name_match)}%", escape="\\"))
|
|
628
|
+
if contributor_sso_sid is not None:
|
|
629
|
+
query_stmt = query_stmt.where(AssessmentTable.contributor_sso_sid == contributor_sso_sid)
|
|
630
|
+
query_stmt = query_stmt.order_by(*db_make_order_by_clause(AssessmentTable, order_by))
|
|
631
|
+
|
|
632
|
+
total = query_stmt.count()
|
|
633
|
+
if total == 0:
|
|
634
|
+
return AssessmentTableDetailsAndPagination(
|
|
635
|
+
assessment_details=[],
|
|
636
|
+
pagination=Pagination(skip=skip, limit=limit, total=0),
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
db_assessment_evaluation_promotions = query_stmt.offset(skip).limit(limit).all()
|
|
640
|
+
|
|
641
|
+
benchmark_uuids = list(set(db_assessment.benchmark_uuid
|
|
642
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
643
|
+
db_benchmarks = (
|
|
644
|
+
db
|
|
645
|
+
.query(BenchmarkTable)
|
|
646
|
+
.where(BenchmarkTable.expired_at.is_(None), BenchmarkTable.uuid.in_(benchmark_uuids))
|
|
647
|
+
.all()
|
|
648
|
+
)
|
|
649
|
+
db_benchmarks_dict = {db_benchmark.uuid: db_benchmark for db_benchmark in db_benchmarks}
|
|
650
|
+
|
|
651
|
+
experiment_uuids = list(set(db_assessment.experiment_uuid
|
|
652
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
653
|
+
db_experiments = (
|
|
654
|
+
db
|
|
655
|
+
.query(ExperimentTable)
|
|
656
|
+
.where(ExperimentTable.expired_at.is_(None), ExperimentTable.uuid.in_(experiment_uuids))
|
|
657
|
+
.all()
|
|
658
|
+
)
|
|
659
|
+
db_experiments_dict = {db_experiment.uuid: db_experiment for db_experiment in db_experiments}
|
|
660
|
+
|
|
661
|
+
dataset_uuids = list(set(db_assessment.dataset_uuid
|
|
662
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
663
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts(dataset_uuids, db)
|
|
664
|
+
|
|
665
|
+
db_datasets = (
|
|
666
|
+
db
|
|
667
|
+
.query(DatasetTable)
|
|
668
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(dataset_uuids))
|
|
669
|
+
.all()
|
|
670
|
+
)
|
|
671
|
+
db_datasets_dict = {db_dataset.uuid: db_dataset for db_dataset in db_datasets}
|
|
672
|
+
|
|
673
|
+
architecture_uuids = list(set(db_experiment.architecture_uuid for db_experiment in db_experiments))
|
|
674
|
+
db_architectures = (
|
|
675
|
+
db
|
|
676
|
+
.query(ArchitectureTable)
|
|
677
|
+
.where(ArchitectureTable.expired_at.is_(None), ArchitectureTable.uuid.in_(architecture_uuids))
|
|
678
|
+
.all()
|
|
679
|
+
)
|
|
680
|
+
db_architectures_dict = {db_architecture.uuid: db_architecture for db_architecture in db_architectures}
|
|
681
|
+
|
|
682
|
+
return AssessmentTableDetailsAndPagination(
|
|
683
|
+
assessment_details=[
|
|
684
|
+
AssessmentTableDetail(
|
|
685
|
+
assessment=db_assessment,
|
|
686
|
+
evaluation=db_evaluation,
|
|
687
|
+
promotion=db_promotion,
|
|
688
|
+
benchmark=db_benchmarks_dict.get(db_assessment.benchmark_uuid),
|
|
689
|
+
experiment=db_experiments_dict.get(db_assessment.experiment_uuid),
|
|
690
|
+
dataset=db_datasets_dict.get(db_assessment.dataset_uuid),
|
|
691
|
+
layouts=layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, []),
|
|
692
|
+
samples_count=sum(layout_samples_count.samples_count
|
|
693
|
+
for layout_samples_count in
|
|
694
|
+
layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, [])),
|
|
695
|
+
architecture=(
|
|
696
|
+
db_architectures_dict.get(
|
|
697
|
+
db_experiments_dict.get(db_assessment.experiment_uuid).architecture_uuid)
|
|
698
|
+
if db_assessment.experiment_uuid in db_experiments_dict else None
|
|
699
|
+
),
|
|
700
|
+
)
|
|
701
|
+
for db_assessment, db_evaluation, db_promotion in db_assessment_evaluation_promotions
|
|
702
|
+
],
|
|
703
|
+
pagination=Pagination(skip=skip, limit=limit, total=total),
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
@router.get("/get_model_assessment/{assessment_uuid}", response_model=AssessmentTableDetail)
|
|
708
|
+
def get_model_assessment(
|
|
709
|
+
assessment_uuid: py_uuid.UUID = Path(),
|
|
710
|
+
db: sa_orm.Session = Depends(make_session),
|
|
711
|
+
) -> AssessmentTableDetail:
|
|
712
|
+
with managed_db_session(db):
|
|
713
|
+
db_assessment_evaluation_promotion = (
|
|
714
|
+
db
|
|
715
|
+
.query(AssessmentTable, EvaluationTable, AssessmentPromotionTable)
|
|
716
|
+
.outerjoin(EvaluationTable, AssessmentTable.uuid == EvaluationTable.assessment_uuid)
|
|
717
|
+
.outerjoin(AssessmentPromotionTable, AssessmentTable.uuid == AssessmentPromotionTable.assessment_uuid)
|
|
718
|
+
.where(AssessmentTable.expired_at.is_(None),
|
|
719
|
+
EvaluationTable.expired_at.is_(None),
|
|
720
|
+
AssessmentPromotionTable.expired_at.is_(None),
|
|
721
|
+
AssessmentTable.uuid == assessment_uuid)
|
|
722
|
+
.one_or_none()
|
|
723
|
+
)
|
|
724
|
+
if db_assessment_evaluation_promotion is None:
|
|
725
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(AssessmentTable)}' with uuid '{assessment_uuid}' not found")
|
|
726
|
+
|
|
727
|
+
db_assessment, db_evaluation, db_promotion = db_assessment_evaluation_promotion
|
|
728
|
+
|
|
729
|
+
db_benchmark = (
|
|
730
|
+
db
|
|
731
|
+
.query(BenchmarkTable)
|
|
732
|
+
.where(BenchmarkTable.expired_at.is_(None), BenchmarkTable.uuid == db_assessment.benchmark_uuid)
|
|
733
|
+
.one_or_none()
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
db_experiment = (
|
|
737
|
+
db
|
|
738
|
+
.query(ExperimentTable)
|
|
739
|
+
.where(ExperimentTable.expired_at.is_(None), ExperimentTable.uuid == db_assessment.experiment_uuid)
|
|
740
|
+
.one_or_none()
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts([db_assessment.dataset_uuid], db)
|
|
744
|
+
layout_samples_counts = layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, [])
|
|
745
|
+
|
|
746
|
+
db_dataset = (
|
|
747
|
+
db
|
|
748
|
+
.query(DatasetTable)
|
|
749
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == db_assessment.dataset_uuid)
|
|
750
|
+
.one_or_none()
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
db_architecture = (
|
|
754
|
+
db
|
|
755
|
+
.query(ArchitectureTable)
|
|
756
|
+
.where(ArchitectureTable.expired_at.is_(None), ArchitectureTable.uuid == db_experiment.architecture_uuid)
|
|
757
|
+
.one_or_none()
|
|
758
|
+
) if db_experiment is not None else None
|
|
759
|
+
|
|
760
|
+
return AssessmentTableDetail(
|
|
761
|
+
assessment=db_assessment,
|
|
762
|
+
evaluation=db_evaluation,
|
|
763
|
+
promotion=db_promotion,
|
|
764
|
+
benchmark=db_benchmark,
|
|
765
|
+
experiment=db_experiment,
|
|
766
|
+
dataset=db_dataset,
|
|
767
|
+
layouts=layout_samples_counts,
|
|
768
|
+
samples_count=sum(layout_samples_count.samples_count for layout_samples_count in layout_samples_counts),
|
|
769
|
+
architecture=db_architecture,
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
class DatasetWithSamples(pdt.BaseModel):
|
|
774
|
+
dataset: Dataset
|
|
775
|
+
samples: list[Sample] = pdt.Field(default_factory=list)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
class DatasetTableWithSampleTables(pdt.BaseModel):
|
|
779
|
+
dataset: DatasetTable
|
|
780
|
+
layouts: list[LayoutTableAndSamplesCount] = pdt.Field(default_factory=list)
|
|
781
|
+
samples: list[SampleTable] = pdt.Field(default_factory=list)
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
@router.post("/create_dataset_with_samples/", response_model=DatasetTableWithSampleTables)
|
|
785
|
+
def create_dataset_with_samples(
|
|
786
|
+
dataset_with_samples: DatasetWithSamples = Body(),
|
|
787
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
788
|
+
db: sa_orm.Session = Depends(make_session),
|
|
789
|
+
) -> DatasetTableWithSampleTables:
|
|
790
|
+
with managed_db_session(db):
|
|
791
|
+
db_dataset = db_create_revision_model(db, DatasetTable, dataset_with_samples.dataset, current_dt)
|
|
792
|
+
|
|
793
|
+
db_samples = db_create_revision_models(db, SampleTable, dataset_with_samples.samples, current_dt)
|
|
794
|
+
db.add_all([DatasetSampleTable(dataset_uuid=db_dataset.uuid, sample_uuid=db_sample.uuid)
|
|
795
|
+
for db_sample in db_samples])
|
|
796
|
+
db.flush()
|
|
797
|
+
|
|
798
|
+
samples_counts = Counter(db_sample.layout_uuid for db_sample in db_samples)
|
|
799
|
+
|
|
800
|
+
db.add_all([DatasetLayoutTable(dataset_uuid=db_dataset.uuid,
|
|
801
|
+
layout_uuid=layout_uuid,
|
|
802
|
+
samples_count=samples_count)
|
|
803
|
+
for layout_uuid, samples_count in samples_counts.items()])
|
|
804
|
+
db.flush()
|
|
805
|
+
|
|
806
|
+
return get_dataset_with_samples(dataset_uuid=db_dataset.uuid, db=db)
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
@router.post("/append_dataset_with_samples/{dataset_uuid}", response_model=DatasetTableWithSampleTables)
|
|
810
|
+
def append_dataset_with_samples(
|
|
811
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
812
|
+
samples: list[Sample] = Body(),
|
|
813
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
814
|
+
db: sa_orm.Session = Depends(make_session),
|
|
815
|
+
) -> DatasetTableWithSampleTables:
|
|
816
|
+
with managed_db_session(db):
|
|
817
|
+
db_dataset = (
|
|
818
|
+
db
|
|
819
|
+
.query(DatasetTable)
|
|
820
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == dataset_uuid)
|
|
821
|
+
.one_or_none()
|
|
822
|
+
)
|
|
823
|
+
if db_dataset is None:
|
|
824
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
825
|
+
|
|
826
|
+
db_samples = db_create_revision_models(db, SampleTable, samples, current_dt)
|
|
827
|
+
db.add_all([DatasetSampleTable(dataset_uuid=db_dataset.uuid, sample_uuid=db_sample.uuid)
|
|
828
|
+
for db_sample in db_samples])
|
|
829
|
+
db.flush()
|
|
830
|
+
|
|
831
|
+
samples_counts = Counter(db_sample.layout_uuid for db_sample in db_samples)
|
|
832
|
+
|
|
833
|
+
db_dataset_layouts = (
|
|
834
|
+
db
|
|
835
|
+
.query(DatasetLayoutTable)
|
|
836
|
+
.where(DatasetLayoutTable.dataset_uuid == db_dataset.uuid,
|
|
837
|
+
DatasetLayoutTable.layout_uuid.in_(list(samples_counts)))
|
|
838
|
+
.all()
|
|
839
|
+
)
|
|
840
|
+
db_dataset_layouts_dict = {db_dataset_layout.layout_uuid: db_dataset_layout
|
|
841
|
+
for db_dataset_layout in db_dataset_layouts}
|
|
842
|
+
|
|
843
|
+
new_db_dataset_layouts = []
|
|
844
|
+
for layout_uuid, samples_count in samples_counts.items():
|
|
845
|
+
db_dataset_layout = db_dataset_layouts_dict.get(layout_uuid)
|
|
846
|
+
if db_dataset_layout is None:
|
|
847
|
+
new_db_dataset_layouts.append(DatasetLayoutTable(dataset_uuid=db_dataset.uuid,
|
|
848
|
+
layout_uuid=layout_uuid,
|
|
849
|
+
samples_count=samples_count))
|
|
850
|
+
else:
|
|
851
|
+
db_dataset_layout.samples_count = db_dataset_layout.samples_count + samples_count
|
|
852
|
+
if new_db_dataset_layouts:
|
|
853
|
+
db.add_all(new_db_dataset_layouts)
|
|
854
|
+
db.flush()
|
|
855
|
+
|
|
856
|
+
return get_dataset_with_samples(dataset_uuid=db_dataset.uuid, db=db)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
@router.get("/get_dataset_with_samples/{dataset_uuid}", response_model=DatasetTableWithSampleTables)
|
|
860
|
+
def get_dataset_with_samples(
|
|
861
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
862
|
+
db: sa_orm.Session = Depends(make_session),
|
|
863
|
+
) -> DatasetTableWithSampleTables:
|
|
864
|
+
with managed_db_session(db):
|
|
865
|
+
db_dataset = (
|
|
866
|
+
db
|
|
867
|
+
.query(DatasetTable)
|
|
868
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == dataset_uuid)
|
|
869
|
+
.one_or_none()
|
|
870
|
+
)
|
|
871
|
+
if db_dataset is None:
|
|
872
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
873
|
+
|
|
874
|
+
db_layout_dataset_layouts = (
|
|
875
|
+
db
|
|
876
|
+
.query(LayoutTable, DatasetLayoutTable)
|
|
877
|
+
.join(DatasetLayoutTable, LayoutTable.uuid == DatasetLayoutTable.layout_uuid)
|
|
878
|
+
.where(LayoutTable.expired_at.is_(None),
|
|
879
|
+
DatasetLayoutTable.dataset_uuid == db_dataset.uuid)
|
|
880
|
+
.order_by(LayoutTable.sqn)
|
|
881
|
+
.all()
|
|
882
|
+
)
|
|
883
|
+
layout_and_samples_counts = [LayoutTableAndSamplesCount(layout=db_layout,
|
|
884
|
+
samples_count=db_dataset_layout.samples_count)
|
|
885
|
+
for db_layout, db_dataset_layout in db_layout_dataset_layouts]
|
|
886
|
+
|
|
887
|
+
db_samples = (
|
|
888
|
+
db
|
|
889
|
+
.query(SampleTable)
|
|
890
|
+
.join(DatasetSampleTable, SampleTable.uuid == DatasetSampleTable.sample_uuid)
|
|
891
|
+
.where(SampleTable.expired_at.is_(None),
|
|
892
|
+
DatasetSampleTable.dataset_uuid == dataset_uuid)
|
|
893
|
+
.order_by(SampleTable.sqn)
|
|
894
|
+
.all()
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
return DatasetTableWithSampleTables(dataset=db_dataset, layouts=layout_and_samples_counts, samples=db_samples)
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
class SampleAndAnnotation(pdt.BaseModel):
|
|
901
|
+
sample: Sample
|
|
902
|
+
annotation: Annotation
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
class DatasetWithSampleAndAnnotations(pdt.BaseModel):
|
|
906
|
+
dataset: Dataset
|
|
907
|
+
sample_and_annotations: list[SampleAndAnnotation] = pdt.Field(default_factory=list)
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
class SampleTableAndAnnotationTable(pdt.BaseModel):
|
|
911
|
+
sample: SampleTable
|
|
912
|
+
annotation: AnnotationTable
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
class DatasetTableWithSampleTableAndAnnotationTables(pdt.BaseModel):
|
|
916
|
+
dataset: DatasetTable
|
|
917
|
+
layouts: list[LayoutTableAndSamplesCount] = pdt.Field(default_factory=list)
|
|
918
|
+
sample_and_annotations: list[SampleTableAndAnnotationTable] = pdt.Field(default_factory=list)
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
@router.post("/create_dataset_with_sample_and_annotations/",
|
|
922
|
+
response_model=DatasetTableWithSampleTableAndAnnotationTables)
|
|
923
|
+
def create_dataset_with_sample_and_annotations(
|
|
924
|
+
dataset_with_sample_and_annotations: DatasetWithSampleAndAnnotations = Body(),
|
|
925
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
926
|
+
db: sa_orm.Session = Depends(make_session),
|
|
927
|
+
) -> DatasetTableWithSampleTableAndAnnotationTables:
|
|
928
|
+
with managed_db_session(db):
|
|
929
|
+
db_dataset = db_create_revision_model(db,
|
|
930
|
+
DatasetTable,
|
|
931
|
+
dataset_with_sample_and_annotations.dataset,
|
|
932
|
+
current_dt)
|
|
933
|
+
|
|
934
|
+
db_samples = db_create_revision_models(
|
|
935
|
+
db,
|
|
936
|
+
SampleTable,
|
|
937
|
+
[sample_and_annotation.sample
|
|
938
|
+
for sample_and_annotation in dataset_with_sample_and_annotations.sample_and_annotations],
|
|
939
|
+
current_dt,
|
|
940
|
+
)
|
|
941
|
+
db_annotations = db_create_revision_models(
|
|
942
|
+
db,
|
|
943
|
+
AnnotationTable,
|
|
944
|
+
[sample_and_annotation.annotation
|
|
945
|
+
for sample_and_annotation in dataset_with_sample_and_annotations.sample_and_annotations],
|
|
946
|
+
current_dt,
|
|
947
|
+
)
|
|
948
|
+
db.add_all([DatasetSampleTable(dataset_uuid=db_dataset.uuid,
|
|
949
|
+
sample_uuid=db_sample.uuid,
|
|
950
|
+
annotation_uuid=db_annotation.uuid)
|
|
951
|
+
for db_sample, db_annotation in zip(db_samples, db_annotations)])
|
|
952
|
+
db.flush()
|
|
953
|
+
|
|
954
|
+
samples_counts = Counter(db_sample.layout_uuid for db_sample in db_samples)
|
|
955
|
+
|
|
956
|
+
db.add_all([DatasetLayoutTable(dataset_uuid=db_dataset.uuid,
|
|
957
|
+
layout_uuid=layout_uuid,
|
|
958
|
+
samples_count=samples_count)
|
|
959
|
+
for layout_uuid, samples_count in samples_counts.items()])
|
|
960
|
+
db.flush()
|
|
961
|
+
|
|
962
|
+
return get_dataset_with_sample_and_annotations(dataset_uuid=db_dataset.uuid, db=db)
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
@router.post("/append_dataset_with_sample_and_annotations/{dataset_uuid}",
|
|
966
|
+
response_model=DatasetTableWithSampleTableAndAnnotationTables)
|
|
967
|
+
def append_dataset_with_sample_and_annotations(
|
|
968
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
969
|
+
sample_and_annotations: list[SampleAndAnnotation] = Body(),
|
|
970
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
971
|
+
db: sa_orm.Session = Depends(make_session),
|
|
972
|
+
) -> DatasetTableWithSampleTableAndAnnotationTables:
|
|
973
|
+
with managed_db_session(db):
|
|
974
|
+
db_dataset = (
|
|
975
|
+
db
|
|
976
|
+
.query(DatasetTable)
|
|
977
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == dataset_uuid)
|
|
978
|
+
.one_or_none()
|
|
979
|
+
)
|
|
980
|
+
if db_dataset is None:
|
|
981
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
982
|
+
|
|
983
|
+
db_samples = db_create_revision_models(
|
|
984
|
+
db,
|
|
985
|
+
SampleTable,
|
|
986
|
+
[sample_and_annotation.sample for sample_and_annotation in sample_and_annotations],
|
|
987
|
+
current_dt,
|
|
988
|
+
)
|
|
989
|
+
db_annotations = db_create_revision_models(
|
|
990
|
+
db,
|
|
991
|
+
AnnotationTable,
|
|
992
|
+
[sample_and_annotation.annotation for sample_and_annotation in sample_and_annotations],
|
|
993
|
+
current_dt,
|
|
994
|
+
)
|
|
995
|
+
db.add_all([DatasetSampleTable(dataset_uuid=db_dataset.uuid,
|
|
996
|
+
sample_uuid=db_sample.uuid,
|
|
997
|
+
annotation_uuid=db_annotation.uuid)
|
|
998
|
+
for db_sample, db_annotation in zip(db_samples, db_annotations)])
|
|
999
|
+
db.flush()
|
|
1000
|
+
|
|
1001
|
+
samples_counts = Counter(db_sample.layout_uuid for db_sample in db_samples)
|
|
1002
|
+
|
|
1003
|
+
db_dataset_layouts = (
|
|
1004
|
+
db
|
|
1005
|
+
.query(DatasetLayoutTable)
|
|
1006
|
+
.where(DatasetLayoutTable.dataset_uuid == db_dataset.uuid,
|
|
1007
|
+
DatasetLayoutTable.layout_uuid.in_(list(samples_counts)))
|
|
1008
|
+
.all()
|
|
1009
|
+
)
|
|
1010
|
+
db_dataset_layouts_dict = {db_dataset_layout.layout_uuid: db_dataset_layout
|
|
1011
|
+
for db_dataset_layout in db_dataset_layouts}
|
|
1012
|
+
|
|
1013
|
+
new_db_dataset_layouts = []
|
|
1014
|
+
for layout_uuid, samples_count in samples_counts.items():
|
|
1015
|
+
db_dataset_layout = db_dataset_layouts_dict.get(layout_uuid)
|
|
1016
|
+
if db_dataset_layout is None:
|
|
1017
|
+
new_db_dataset_layouts.append(DatasetLayoutTable(dataset_uuid=db_dataset.uuid,
|
|
1018
|
+
layout_uuid=layout_uuid,
|
|
1019
|
+
samples_count=samples_count))
|
|
1020
|
+
else:
|
|
1021
|
+
db_dataset_layout.samples_count = db_dataset_layout.samples_count + samples_count
|
|
1022
|
+
if new_db_dataset_layouts:
|
|
1023
|
+
db.add_all(new_db_dataset_layouts)
|
|
1024
|
+
db.flush()
|
|
1025
|
+
|
|
1026
|
+
return get_dataset_with_sample_and_annotations(dataset_uuid=db_dataset.uuid, db=db)
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
@router.get("/get_dataset_with_sample_and_annotations/{dataset_uuid}",
|
|
1030
|
+
response_model=DatasetTableWithSampleTableAndAnnotationTables)
|
|
1031
|
+
def get_dataset_with_sample_and_annotations(
|
|
1032
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
1033
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1034
|
+
) -> DatasetTableWithSampleTableAndAnnotationTables:
|
|
1035
|
+
with managed_db_session(db):
|
|
1036
|
+
db_dataset = (
|
|
1037
|
+
db
|
|
1038
|
+
.query(DatasetTable)
|
|
1039
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == dataset_uuid)
|
|
1040
|
+
.one_or_none()
|
|
1041
|
+
)
|
|
1042
|
+
if db_dataset is None:
|
|
1043
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
1044
|
+
|
|
1045
|
+
db_layout_dataset_layouts = (
|
|
1046
|
+
db
|
|
1047
|
+
.query(LayoutTable, DatasetLayoutTable)
|
|
1048
|
+
.join(DatasetLayoutTable, LayoutTable.uuid == DatasetLayoutTable.layout_uuid)
|
|
1049
|
+
.where(LayoutTable.expired_at.is_(None),
|
|
1050
|
+
DatasetLayoutTable.dataset_uuid == db_dataset.uuid)
|
|
1051
|
+
.order_by(LayoutTable.sqn)
|
|
1052
|
+
.all()
|
|
1053
|
+
)
|
|
1054
|
+
layout_and_samples_counts = [LayoutTableAndSamplesCount(layout=db_layout,
|
|
1055
|
+
samples_count=db_dataset_layout.samples_count)
|
|
1056
|
+
for db_layout, db_dataset_layout in db_layout_dataset_layouts]
|
|
1057
|
+
|
|
1058
|
+
db_dataset_sample_sample_annotations = (
|
|
1059
|
+
db
|
|
1060
|
+
.query(DatasetSampleTable, SampleTable, AnnotationTable)
|
|
1061
|
+
.join(SampleTable, SampleTable.uuid == DatasetSampleTable.sample_uuid)
|
|
1062
|
+
.join(AnnotationTable, AnnotationTable.uuid == DatasetSampleTable.annotation_uuid)
|
|
1063
|
+
.where(SampleTable.expired_at.is_(None),
|
|
1064
|
+
AnnotationTable.expired_at.is_(None),
|
|
1065
|
+
DatasetSampleTable.dataset_uuid == dataset_uuid)
|
|
1066
|
+
.order_by(SampleTable.sqn)
|
|
1067
|
+
.all()
|
|
1068
|
+
)
|
|
1069
|
+
db_sample_and_annotations = [SampleTableAndAnnotationTable(sample=db_sample, annotation=db_annotation)
|
|
1070
|
+
for _, db_sample, db_annotation in db_dataset_sample_sample_annotations]
|
|
1071
|
+
|
|
1072
|
+
return DatasetTableWithSampleTableAndAnnotationTables(
|
|
1073
|
+
dataset=db_dataset,
|
|
1074
|
+
layouts=layout_and_samples_counts,
|
|
1075
|
+
sample_and_annotations=db_sample_and_annotations,
|
|
1076
|
+
)
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
class SampleWithClassifiers(pdt.BaseModel):
|
|
1080
|
+
sample: Sample
|
|
1081
|
+
classifiers: list[Classifier] = pdt.Field(default_factory=list)
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
class DatasetWithSamplesWithClassifiers(pdt.BaseModel):
|
|
1085
|
+
dataset: Dataset
|
|
1086
|
+
samples_with_classifiers: list[SampleWithClassifiers] = pdt.Field(default_factory=list)
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
class SampleTableWithClassifierTables(pdt.BaseModel):
|
|
1090
|
+
sample: SampleTable
|
|
1091
|
+
classifiers: list[ClassifierTable] = pdt.Field(default_factory=list)
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
class DatasetTableWithSampleTablesWithClassifierTables(pdt.BaseModel):
|
|
1095
|
+
dataset: DatasetTable
|
|
1096
|
+
layouts: list[LayoutTableAndSamplesCount] = pdt.Field(default_factory=list)
|
|
1097
|
+
samples_with_classifiers: list[SampleTableWithClassifierTables] = pdt.Field(default_factory=list)
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
@router.post("/create_dataset_with_samples_with_classifiers",
|
|
1101
|
+
response_model=DatasetTableWithSampleTablesWithClassifierTables)
|
|
1102
|
+
def create_dataset_with_samples_with_classifiers(
|
|
1103
|
+
dataset_with_samples_with_classifiers: DatasetWithSamplesWithClassifiers = Body(),
|
|
1104
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
1105
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1106
|
+
) -> DatasetTableWithSampleTablesWithClassifierTables:
|
|
1107
|
+
with managed_db_session(db):
|
|
1108
|
+
db_dataset = db_create_revision_model(db,
|
|
1109
|
+
DatasetTable,
|
|
1110
|
+
dataset_with_samples_with_classifiers.dataset,
|
|
1111
|
+
current_dt)
|
|
1112
|
+
|
|
1113
|
+
db_samples = db_create_revision_models(
|
|
1114
|
+
db,
|
|
1115
|
+
SampleTable,
|
|
1116
|
+
[sample_with_classifiers.sample
|
|
1117
|
+
for sample_with_classifiers in dataset_with_samples_with_classifiers.samples_with_classifiers],
|
|
1118
|
+
current_dt,
|
|
1119
|
+
)
|
|
1120
|
+
db_classifiers = db_create_revision_models(
|
|
1121
|
+
db,
|
|
1122
|
+
ClassifierTable,
|
|
1123
|
+
[classifier
|
|
1124
|
+
for sample_with_classifiers in dataset_with_samples_with_classifiers.samples_with_classifiers
|
|
1125
|
+
for classifier in sample_with_classifiers.classifiers],
|
|
1126
|
+
current_dt,
|
|
1127
|
+
)
|
|
1128
|
+
db.add_all([DatasetSampleTable(dataset_uuid=db_dataset.uuid, sample_uuid=db_sample.uuid)
|
|
1129
|
+
for db_sample in db_samples])
|
|
1130
|
+
db.flush()
|
|
1131
|
+
|
|
1132
|
+
samples_counts = Counter(db_sample.layout_uuid for db_sample in db_samples)
|
|
1133
|
+
|
|
1134
|
+
db.add_all([DatasetLayoutTable(dataset_uuid=db_dataset.uuid,
|
|
1135
|
+
layout_uuid=layout_uuid,
|
|
1136
|
+
samples_count=samples_count)
|
|
1137
|
+
for layout_uuid, samples_count in samples_counts.items()])
|
|
1138
|
+
db.flush()
|
|
1139
|
+
|
|
1140
|
+
return get_dataset_with_samples_with_classifiers(dataset_uuid=db_dataset.uuid, db=db)
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
@router.post("/append_dataset_with_samples_with_classifiers/{dataset_uuid}",
|
|
1144
|
+
response_model=DatasetTableWithSampleTablesWithClassifierTables)
|
|
1145
|
+
def append_dataset_with_samples_with_classifiers(
|
|
1146
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
1147
|
+
samples_with_classifiers: list[SampleWithClassifiers] = Body(),
|
|
1148
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
1149
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1150
|
+
) -> DatasetTableWithSampleTablesWithClassifierTables:
|
|
1151
|
+
with managed_db_session(db):
|
|
1152
|
+
db_dataset = (
|
|
1153
|
+
db
|
|
1154
|
+
.query(DatasetTable)
|
|
1155
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == dataset_uuid)
|
|
1156
|
+
.one_or_none()
|
|
1157
|
+
)
|
|
1158
|
+
if db_dataset is None:
|
|
1159
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
1160
|
+
|
|
1161
|
+
db_samples = db_create_revision_models(
|
|
1162
|
+
db,
|
|
1163
|
+
SampleTable,
|
|
1164
|
+
[sample_with_classifiers.sample for sample_with_classifiers in samples_with_classifiers],
|
|
1165
|
+
current_dt,
|
|
1166
|
+
)
|
|
1167
|
+
db_classifiers = db_create_revision_models(
|
|
1168
|
+
db,
|
|
1169
|
+
ClassifierTable,
|
|
1170
|
+
[classifier
|
|
1171
|
+
for sample_with_classifiers in samples_with_classifiers
|
|
1172
|
+
for classifier in sample_with_classifiers.classifiers],
|
|
1173
|
+
current_dt,
|
|
1174
|
+
)
|
|
1175
|
+
db.add_all([DatasetSampleTable(dataset_uuid=db_dataset.uuid, sample_uuid=db_sample.uuid)
|
|
1176
|
+
for db_sample in db_samples])
|
|
1177
|
+
db.flush()
|
|
1178
|
+
|
|
1179
|
+
samples_counts = Counter(db_sample.layout_uuid for db_sample in db_samples)
|
|
1180
|
+
|
|
1181
|
+
db_dataset_layouts = (
|
|
1182
|
+
db
|
|
1183
|
+
.query(DatasetLayoutTable)
|
|
1184
|
+
.where(DatasetLayoutTable.dataset_uuid == db_dataset.uuid,
|
|
1185
|
+
DatasetLayoutTable.layout_uuid.in_(list(samples_counts)))
|
|
1186
|
+
.all()
|
|
1187
|
+
)
|
|
1188
|
+
db_dataset_layouts_dict = {db_dataset_layout.layout_uuid: db_dataset_layout
|
|
1189
|
+
for db_dataset_layout in db_dataset_layouts}
|
|
1190
|
+
|
|
1191
|
+
new_db_dataset_layouts = []
|
|
1192
|
+
for layout_uuid, samples_count in samples_counts.items():
|
|
1193
|
+
db_dataset_layout = db_dataset_layouts_dict.get(layout_uuid)
|
|
1194
|
+
if db_dataset_layout is None:
|
|
1195
|
+
new_db_dataset_layouts.append(DatasetLayoutTable(dataset_uuid=db_dataset.uuid,
|
|
1196
|
+
layout_uuid=layout_uuid,
|
|
1197
|
+
samples_count=samples_count))
|
|
1198
|
+
else:
|
|
1199
|
+
db_dataset_layout.samples_count = db_dataset_layout.samples_count + samples_count
|
|
1200
|
+
if new_db_dataset_layouts:
|
|
1201
|
+
db.add_all(new_db_dataset_layouts)
|
|
1202
|
+
db.flush()
|
|
1203
|
+
|
|
1204
|
+
return get_dataset_with_samples_with_classifiers(dataset_uuid=db_dataset.uuid, db=db)
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
@router.get("/get_dataset_with_samples_with_classifiers/{dataset_uuid}",
|
|
1208
|
+
response_model=DatasetTableWithSampleTablesWithClassifierTables)
|
|
1209
|
+
def get_dataset_with_samples_with_classifiers(
|
|
1210
|
+
dataset_uuid: py_uuid.UUID = Path(),
|
|
1211
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1212
|
+
) -> DatasetTableWithSampleTablesWithClassifierTables:
|
|
1213
|
+
with managed_db_session(db):
|
|
1214
|
+
db_dataset = (
|
|
1215
|
+
db
|
|
1216
|
+
.query(DatasetTable)
|
|
1217
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid == dataset_uuid)
|
|
1218
|
+
.one_or_none()
|
|
1219
|
+
)
|
|
1220
|
+
if db_dataset is None:
|
|
1221
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
1222
|
+
|
|
1223
|
+
db_layout_dataset_layouts = (
|
|
1224
|
+
db
|
|
1225
|
+
.query(LayoutTable, DatasetLayoutTable)
|
|
1226
|
+
.join(DatasetLayoutTable, LayoutTable.uuid == DatasetLayoutTable.layout_uuid)
|
|
1227
|
+
.where(LayoutTable.expired_at.is_(None),
|
|
1228
|
+
DatasetLayoutTable.dataset_uuid == db_dataset.uuid)
|
|
1229
|
+
.order_by(LayoutTable.sqn)
|
|
1230
|
+
.all()
|
|
1231
|
+
)
|
|
1232
|
+
layout_and_samples_counts = [LayoutTableAndSamplesCount(layout=db_layout,
|
|
1233
|
+
samples_count=db_dataset_layout.samples_count)
|
|
1234
|
+
for db_layout, db_dataset_layout in db_layout_dataset_layouts]
|
|
1235
|
+
|
|
1236
|
+
db_samples = (
|
|
1237
|
+
db
|
|
1238
|
+
.query(SampleTable)
|
|
1239
|
+
.join(DatasetSampleTable, SampleTable.uuid == DatasetSampleTable.sample_uuid)
|
|
1240
|
+
.where(SampleTable.expired_at.is_(None),
|
|
1241
|
+
DatasetSampleTable.dataset_uuid == dataset_uuid)
|
|
1242
|
+
.order_by(SampleTable.sqn)
|
|
1243
|
+
.all()
|
|
1244
|
+
)
|
|
1245
|
+
db_classifiers = (
|
|
1246
|
+
db
|
|
1247
|
+
.query(ClassifierTable)
|
|
1248
|
+
.join(DatasetSampleTable, ClassifierTable.sample_uuid == DatasetSampleTable.sample_uuid)
|
|
1249
|
+
.where(ClassifierTable.expired_at.is_(None),
|
|
1250
|
+
DatasetSampleTable.dataset_uuid == dataset_uuid)
|
|
1251
|
+
.order_by(ClassifierTable.sqn)
|
|
1252
|
+
.all()
|
|
1253
|
+
)
|
|
1254
|
+
db_samples_classifiers_dict = {}
|
|
1255
|
+
for db_classifier in db_classifiers:
|
|
1256
|
+
db_samples_classifiers_dict.setdefault(db_classifier.sample_uuid, []).append(db_classifier)
|
|
1257
|
+
db_samples_with_classifiers = [
|
|
1258
|
+
SampleTableWithClassifierTables(sample=db_sample,
|
|
1259
|
+
classifiers=db_samples_classifiers_dict.get(db_sample.uuid, []))
|
|
1260
|
+
for db_sample in db_samples
|
|
1261
|
+
]
|
|
1262
|
+
|
|
1263
|
+
return DatasetTableWithSampleTablesWithClassifierTables(
|
|
1264
|
+
dataset=db_dataset,
|
|
1265
|
+
layouts=layout_and_samples_counts,
|
|
1266
|
+
samples_with_classifiers=db_samples_with_classifiers,
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
class DatasetConditions(pdt.BaseModel):
|
|
1271
|
+
included_dataset_uuids: list[py_uuid.UUID]
|
|
1272
|
+
excluded_dataset_uuids: list[py_uuid.UUID] = pdt.Field(default_factory=list)
|
|
1273
|
+
required_tags: list[str] = pdt.Field(default_factory=list)
|
|
1274
|
+
unwanted_tags: list[str] = pdt.Field(default_factory=list)
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
class CreateDatasetFromConditionsRequest(pdt.BaseModel):
|
|
1278
|
+
dataset: Dataset
|
|
1279
|
+
conditions: DatasetConditions
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
@router.post("/create_dataset_from_conditions/", response_model=DatasetTableDetail)
|
|
1283
|
+
def create_dataset_from_conditions(
|
|
1284
|
+
request: CreateDatasetFromConditionsRequest = Body(),
|
|
1285
|
+
current_dt: datetime.datetime = Depends(current_dt_clock),
|
|
1286
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1287
|
+
) -> DatasetTableDetail:
|
|
1288
|
+
with managed_db_session(db):
|
|
1289
|
+
db_dataset = db_create_revision_model(db, DatasetTable, request.dataset, current_dt)
|
|
1290
|
+
|
|
1291
|
+
query_stmt = (
|
|
1292
|
+
sa
|
|
1293
|
+
.select(
|
|
1294
|
+
sa.literal(db_dataset.uuid).label("dataset_uuid"),
|
|
1295
|
+
DatasetSampleTable.sample_uuid.label("sample_uuid"),
|
|
1296
|
+
)
|
|
1297
|
+
.select_from(DatasetSampleTable)
|
|
1298
|
+
.where(DatasetSampleTable.dataset_uuid.in_(request.conditions.included_dataset_uuids))
|
|
1299
|
+
)
|
|
1300
|
+
if request.conditions.excluded_dataset_uuids:
|
|
1301
|
+
query_stmt = query_stmt.where(
|
|
1302
|
+
DatasetSampleTable.dataset_uuid.notin_(request.conditions.excluded_dataset_uuids),
|
|
1303
|
+
)
|
|
1304
|
+
if request.conditions.required_tags or request.conditions.unwanted_tags:
|
|
1305
|
+
query_stmt = query_stmt.join(SampleTable, SampleTable.uuid == DatasetSampleTable.sample_uuid)
|
|
1306
|
+
for tag in (request.conditions.required_tags or []):
|
|
1307
|
+
query_stmt = query_stmt.where(SampleTable.expired_at.is_(None), SampleTable.tags.contains([tag]))
|
|
1308
|
+
for tag in (request.conditions.unwanted_tags or []):
|
|
1309
|
+
query_stmt = query_stmt.where(SampleTable.expired_at.is_(None), ~SampleTable.tags.contains([tag]))
|
|
1310
|
+
insert_stmt = (
|
|
1311
|
+
sa
|
|
1312
|
+
.insert(DatasetSampleTable)
|
|
1313
|
+
.from_select(["dataset_uuid", "sample_uuid"], query_stmt)
|
|
1314
|
+
)
|
|
1315
|
+
db.execute(insert_stmt)
|
|
1316
|
+
db.flush()
|
|
1317
|
+
|
|
1318
|
+
query_stmt = (
|
|
1319
|
+
sa
|
|
1320
|
+
.select(
|
|
1321
|
+
sa.literal(db_dataset.uuid).label("dataset_uuid"),
|
|
1322
|
+
SampleTable.layout_uuid.label("layout_uuid"),
|
|
1323
|
+
sa.func.count().label("samples_count"),
|
|
1324
|
+
)
|
|
1325
|
+
.select_from(SampleTable)
|
|
1326
|
+
.join(DatasetSampleTable, SampleTable.uuid == DatasetSampleTable.sample_uuid)
|
|
1327
|
+
.where(SampleTable.expired_at.is_(None),
|
|
1328
|
+
DatasetSampleTable.dataset_uuid == db_dataset.uuid)
|
|
1329
|
+
.group_by(SampleTable.layout_uuid)
|
|
1330
|
+
)
|
|
1331
|
+
insert_stmt = (
|
|
1332
|
+
sa
|
|
1333
|
+
.insert(DatasetLayoutTable)
|
|
1334
|
+
.from_select(["dataset_uuid", "layout_uuid", "samples_count"], query_stmt)
|
|
1335
|
+
)
|
|
1336
|
+
db.execute(insert_stmt)
|
|
1337
|
+
db.flush()
|
|
1338
|
+
|
|
1339
|
+
return get_dataset(dataset_uuid=db_dataset.uuid, db=db)
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
class ClassifierStatItem(pdt.BaseModel):
|
|
1343
|
+
classifier: str
|
|
1344
|
+
count: int
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
class DatasetTableClassifierStat(pdt.BaseModel):
|
|
1348
|
+
dataset: DatasetTable
|
|
1349
|
+
items: list[ClassifierStatItem]
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
class DatasetTablesClassifiersStat(pdt.BaseModel):
|
|
1353
|
+
stats: list[DatasetTableClassifierStat]
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
@router.get("/stat_datasets_classifiers/", response_model=DatasetTablesClassifiersStat)
|
|
1357
|
+
def stat_datasets_classifiers(
|
|
1358
|
+
dataset_uuids: list[py_uuid.UUID] = Query(),
|
|
1359
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1360
|
+
) -> DatasetTablesClassifiersStat:
|
|
1361
|
+
with managed_db_session(db):
|
|
1362
|
+
db_datasets = (
|
|
1363
|
+
db
|
|
1364
|
+
.query(DatasetTable)
|
|
1365
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(dataset_uuids))
|
|
1366
|
+
.all()
|
|
1367
|
+
)
|
|
1368
|
+
for dataset_uuid in dataset_uuids:
|
|
1369
|
+
if dataset_uuid not in set(db_dataset.uuid for db_dataset in db_datasets):
|
|
1370
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
1371
|
+
|
|
1372
|
+
stats = []
|
|
1373
|
+
for db_dataset in db_datasets:
|
|
1374
|
+
db_classifiers = (
|
|
1375
|
+
db
|
|
1376
|
+
.query(ClassifierTable)
|
|
1377
|
+
.join(DatasetSampleTable, ClassifierTable.sample_uuid == DatasetSampleTable.sample_uuid)
|
|
1378
|
+
.where(ClassifierTable.expired_at.is_(None),
|
|
1379
|
+
DatasetSampleTable.dataset_uuid == db_dataset.uuid)
|
|
1380
|
+
.all()
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
counter = Counter(classifiers.get("name")
|
|
1384
|
+
for db_classifier in db_classifiers
|
|
1385
|
+
for classifiers in db_classifier.classifier_data.get("classifiers", [])
|
|
1386
|
+
if classifiers.get("name") is not None)
|
|
1387
|
+
|
|
1388
|
+
stats.append(
|
|
1389
|
+
DatasetTableClassifierStat(
|
|
1390
|
+
dataset=db_dataset,
|
|
1391
|
+
items=[ClassifierStatItem(classifier=classifier, count=count)
|
|
1392
|
+
for classifier, count in sorted(counter.items())],
|
|
1393
|
+
)
|
|
1394
|
+
)
|
|
1395
|
+
|
|
1396
|
+
return DatasetTablesClassifiersStat(stats=stats)
|
|
1397
|
+
|
|
1398
|
+
|
|
1399
|
+
class ClassifierComparisonMetricItem(pdt.BaseModel):
|
|
1400
|
+
name: str
|
|
1401
|
+
data: float
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
class ClassifierComparisonMetric(pdt.BaseModel):
|
|
1405
|
+
dataset_uuid: py_uuid.UUID
|
|
1406
|
+
items: list[ClassifierComparisonMetricItem]
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
class ClassifierComparisonCollection(pdt.BaseModel):
|
|
1410
|
+
name: str
|
|
1411
|
+
metrics: list[ClassifierComparisonMetric]
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
class DatasetTablesClassifiersComparison(pdt.BaseModel):
|
|
1415
|
+
comparison_view: Literal["default"]
|
|
1416
|
+
datasets: list[DatasetTable]
|
|
1417
|
+
collections: list[ClassifierComparisonCollection]
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
class CompareDatasetClassifiersRequest(pdt.BaseModel):
|
|
1421
|
+
comparison_view: Literal["default"] = pdt.Field(default="default")
|
|
1422
|
+
dataset_uuids: list[py_uuid.UUID]
|
|
1423
|
+
|
|
1424
|
+
|
|
1425
|
+
@router.post("/compare_dataset_classifiers/", response_model=DatasetTablesClassifiersComparison)
|
|
1426
|
+
def compare_dataset_classifiers(
|
|
1427
|
+
request: CompareDatasetClassifiersRequest = Body(),
|
|
1428
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1429
|
+
) -> DatasetTablesClassifiersComparison:
|
|
1430
|
+
with managed_db_session(db):
|
|
1431
|
+
db_datasets = (
|
|
1432
|
+
db
|
|
1433
|
+
.query(DatasetTable)
|
|
1434
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(request.dataset_uuids))
|
|
1435
|
+
.all()
|
|
1436
|
+
)
|
|
1437
|
+
for dataset_uuid in request.dataset_uuids:
|
|
1438
|
+
if dataset_uuid not in set(db_dataset.uuid for db_dataset in db_datasets):
|
|
1439
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
1440
|
+
|
|
1441
|
+
collections = {}
|
|
1442
|
+
|
|
1443
|
+
classifiers_view_query_stmt = (
|
|
1444
|
+
sa
|
|
1445
|
+
.lateral(sa.func.jsonb_array_elements(ClassifierTable.classifier_data["classifiers"]).table_valued("value"))
|
|
1446
|
+
.alias("classifiers")
|
|
1447
|
+
)
|
|
1448
|
+
|
|
1449
|
+
classifier_query_stmt = (
|
|
1450
|
+
sa
|
|
1451
|
+
.select(DatasetSampleTable.dataset_uuid.label("dataset_uuid"),
|
|
1452
|
+
classifiers_view_query_stmt.c.value.op("->>")("name").label("classifier_name"))
|
|
1453
|
+
.select_from(ClassifierTable)
|
|
1454
|
+
.join(DatasetSampleTable, ClassifierTable.sample_uuid == DatasetSampleTable.sample_uuid)
|
|
1455
|
+
.join(classifiers_view_query_stmt, sa.true())
|
|
1456
|
+
.where(ClassifierTable.expired_at.is_(None),
|
|
1457
|
+
DatasetSampleTable.dataset_uuid.in_(request.dataset_uuids))
|
|
1458
|
+
.subquery()
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
classifier_count_query_stmt = (
|
|
1462
|
+
sa
|
|
1463
|
+
.select(classifier_query_stmt.c.dataset_uuid,
|
|
1464
|
+
classifier_query_stmt.c.classifier_name,
|
|
1465
|
+
sa.func.count().label("count"))
|
|
1466
|
+
.group_by(classifier_query_stmt.c.dataset_uuid, classifier_query_stmt.c.classifier_name)
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
for dataset_uuid, classifier_name, count in db.execute(classifier_count_query_stmt).all():
|
|
1470
|
+
*collection_name_segments, metric_name = classifier_name.split(":")
|
|
1471
|
+
collection_name = ":".join(collection_name_segments)
|
|
1472
|
+
collections.setdefault(collection_name, {}).setdefault(dataset_uuid, {}).setdefault(metric_name, 0)
|
|
1473
|
+
collections[collection_name][dataset_uuid][metric_name] += count
|
|
1474
|
+
|
|
1475
|
+
return DatasetTablesClassifiersComparison(
|
|
1476
|
+
datasets=db_datasets,
|
|
1477
|
+
comparison_view=request.comparison_view,
|
|
1478
|
+
collections=[
|
|
1479
|
+
ClassifierComparisonCollection(
|
|
1480
|
+
name=collection_name,
|
|
1481
|
+
metrics=[
|
|
1482
|
+
ClassifierComparisonMetric(dataset_uuid=dataset_uuid,
|
|
1483
|
+
items=[
|
|
1484
|
+
ClassifierComparisonMetricItem(name=metric_name, data=metric_value)
|
|
1485
|
+
for metric_name, metric_value in sorted(metrics.items())
|
|
1486
|
+
])
|
|
1487
|
+
for dataset_uuid, metrics in sorted(dataset_values.items())
|
|
1488
|
+
],
|
|
1489
|
+
)
|
|
1490
|
+
for collection_name, dataset_values in sorted(collections.items())
|
|
1491
|
+
],
|
|
1492
|
+
)
|
|
1493
|
+
|
|
1494
|
+
|
|
1495
|
+
class CompareDatasetClassifiersWithConditionsRequest(pdt.BaseModel):
|
|
1496
|
+
comparison_view: Literal["default"] = pdt.Field(default="default")
|
|
1497
|
+
dataset_uuids: list[py_uuid.UUID]
|
|
1498
|
+
conditions: DatasetConditions
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
@router.post("/compare_dataset_classifiers_with_conditions/", response_model=DatasetTablesClassifiersComparison)
|
|
1502
|
+
def compare_dataset_classifiers_with_conditions(
|
|
1503
|
+
request: CompareDatasetClassifiersWithConditionsRequest = Body(),
|
|
1504
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1505
|
+
) -> DatasetTablesClassifiersComparison:
|
|
1506
|
+
with managed_db_session(db):
|
|
1507
|
+
db_datasets = (
|
|
1508
|
+
db
|
|
1509
|
+
.query(DatasetTable)
|
|
1510
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(request.dataset_uuids))
|
|
1511
|
+
.all()
|
|
1512
|
+
)
|
|
1513
|
+
for dataset_uuid in request.dataset_uuids:
|
|
1514
|
+
if dataset_uuid not in set(db_dataset.uuid for db_dataset in db_datasets):
|
|
1515
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(DatasetTable)}' with uuid '{dataset_uuid}' not found")
|
|
1516
|
+
|
|
1517
|
+
collections = {}
|
|
1518
|
+
|
|
1519
|
+
# The first query counts classifiers only for samples in datasets matching the conditions
|
|
1520
|
+
classifiers_view_query_stmt = (
|
|
1521
|
+
sa
|
|
1522
|
+
.lateral(sa.func.jsonb_array_elements(ClassifierTable.classifier_data["classifiers"]).table_valued("value"))
|
|
1523
|
+
.alias("classifiers")
|
|
1524
|
+
)
|
|
1525
|
+
|
|
1526
|
+
classifier_query_stmt = (
|
|
1527
|
+
sa
|
|
1528
|
+
.select(
|
|
1529
|
+
sa.cast(sa.literal("00000000-0000-0000-0000-000000000000"), sa.UUID).label("dataset_uuid"),
|
|
1530
|
+
classifiers_view_query_stmt.c.value.op("->>")("name").label("classifier_name"),
|
|
1531
|
+
)
|
|
1532
|
+
.select_from(ClassifierTable)
|
|
1533
|
+
.join(DatasetSampleTable, ClassifierTable.sample_uuid == DatasetSampleTable.sample_uuid)
|
|
1534
|
+
.join(classifiers_view_query_stmt, sa.true())
|
|
1535
|
+
.where(ClassifierTable.expired_at.is_(None),
|
|
1536
|
+
DatasetSampleTable.dataset_uuid.in_(request.dataset_uuids))
|
|
1537
|
+
)
|
|
1538
|
+
if request.conditions.included_dataset_uuids:
|
|
1539
|
+
classifier_query_stmt = classifier_query_stmt.where(
|
|
1540
|
+
DatasetSampleTable.dataset_uuid.in_(request.conditions.included_dataset_uuids),
|
|
1541
|
+
)
|
|
1542
|
+
if request.conditions.excluded_dataset_uuids:
|
|
1543
|
+
classifier_query_stmt = classifier_query_stmt.where(
|
|
1544
|
+
DatasetSampleTable.dataset_uuid.notin_(request.conditions.excluded_dataset_uuids),
|
|
1545
|
+
)
|
|
1546
|
+
if request.conditions.required_tags or request.conditions.unwanted_tags:
|
|
1547
|
+
classifier_query_stmt = classifier_query_stmt.join(SampleTable,
|
|
1548
|
+
SampleTable.uuid == DatasetSampleTable.sample_uuid)
|
|
1549
|
+
for tag in (request.conditions.required_tags or []):
|
|
1550
|
+
classifier_query_stmt = classifier_query_stmt.where(SampleTable.expired_at.is_(None),
|
|
1551
|
+
SampleTable.tags.contains([tag]))
|
|
1552
|
+
for tag in (request.conditions.unwanted_tags or []):
|
|
1553
|
+
classifier_query_stmt = classifier_query_stmt.where(SampleTable.expired_at.is_(None),
|
|
1554
|
+
~SampleTable.tags.contains([tag]))
|
|
1555
|
+
classifier_query_stmt = classifier_query_stmt.subquery()
|
|
1556
|
+
|
|
1557
|
+
classifier_count_query_stmt = (
|
|
1558
|
+
sa
|
|
1559
|
+
.select(
|
|
1560
|
+
classifier_query_stmt.c.dataset_uuid,
|
|
1561
|
+
classifier_query_stmt.c.classifier_name,
|
|
1562
|
+
sa.func.count().label("count"),
|
|
1563
|
+
)
|
|
1564
|
+
.group_by(classifier_query_stmt.c.dataset_uuid, classifier_query_stmt.c.classifier_name)
|
|
1565
|
+
)
|
|
1566
|
+
|
|
1567
|
+
for dataset_uuid, classifier_name, count in db.execute(classifier_count_query_stmt).all():
|
|
1568
|
+
*collection_name_segments, metric_name = classifier_name.split(":")
|
|
1569
|
+
collection_name = ":".join(collection_name_segments)
|
|
1570
|
+
collections.setdefault(collection_name, {}).setdefault(dataset_uuid, {}).setdefault(metric_name, 0)
|
|
1571
|
+
collections[collection_name][dataset_uuid][metric_name] += count
|
|
1572
|
+
|
|
1573
|
+
# The second query counts classifiers for samples in all reference datasets
|
|
1574
|
+
classifier_query_stmt = (
|
|
1575
|
+
sa
|
|
1576
|
+
.select(DatasetSampleTable.dataset_uuid.label("dataset_uuid"),
|
|
1577
|
+
classifiers_view_query_stmt.c.value.op("->>")("name").label("classifier_name"))
|
|
1578
|
+
.select_from(ClassifierTable)
|
|
1579
|
+
.join(DatasetSampleTable, ClassifierTable.sample_uuid == DatasetSampleTable.sample_uuid)
|
|
1580
|
+
.join(classifiers_view_query_stmt, sa.true())
|
|
1581
|
+
.where(ClassifierTable.expired_at.is_(None),
|
|
1582
|
+
DatasetSampleTable.dataset_uuid.in_(request.dataset_uuids))
|
|
1583
|
+
.subquery()
|
|
1584
|
+
)
|
|
1585
|
+
|
|
1586
|
+
classifier_count_query_stmt = (
|
|
1587
|
+
sa
|
|
1588
|
+
.select(
|
|
1589
|
+
classifier_query_stmt.c.dataset_uuid,
|
|
1590
|
+
classifier_query_stmt.c.classifier_name,
|
|
1591
|
+
sa.func.count().label("count"),
|
|
1592
|
+
)
|
|
1593
|
+
.group_by(classifier_query_stmt.c.dataset_uuid, classifier_query_stmt.c.classifier_name)
|
|
1594
|
+
)
|
|
1595
|
+
|
|
1596
|
+
for dataset_uuid, classifier_name, count in db.execute(classifier_count_query_stmt).all():
|
|
1597
|
+
*collection_name_segments, metric_name = classifier_name.split(":")
|
|
1598
|
+
collection_name = ":".join(collection_name_segments)
|
|
1599
|
+
collections.setdefault(collection_name, {}).setdefault(dataset_uuid, {}).setdefault(metric_name, 0)
|
|
1600
|
+
collections[collection_name][dataset_uuid][metric_name] += count
|
|
1601
|
+
|
|
1602
|
+
return DatasetTablesClassifiersComparison(
|
|
1603
|
+
datasets=db_datasets,
|
|
1604
|
+
comparison_view=request.comparison_view,
|
|
1605
|
+
collections=[ClassifierComparisonCollection(
|
|
1606
|
+
name=collection_name,
|
|
1607
|
+
metrics=[ClassifierComparisonMetric(
|
|
1608
|
+
dataset_uuid=dataset_uuid,
|
|
1609
|
+
items=[ClassifierComparisonMetricItem(
|
|
1610
|
+
name=metric_name,
|
|
1611
|
+
data=metric_value,
|
|
1612
|
+
) for metric_name, metric_value in sorted(metrics.items())],
|
|
1613
|
+
) for dataset_uuid, metrics in sorted(dataset_values.items())],
|
|
1614
|
+
) for collection_name, dataset_values in sorted(collections.items())],
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1617
|
+
|
|
1618
|
+
@router.get("/get_model_experiment_lineage/{experiment_uuid}", response_model=list[ExperimentTableDetail])
|
|
1619
|
+
def get_model_experiment_lineage(
|
|
1620
|
+
experiment_uuid: py_uuid.UUID = Path(),
|
|
1621
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1622
|
+
) -> list[ExperimentTableDetail]:
|
|
1623
|
+
with managed_db_session(db):
|
|
1624
|
+
experiment_lineage: list[ExperimentTableDetail] = []
|
|
1625
|
+
current_experiment_uuid = experiment_uuid
|
|
1626
|
+
while current_experiment_uuid is not None:
|
|
1627
|
+
try:
|
|
1628
|
+
experiment_table_detail = get_model_experiment(current_experiment_uuid, db)
|
|
1629
|
+
except Exception:
|
|
1630
|
+
experiment_table_detail = None
|
|
1631
|
+
if experiment_table_detail is None:
|
|
1632
|
+
if len(experiment_lineage) == 0:
|
|
1633
|
+
raise sa_exc.NoResultFound(
|
|
1634
|
+
f"'{model_name_of(ExperimentTable)}' with uuid '{experiment_uuid}' not found")
|
|
1635
|
+
else:
|
|
1636
|
+
last_experiment_table_detail = experiment_lineage[-1]
|
|
1637
|
+
raise sa_exc.NoResultFound(
|
|
1638
|
+
f"'{model_name_of(ExperimentTable)}' with uuid '{last_experiment_table_detail.experiment.uuid}' has unreachable parent")
|
|
1639
|
+
experiment_lineage.append(experiment_table_detail)
|
|
1640
|
+
current_experiment_uuid = experiment_table_detail.experiment.parent_uuid
|
|
1641
|
+
|
|
1642
|
+
return experiment_lineage
|
|
1643
|
+
|
|
1644
|
+
|
|
1645
|
+
class DetailedMetricPivotHeader(pdt.BaseModel):
|
|
1646
|
+
name: str
|
|
1647
|
+
title_columns: list[TableColumn]
|
|
1648
|
+
metric_columns: list[TableColumn]
|
|
1649
|
+
|
|
1650
|
+
|
|
1651
|
+
class DetailedMetricPivotTable(pdt.BaseModel):
|
|
1652
|
+
name: str
|
|
1653
|
+
title_values_sets: list[list[str]]
|
|
1654
|
+
metric_values_sets: list[list[float]]
|
|
1655
|
+
|
|
1656
|
+
|
|
1657
|
+
@dataclasses.dataclass
|
|
1658
|
+
class MetricsDisplayOperation(object):
|
|
1659
|
+
order_maker: Callable[[type[AssessmentTable]], list[Any]]
|
|
1660
|
+
metric_columns: list[TableColumn]
|
|
1661
|
+
detailed_metric_pivot_headers: list[DetailedMetricPivotHeader]
|
|
1662
|
+
metric_values_maker: Callable[[AssessmentTable, list[TableColumn]], list[float]]
|
|
1663
|
+
detailed_metric_pivot_tables_maker: Callable[
|
|
1664
|
+
[AssessmentTable, list[DetailedMetricPivotHeader]], list[DetailedMetricPivotTable]]
|
|
1665
|
+
|
|
1666
|
+
|
|
1667
|
+
@singleton
|
|
1668
|
+
def registered_metrics_display_operations() -> dict[str, MetricsDisplayOperation]:
|
|
1669
|
+
"""
|
|
1670
|
+
Register metrics display operations for different benchmarks.
|
|
1671
|
+
|
|
1672
|
+
Example:
|
|
1673
|
+
|
|
1674
|
+
>>> import sqlalchemy
|
|
1675
|
+
>>> def order_maker(assessment_table: type[AssessmentTable]) -> list[Any]:
|
|
1676
|
+
... return [sqlalchemy.desc(assessment_table.assessment_data["accuracy"]), assessment_table.created_at.asc()]
|
|
1677
|
+
...
|
|
1678
|
+
>>> metric_columns = [
|
|
1679
|
+
... TableColumn(name="accuracy",
|
|
1680
|
+
... ordering=ColumnOrdering(semantic="numeric", direction="descending"),
|
|
1681
|
+
... formatter=ColumnFormatter(syntax="python_format", template="{:.2%}")),
|
|
1682
|
+
... TableColumn(name="error_rate",
|
|
1683
|
+
... ordering=ColumnOrdering(semantic="numeric", direction="ascending"),
|
|
1684
|
+
... formatter=ColumnFormatter(syntax="python_format", template="{:.2%}")),
|
|
1685
|
+
... TableColumn(name="created_at",
|
|
1686
|
+
... ordering=ColumnOrdering(semantic="datetime", direction="ascending"),
|
|
1687
|
+
... formatter=ColumnFormatter(syntax="python_strftime", template="%Y-%m-%d %H:%M:%S")),
|
|
1688
|
+
... ]
|
|
1689
|
+
...
|
|
1690
|
+
>>> detailed_metric_pivot_headers = [
|
|
1691
|
+
... DetailedMetricPivotHeader(
|
|
1692
|
+
... name="per_class_metrics",
|
|
1693
|
+
... title_columns=[
|
|
1694
|
+
... TableColumn(name="class",
|
|
1695
|
+
... ordering=ColumnOrdering(semantic="lexical", direction="ascending"),
|
|
1696
|
+
... formatter=ColumnFormatter(syntax="python_format", template="{}")),
|
|
1697
|
+
... ],
|
|
1698
|
+
... metric_columns=[
|
|
1699
|
+
... TableColumn(name="accuracy",
|
|
1700
|
+
... ordering=ColumnOrdering(semantic="numeric", direction="descending"),
|
|
1701
|
+
... formatter=ColumnFormatter(syntax="python_format", template="{:.2%}")),
|
|
1702
|
+
... TableColumn(name="error_rate",
|
|
1703
|
+
... ordering=ColumnOrdering(semantic="numeric", direction="ascending"),
|
|
1704
|
+
... formatter=ColumnFormatter(syntax="python_format", template="{:.2%}")),
|
|
1705
|
+
... ],
|
|
1706
|
+
... ),
|
|
1707
|
+
... ]
|
|
1708
|
+
...
|
|
1709
|
+
>>> def metric_values_maker(assessment: AssessmentTable, columns: list[TableColumn]) -> list[float]:
|
|
1710
|
+
... return [
|
|
1711
|
+
... assessment.assessment_data.get("accuracy", 0.0),
|
|
1712
|
+
... assessment.assessment_data.get("error_rate", 0.0),
|
|
1713
|
+
... assessment.created_at,
|
|
1714
|
+
... ]
|
|
1715
|
+
...
|
|
1716
|
+
>>> def detailed_metric_pivot_tables_maker(
|
|
1717
|
+
... assessment: AssessmentTable,
|
|
1718
|
+
... headers: list[DetailedMetricPivotHeader],
|
|
1719
|
+
... ) -> list[DetailedMetricPivotTable]:
|
|
1720
|
+
... pivot_tables = []
|
|
1721
|
+
... for header in headers:
|
|
1722
|
+
... pivot_data = assessment.assessment_data.get(header.name, {})
|
|
1723
|
+
... title_values_sets = [[class_name] for class_name in sorted(pivot_data.keys())]
|
|
1724
|
+
... metric_values_sets = [[pivot_data[class_name].get("accuracy", 0.0),
|
|
1725
|
+
... pivot_data[class_name].get("error_rate", 0.0),
|
|
1726
|
+
... ]
|
|
1727
|
+
... for class_name in sorted(pivot_data.keys())]
|
|
1728
|
+
... # You don't need to validate data dimensions here, as they will be validated by the caller.
|
|
1729
|
+
... pivot_tables.append(
|
|
1730
|
+
... DetailedMetricPivotTable(
|
|
1731
|
+
... name="per_class_metrics",
|
|
1732
|
+
... title_values_sets=title_values_sets,
|
|
1733
|
+
... metric_values_sets=metric_values_sets,
|
|
1734
|
+
... )
|
|
1735
|
+
... )
|
|
1736
|
+
... return pivot_tables
|
|
1737
|
+
...
|
|
1738
|
+
>>> registered_metrics_display_operations()["my/benchmark"] = MetricsDisplayOperation(
|
|
1739
|
+
... order_maker,
|
|
1740
|
+
... metric_columns,
|
|
1741
|
+
... detailed_metric_pivot_headers,
|
|
1742
|
+
... metric_values_maker,
|
|
1743
|
+
... detailed_metric_pivot_tables_maker,
|
|
1744
|
+
... )
|
|
1745
|
+
|
|
1746
|
+
:return: A dictionary mapping benchmark names to their corresponding ``MetricsOperation``.
|
|
1747
|
+
"""
|
|
1748
|
+
return {}
|
|
1749
|
+
|
|
1750
|
+
|
|
1751
|
+
class AssessmentTableDetailWithMetricValues(pdt.BaseModel):
|
|
1752
|
+
assessment_detail: AssessmentTableDetail
|
|
1753
|
+
metric_columns: list[TableColumn]
|
|
1754
|
+
metric_values: list[float] = pdt.Field(default_factory=list)
|
|
1755
|
+
detailed_metric_pivot_headers: list[DetailedMetricPivotHeader]
|
|
1756
|
+
detailed_metric_pivot_tables: list[DetailedMetricPivotTable] = pdt.Field(default_factory=list)
|
|
1757
|
+
|
|
1758
|
+
|
|
1759
|
+
@router.get("/list_model_assessments_of/{experiment_uuid}",
|
|
1760
|
+
response_model=list[AssessmentTableDetailWithMetricValues])
|
|
1761
|
+
def list_model_assessments_of(
|
|
1762
|
+
experiment_uuid: py_uuid.UUID = Path(),
|
|
1763
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1764
|
+
) -> list[AssessmentTableDetail]:
|
|
1765
|
+
with managed_db_session(db):
|
|
1766
|
+
db_assessment_evaluation_promotions = (
|
|
1767
|
+
db
|
|
1768
|
+
.query(AssessmentTable, EvaluationTable, AssessmentPromotionTable)
|
|
1769
|
+
.outerjoin(EvaluationTable, AssessmentTable.uuid == EvaluationTable.assessment_uuid)
|
|
1770
|
+
.outerjoin(AssessmentPromotionTable, AssessmentTable.uuid == AssessmentPromotionTable.assessment_uuid)
|
|
1771
|
+
.where(AssessmentTable.expired_at.is_(None),
|
|
1772
|
+
EvaluationTable.expired_at.is_(None),
|
|
1773
|
+
AssessmentPromotionTable.expired_at.is_(None),
|
|
1774
|
+
AssessmentTable.experiment_uuid == experiment_uuid)
|
|
1775
|
+
.order_by(AssessmentTable.created_at.desc())
|
|
1776
|
+
.all()
|
|
1777
|
+
)
|
|
1778
|
+
if not db_assessment_evaluation_promotions:
|
|
1779
|
+
raise sa_exc.NoResultFound(
|
|
1780
|
+
f"'{model_name_of(ExperimentTable)}' of experiment_uuid '{experiment_uuid}' not found")
|
|
1781
|
+
|
|
1782
|
+
benchmark_uuids = list(set(db_assessment.benchmark_uuid
|
|
1783
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
1784
|
+
db_benchmarks = (
|
|
1785
|
+
db
|
|
1786
|
+
.query(BenchmarkTable)
|
|
1787
|
+
.where(BenchmarkTable.expired_at.is_(None), BenchmarkTable.uuid.in_(benchmark_uuids))
|
|
1788
|
+
.all()
|
|
1789
|
+
)
|
|
1790
|
+
db_benchmarks_dict = {db_benchmark.uuid: db_benchmark for db_benchmark in db_benchmarks}
|
|
1791
|
+
|
|
1792
|
+
dataset_uuids = list(set(db_assessment.dataset_uuid
|
|
1793
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
1794
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts(dataset_uuids, db)
|
|
1795
|
+
|
|
1796
|
+
db_datasets = (
|
|
1797
|
+
db
|
|
1798
|
+
.query(DatasetTable)
|
|
1799
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(dataset_uuids))
|
|
1800
|
+
.all()
|
|
1801
|
+
)
|
|
1802
|
+
db_datasets_dict = {db_dataset.uuid: db_dataset for db_dataset in db_datasets}
|
|
1803
|
+
|
|
1804
|
+
result = []
|
|
1805
|
+
|
|
1806
|
+
for db_assessment, db_evaluation, db_promotion in db_assessment_evaluation_promotions:
|
|
1807
|
+
db_benchmark = db_benchmarks_dict.get(db_assessment.benchmark_uuid)
|
|
1808
|
+
metrics_display_operation = registered_metrics_display_operations().get(db_benchmark.name)
|
|
1809
|
+
|
|
1810
|
+
def metric_values_maker(assessment: AssessmentTable, columns: list[TableColumn]) -> list[float]:
|
|
1811
|
+
metric_values = metrics_display_operation.metric_values_maker(assessment, columns)
|
|
1812
|
+
if len(metric_values) != len(columns):
|
|
1813
|
+
raise ValueError("mismatched metric columns and metric values lengths")
|
|
1814
|
+
return metric_values
|
|
1815
|
+
|
|
1816
|
+
def detailed_metric_pivot_tables_maker(
|
|
1817
|
+
assessment: AssessmentTable,
|
|
1818
|
+
headers: list[DetailedMetricPivotHeader],
|
|
1819
|
+
) -> list[DetailedMetricPivotTable]:
|
|
1820
|
+
pivot_tables = metrics_display_operation.detailed_metric_pivot_tables_maker(assessment, headers)
|
|
1821
|
+
if len(pivot_tables) != len(headers):
|
|
1822
|
+
raise ValueError("mismatched detailed metric pivot headers and pivot tables lengths")
|
|
1823
|
+
for header, pivot_table in zip(headers, pivot_tables):
|
|
1824
|
+
if len(pivot_table.title_values_sets) != len(pivot_table.metric_values_sets):
|
|
1825
|
+
raise ValueError("mismatched title values sets and metric values sets lengths")
|
|
1826
|
+
for title_values in pivot_table.title_values_sets:
|
|
1827
|
+
if len(title_values) != len(header.title_columns):
|
|
1828
|
+
raise ValueError("mismatched title columns and title values lengths")
|
|
1829
|
+
for metric_values in pivot_table.metric_values_sets:
|
|
1830
|
+
if len(metric_values) != len(header.metric_columns):
|
|
1831
|
+
raise ValueError("mismatched metric columns and metric values lengths")
|
|
1832
|
+
return pivot_tables
|
|
1833
|
+
|
|
1834
|
+
result.append(
|
|
1835
|
+
AssessmentTableDetailWithMetricValues(
|
|
1836
|
+
assessment_detail=AssessmentTableDetail(
|
|
1837
|
+
assessment=db_assessment,
|
|
1838
|
+
evaluation=db_evaluation,
|
|
1839
|
+
promotion=db_promotion,
|
|
1840
|
+
benchmark=db_benchmarks_dict.get(db_assessment.benchmark_uuid),
|
|
1841
|
+
experiment=None,
|
|
1842
|
+
dataset=db_datasets_dict.get(db_assessment.dataset_uuid),
|
|
1843
|
+
layouts=layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, []),
|
|
1844
|
+
samples_count=sum(layout_samples_count.samples_count
|
|
1845
|
+
for layout_samples_count in
|
|
1846
|
+
layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, [])),
|
|
1847
|
+
architecture=None,
|
|
1848
|
+
),
|
|
1849
|
+
metric_columns=metrics_display_operation.metric_columns,
|
|
1850
|
+
metric_values=metric_values_maker(
|
|
1851
|
+
db_assessment,
|
|
1852
|
+
metrics_display_operation.metric_columns,
|
|
1853
|
+
),
|
|
1854
|
+
detailed_metric_pivot_headers=metrics_display_operation.detailed_metric_pivot_headers,
|
|
1855
|
+
detailed_metric_pivot_tables=detailed_metric_pivot_tables_maker(
|
|
1856
|
+
db_assessment,
|
|
1857
|
+
metrics_display_operation.detailed_metric_pivot_headers,
|
|
1858
|
+
),
|
|
1859
|
+
)
|
|
1860
|
+
)
|
|
1861
|
+
|
|
1862
|
+
return result
|
|
1863
|
+
|
|
1864
|
+
|
|
1865
|
+
class AssessmentTableDetailWithRankingMetricValues(pdt.BaseModel):
|
|
1866
|
+
assessment_detail: AssessmentTableDetail
|
|
1867
|
+
metric_values: list[float] = pdt.Field(default_factory=list)
|
|
1868
|
+
detailed_metric_pivot_tables: list[DetailedMetricPivotTable] = pdt.Field(default_factory=list)
|
|
1869
|
+
|
|
1870
|
+
|
|
1871
|
+
class AssessmentTableDetailWithRankingMetricValuesAndPagination(pdt.BaseModel):
|
|
1872
|
+
metric_columns: list[TableColumn]
|
|
1873
|
+
detailed_metric_pivot_headers: list[DetailedMetricPivotHeader]
|
|
1874
|
+
assessment_details_with_metrics: list[AssessmentTableDetailWithRankingMetricValues]
|
|
1875
|
+
pagination: Pagination
|
|
1876
|
+
|
|
1877
|
+
|
|
1878
|
+
@router.get("/benchmark_rankings/", response_model=AssessmentTableDetailWithRankingMetricValuesAndPagination)
|
|
1879
|
+
def benchmark_rankings(
|
|
1880
|
+
benchmark: str = Query(),
|
|
1881
|
+
skip: int = Query(0),
|
|
1882
|
+
limit: int = Query(1000),
|
|
1883
|
+
begin_at: datetime.datetime | None = Query(None),
|
|
1884
|
+
end_at: datetime.datetime | None = Query(None),
|
|
1885
|
+
name_match: str | None = Query(None),
|
|
1886
|
+
contributor_sso_sid: int | None = Query(None),
|
|
1887
|
+
only_completed_jobs: bool = Query(True),
|
|
1888
|
+
only_shared: bool = Query(True),
|
|
1889
|
+
db: sa_orm.Session = Depends(make_session),
|
|
1890
|
+
) -> AssessmentTableDetailWithRankingMetricValuesAndPagination:
|
|
1891
|
+
with managed_db_session(db):
|
|
1892
|
+
metrics_display_operation = registered_metrics_display_operations().get(benchmark)
|
|
1893
|
+
if metrics_display_operation is None:
|
|
1894
|
+
raise ValueError(f"missing ranking metrics registered for benchmark '{benchmark}'")
|
|
1895
|
+
|
|
1896
|
+
def metric_values_maker(assessment: AssessmentTable, columns: list[TableColumn]) -> list[float]:
|
|
1897
|
+
metric_values = metrics_display_operation.metric_values_maker(assessment, columns)
|
|
1898
|
+
if len(metric_values) != len(columns):
|
|
1899
|
+
raise ValueError("mismatched metric columns and metric values lengths")
|
|
1900
|
+
return metric_values
|
|
1901
|
+
|
|
1902
|
+
def detailed_metric_pivot_tables_maker(
|
|
1903
|
+
assessment: AssessmentTable,
|
|
1904
|
+
headers: list[DetailedMetricPivotHeader],
|
|
1905
|
+
) -> list[DetailedMetricPivotTable]:
|
|
1906
|
+
pivot_tables = metrics_display_operation.detailed_metric_pivot_tables_maker(assessment, headers)
|
|
1907
|
+
if len(pivot_tables) != len(headers):
|
|
1908
|
+
raise ValueError("mismatched detailed metric pivot headers and pivot tables lengths")
|
|
1909
|
+
for header, pivot_table in zip(headers, pivot_tables):
|
|
1910
|
+
if len(pivot_table.title_values_sets) != len(pivot_table.metric_values_sets):
|
|
1911
|
+
raise ValueError("mismatched title values sets and metric values sets lengths")
|
|
1912
|
+
for title_values in pivot_table.title_values_sets:
|
|
1913
|
+
if len(title_values) != len(header.title_columns):
|
|
1914
|
+
raise ValueError("mismatched title columns and title values lengths")
|
|
1915
|
+
for metric_values in pivot_table.metric_values_sets:
|
|
1916
|
+
if len(metric_values) != len(header.metric_columns):
|
|
1917
|
+
raise ValueError("mismatched metric columns and metric values lengths")
|
|
1918
|
+
return pivot_tables
|
|
1919
|
+
|
|
1920
|
+
db_benchmark = (
|
|
1921
|
+
db
|
|
1922
|
+
.query(BenchmarkTable)
|
|
1923
|
+
.where(BenchmarkTable.expired_at.is_(None), BenchmarkTable.name == benchmark)
|
|
1924
|
+
.one_or_none()
|
|
1925
|
+
)
|
|
1926
|
+
if db_benchmark is None:
|
|
1927
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(BenchmarkTable)}' with name '{benchmark}' not found")
|
|
1928
|
+
|
|
1929
|
+
query_stmt = (
|
|
1930
|
+
db
|
|
1931
|
+
.query(AssessmentTable, EvaluationTable, AssessmentPromotionTable)
|
|
1932
|
+
.outerjoin(EvaluationTable, AssessmentTable.uuid == EvaluationTable.assessment_uuid)
|
|
1933
|
+
.outerjoin(AssessmentPromotionTable, AssessmentTable.uuid == AssessmentPromotionTable.assessment_uuid)
|
|
1934
|
+
.where(AssessmentTable.expired_at.is_(None),
|
|
1935
|
+
EvaluationTable.expired_at.is_(None),
|
|
1936
|
+
AssessmentPromotionTable.expired_at.is_(None),
|
|
1937
|
+
AssessmentTable.benchmark_uuid == db_benchmark.uuid)
|
|
1938
|
+
)
|
|
1939
|
+
if only_completed_jobs:
|
|
1940
|
+
query_stmt = query_stmt.where(sa.or_(EvaluationTable.sqn.is_(None),
|
|
1941
|
+
EvaluationTable.flags.op("&")(JobStatusFlag.Completed.value) > 0))
|
|
1942
|
+
if only_shared:
|
|
1943
|
+
query_stmt = query_stmt.where(AssessmentPromotionTable.flags.op("&")(JobStatusFlag.Completed.value) > 0)
|
|
1944
|
+
if begin_at is not None:
|
|
1945
|
+
query_stmt = query_stmt.where(AssessmentTable.created_at >= begin_at)
|
|
1946
|
+
if end_at is not None:
|
|
1947
|
+
query_stmt = query_stmt.where(AssessmentTable.created_at < end_at)
|
|
1948
|
+
if name_match is not None:
|
|
1949
|
+
query_stmt = query_stmt.where(AssessmentTable.name.ilike(f"%{escape_sql_like(name_match)}%", escape="\\"))
|
|
1950
|
+
if contributor_sso_sid is not None:
|
|
1951
|
+
query_stmt = query_stmt.where(AssessmentTable.contributor_sso_sid == contributor_sso_sid)
|
|
1952
|
+
|
|
1953
|
+
total = query_stmt.count()
|
|
1954
|
+
if total == 0:
|
|
1955
|
+
return AssessmentTableDetailWithRankingMetricValuesAndPagination(
|
|
1956
|
+
metric_columns=metrics_display_operation.metric_columns,
|
|
1957
|
+
detailed_metric_pivot_headers=metrics_display_operation.detailed_metric_pivot_headers,
|
|
1958
|
+
assessment_details_with_metrics=[],
|
|
1959
|
+
pagination=Pagination(skip=skip, limit=limit, total=0),
|
|
1960
|
+
)
|
|
1961
|
+
|
|
1962
|
+
if contributor_sso_sid is None:
|
|
1963
|
+
query_stmt = query_stmt.order_by(*metrics_display_operation.order_maker(AssessmentTable))
|
|
1964
|
+
else:
|
|
1965
|
+
query_stmt = query_stmt.order_by(AssessmentTable.sqn)
|
|
1966
|
+
|
|
1967
|
+
db_assessment_evaluation_promotions = query_stmt.offset(skip).limit(limit).all()
|
|
1968
|
+
|
|
1969
|
+
benchmark_uuids = list(set(db_assessment.benchmark_uuid
|
|
1970
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
1971
|
+
db_benchmarks = (
|
|
1972
|
+
db
|
|
1973
|
+
.query(BenchmarkTable)
|
|
1974
|
+
.where(BenchmarkTable.expired_at.is_(None), BenchmarkTable.uuid.in_(benchmark_uuids))
|
|
1975
|
+
.all()
|
|
1976
|
+
)
|
|
1977
|
+
db_benchmarks_dict = {db_benchmark.uuid: db_benchmark for db_benchmark in db_benchmarks}
|
|
1978
|
+
|
|
1979
|
+
experiment_uuids = list(set(db_assessment.experiment_uuid
|
|
1980
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
1981
|
+
db_experiments = (
|
|
1982
|
+
db
|
|
1983
|
+
.query(ExperimentTable)
|
|
1984
|
+
.where(ExperimentTable.expired_at.is_(None), ExperimentTable.uuid.in_(experiment_uuids))
|
|
1985
|
+
.all()
|
|
1986
|
+
)
|
|
1987
|
+
db_experiments_dict = {db_experiment.uuid: db_experiment for db_experiment in db_experiments}
|
|
1988
|
+
|
|
1989
|
+
dataset_uuids = list(set(db_assessment.dataset_uuid
|
|
1990
|
+
for db_assessment, _, _ in db_assessment_evaluation_promotions))
|
|
1991
|
+
layout_samples_counts_by_datasets = query_layout_samples_counts(dataset_uuids, db)
|
|
1992
|
+
|
|
1993
|
+
db_datasets = (
|
|
1994
|
+
db
|
|
1995
|
+
.query(DatasetTable)
|
|
1996
|
+
.where(DatasetTable.expired_at.is_(None), DatasetTable.uuid.in_(dataset_uuids))
|
|
1997
|
+
.all()
|
|
1998
|
+
)
|
|
1999
|
+
db_datasets_dict = {db_dataset.uuid: db_dataset for db_dataset in db_datasets}
|
|
2000
|
+
|
|
2001
|
+
architecture_uuids = list(set(db_experiment.architecture_uuid for db_experiment in db_experiments))
|
|
2002
|
+
db_architectures = (
|
|
2003
|
+
db
|
|
2004
|
+
.query(ArchitectureTable)
|
|
2005
|
+
.where(ArchitectureTable.expired_at.is_(None), ArchitectureTable.uuid.in_(architecture_uuids))
|
|
2006
|
+
.all()
|
|
2007
|
+
)
|
|
2008
|
+
db_architectures_dict = {db_architecture.uuid: db_architecture for db_architecture in db_architectures}
|
|
2009
|
+
|
|
2010
|
+
return AssessmentTableDetailWithRankingMetricValuesAndPagination(
|
|
2011
|
+
metric_columns=metrics_display_operation.metric_columns,
|
|
2012
|
+
detailed_metric_pivot_headers=metrics_display_operation.detailed_metric_pivot_headers,
|
|
2013
|
+
assessment_details_with_metrics=[
|
|
2014
|
+
AssessmentTableDetailWithRankingMetricValues(
|
|
2015
|
+
assessment_detail=AssessmentTableDetail(
|
|
2016
|
+
assessment=db_assessment,
|
|
2017
|
+
evaluation=db_evaluation,
|
|
2018
|
+
promotion=db_promotion,
|
|
2019
|
+
benchmark=db_benchmarks_dict.get(db_assessment.benchmark_uuid),
|
|
2020
|
+
experiment=db_experiments_dict.get(db_assessment.experiment_uuid),
|
|
2021
|
+
dataset=db_datasets_dict.get(db_assessment.dataset_uuid),
|
|
2022
|
+
layouts=layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, []),
|
|
2023
|
+
samples_count=sum(layout_samples_count.samples_count
|
|
2024
|
+
for layout_samples_count in
|
|
2025
|
+
layout_samples_counts_by_datasets.get(db_assessment.dataset_uuid, [])),
|
|
2026
|
+
architecture=(
|
|
2027
|
+
db_architectures_dict.get(
|
|
2028
|
+
db_experiments_dict.get(db_assessment.experiment_uuid).architecture_uuid)
|
|
2029
|
+
if db_assessment.experiment_uuid in db_experiments_dict else None
|
|
2030
|
+
),
|
|
2031
|
+
),
|
|
2032
|
+
metric_values=metric_values_maker(
|
|
2033
|
+
db_assessment,
|
|
2034
|
+
metrics_display_operation.metric_columns,
|
|
2035
|
+
),
|
|
2036
|
+
detailed_metric_pivot_tables=detailed_metric_pivot_tables_maker(
|
|
2037
|
+
db_assessment,
|
|
2038
|
+
metrics_display_operation.detailed_metric_pivot_headers,
|
|
2039
|
+
),
|
|
2040
|
+
)
|
|
2041
|
+
for db_assessment, db_evaluation, db_promotion in db_assessment_evaluation_promotions
|
|
2042
|
+
],
|
|
2043
|
+
pagination=Pagination(skip=skip, limit=limit, total=total),
|
|
2044
|
+
)
|
|
2045
|
+
|
|
2046
|
+
|
|
2047
|
+
class SamplesCountByLayout(pdt.BaseModel):
|
|
2048
|
+
layout: LayoutTable
|
|
2049
|
+
count: int
|
|
2050
|
+
|
|
2051
|
+
|
|
2052
|
+
class ExperimentsCountByArchitecture(pdt.BaseModel):
|
|
2053
|
+
architecture: ArchitectureTable
|
|
2054
|
+
count: int
|
|
2055
|
+
|
|
2056
|
+
|
|
2057
|
+
class AssessmentsCountByBenchmark(pdt.BaseModel):
|
|
2058
|
+
benchmark: BenchmarkTable
|
|
2059
|
+
count: int
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
class Dashboard(pdt.BaseModel):
|
|
2063
|
+
samples_counts_by_layouts: list[SamplesCountByLayout]
|
|
2064
|
+
experiments_counts_by_architectures: list[ExperimentsCountByArchitecture]
|
|
2065
|
+
assessments_counts_by_benchmarks: list[AssessmentsCountByBenchmark]
|
|
2066
|
+
samples_counts_by_layouts_for_annotation: list[SamplesCountByLayout] = pdt.Field(default_factory=list)
|
|
2067
|
+
samples_counts_by_layouts_for_evaluation: list[SamplesCountByLayout] = pdt.Field(default_factory=list)
|
|
2068
|
+
|
|
2069
|
+
|
|
2070
|
+
@router.get("/dashboard/", response_model=Dashboard)
|
|
2071
|
+
def dashboard(db: sa_orm.Session = Depends(make_session)) -> Dashboard:
|
|
2072
|
+
with managed_db_session(db):
|
|
2073
|
+
def compute_samples_counts_by_layouts():
|
|
2074
|
+
counts_query_stmt = (
|
|
2075
|
+
db
|
|
2076
|
+
.query(SampleTable.layout_uuid, sa.func.count().label("counts"))
|
|
2077
|
+
.where(SampleTable.expired_at.is_(None))
|
|
2078
|
+
.group_by(SampleTable.layout_uuid)
|
|
2079
|
+
.subquery()
|
|
2080
|
+
)
|
|
2081
|
+
|
|
2082
|
+
counts = (
|
|
2083
|
+
db
|
|
2084
|
+
.query(LayoutTable, sa.func.coalesce(counts_query_stmt.c.counts, 0))
|
|
2085
|
+
.where(LayoutTable.expired_at.is_(None))
|
|
2086
|
+
.outerjoin(counts_query_stmt, LayoutTable.uuid == counts_query_stmt.c.layout_uuid)
|
|
2087
|
+
.order_by(LayoutTable.sqn)
|
|
2088
|
+
.all()
|
|
2089
|
+
)
|
|
2090
|
+
|
|
2091
|
+
return [SamplesCountByLayout(layout=db_layout, count=count) for db_layout, count in counts]
|
|
2092
|
+
|
|
2093
|
+
def compute_experiments_counts_by_architectures():
|
|
2094
|
+
counts_query_stmt = (
|
|
2095
|
+
db
|
|
2096
|
+
.query(ExperimentTable.architecture_uuid, sa.func.count().label("counts"))
|
|
2097
|
+
.where(ExperimentTable.expired_at.is_(None))
|
|
2098
|
+
.group_by(ExperimentTable.architecture_uuid)
|
|
2099
|
+
.subquery()
|
|
2100
|
+
)
|
|
2101
|
+
|
|
2102
|
+
counts = (
|
|
2103
|
+
db
|
|
2104
|
+
.query(ArchitectureTable, sa.func.coalesce(counts_query_stmt.c.counts, 0))
|
|
2105
|
+
.where(ArchitectureTable.expired_at.is_(None))
|
|
2106
|
+
.outerjoin(counts_query_stmt, ArchitectureTable.uuid == counts_query_stmt.c.architecture_uuid)
|
|
2107
|
+
.order_by(ArchitectureTable.sqn)
|
|
2108
|
+
.all()
|
|
2109
|
+
)
|
|
2110
|
+
|
|
2111
|
+
return [ExperimentsCountByArchitecture(architecture=db_architecture, count=count)
|
|
2112
|
+
for db_architecture, count in counts]
|
|
2113
|
+
|
|
2114
|
+
def compute_assessments_counts_by_benchmarks():
|
|
2115
|
+
counts_query_stmt = (
|
|
2116
|
+
db
|
|
2117
|
+
.query(AssessmentTable.benchmark_uuid, sa.func.count().label("counts"))
|
|
2118
|
+
.where(AssessmentTable.expired_at.is_(None))
|
|
2119
|
+
.group_by(AssessmentTable.benchmark_uuid)
|
|
2120
|
+
.subquery()
|
|
2121
|
+
)
|
|
2122
|
+
|
|
2123
|
+
counts = (
|
|
2124
|
+
db
|
|
2125
|
+
.query(BenchmarkTable, sa.func.coalesce(counts_query_stmt.c.counts, 0))
|
|
2126
|
+
.where(BenchmarkTable.expired_at.is_(None))
|
|
2127
|
+
.outerjoin(counts_query_stmt, BenchmarkTable.uuid == counts_query_stmt.c.benchmark_uuid)
|
|
2128
|
+
.order_by(BenchmarkTable.sqn)
|
|
2129
|
+
.all()
|
|
2130
|
+
)
|
|
2131
|
+
|
|
2132
|
+
return [AssessmentsCountByBenchmark(benchmark=db_benchmark, count=count)
|
|
2133
|
+
for db_benchmark, count in counts]
|
|
2134
|
+
|
|
2135
|
+
return Dashboard(
|
|
2136
|
+
samples_counts_by_layouts=compute_samples_counts_by_layouts(),
|
|
2137
|
+
experiments_counts_by_architectures=compute_experiments_counts_by_architectures(),
|
|
2138
|
+
assessments_counts_by_benchmarks=compute_assessments_counts_by_benchmarks(),
|
|
2139
|
+
)
|
|
2140
|
+
|
|
2141
|
+
|
|
2142
|
+
class Tagset(pdt.BaseModel):
|
|
2143
|
+
namespace: str
|
|
2144
|
+
tags: list[str]
|
|
2145
|
+
|
|
2146
|
+
@pdt.field_validator("namespace", mode="after")
|
|
2147
|
+
@classmethod
|
|
2148
|
+
def validate_namespace(cls, v: str) -> str:
|
|
2149
|
+
if v not in predefined_tagsets():
|
|
2150
|
+
raise ValueError(f"invalid tagset namespace '{v}'")
|
|
2151
|
+
return v
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
@router.get("/get_tagset/", response_model=Tagset)
|
|
2155
|
+
def get_tagset(namespace: str = Query("universal")) -> Tagset:
|
|
2156
|
+
if namespace not in predefined_tagsets():
|
|
2157
|
+
raise ValueError(f"invalid tagset namespace '{namespace}'")
|
|
2158
|
+
|
|
2159
|
+
predefined_tagset = predefined_tagsets()[namespace]
|
|
2160
|
+
|
|
2161
|
+
return Tagset(namespace=predefined_tagset.namespace, tags=[tag.name for tag in predefined_tagset.tags])
|