diff --git a/backend/app/api/v1/routes_glossaries.py b/backend/app/api/v1/routes_glossaries.py index 7017795..afd2391 100644 --- a/backend/app/api/v1/routes_glossaries.py +++ b/backend/app/api/v1/routes_glossaries.py @@ -47,7 +47,8 @@ async def list_glossaries( """List all active glossaries for a client.""" assert_user_in_org(ctx, client_id, OrgRole.VIEWER) glossaries = await svc.get_glossaries_for_client(client_id) - return [_to_response(g) for g in glossaries] + version_map = await svc.get_versions_by_ids([g.current_version_id for g in glossaries if g.current_version_id]) + return [_to_response(g, version_map.get(g.current_version_id)) for g in glossaries] # ── Upload new glossary ─────────────────────────────────────────────────────── @@ -286,7 +287,7 @@ def _validate_xlsx(file: UploadFile) -> None: ) -def _to_response(g) -> GlossaryResponse: +def _to_response(g, current_version=None) -> GlossaryResponse: return GlossaryResponse( id=str(g.id), client_id=g.client_id, @@ -296,6 +297,9 @@ def _to_response(g) -> GlossaryResponse: source=g.source, status=g.status, current_version_id=g.current_version_id, + current_version_embedding_status=current_version.embedding_status if current_version else None, + current_version_embedded_count=current_version.embedded_count if current_version else None, + current_version_term_count=current_version.term_count if current_version else None, created_at=g.created_at, created_by=g.created_by, ) diff --git a/backend/app/models/glossary.py b/backend/app/models/glossary.py index 0dfc60a..347ddb6 100644 --- a/backend/app/models/glossary.py +++ b/backend/app/models/glossary.py @@ -91,6 +91,9 @@ class GlossaryResponse(BaseModel): source: GlossarySource status: GlossaryStatus current_version_id: str | None = None + current_version_embedding_status: EmbeddingStatus | None = None + current_version_embedded_count: int | None = None + current_version_term_count: int | None = None created_at: datetime created_by: str diff --git a/backend/app/services/glossary_service.py b/backend/app/services/glossary_service.py index 7eee6cb..fa282e6 100644 --- a/backend/app/services/glossary_service.py +++ b/backend/app/services/glossary_service.py @@ -728,6 +728,17 @@ async def get_glossary(glossary_id: str) -> Glossary | None: return glossary_from_doc(doc) if doc else None +async def get_versions_by_ids(version_ids: list[str]) -> dict[str, GlossaryVersion]: + """Batch-fetch versions by ID, returns {version_id: GlossaryVersion}.""" + if not version_ids: + return {} + db = await get_database() + docs = await db[_COLL_VERSIONS].find( + {"_id": {"$in": [ObjectId(vid) for vid in version_ids]}} + ).to_list(length=len(version_ids)) + return {str(d["_id"]): glossary_version_from_doc(d) for d in docs} + + async def get_versions(glossary_id: str) -> list[GlossaryVersion]: db = await get_database() cursor = db[_COLL_VERSIONS].find( diff --git a/frontend/src/routes/admin/glossaries/GlossaryList.tsx b/frontend/src/routes/admin/glossaries/GlossaryList.tsx index 31c717a..c5bde63 100644 --- a/frontend/src/routes/admin/glossaries/GlossaryList.tsx +++ b/frontend/src/routes/admin/glossaries/GlossaryList.tsx @@ -11,12 +11,17 @@ function statusBadge(status: string) { : 'bg-gray-100 text-gray-500'; } -function embeddingBadge(status: string) { +function embeddingBadge(g: import('../../../types/api').Glossary) { + const status = g.current_version_embedding_status; + const pct = g.current_version_term_count && g.current_version_term_count > 0 + ? Math.round(((g.current_version_embedded_count ?? 0) / g.current_version_term_count) * 100) + : 0; switch (status) { - case 'done': return Embedded ✓; - case 'in_progress': return Embedding…; + case 'done': return Embedded ✓ ({g.current_version_term_count?.toLocaleString()}); + case 'in_progress': return Embedding… {pct}%; case 'failed': return Embed failed; - default: return Pending embed; + case 'pending': return Pending embed; + default: return null; } } @@ -107,7 +112,7 @@ export function GlossaryList() {
- {g.current_version_id ? embeddingBadge('') : null} + {g.current_version_id ? embeddingBadge(g) : null}
{isAdmin && g.status === 'active' && (