truthound-dashboard 1.0.2__py3-none-any.whl → 1.2.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/catalog.py +343 -0
- truthound_dashboard/api/collaboration.py +148 -0
- truthound_dashboard/api/glossary.py +329 -0
- truthound_dashboard/api/router.py +29 -0
- truthound_dashboard/cli.py +397 -0
- truthound_dashboard/core/__init__.py +12 -0
- truthound_dashboard/core/phase5/__init__.py +17 -0
- truthound_dashboard/core/phase5/activity.py +144 -0
- truthound_dashboard/core/phase5/catalog.py +868 -0
- truthound_dashboard/core/phase5/collaboration.py +305 -0
- truthound_dashboard/core/phase5/glossary.py +828 -0
- truthound_dashboard/db/__init__.py +37 -0
- truthound_dashboard/db/models.py +693 -0
- truthound_dashboard/schemas/__init__.py +114 -0
- truthound_dashboard/schemas/catalog.py +352 -0
- truthound_dashboard/schemas/collaboration.py +169 -0
- truthound_dashboard/schemas/glossary.py +349 -0
- truthound_dashboard/translate/__init__.py +61 -0
- truthound_dashboard/translate/config_updater.py +327 -0
- truthound_dashboard/translate/exceptions.py +98 -0
- truthound_dashboard/translate/providers/__init__.py +49 -0
- truthound_dashboard/translate/providers/anthropic.py +135 -0
- truthound_dashboard/translate/providers/base.py +225 -0
- truthound_dashboard/translate/providers/mistral.py +138 -0
- truthound_dashboard/translate/providers/ollama.py +226 -0
- truthound_dashboard/translate/providers/openai.py +187 -0
- truthound_dashboard/translate/providers/registry.py +217 -0
- truthound_dashboard/translate/translator.py +443 -0
- {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/METADATA +123 -4
- {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/RECORD +33 -11
- {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""Glossary API endpoints.
|
|
2
|
+
|
|
3
|
+
This module provides REST API endpoints for managing business glossary
|
|
4
|
+
terms, categories, and relationships.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Annotated
|
|
10
|
+
|
|
11
|
+
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
12
|
+
|
|
13
|
+
from truthound_dashboard.core.phase5 import GlossaryService
|
|
14
|
+
from truthound_dashboard.schemas import (
|
|
15
|
+
CategoryCreate,
|
|
16
|
+
CategoryListResponse,
|
|
17
|
+
CategoryResponse,
|
|
18
|
+
CategoryUpdate,
|
|
19
|
+
MessageResponse,
|
|
20
|
+
RelationshipCreate,
|
|
21
|
+
RelationshipListResponse,
|
|
22
|
+
RelationshipResponse,
|
|
23
|
+
TermCreate,
|
|
24
|
+
TermHistoryListResponse,
|
|
25
|
+
TermHistoryResponse,
|
|
26
|
+
TermListItem,
|
|
27
|
+
TermListResponse,
|
|
28
|
+
TermResponse,
|
|
29
|
+
TermUpdate,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from .deps import SessionDep
|
|
33
|
+
|
|
34
|
+
router = APIRouter()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# =============================================================================
|
|
38
|
+
# Dependencies
|
|
39
|
+
# =============================================================================
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def get_glossary_service(session: SessionDep) -> GlossaryService:
|
|
43
|
+
"""Get glossary service dependency."""
|
|
44
|
+
return GlossaryService(session)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
GlossaryServiceDep = Annotated[GlossaryService, Depends(get_glossary_service)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# =============================================================================
|
|
51
|
+
# Term Endpoints
|
|
52
|
+
# =============================================================================
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@router.get("/terms", response_model=TermListResponse)
|
|
56
|
+
async def list_terms(
|
|
57
|
+
service: GlossaryServiceDep,
|
|
58
|
+
search: Annotated[str | None, Query(description="Search query")] = None,
|
|
59
|
+
category_id: Annotated[str | None, Query(description="Filter by category")] = None,
|
|
60
|
+
status: Annotated[str | None, Query(description="Filter by status")] = None,
|
|
61
|
+
offset: Annotated[int, Query(ge=0)] = 0,
|
|
62
|
+
limit: Annotated[int, Query(ge=1, le=100)] = 100,
|
|
63
|
+
) -> TermListResponse:
|
|
64
|
+
"""List glossary terms with optional filters.
|
|
65
|
+
|
|
66
|
+
- **search**: Search in term name and definition
|
|
67
|
+
- **category_id**: Filter by category
|
|
68
|
+
- **status**: Filter by status (draft, approved, deprecated)
|
|
69
|
+
"""
|
|
70
|
+
terms, total = await service.list_terms(
|
|
71
|
+
query=search,
|
|
72
|
+
category_id=category_id,
|
|
73
|
+
status=status,
|
|
74
|
+
offset=offset,
|
|
75
|
+
limit=limit,
|
|
76
|
+
)
|
|
77
|
+
return TermListResponse(
|
|
78
|
+
data=[TermListItem.from_model(t) for t in terms],
|
|
79
|
+
total=total,
|
|
80
|
+
offset=offset,
|
|
81
|
+
limit=limit,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@router.post("/terms", response_model=TermResponse, status_code=status.HTTP_201_CREATED)
|
|
86
|
+
async def create_term(
|
|
87
|
+
service: GlossaryServiceDep,
|
|
88
|
+
data: TermCreate,
|
|
89
|
+
) -> TermResponse:
|
|
90
|
+
"""Create a new glossary term."""
|
|
91
|
+
try:
|
|
92
|
+
term = await service.create_term(
|
|
93
|
+
name=data.name,
|
|
94
|
+
definition=data.definition,
|
|
95
|
+
category_id=data.category_id,
|
|
96
|
+
status=data.status.value,
|
|
97
|
+
owner_id=data.owner_id,
|
|
98
|
+
)
|
|
99
|
+
return TermResponse.from_model(term)
|
|
100
|
+
except ValueError as e:
|
|
101
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@router.get("/terms/{term_id}", response_model=TermResponse)
|
|
105
|
+
async def get_term(
|
|
106
|
+
service: GlossaryServiceDep,
|
|
107
|
+
term_id: str,
|
|
108
|
+
) -> TermResponse:
|
|
109
|
+
"""Get a glossary term by ID."""
|
|
110
|
+
term = await service.get_term(term_id)
|
|
111
|
+
if not term:
|
|
112
|
+
raise HTTPException(
|
|
113
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
114
|
+
detail=f"Term '{term_id}' not found",
|
|
115
|
+
)
|
|
116
|
+
return TermResponse.from_model(term)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@router.put("/terms/{term_id}", response_model=TermResponse)
|
|
120
|
+
async def update_term(
|
|
121
|
+
service: GlossaryServiceDep,
|
|
122
|
+
term_id: str,
|
|
123
|
+
data: TermUpdate,
|
|
124
|
+
) -> TermResponse:
|
|
125
|
+
"""Update a glossary term."""
|
|
126
|
+
try:
|
|
127
|
+
term = await service.update_term(
|
|
128
|
+
term_id,
|
|
129
|
+
name=data.name,
|
|
130
|
+
definition=data.definition,
|
|
131
|
+
category_id=data.category_id,
|
|
132
|
+
status=data.status.value if data.status else None,
|
|
133
|
+
owner_id=data.owner_id,
|
|
134
|
+
)
|
|
135
|
+
if not term:
|
|
136
|
+
raise HTTPException(
|
|
137
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
138
|
+
detail=f"Term '{term_id}' not found",
|
|
139
|
+
)
|
|
140
|
+
return TermResponse.from_model(term)
|
|
141
|
+
except ValueError as e:
|
|
142
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@router.delete("/terms/{term_id}", response_model=MessageResponse)
|
|
146
|
+
async def delete_term(
|
|
147
|
+
service: GlossaryServiceDep,
|
|
148
|
+
term_id: str,
|
|
149
|
+
) -> MessageResponse:
|
|
150
|
+
"""Delete a glossary term."""
|
|
151
|
+
deleted = await service.delete_term(term_id)
|
|
152
|
+
if not deleted:
|
|
153
|
+
raise HTTPException(
|
|
154
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
155
|
+
detail=f"Term '{term_id}' not found",
|
|
156
|
+
)
|
|
157
|
+
return MessageResponse(message="Term deleted successfully")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@router.get("/terms/{term_id}/history", response_model=TermHistoryListResponse)
|
|
161
|
+
async def get_term_history(
|
|
162
|
+
service: GlossaryServiceDep,
|
|
163
|
+
term_id: str,
|
|
164
|
+
limit: Annotated[int, Query(ge=1, le=100)] = 50,
|
|
165
|
+
) -> TermHistoryListResponse:
|
|
166
|
+
"""Get change history for a term."""
|
|
167
|
+
# Verify term exists
|
|
168
|
+
term = await service.get_term(term_id)
|
|
169
|
+
if not term:
|
|
170
|
+
raise HTTPException(
|
|
171
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
172
|
+
detail=f"Term '{term_id}' not found",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
history = await service.get_term_history(term_id, limit=limit)
|
|
176
|
+
return TermHistoryListResponse(
|
|
177
|
+
data=[TermHistoryResponse.from_model(h) for h in history],
|
|
178
|
+
total=len(history),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# =============================================================================
|
|
183
|
+
# Category Endpoints
|
|
184
|
+
# =============================================================================
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@router.get("/categories", response_model=CategoryListResponse)
|
|
188
|
+
async def list_categories(
|
|
189
|
+
service: GlossaryServiceDep,
|
|
190
|
+
offset: Annotated[int, Query(ge=0)] = 0,
|
|
191
|
+
limit: Annotated[int, Query(ge=1, le=100)] = 100,
|
|
192
|
+
) -> CategoryListResponse:
|
|
193
|
+
"""List all glossary categories."""
|
|
194
|
+
categories, total = await service.list_categories(offset=offset, limit=limit)
|
|
195
|
+
return CategoryListResponse(
|
|
196
|
+
data=[CategoryResponse.from_model(c) for c in categories],
|
|
197
|
+
total=total,
|
|
198
|
+
offset=offset,
|
|
199
|
+
limit=limit,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@router.post("/categories", response_model=CategoryResponse, status_code=status.HTTP_201_CREATED)
|
|
204
|
+
async def create_category(
|
|
205
|
+
service: GlossaryServiceDep,
|
|
206
|
+
data: CategoryCreate,
|
|
207
|
+
) -> CategoryResponse:
|
|
208
|
+
"""Create a new glossary category."""
|
|
209
|
+
try:
|
|
210
|
+
category = await service.create_category(
|
|
211
|
+
name=data.name,
|
|
212
|
+
description=data.description,
|
|
213
|
+
parent_id=data.parent_id,
|
|
214
|
+
)
|
|
215
|
+
return CategoryResponse.from_model(category)
|
|
216
|
+
except ValueError as e:
|
|
217
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@router.get("/categories/{category_id}", response_model=CategoryResponse)
|
|
221
|
+
async def get_category(
|
|
222
|
+
service: GlossaryServiceDep,
|
|
223
|
+
category_id: str,
|
|
224
|
+
) -> CategoryResponse:
|
|
225
|
+
"""Get a glossary category by ID."""
|
|
226
|
+
category = await service.get_category(category_id)
|
|
227
|
+
if not category:
|
|
228
|
+
raise HTTPException(
|
|
229
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
230
|
+
detail=f"Category '{category_id}' not found",
|
|
231
|
+
)
|
|
232
|
+
return CategoryResponse.from_model(category)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@router.put("/categories/{category_id}", response_model=CategoryResponse)
|
|
236
|
+
async def update_category(
|
|
237
|
+
service: GlossaryServiceDep,
|
|
238
|
+
category_id: str,
|
|
239
|
+
data: CategoryUpdate,
|
|
240
|
+
) -> CategoryResponse:
|
|
241
|
+
"""Update a glossary category."""
|
|
242
|
+
try:
|
|
243
|
+
category = await service.update_category(
|
|
244
|
+
category_id,
|
|
245
|
+
name=data.name,
|
|
246
|
+
description=data.description,
|
|
247
|
+
parent_id=data.parent_id,
|
|
248
|
+
)
|
|
249
|
+
if not category:
|
|
250
|
+
raise HTTPException(
|
|
251
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
252
|
+
detail=f"Category '{category_id}' not found",
|
|
253
|
+
)
|
|
254
|
+
return CategoryResponse.from_model(category)
|
|
255
|
+
except ValueError as e:
|
|
256
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@router.delete("/categories/{category_id}", response_model=MessageResponse)
|
|
260
|
+
async def delete_category(
|
|
261
|
+
service: GlossaryServiceDep,
|
|
262
|
+
category_id: str,
|
|
263
|
+
) -> MessageResponse:
|
|
264
|
+
"""Delete a glossary category."""
|
|
265
|
+
deleted = await service.delete_category(category_id)
|
|
266
|
+
if not deleted:
|
|
267
|
+
raise HTTPException(
|
|
268
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
269
|
+
detail=f"Category '{category_id}' not found",
|
|
270
|
+
)
|
|
271
|
+
return MessageResponse(message="Category deleted successfully")
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# =============================================================================
|
|
275
|
+
# Relationship Endpoints
|
|
276
|
+
# =============================================================================
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@router.get("/terms/{term_id}/relationships", response_model=RelationshipListResponse)
|
|
280
|
+
async def get_term_relationships(
|
|
281
|
+
service: GlossaryServiceDep,
|
|
282
|
+
term_id: str,
|
|
283
|
+
) -> RelationshipListResponse:
|
|
284
|
+
"""Get all relationships for a term."""
|
|
285
|
+
# Verify term exists
|
|
286
|
+
term = await service.get_term(term_id)
|
|
287
|
+
if not term:
|
|
288
|
+
raise HTTPException(
|
|
289
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
290
|
+
detail=f"Term '{term_id}' not found",
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
relationships = await service.get_term_relationships(term_id)
|
|
294
|
+
return RelationshipListResponse(
|
|
295
|
+
data=[RelationshipResponse.from_model(r) for r in relationships],
|
|
296
|
+
total=len(relationships),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@router.post("/relationships", response_model=RelationshipResponse, status_code=status.HTTP_201_CREATED)
|
|
301
|
+
async def create_relationship(
|
|
302
|
+
service: GlossaryServiceDep,
|
|
303
|
+
data: RelationshipCreate,
|
|
304
|
+
) -> RelationshipResponse:
|
|
305
|
+
"""Create a relationship between terms."""
|
|
306
|
+
try:
|
|
307
|
+
relationship = await service.create_relationship(
|
|
308
|
+
source_term_id=data.source_term_id,
|
|
309
|
+
target_term_id=data.target_term_id,
|
|
310
|
+
relationship_type=data.relationship_type.value,
|
|
311
|
+
)
|
|
312
|
+
return RelationshipResponse.from_model(relationship)
|
|
313
|
+
except ValueError as e:
|
|
314
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@router.delete("/relationships/{relationship_id}", response_model=MessageResponse)
|
|
318
|
+
async def delete_relationship(
|
|
319
|
+
service: GlossaryServiceDep,
|
|
320
|
+
relationship_id: str,
|
|
321
|
+
) -> MessageResponse:
|
|
322
|
+
"""Delete a term relationship."""
|
|
323
|
+
deleted = await service.delete_relationship(relationship_id)
|
|
324
|
+
if not deleted:
|
|
325
|
+
raise HTTPException(
|
|
326
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
327
|
+
detail=f"Relationship '{relationship_id}' not found",
|
|
328
|
+
)
|
|
329
|
+
return MessageResponse(message="Relationship deleted successfully")
|
|
@@ -6,6 +6,7 @@ This module configures the main API router and includes all sub-routers.
|
|
|
6
6
|
from fastapi import APIRouter
|
|
7
7
|
|
|
8
8
|
from . import (
|
|
9
|
+
# Phase 1-4
|
|
9
10
|
drift,
|
|
10
11
|
health,
|
|
11
12
|
history,
|
|
@@ -16,6 +17,10 @@ from . import (
|
|
|
16
17
|
schemas,
|
|
17
18
|
sources,
|
|
18
19
|
validations,
|
|
20
|
+
# Phase 5
|
|
21
|
+
catalog,
|
|
22
|
+
collaboration,
|
|
23
|
+
glossary,
|
|
19
24
|
)
|
|
20
25
|
|
|
21
26
|
api_router = APIRouter()
|
|
@@ -81,3 +86,27 @@ api_router.include_router(
|
|
|
81
86
|
notifications.router,
|
|
82
87
|
tags=["notifications"],
|
|
83
88
|
)
|
|
89
|
+
|
|
90
|
+
# =============================================================================
|
|
91
|
+
# Phase 5: Business Glossary & Data Catalog
|
|
92
|
+
# =============================================================================
|
|
93
|
+
|
|
94
|
+
# Glossary management endpoints
|
|
95
|
+
api_router.include_router(
|
|
96
|
+
glossary.router,
|
|
97
|
+
prefix="/glossary",
|
|
98
|
+
tags=["glossary"],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Catalog management endpoints
|
|
102
|
+
api_router.include_router(
|
|
103
|
+
catalog.router,
|
|
104
|
+
prefix="/catalog",
|
|
105
|
+
tags=["catalog"],
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Collaboration endpoints (comments, activities)
|
|
109
|
+
api_router.include_router(
|
|
110
|
+
collaboration.router,
|
|
111
|
+
tags=["collaboration"],
|
|
112
|
+
)
|