|
8 | 8 | from datetime import timedelta
|
9 | 9 | from uuid import UUID
|
10 | 10 |
|
11 |
| -from fastapi import APIRouter |
| 11 | +from fastapi import APIRouter,Path |
12 | 12 | from fastapi import Depends
|
13 | 13 | from fastapi import HTTPException
|
14 | 14 | from fastapi import Query
|
|
100 | 100 | from shared_configs.contextvars import get_current_tenant_id
|
101 | 101 | from sqlalchemy.orm import Session, joinedload
|
102 | 102 | from sqlalchemy import desc
|
103 |
| - |
| 103 | +from typing import Optional |
| 104 | +from datetime import datetime |
| 105 | +from fastapi import Query |
| 106 | +from sqlalchemy.exc import SQLAlchemyError |
| 107 | +from io import StringIO |
| 108 | +import csv |
| 109 | +from sqlalchemy import cast, Date, or_ |
| 110 | +from fastapi import Query |
104 | 111 | RECENT_DOCS_FOLDER_ID = -1
|
105 | 112 |
|
106 | 113 | logger = setup_logger()
|
@@ -196,52 +203,191 @@ def update_chat_session_model(
|
196 | 203 | db_session.add(chat_session)
|
197 | 204 | db_session.commit()
|
198 | 205 |
|
| 206 | +# @router.get("/get-user-feedback") |
| 207 | +# def get_user_feedback( |
| 208 | +# skip: int = Query(0, ge=0, description="Number of records to skip for pagination"), |
| 209 | +# limit: int = Query(50, ge=1, le=100, description="Number of records to return"), |
| 210 | +# user: User = Depends(current_user), |
| 211 | +# db_session: Session = Depends(get_session), |
| 212 | +# ): |
| 213 | +# """ |
| 214 | +# Get paginated chat feedbacks for the logged-in user. |
| 215 | +# Works by joining ChatMessageFeedback -> ChatMessage -> ChatSession |
| 216 | +# and filtering by ChatSession.user_id = current user. |
| 217 | +# """ |
| 218 | + |
| 219 | +# # Build query using relationships instead of raw join conditions |
| 220 | +# base_query = ( |
| 221 | +# db_session.query(ChatMessageFeedback) |
| 222 | +# .join(ChatMessage, ChatMessage.id == ChatMessageFeedback.chat_message_id) |
| 223 | +# .join(ChatSession, ChatSession.id == ChatMessage.chat_session_id) |
| 224 | +# .filter(ChatSession.user_id == user.id) # strictly this user's sessions |
| 225 | +# .order_by(ChatMessageFeedback.feedback_timestamp.desc()) |
| 226 | +# ) |
| 227 | + |
| 228 | +# total_count = base_query.count() |
| 229 | +# feedback_rows = base_query.offset(skip).limit(limit).all() |
| 230 | + |
| 231 | +# return { |
| 232 | +# "total": total_count, |
| 233 | +# "skip": skip, |
| 234 | +# "limit": limit, |
| 235 | +# "feedbacks": [ |
| 236 | +# { |
| 237 | +# "id": fb.id, |
| 238 | +# "chat_message_id": fb.chat_message_id, |
| 239 | +# "is_positive": fb.is_positive, |
| 240 | +# "feedback_text": fb.feedback_text, |
| 241 | +# "required_followup": fb.required_followup, |
| 242 | +# "predefined_feedback": fb.predefined_feedback, |
| 243 | +# "feedback_timestamp": fb.feedback_timestamp, |
| 244 | +# } |
| 245 | +# for fb in feedback_rows |
| 246 | +# ], |
| 247 | +# } |
| 248 | + |
199 | 249 | @router.get("/get-user-feedback")
|
200 | 250 | def get_user_feedback(
|
201 |
| - skip: int = Query(0, ge=0, description="Number of records to skip for pagination"), |
202 |
| - limit: int = Query(50, ge=1, le=100, description="Number of records to return"), |
| 251 | + skip: int = Query(0, ge=0, description="Number of records to skip for pagination. Must be >= 0."), |
| 252 | + limit: int = Query(50, ge=1, le=100, description="Number of records to return. Must be between 1 and 100."), |
| 253 | + start_date: Optional[datetime] = Query(None, description="Start date filter (YYYY-MM-DD), time ignored."), |
| 254 | + end_date: Optional[datetime] = Query(None, description="End date filter (YYYY-MM-DD), time ignored."), |
| 255 | + search: Optional[str] = Query(None, description="Search feedback by ID or feedback text."), |
| 256 | + download: bool = Query(False, description="Download the current page's feedback as CSV if true."), |
203 | 257 | user: User = Depends(current_user),
|
204 | 258 | db_session: Session = Depends(get_session),
|
205 | 259 | ):
|
206 |
| - """ |
207 |
| - Get paginated chat feedbacks for the logged-in user. |
208 |
| - Works by joining ChatMessageFeedback -> ChatMessage -> ChatSession |
209 |
| - and filtering by ChatSession.user_id = current user. |
210 |
| - """ |
| 260 | + if start_date and end_date and start_date.date() > end_date.date(): |
| 261 | + raise HTTPException( |
| 262 | + status_code=400, |
| 263 | + detail="Invalid date range: start_date must be less than or equal to end_date." |
| 264 | + ) |
| 265 | + |
| 266 | + try: |
| 267 | + base_query = ( |
| 268 | + db_session.query(ChatMessageFeedback, ChatSession) |
| 269 | + .join(ChatMessage, ChatMessage.id == ChatMessageFeedback.chat_message_id) |
| 270 | + .join(ChatSession, ChatSession.id == ChatMessage.chat_session_id) |
| 271 | + .filter(ChatSession.user_id == user.id) |
| 272 | + ) |
| 273 | + |
| 274 | + if start_date: |
| 275 | + base_query = base_query.filter( |
| 276 | + cast(ChatMessageFeedback.feedback_timestamp, Date) >= start_date.date() |
| 277 | + ) |
| 278 | + if end_date: |
| 279 | + base_query = base_query.filter( |
| 280 | + cast(ChatMessageFeedback.feedback_timestamp, Date) <= end_date.date() |
| 281 | + ) |
| 282 | + |
| 283 | + if search: |
| 284 | + if search.isdigit(): |
| 285 | + base_query = base_query.filter( |
| 286 | + or_( |
| 287 | + ChatMessageFeedback.id == int(search), |
| 288 | + ChatMessageFeedback.feedback_text.ilike(f"%{search}%") |
| 289 | + ) |
| 290 | + ) |
| 291 | + else: |
| 292 | + base_query = base_query.filter(ChatMessageFeedback.feedback_text.ilike(f"%{search}%")) |
| 293 | + |
| 294 | + base_query = base_query.order_by(ChatMessageFeedback.feedback_timestamp.desc()) |
| 295 | + |
| 296 | + total_count = base_query.count() |
| 297 | + |
| 298 | + if download: |
| 299 | + feedback_rows = base_query.all() |
| 300 | + else: |
| 301 | + feedback_rows = base_query.offset(skip).limit(limit).all() |
| 302 | + |
| 303 | + if download: |
| 304 | + from io import StringIO |
| 305 | + import csv |
| 306 | + from fastapi.responses import StreamingResponse |
| 307 | + |
| 308 | + output = StringIO() |
| 309 | + writer = csv.writer(output) |
| 310 | + writer.writerow([ |
| 311 | + "feedback_text", "is_positive", "feedback_timestamp", |
| 312 | + "chat_session_id", "chat_session_name" |
| 313 | + ]) |
| 314 | + for fb, chat in feedback_rows: |
| 315 | + writer.writerow([ |
| 316 | + fb.feedback_text, fb.is_positive, fb.feedback_timestamp, |
| 317 | + chat.id, chat.description |
| 318 | + ]) |
| 319 | + output.seek(0) |
| 320 | + return StreamingResponse( |
| 321 | + output, |
| 322 | + media_type="text/csv", |
| 323 | + headers={"Content-Disposition": f"attachment; filename=chat_feedback_{user.id}_all.csv"} |
| 324 | + ) |
| 325 | + |
| 326 | + return { |
| 327 | + "total": total_count, |
| 328 | + "skip": skip, |
| 329 | + "limit": limit, |
| 330 | + "feedbacks": [ |
| 331 | + { |
| 332 | + "id": fb.id, |
| 333 | + "chat_message_id": fb.chat_message_id, |
| 334 | + "is_positive": fb.is_positive, |
| 335 | + "feedback_text": fb.feedback_text, |
| 336 | + "required_followup": fb.required_followup, |
| 337 | + "predefined_feedback": fb.predefined_feedback, |
| 338 | + "feedback_timestamp": fb.feedback_timestamp, |
| 339 | + "chat_session": { |
| 340 | + "id": chat.id, |
| 341 | + "name": chat.description, |
| 342 | + "persona_id": chat.persona_id, |
| 343 | + "time_created": chat.time_created.isoformat(), |
| 344 | + "time_updated": chat.time_updated.isoformat(), |
| 345 | + "shared_status": chat.shared_status, |
| 346 | + "folder_id": chat.folder_id, |
| 347 | + "current_alternate_model": chat.current_alternate_model, |
| 348 | + "current_temperature_override": chat.temperature_override, |
| 349 | + } |
| 350 | + } |
| 351 | + for fb, chat in feedback_rows |
| 352 | + ], |
| 353 | + } |
| 354 | + |
| 355 | + except ValueError as ve: |
| 356 | + raise HTTPException(status_code=422, detail=f"Value error: {str(ve)}") |
| 357 | + except SQLAlchemyError: |
| 358 | + raise HTTPException(status_code=500, detail="Database error occurred. Please try again later.") |
| 359 | + except Exception as e: |
| 360 | + raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}") |
211 | 361 |
|
212 |
| - # Build query using relationships instead of raw join conditions |
213 |
| - base_query = ( |
| 362 | +@router.delete("/delete-feedback/{feedback_id}") |
| 363 | +def delete_feedback( |
| 364 | + feedback_id: int = Path(..., description="ID of the feedback to delete"), |
| 365 | + user: User = Depends(current_user), |
| 366 | + db_session: Session = Depends(get_session), |
| 367 | +): |
| 368 | + # Find feedback by ID and ensure it belongs to current user |
| 369 | + feedback = ( |
214 | 370 | db_session.query(ChatMessageFeedback)
|
215 | 371 | .join(ChatMessage, ChatMessage.id == ChatMessageFeedback.chat_message_id)
|
216 | 372 | .join(ChatSession, ChatSession.id == ChatMessage.chat_session_id)
|
217 |
| - .filter(ChatSession.user_id == user.id) # strictly this user's sessions |
218 |
| - .order_by(ChatMessageFeedback.feedback_timestamp.desc()) |
| 373 | + .filter(ChatMessageFeedback.id == feedback_id) |
| 374 | + .filter(ChatSession.user_id == user.id) |
| 375 | + .first() |
219 | 376 | )
|
220 | 377 |
|
221 |
| - total_count = base_query.count() |
222 |
| - feedback_rows = base_query.offset(skip).limit(limit).all() |
| 378 | + if not feedback: |
| 379 | + raise HTTPException(status_code=404, detail="Feedback not found or you do not have permission to delete it.") |
| 380 | + |
| 381 | + # Delete feedback |
| 382 | + db_session.delete(feedback) |
| 383 | + db_session.commit() |
223 | 384 |
|
224 | 385 | return {
|
225 |
| - "total": total_count, |
226 |
| - "skip": skip, |
227 |
| - "limit": limit, |
228 |
| - "feedbacks": [ |
229 |
| - { |
230 |
| - "id": fb.id, |
231 |
| - "chat_message_id": fb.chat_message_id, |
232 |
| - "is_positive": fb.is_positive, |
233 |
| - "feedback_text": fb.feedback_text, |
234 |
| - "required_followup": fb.required_followup, |
235 |
| - "predefined_feedback": fb.predefined_feedback, |
236 |
| - "feedback_timestamp": fb.feedback_timestamp, |
237 |
| - } |
238 |
| - for fb in feedback_rows |
239 |
| - ], |
| 386 | + "message": "Successfully deleted", |
| 387 | + "status": "Success" |
240 | 388 | }
|
241 | 389 |
|
242 | 390 |
|
243 |
| - |
244 |
| - |
245 | 391 | @router.get("/get-chat-session/{session_id}")
|
246 | 392 | def get_chat_session(
|
247 | 393 | session_id: UUID,
|
|
0 commit comments