Skip to content

Commit a1183c8

Browse files
committed
Optimize dashboard, summary api
1 parent 889ee3e commit a1183c8

9 files changed

Lines changed: 194 additions & 23 deletions

File tree

Architect_MultiClient_Server/dashboard/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Architect_MultiClient_Server/dashboard/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
"preview": "vite preview"
99
},
1010
"dependencies": {
11+
"axios": "^1.6.2",
1112
"react": "^18.2.0",
1213
"react-dom": "^18.2.0",
13-
"react-router-dom": "^6.20.0",
14-
"axios": "^1.6.2"
14+
"react-router-dom": "^6.20.0"
1515
},
1616
"devDependencies": {
1717
"@types/react": "^18.2.43",
@@ -20,6 +20,6 @@
2020
"autoprefixer": "^10.4.16",
2121
"postcss": "^8.4.32",
2222
"tailwindcss": "^3.3.6",
23-
"vite": "^5.0.8"
23+
"vite": "^5.4.21"
2424
}
2525
}

Architect_MultiClient_Server/dashboard/src/App.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function App() {
2121
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
2222
<Routes>
2323
<Route path="/" element={<RoomList />} />
24-
<Route path="/room/:roomName" element={<RoomDetail />} />
24+
<Route path="/room/:roomId" element={<RoomDetail />} />
2525
</Routes>
2626
</main>
2727
</div>

Architect_MultiClient_Server/dashboard/src/components/RoomDetail.jsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { useState, useEffect } from 'react';
22
import { useParams, useNavigate } from 'react-router-dom';
33
import {
4-
getRoomByName,
5-
getRoomStatistics,
6-
getSummaryByRoom
4+
getRoomById,
5+
getRoomStatisticsById,
6+
getSummaryByRoomId
77
} from '../services/api';
88

99
const RoomDetail = () => {
10-
const { roomName } = useParams();
10+
const { roomId } = useParams();
1111
const navigate = useNavigate();
1212

1313
const [room, setRoom] = useState(null);
@@ -19,7 +19,7 @@ const RoomDetail = () => {
1919

2020
useEffect(() => {
2121
fetchRoomData();
22-
}, [roomName]);
22+
}, [roomId]);
2323

2424
const fetchRoomData = async () => {
2525
try {
@@ -28,9 +28,9 @@ const RoomDetail = () => {
2828

2929
// Fetch room details, statistics, and summaries in parallel
3030
const [roomData, statsData, summaryData] = await Promise.all([
31-
getRoomByName(roomName),
32-
getRoomStatistics(roomName),
33-
getSummaryByRoom(roomName)
31+
getRoomById(roomId),
32+
getRoomStatisticsById(roomId),
33+
getSummaryByRoomId(roomId)
3434
]);
3535

3636
setRoom(roomData.room);
@@ -174,7 +174,7 @@ const RoomDetail = () => {
174174
>
175175
← Back
176176
</button>
177-
<h2 className="text-3xl font-bold text-gray-900">{roomName}</h2>
177+
<h2 className="text-3xl font-bold text-gray-900">{room.room_name}</h2>
178178
{getStatusBadge(room.status)}
179179
</div>
180180
<button

Architect_MultiClient_Server/dashboard/src/components/RoomList.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ const RoomList = () => {
141141
<tr
142142
key={room._id}
143143
className="hover:bg-gray-50 cursor-pointer transition"
144-
onClick={() => navigate(`/room/${room.room_name}`)}
144+
onClick={() => navigate(`/room/${room._id}`)}
145145
>
146146
<td className="px-6 py-4 whitespace-nowrap">
147147
<div className="text-sm font-medium text-gray-900">
@@ -161,7 +161,7 @@ const RoomList = () => {
161161
<button
162162
onClick={(e) => {
163163
e.stopPropagation();
164-
navigate(`/room/${room.room_name}`);
164+
navigate(`/room/${room._id}`);
165165
}}
166166
className="text-blue-600 hover:text-blue-900 font-medium"
167167
>

Architect_MultiClient_Server/dashboard/src/services/api.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,21 @@ export const getRoomByName = async (roomName) => {
2929
return response.data;
3030
};
3131

32+
export const getRoomById = async (roomId) => {
33+
const response = await apiClient.get(`/api/transcripts/rooms/id/${roomId}`);
34+
return response.data;
35+
};
36+
3237
export const getRoomStatistics = async (roomName) => {
3338
const response = await apiClient.get(`/api/transcripts/rooms/${roomName}/statistics`);
3439
return response.data;
3540
};
3641

42+
export const getRoomStatisticsById = async (roomId) => {
43+
const response = await apiClient.get(`/api/transcripts/rooms/id/${roomId}/statistics`);
44+
return response.data;
45+
};
46+
3747
// Summary APIs
3848
export const getSummaryByRoom = async (roomName, startTime = null, endTime = null) => {
3949
const queryParams = new URLSearchParams();
@@ -46,6 +56,11 @@ export const getSummaryByRoom = async (roomName, startTime = null, endTime = nul
4656
return response.data;
4757
};
4858

59+
export const getSummaryByRoomId = async (roomId) => {
60+
const response = await apiClient.get(`/api/summary/room/id/${roomId}`);
61+
return response.data;
62+
};
63+
4964
// Transcript APIs
5065
export const getFullTranscript = async (trackId) => {
5166
const response = await apiClient.get(`/api/transcripts/tracks/${trackId}/transcript`);

Architect_MultiClient_Server/orchestrator_service/api/room_api.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
SkipQuery,
2121
validate_date_range
2222
)
23+
from bson import ObjectId
2324

2425
router = APIRouter(prefix="/api/transcripts/rooms", tags=["Rooms"])
2526
logger = get_logger(__name__)
@@ -170,3 +171,82 @@ async def get_room_statistics(
170171
except Exception as e:
171172
logger.error(f"Failed to get room statistics: {e}")
172173
raise HTTPException(status_code=500, detail=str(e))
174+
175+
176+
@router.get("/id/{room_id}", response_description="Get room by ID")
177+
async def get_room_by_id(
178+
room_id: str,
179+
auth: Dict[str, Any] = Depends(verify_api_key)
180+
):
181+
"""
182+
Get room details by room ID.
183+
184+
- **room_id**: The ObjectId of the room to retrieve
185+
"""
186+
try:
187+
mongodb = get_mongodb_service()
188+
if not mongodb.connected:
189+
await mongodb.connect()
190+
191+
# Validate ObjectId format
192+
try:
193+
ObjectId(room_id)
194+
except Exception:
195+
raise HTTPException(status_code=400, detail=f"Invalid room_id format: '{room_id}'")
196+
197+
room = await mongodb.get_room_by_id(room_id)
198+
if not room:
199+
raise HTTPException(status_code=404, detail=f"Room with ID '{room_id}' not found")
200+
201+
room["_id"] = str(room["_id"])
202+
203+
return {
204+
"status": "ok",
205+
"room": room
206+
}
207+
except HTTPException:
208+
raise
209+
except Exception as e:
210+
logger.error(f"Failed to get room: {e}")
211+
raise HTTPException(status_code=500, detail=str(e))
212+
213+
214+
@router.get("/id/{room_id}/statistics", response_description="Get room statistics by ID")
215+
async def get_room_statistics_by_id(
216+
room_id: str,
217+
auth: Dict[str, Any] = Depends(verify_api_key)
218+
):
219+
"""
220+
Get detailed statistics for a specific room by ID.
221+
222+
- **room_id**: The ObjectId of the room
223+
224+
Returns:
225+
- Total tracks, completed/remaining tracks
226+
- Total duration in seconds
227+
- Total transcript segments
228+
"""
229+
try:
230+
mongodb = get_mongodb_service()
231+
if not mongodb.connected:
232+
await mongodb.connect()
233+
234+
# Validate ObjectId format
235+
try:
236+
ObjectId(room_id)
237+
except Exception:
238+
raise HTTPException(status_code=400, detail=f"Invalid room_id format: '{room_id}'")
239+
240+
stats = await mongodb.get_room_statistics_by_id(room_id)
241+
if not stats:
242+
raise HTTPException(status_code=404, detail=f"Room with ID '{room_id}' not found")
243+
244+
return {
245+
"status": "ok",
246+
"statistics": stats
247+
}
248+
except HTTPException:
249+
raise
250+
except Exception as e:
251+
logger.error(f"Failed to get room statistics: {e}")
252+
raise HTTPException(status_code=500, detail=str(e))

Architect_MultiClient_Server/orchestrator_service/api/summary_api.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,24 @@ async def get_summary_by_room_name(
7979
"""
8080
mongodb = get_mongodb_service()
8181
summaries = await mongodb.get_summary_by_room_name(room_name, start_time, end_time)
82+
return {
83+
"status": "ok",
84+
"data": summaries,
85+
"count": len(summaries)
86+
}
87+
88+
@client_router.get("/room/id/{room_id}", response_description="Get summary by room ID")
89+
async def get_summary_by_room_id(
90+
room_id: str,
91+
):
92+
"""
93+
Get summary by room id.
94+
"""
95+
mongodb = get_mongodb_service()
96+
summaries = await mongodb.get_summary_by_room_id(room_id)
8297
# remove unnecessary fields
8398
for summary in summaries:
8499
summary.pop("_id", None)
85-
summary.pop("room_id", None)
86100
summary.pop("summary_text", None)
87101
return {
88102
"status": "ok",

Architect_MultiClient_Server/orchestrator_service/services/mongodb_service.py

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,43 @@ async def get_room_statistics(self, room_name: str) -> Dict[str, Any]:
410410
logger.error(f"Failed to get room statistics: {e}")
411411
return {}
412412

413+
async def get_room_statistics_by_id(self, room_id: str) -> Dict[str, Any]:
414+
"""Get detailed statistics for a room by ID"""
415+
try:
416+
room = await self.get_room_by_id(room_id)
417+
if not room:
418+
return {}
419+
420+
tracks = await self.get_tracks_by_room(room_id)
421+
422+
total_duration = 0
423+
total_segments = 0
424+
425+
for track in tracks:
426+
chunks = await self.get_chunks_by_track(str(track["_id"]))
427+
for chunk in chunks:
428+
total_segments += chunk.get("item_count", 0)
429+
430+
audio_info = track.get("audio_info", {})
431+
duration_ns = int(audio_info.get("duration_sec", "0"))
432+
total_duration += duration_ns / 1_000_000_000 # Convert to seconds
433+
434+
return {
435+
"room_id": room_id,
436+
"room_name": room.get("room_name"),
437+
"status": room.get("status"),
438+
"total_tracks": len(tracks),
439+
"completed_tracks": room.get("completed_tracks", 0),
440+
"remain_tracks": room.get("remain_tracks", 0),
441+
"total_duration_sec": total_duration,
442+
"total_segments": total_segments,
443+
"created_at": room.get("created_at"),
444+
"completed_at": room.get("completed_at")
445+
}
446+
except Exception as e:
447+
logger.error(f"Failed to get room statistics by ID: {e}")
448+
return {}
449+
413450
async def get_participant_statistics(self, participant_identity: str) -> Dict[str, Any]:
414451
"""Get statistics for a participant across all rooms"""
415452
try:
@@ -499,10 +536,12 @@ async def get_summary_by_room_name(self, room_name: str, start_time: Optional[da
499536
try:
500537
# 1. get room list
501538
query = {"room_name": room_name}
502-
if start_time:
503-
query["created_at"] = {"$gte": start_time}
504-
if end_time:
505-
query["created_at"] = {"$lte": end_time}
539+
if start_time or end_time:
540+
query["created_at"] = {}
541+
if start_time:
542+
query["created_at"]["$gte"] = start_time
543+
if end_time:
544+
query["created_at"]["$lte"] = end_time
506545
cursor = self.rooms_collection.find(query).sort("created_at", -1)
507546
room_list = await cursor.to_list(None)
508547
room_dict = {str(room["_id"]): room for room in room_list}
@@ -515,13 +554,36 @@ async def get_summary_by_room_name(self, room_name: str, start_time: Optional[da
515554

516555
# Override created_at and completed_at
517556
for summary in summary_list:
518-
summary["created_at"] = room_dict.get(str(summary["room_id"])).get("created_at")
519-
summary["completed_at"] = room_dict.get(str(summary["room_id"])).get("completed_at")
557+
created_at = room_dict.get(str(summary["room_id"])).get("created_at")
558+
completed_at = room_dict.get(str(summary["room_id"])).get("completed_at")
559+
560+
# Format datetime to ISO 8601 with Z suffix (UTC) and rounded to seconds
561+
if isinstance(created_at, datetime):
562+
summary["created_at"] = created_at.replace(microsecond=0).isoformat() + 'Z'
563+
else:
564+
summary["created_at"] = created_at
565+
566+
if isinstance(completed_at, datetime):
567+
summary["completed_at"] = completed_at.replace(microsecond=0).isoformat() + 'Z'
568+
else:
569+
summary["completed_at"] = completed_at
570+
571+
summary.pop("_id", None)
572+
summary.pop("room_id", None)
573+
summary.pop("summary_text", None)
520574
return summary_list
521575
except Exception as e:
522576
logger.error(f"Failed to get summary by room name: {e}")
523577
return []
524578

579+
async def get_summary_by_room_id(self, room_id: str) -> List[Dict[str, Any]]:
580+
"""Get summary by room id"""
581+
try:
582+
return await self.summary_collection.find({"room_id": room_id}).to_list(None)
583+
except Exception as e:
584+
logger.error(f"Failed to get summary by room id: {e}")
585+
return []
586+
525587
# ========================================
526588
# 🏭 SINGLETON PATTERN
527589
# ========================================

0 commit comments

Comments
 (0)