truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.0__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.
- truthound_dashboard/api/alerts.py +75 -86
- truthound_dashboard/api/anomaly.py +7 -13
- truthound_dashboard/api/cross_alerts.py +38 -52
- truthound_dashboard/api/drift.py +49 -59
- truthound_dashboard/api/drift_monitor.py +234 -79
- truthound_dashboard/api/enterprise_sampling.py +498 -0
- truthound_dashboard/api/history.py +57 -5
- truthound_dashboard/api/lineage.py +3 -48
- truthound_dashboard/api/maintenance.py +104 -49
- truthound_dashboard/api/mask.py +1 -2
- truthound_dashboard/api/middleware.py +2 -1
- truthound_dashboard/api/model_monitoring.py +435 -311
- truthound_dashboard/api/notifications.py +227 -191
- truthound_dashboard/api/notifications_advanced.py +21 -20
- truthound_dashboard/api/observability.py +586 -0
- truthound_dashboard/api/plugins.py +2 -433
- truthound_dashboard/api/profile.py +199 -37
- truthound_dashboard/api/quality_reporter.py +701 -0
- truthound_dashboard/api/reports.py +7 -16
- truthound_dashboard/api/router.py +66 -0
- truthound_dashboard/api/rule_suggestions.py +5 -5
- truthound_dashboard/api/scan.py +17 -19
- truthound_dashboard/api/schedules.py +85 -50
- truthound_dashboard/api/schema_evolution.py +6 -6
- truthound_dashboard/api/schema_watcher.py +667 -0
- truthound_dashboard/api/sources.py +98 -27
- truthound_dashboard/api/tiering.py +1323 -0
- truthound_dashboard/api/triggers.py +14 -11
- truthound_dashboard/api/validations.py +12 -11
- truthound_dashboard/api/versioning.py +1 -6
- truthound_dashboard/core/__init__.py +129 -3
- truthound_dashboard/core/actions/__init__.py +62 -0
- truthound_dashboard/core/actions/custom.py +426 -0
- truthound_dashboard/core/actions/notifications.py +910 -0
- truthound_dashboard/core/actions/storage.py +472 -0
- truthound_dashboard/core/actions/webhook.py +281 -0
- truthound_dashboard/core/anomaly.py +262 -67
- truthound_dashboard/core/anomaly_explainer.py +4 -3
- truthound_dashboard/core/backends/__init__.py +67 -0
- truthound_dashboard/core/backends/base.py +299 -0
- truthound_dashboard/core/backends/errors.py +191 -0
- truthound_dashboard/core/backends/factory.py +423 -0
- truthound_dashboard/core/backends/mock_backend.py +451 -0
- truthound_dashboard/core/backends/truthound_backend.py +718 -0
- truthound_dashboard/core/checkpoint/__init__.py +87 -0
- truthound_dashboard/core/checkpoint/adapters.py +814 -0
- truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
- truthound_dashboard/core/checkpoint/runner.py +270 -0
- truthound_dashboard/core/connections.py +437 -10
- truthound_dashboard/core/converters/__init__.py +14 -0
- truthound_dashboard/core/converters/truthound.py +620 -0
- truthound_dashboard/core/cross_alerts.py +540 -320
- truthound_dashboard/core/datasource_factory.py +1672 -0
- truthound_dashboard/core/drift_monitor.py +216 -20
- truthound_dashboard/core/enterprise_sampling.py +1291 -0
- truthound_dashboard/core/interfaces/__init__.py +225 -0
- truthound_dashboard/core/interfaces/actions.py +652 -0
- truthound_dashboard/core/interfaces/base.py +247 -0
- truthound_dashboard/core/interfaces/checkpoint.py +676 -0
- truthound_dashboard/core/interfaces/protocols.py +664 -0
- truthound_dashboard/core/interfaces/reporters.py +650 -0
- truthound_dashboard/core/interfaces/routing.py +646 -0
- truthound_dashboard/core/interfaces/triggers.py +619 -0
- truthound_dashboard/core/lineage.py +407 -71
- truthound_dashboard/core/model_monitoring.py +431 -3
- truthound_dashboard/core/notifications/base.py +4 -0
- truthound_dashboard/core/notifications/channels.py +501 -1203
- truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
- truthound_dashboard/core/notifications/deduplication/service.py +131 -348
- truthound_dashboard/core/notifications/dispatcher.py +202 -11
- truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
- truthound_dashboard/core/notifications/escalation/engine.py +168 -358
- truthound_dashboard/core/notifications/routing/__init__.py +88 -128
- truthound_dashboard/core/notifications/routing/engine.py +90 -317
- truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
- truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
- truthound_dashboard/core/notifications/throttling/builder.py +117 -255
- truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
- truthound_dashboard/core/phase5/collaboration.py +1 -1
- truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
- truthound_dashboard/core/quality_reporter.py +1359 -0
- truthound_dashboard/core/report_history.py +0 -6
- truthound_dashboard/core/reporters/__init__.py +175 -14
- truthound_dashboard/core/reporters/adapters.py +943 -0
- truthound_dashboard/core/reporters/base.py +0 -3
- truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
- truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
- truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
- truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
- truthound_dashboard/core/reporters/compat.py +266 -0
- truthound_dashboard/core/reporters/csv_reporter.py +2 -35
- truthound_dashboard/core/reporters/factory.py +526 -0
- truthound_dashboard/core/reporters/interfaces.py +745 -0
- truthound_dashboard/core/reporters/registry.py +1 -10
- truthound_dashboard/core/scheduler.py +165 -0
- truthound_dashboard/core/schema_evolution.py +3 -3
- truthound_dashboard/core/schema_watcher.py +1528 -0
- truthound_dashboard/core/services.py +595 -76
- truthound_dashboard/core/store_manager.py +810 -0
- truthound_dashboard/core/streaming_anomaly.py +169 -4
- truthound_dashboard/core/tiering.py +1309 -0
- truthound_dashboard/core/triggers/evaluators.py +178 -8
- truthound_dashboard/core/truthound_adapter.py +2620 -197
- truthound_dashboard/core/unified_alerts.py +23 -20
- truthound_dashboard/db/__init__.py +8 -0
- truthound_dashboard/db/database.py +8 -2
- truthound_dashboard/db/models.py +944 -25
- truthound_dashboard/db/repository.py +2 -0
- truthound_dashboard/main.py +11 -0
- truthound_dashboard/schemas/__init__.py +177 -16
- truthound_dashboard/schemas/base.py +44 -23
- truthound_dashboard/schemas/collaboration.py +19 -6
- truthound_dashboard/schemas/cross_alerts.py +19 -3
- truthound_dashboard/schemas/drift.py +61 -55
- truthound_dashboard/schemas/drift_monitor.py +67 -23
- truthound_dashboard/schemas/enterprise_sampling.py +653 -0
- truthound_dashboard/schemas/lineage.py +0 -33
- truthound_dashboard/schemas/mask.py +10 -8
- truthound_dashboard/schemas/model_monitoring.py +89 -10
- truthound_dashboard/schemas/notifications_advanced.py +13 -0
- truthound_dashboard/schemas/observability.py +453 -0
- truthound_dashboard/schemas/plugins.py +0 -280
- truthound_dashboard/schemas/profile.py +154 -247
- truthound_dashboard/schemas/quality_reporter.py +403 -0
- truthound_dashboard/schemas/reports.py +2 -2
- truthound_dashboard/schemas/rule_suggestion.py +8 -1
- truthound_dashboard/schemas/scan.py +4 -24
- truthound_dashboard/schemas/schedule.py +11 -3
- truthound_dashboard/schemas/schema_watcher.py +727 -0
- truthound_dashboard/schemas/source.py +17 -2
- truthound_dashboard/schemas/tiering.py +822 -0
- truthound_dashboard/schemas/triggers.py +16 -0
- truthound_dashboard/schemas/unified_alerts.py +7 -0
- truthound_dashboard/schemas/validation.py +0 -13
- truthound_dashboard/schemas/validators/base.py +41 -21
- truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
- truthound_dashboard/schemas/validators/localization_validators.py +273 -0
- truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
- truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
- truthound_dashboard/schemas/validators/referential_validators.py +312 -0
- truthound_dashboard/schemas/validators/registry.py +93 -8
- truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
- truthound_dashboard/schemas/versioning.py +1 -6
- truthound_dashboard/static/index.html +2 -2
- truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
- truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
- truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
- truthound_dashboard/core/plugins/hooks/manager.py +0 -403
- truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
- truthound_dashboard/core/reporters/junit_reporter.py +0 -233
- truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
- truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
- truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
- truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
- truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
- truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
- truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
- truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
- truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
- truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
- truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
- truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
- truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
- truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
- truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
- truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
- truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
- truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
- truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
- truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
- truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
- truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
- truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
- truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
- truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
- truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
- truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
- truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
- truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
- truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
- truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
- truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
- truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
- truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
- truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
- truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
- truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
- truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
- truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
- truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
- truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
- truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
- truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
- truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
- truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
- truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
- truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
- truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
truthound_dashboard/api/drift.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"""Drift detection API endpoints.
|
|
2
2
|
|
|
3
3
|
Provides endpoints for drift comparison between datasets.
|
|
4
|
+
|
|
5
|
+
API Design: Direct Response Style
|
|
6
|
+
- Single resources return the resource directly
|
|
7
|
+
- List endpoints return PaginatedResponse with data, total, offset, limit
|
|
8
|
+
- Errors are handled via HTTPException
|
|
4
9
|
"""
|
|
5
10
|
|
|
6
11
|
from __future__ import annotations
|
|
@@ -13,6 +18,7 @@ from truthound_dashboard.core import DriftService
|
|
|
13
18
|
from truthound_dashboard.schemas import (
|
|
14
19
|
DriftCompareRequest,
|
|
15
20
|
DriftComparisonListResponse,
|
|
21
|
+
DriftComparisonResponse,
|
|
16
22
|
)
|
|
17
23
|
|
|
18
24
|
from .deps import SessionDep
|
|
@@ -28,16 +34,34 @@ async def get_drift_service(session: SessionDep) -> DriftService:
|
|
|
28
34
|
DriftServiceDep = Annotated[DriftService, Depends(get_drift_service)]
|
|
29
35
|
|
|
30
36
|
|
|
37
|
+
def _comparison_to_response(comparison) -> DriftComparisonResponse:
|
|
38
|
+
"""Convert comparison model to response schema."""
|
|
39
|
+
return DriftComparisonResponse(
|
|
40
|
+
id=comparison.id,
|
|
41
|
+
baseline_source_id=comparison.baseline_source_id,
|
|
42
|
+
current_source_id=comparison.current_source_id,
|
|
43
|
+
has_drift=comparison.has_drift,
|
|
44
|
+
has_high_drift=comparison.has_high_drift,
|
|
45
|
+
total_columns=comparison.total_columns,
|
|
46
|
+
drifted_columns=comparison.drifted_columns,
|
|
47
|
+
drift_percentage=comparison.drift_percentage,
|
|
48
|
+
result=comparison.result_json,
|
|
49
|
+
config=comparison.config,
|
|
50
|
+
created_at=comparison.created_at,
|
|
51
|
+
updated_at=comparison.updated_at,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
31
55
|
@router.post(
|
|
32
56
|
"/drift/compare",
|
|
33
|
-
response_model=
|
|
57
|
+
response_model=DriftComparisonResponse,
|
|
34
58
|
summary="Compare datasets for drift",
|
|
35
59
|
description="Compare two datasets to detect data drift.",
|
|
36
60
|
)
|
|
37
61
|
async def compare_datasets(
|
|
38
62
|
request: DriftCompareRequest,
|
|
39
63
|
service: DriftServiceDep,
|
|
40
|
-
) ->
|
|
64
|
+
) -> DriftComparisonResponse:
|
|
41
65
|
"""Compare two datasets for drift detection.
|
|
42
66
|
|
|
43
67
|
Args:
|
|
@@ -54,28 +78,9 @@ async def compare_datasets(
|
|
|
54
78
|
columns=request.columns,
|
|
55
79
|
method=request.method,
|
|
56
80
|
threshold=request.threshold,
|
|
57
|
-
correction=request.correction,
|
|
58
81
|
sample_size=request.sample_size,
|
|
59
82
|
)
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
"success": True,
|
|
63
|
-
"data": {
|
|
64
|
-
"id": comparison.id,
|
|
65
|
-
"baseline_source_id": comparison.baseline_source_id,
|
|
66
|
-
"current_source_id": comparison.current_source_id,
|
|
67
|
-
"has_drift": comparison.has_drift,
|
|
68
|
-
"has_high_drift": comparison.has_high_drift,
|
|
69
|
-
"total_columns": comparison.total_columns,
|
|
70
|
-
"drifted_columns": comparison.drifted_columns,
|
|
71
|
-
"drift_percentage": comparison.drift_percentage,
|
|
72
|
-
"result": comparison.result_json,
|
|
73
|
-
"config": comparison.config,
|
|
74
|
-
"created_at": (
|
|
75
|
-
comparison.created_at.isoformat() if comparison.created_at else None
|
|
76
|
-
),
|
|
77
|
-
},
|
|
78
|
-
}
|
|
83
|
+
return _comparison_to_response(comparison)
|
|
79
84
|
except ValueError as e:
|
|
80
85
|
raise HTTPException(status_code=404, detail=str(e))
|
|
81
86
|
except Exception as e:
|
|
@@ -94,6 +99,7 @@ async def list_comparisons(
|
|
|
94
99
|
None, description="Filter by baseline source"
|
|
95
100
|
),
|
|
96
101
|
current_source_id: str | None = Query(None, description="Filter by current source"),
|
|
102
|
+
offset: int = Query(0, ge=0, description="Offset for pagination"),
|
|
97
103
|
limit: int = Query(20, ge=1, le=100, description="Maximum results"),
|
|
98
104
|
) -> DriftComparisonListResponse:
|
|
99
105
|
"""List drift comparisons.
|
|
@@ -102,10 +108,11 @@ async def list_comparisons(
|
|
|
102
108
|
service: Drift service.
|
|
103
109
|
baseline_source_id: Optional baseline source ID filter.
|
|
104
110
|
current_source_id: Optional current source ID filter.
|
|
111
|
+
offset: Offset for pagination.
|
|
105
112
|
limit: Maximum results to return.
|
|
106
113
|
|
|
107
114
|
Returns:
|
|
108
|
-
|
|
115
|
+
Paginated list of drift comparisons.
|
|
109
116
|
"""
|
|
110
117
|
comparisons = await service.list_comparisons(
|
|
111
118
|
baseline_source_id=baseline_source_id,
|
|
@@ -113,37 +120,40 @@ async def list_comparisons(
|
|
|
113
120
|
limit=limit,
|
|
114
121
|
)
|
|
115
122
|
|
|
123
|
+
from truthound_dashboard.schemas.drift import DriftComparisonListItem
|
|
124
|
+
|
|
116
125
|
return DriftComparisonListResponse(
|
|
117
|
-
success=True,
|
|
118
126
|
data=[
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
DriftComparisonListItem(
|
|
128
|
+
id=c.id,
|
|
129
|
+
baseline_source_id=c.baseline_source_id,
|
|
130
|
+
current_source_id=c.current_source_id,
|
|
131
|
+
has_drift=c.has_drift,
|
|
132
|
+
has_high_drift=c.has_high_drift,
|
|
133
|
+
total_columns=c.total_columns,
|
|
134
|
+
drifted_columns=c.drifted_columns,
|
|
135
|
+
drift_percentage=c.drift_percentage,
|
|
136
|
+
created_at=c.created_at,
|
|
137
|
+
updated_at=c.updated_at,
|
|
138
|
+
)
|
|
131
139
|
for c in comparisons
|
|
132
140
|
],
|
|
133
141
|
total=len(comparisons),
|
|
142
|
+
offset=offset,
|
|
143
|
+
limit=limit,
|
|
134
144
|
)
|
|
135
145
|
|
|
136
146
|
|
|
137
147
|
@router.get(
|
|
138
148
|
"/drift/comparisons/{comparison_id}",
|
|
139
|
-
response_model=
|
|
149
|
+
response_model=DriftComparisonResponse,
|
|
140
150
|
summary="Get drift comparison",
|
|
141
151
|
description="Get a specific drift comparison by ID.",
|
|
142
152
|
)
|
|
143
153
|
async def get_comparison(
|
|
144
154
|
comparison_id: str,
|
|
145
155
|
service: DriftServiceDep,
|
|
146
|
-
) ->
|
|
156
|
+
) -> DriftComparisonResponse:
|
|
147
157
|
"""Get a drift comparison by ID.
|
|
148
158
|
|
|
149
159
|
Args:
|
|
@@ -157,24 +167,4 @@ async def get_comparison(
|
|
|
157
167
|
if comparison is None:
|
|
158
168
|
raise HTTPException(status_code=404, detail="Comparison not found")
|
|
159
169
|
|
|
160
|
-
return
|
|
161
|
-
"success": True,
|
|
162
|
-
"data": {
|
|
163
|
-
"id": comparison.id,
|
|
164
|
-
"baseline_source_id": comparison.baseline_source_id,
|
|
165
|
-
"current_source_id": comparison.current_source_id,
|
|
166
|
-
"has_drift": comparison.has_drift,
|
|
167
|
-
"has_high_drift": comparison.has_high_drift,
|
|
168
|
-
"total_columns": comparison.total_columns,
|
|
169
|
-
"drifted_columns": comparison.drifted_columns,
|
|
170
|
-
"drift_percentage": comparison.drift_percentage,
|
|
171
|
-
"result": comparison.result_json,
|
|
172
|
-
"config": comparison.config,
|
|
173
|
-
"created_at": (
|
|
174
|
-
comparison.created_at.isoformat() if comparison.created_at else None
|
|
175
|
-
),
|
|
176
|
-
"updated_at": (
|
|
177
|
-
comparison.updated_at.isoformat() if comparison.updated_at else None
|
|
178
|
-
),
|
|
179
|
-
},
|
|
180
|
-
}
|
|
170
|
+
return _comparison_to_response(comparison)
|