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.
Files changed (33) hide show
  1. truthound_dashboard/api/catalog.py +343 -0
  2. truthound_dashboard/api/collaboration.py +148 -0
  3. truthound_dashboard/api/glossary.py +329 -0
  4. truthound_dashboard/api/router.py +29 -0
  5. truthound_dashboard/cli.py +397 -0
  6. truthound_dashboard/core/__init__.py +12 -0
  7. truthound_dashboard/core/phase5/__init__.py +17 -0
  8. truthound_dashboard/core/phase5/activity.py +144 -0
  9. truthound_dashboard/core/phase5/catalog.py +868 -0
  10. truthound_dashboard/core/phase5/collaboration.py +305 -0
  11. truthound_dashboard/core/phase5/glossary.py +828 -0
  12. truthound_dashboard/db/__init__.py +37 -0
  13. truthound_dashboard/db/models.py +693 -0
  14. truthound_dashboard/schemas/__init__.py +114 -0
  15. truthound_dashboard/schemas/catalog.py +352 -0
  16. truthound_dashboard/schemas/collaboration.py +169 -0
  17. truthound_dashboard/schemas/glossary.py +349 -0
  18. truthound_dashboard/translate/__init__.py +61 -0
  19. truthound_dashboard/translate/config_updater.py +327 -0
  20. truthound_dashboard/translate/exceptions.py +98 -0
  21. truthound_dashboard/translate/providers/__init__.py +49 -0
  22. truthound_dashboard/translate/providers/anthropic.py +135 -0
  23. truthound_dashboard/translate/providers/base.py +225 -0
  24. truthound_dashboard/translate/providers/mistral.py +138 -0
  25. truthound_dashboard/translate/providers/ollama.py +226 -0
  26. truthound_dashboard/translate/providers/openai.py +187 -0
  27. truthound_dashboard/translate/providers/registry.py +217 -0
  28. truthound_dashboard/translate/translator.py +443 -0
  29. {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/METADATA +123 -4
  30. {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/RECORD +33 -11
  31. {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/WHEEL +0 -0
  32. {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/entry_points.txt +0 -0
  33. {truthound_dashboard-1.0.2.dist-info → truthound_dashboard-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,349 @@
1
+ """Pydantic schemas for Business Glossary API.
2
+
3
+ This module defines request/response schemas for glossary terms,
4
+ categories, and relationships.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime
10
+ from enum import Enum
11
+
12
+ from pydantic import Field, field_validator
13
+
14
+ from .base import BaseSchema, IDMixin, ListResponseWrapper, TimestampMixin
15
+
16
+
17
+ # =============================================================================
18
+ # Enums
19
+ # =============================================================================
20
+
21
+
22
+ class TermStatus(str, Enum):
23
+ """Status of a glossary term."""
24
+
25
+ DRAFT = "draft"
26
+ APPROVED = "approved"
27
+ DEPRECATED = "deprecated"
28
+
29
+
30
+ class RelationshipType(str, Enum):
31
+ """Type of relationship between terms."""
32
+
33
+ SYNONYM = "synonym"
34
+ RELATED = "related"
35
+ PARENT = "parent"
36
+ CHILD = "child"
37
+
38
+
39
+ # =============================================================================
40
+ # Category Schemas
41
+ # =============================================================================
42
+
43
+
44
+ class CategoryBase(BaseSchema):
45
+ """Base schema for glossary categories."""
46
+
47
+ name: str = Field(..., min_length=1, max_length=255, description="Category name")
48
+ description: str | None = Field(None, description="Category description")
49
+ parent_id: str | None = Field(None, description="Parent category ID")
50
+
51
+
52
+ class CategoryCreate(CategoryBase):
53
+ """Schema for creating a glossary category."""
54
+
55
+ pass
56
+
57
+
58
+ class CategoryUpdate(BaseSchema):
59
+ """Schema for updating a glossary category."""
60
+
61
+ name: str | None = Field(None, min_length=1, max_length=255)
62
+ description: str | None = None
63
+ parent_id: str | None = None
64
+
65
+
66
+ class CategorySummary(BaseSchema, IDMixin):
67
+ """Summary schema for category references."""
68
+
69
+ name: str
70
+ full_path: str | None = None
71
+
72
+
73
+ class CategoryResponse(BaseSchema, IDMixin, TimestampMixin):
74
+ """Response schema for a glossary category."""
75
+
76
+ name: str
77
+ description: str | None
78
+ parent_id: str | None
79
+ parent: CategorySummary | None = None
80
+ term_count: int = 0
81
+ full_path: str
82
+
83
+ @classmethod
84
+ def from_model(cls, category: any) -> CategoryResponse:
85
+ """Create response from model."""
86
+ parent_summary = None
87
+ if category.parent:
88
+ parent_summary = CategorySummary(
89
+ id=category.parent.id,
90
+ name=category.parent.name,
91
+ full_path=category.parent.full_path,
92
+ )
93
+ return cls(
94
+ id=category.id,
95
+ name=category.name,
96
+ description=category.description,
97
+ parent_id=category.parent_id,
98
+ parent=parent_summary,
99
+ term_count=category.term_count,
100
+ full_path=category.full_path,
101
+ created_at=category.created_at,
102
+ updated_at=category.updated_at,
103
+ )
104
+
105
+
106
+ class CategoryListResponse(ListResponseWrapper[CategoryResponse]):
107
+ """Paginated list of categories."""
108
+
109
+ pass
110
+
111
+
112
+ # =============================================================================
113
+ # Term Schemas
114
+ # =============================================================================
115
+
116
+
117
+ class TermBase(BaseSchema):
118
+ """Base schema for glossary terms."""
119
+
120
+ name: str = Field(..., min_length=1, max_length=255, description="Term name")
121
+ definition: str = Field(..., min_length=1, description="Term definition")
122
+ category_id: str | None = Field(None, description="Category ID")
123
+ status: TermStatus = Field(default=TermStatus.DRAFT, description="Term status")
124
+ owner_id: str | None = Field(None, description="Owner identifier")
125
+
126
+
127
+ class TermCreate(TermBase):
128
+ """Schema for creating a glossary term."""
129
+
130
+ @field_validator("name")
131
+ @classmethod
132
+ def validate_name(cls, v: str) -> str:
133
+ """Validate term name."""
134
+ return v.strip()
135
+
136
+ @field_validator("definition")
137
+ @classmethod
138
+ def validate_definition(cls, v: str) -> str:
139
+ """Validate term definition."""
140
+ return v.strip()
141
+
142
+
143
+ class TermUpdate(BaseSchema):
144
+ """Schema for updating a glossary term."""
145
+
146
+ name: str | None = Field(None, min_length=1, max_length=255)
147
+ definition: str | None = Field(None, min_length=1)
148
+ category_id: str | None = None
149
+ status: TermStatus | None = None
150
+ owner_id: str | None = None
151
+
152
+
153
+ class TermSummary(BaseSchema, IDMixin):
154
+ """Summary schema for term references."""
155
+
156
+ name: str
157
+ status: TermStatus
158
+
159
+
160
+ class RelatedTermSummary(BaseSchema, IDMixin):
161
+ """Summary for related terms with relationship type."""
162
+
163
+ name: str
164
+ status: TermStatus
165
+ relationship_type: RelationshipType
166
+
167
+
168
+ class TermResponse(BaseSchema, IDMixin, TimestampMixin):
169
+ """Response schema for a glossary term."""
170
+
171
+ name: str
172
+ definition: str
173
+ category_id: str | None
174
+ category: CategorySummary | None = None
175
+ status: TermStatus
176
+ owner_id: str | None
177
+ synonyms: list[TermSummary] = Field(default_factory=list)
178
+ related_terms: list[TermSummary] = Field(default_factory=list)
179
+ mapped_column_count: int = 0
180
+
181
+ @classmethod
182
+ def from_model(cls, term: any) -> TermResponse:
183
+ """Create response from model."""
184
+ category_summary = None
185
+ if term.category:
186
+ category_summary = CategorySummary(
187
+ id=term.category.id,
188
+ name=term.category.name,
189
+ full_path=term.category.full_path,
190
+ )
191
+
192
+ synonyms = [
193
+ TermSummary(id=t.id, name=t.name, status=TermStatus(t.status))
194
+ for t in term.synonyms
195
+ ]
196
+
197
+ related = [
198
+ TermSummary(id=t.id, name=t.name, status=TermStatus(t.status))
199
+ for t in term.related_terms
200
+ ]
201
+
202
+ return cls(
203
+ id=term.id,
204
+ name=term.name,
205
+ definition=term.definition,
206
+ category_id=term.category_id,
207
+ category=category_summary,
208
+ status=TermStatus(term.status),
209
+ owner_id=term.owner_id,
210
+ synonyms=synonyms,
211
+ related_terms=related,
212
+ mapped_column_count=len(term.mapped_columns),
213
+ created_at=term.created_at,
214
+ updated_at=term.updated_at,
215
+ )
216
+
217
+
218
+ class TermListItem(BaseSchema, IDMixin, TimestampMixin):
219
+ """List item schema for terms (lighter than full response)."""
220
+
221
+ name: str
222
+ definition: str
223
+ category_id: str | None
224
+ category_name: str | None = None
225
+ status: TermStatus
226
+ owner_id: str | None
227
+ synonym_count: int = 0
228
+ related_count: int = 0
229
+
230
+ @classmethod
231
+ def from_model(cls, term: any) -> TermListItem:
232
+ """Create list item from model."""
233
+ return cls(
234
+ id=term.id,
235
+ name=term.name,
236
+ definition=term.definition,
237
+ category_id=term.category_id,
238
+ category_name=term.category.name if term.category else None,
239
+ status=TermStatus(term.status),
240
+ owner_id=term.owner_id,
241
+ synonym_count=len(term.synonyms),
242
+ related_count=len(term.related_terms),
243
+ created_at=term.created_at,
244
+ updated_at=term.updated_at,
245
+ )
246
+
247
+
248
+ class TermListResponse(ListResponseWrapper[TermListItem]):
249
+ """Paginated list of terms."""
250
+
251
+ pass
252
+
253
+
254
+ # =============================================================================
255
+ # Relationship Schemas
256
+ # =============================================================================
257
+
258
+
259
+ class RelationshipBase(BaseSchema):
260
+ """Base schema for term relationships."""
261
+
262
+ source_term_id: str = Field(..., description="Source term ID")
263
+ target_term_id: str = Field(..., description="Target term ID")
264
+ relationship_type: RelationshipType = Field(..., description="Relationship type")
265
+
266
+
267
+ class RelationshipCreate(RelationshipBase):
268
+ """Schema for creating a term relationship."""
269
+
270
+ @field_validator("target_term_id")
271
+ @classmethod
272
+ def validate_different_terms(cls, v: str, info) -> str:
273
+ """Validate that source and target are different."""
274
+ if info.data.get("source_term_id") == v:
275
+ raise ValueError("Source and target terms must be different")
276
+ return v
277
+
278
+
279
+ class RelationshipResponse(BaseSchema, IDMixin):
280
+ """Response schema for a term relationship."""
281
+
282
+ source_term_id: str
283
+ target_term_id: str
284
+ source_term: TermSummary
285
+ target_term: TermSummary
286
+ relationship_type: RelationshipType
287
+ created_at: datetime
288
+
289
+ @classmethod
290
+ def from_model(cls, rel: any) -> RelationshipResponse:
291
+ """Create response from model."""
292
+ return cls(
293
+ id=rel.id,
294
+ source_term_id=rel.source_term_id,
295
+ target_term_id=rel.target_term_id,
296
+ source_term=TermSummary(
297
+ id=rel.source_term.id,
298
+ name=rel.source_term.name,
299
+ status=TermStatus(rel.source_term.status),
300
+ ),
301
+ target_term=TermSummary(
302
+ id=rel.target_term.id,
303
+ name=rel.target_term.name,
304
+ status=TermStatus(rel.target_term.status),
305
+ ),
306
+ relationship_type=RelationshipType(rel.relationship_type),
307
+ created_at=rel.created_at,
308
+ )
309
+
310
+
311
+ class RelationshipListResponse(ListResponseWrapper[RelationshipResponse]):
312
+ """List of relationships."""
313
+
314
+ pass
315
+
316
+
317
+ # =============================================================================
318
+ # History Schemas
319
+ # =============================================================================
320
+
321
+
322
+ class TermHistoryResponse(BaseSchema, IDMixin):
323
+ """Response schema for term change history."""
324
+
325
+ term_id: str
326
+ field_name: str
327
+ old_value: str | None
328
+ new_value: str | None
329
+ changed_by: str | None
330
+ changed_at: datetime
331
+
332
+ @classmethod
333
+ def from_model(cls, history: any) -> TermHistoryResponse:
334
+ """Create response from model."""
335
+ return cls(
336
+ id=history.id,
337
+ term_id=history.term_id,
338
+ field_name=history.field_name,
339
+ old_value=history.old_value,
340
+ new_value=history.new_value,
341
+ changed_by=history.changed_by,
342
+ changed_at=history.changed_at,
343
+ )
344
+
345
+
346
+ class TermHistoryListResponse(ListResponseWrapper[TermHistoryResponse]):
347
+ """List of term history entries."""
348
+
349
+ pass
@@ -0,0 +1,61 @@
1
+ """Translation module for truthound-dashboard.
2
+
3
+ This module provides AI-powered translation capabilities for Intlayer content files.
4
+ It supports multiple AI providers and can automatically detect available providers
5
+ based on environment variables.
6
+
7
+ Example:
8
+ # CLI usage
9
+ truthound translate -l ja,zh,de -p openai
10
+
11
+ # Auto-detect provider
12
+ truthound translate -l ja,zh
13
+
14
+ Supported Providers:
15
+ - OpenAI (GPT-4, GPT-3.5)
16
+ - Anthropic (Claude 3)
17
+ - Ollama (local LLM, no API key required)
18
+ - Mistral
19
+ """
20
+
21
+ from truthound_dashboard.translate.providers import (
22
+ AIProvider,
23
+ ProviderConfig,
24
+ ProviderRegistry,
25
+ get_provider,
26
+ detect_provider,
27
+ list_available_providers,
28
+ )
29
+ from truthound_dashboard.translate.translator import (
30
+ ContentTranslator,
31
+ TranslationResult,
32
+ )
33
+ from truthound_dashboard.translate.config_updater import (
34
+ IntlayerConfigUpdater,
35
+ )
36
+ from truthound_dashboard.translate.exceptions import (
37
+ TranslationError,
38
+ ProviderNotFoundError,
39
+ APIKeyNotFoundError,
40
+ TranslationAPIError,
41
+ )
42
+
43
+ __all__ = [
44
+ # Providers
45
+ "AIProvider",
46
+ "ProviderConfig",
47
+ "ProviderRegistry",
48
+ "get_provider",
49
+ "detect_provider",
50
+ "list_available_providers",
51
+ # Translator
52
+ "ContentTranslator",
53
+ "TranslationResult",
54
+ # Config
55
+ "IntlayerConfigUpdater",
56
+ # Exceptions
57
+ "TranslationError",
58
+ "ProviderNotFoundError",
59
+ "APIKeyNotFoundError",
60
+ "TranslationAPIError",
61
+ ]