From 0b6d2442a8acc13ac360f0a4278cb00ad2175a48 Mon Sep 17 00:00:00 2001 From: deepvasoya Date: Fri, 30 May 2025 15:32:31 +0530 Subject: [PATCH] feat: call transcripts api fix: other small fixes --- apis/__init__.py | 2 +- apis/endpoints/admin.py | 11 +++++-- apis/endpoints/call_transcripts.py | 37 ++++++++++++++++++------ models/CallTranscripts.py | 7 +++-- schemas/BaseSchemas.py | 4 +-- services/authService.py | 2 +- services/callTranscripts.py | 41 +++++++++++++++++++++------ services/masterAppointmentServices.py | 12 ++++++-- services/s3Service.py | 3 +- 9 files changed, 89 insertions(+), 30 deletions(-) diff --git a/apis/__init__.py b/apis/__init__.py index 41dbcf8..f411d02 100644 --- a/apis/__init__.py +++ b/apis/__init__.py @@ -39,6 +39,6 @@ api_router.include_router(clinicDoctor.router, prefix="/clinic-doctors", tags=[" api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboard"], dependencies=[Depends(auth_required)]) -api_router.include_router(call_transcripts.router, prefix="/call-transcripts", tags=["call-transcripts"], dependencies=[Depends(auth_required)]) +api_router.include_router(call_transcripts.router, prefix="/call-transcripts", tags=["call-transcripts"]) api_router.include_router(notifications.router, prefix="/notifications", tags=["notifications"], dependencies=[Depends(auth_required)]) diff --git a/apis/endpoints/admin.py b/apis/endpoints/admin.py index 6dd3386..374d4b8 100644 --- a/apis/endpoints/admin.py +++ b/apis/endpoints/admin.py @@ -60,8 +60,15 @@ async def update_master_data(master_appointment_type_id: int, appointment_type: @router.get("/master-data") -async def get_master_data(): - appointment_types = await MasterAppointmentServices().get_master_appointment_types() +async def get_master_data( + limit: int = DEFAULT_LIMIT, + page: int = DEFAULT_PAGE, + search: str = "" +): + if page < 1: + page = 1 + offset = (page - 1) * limit + appointment_types = await MasterAppointmentServices().get_master_appointment_types(limit, offset, search) return ApiResponse(data=appointment_types, message="Master data retrieved successfully") diff --git a/apis/endpoints/call_transcripts.py b/apis/endpoints/call_transcripts.py index 6202595..f65d196 100644 --- a/apis/endpoints/call_transcripts.py +++ b/apis/endpoints/call_transcripts.py @@ -1,21 +1,42 @@ -from fastapi import APIRouter, BackgroundTasks +import datetime +from typing import Optional +from fastapi import APIRouter, BackgroundTasks, Depends from services.callTranscripts import CallTranscriptServices -from utils.constants import DEFAULT_LIMIT, DEFAULT_PAGE +from middleware.auth_dependency import auth_required +from utils.constants import DEFAULT_LIMIT, DEFAULT_ORDER, DEFAULT_ORDER_BY, DEFAULT_PAGE from schemas.ApiResponse import ApiResponse +from schemas.CreateSchemas import CallTranscriptsCreate router = APIRouter() -@router.get("/") -async def get_call_transcripts(limit:int = DEFAULT_LIMIT, page:int = DEFAULT_PAGE): +@router.get("/", dependencies=[Depends(auth_required)]) +async def get_call_transcripts(limit: int = DEFAULT_LIMIT, page: int = DEFAULT_PAGE, search: str = "", orderBy: str = DEFAULT_ORDER, order: str = DEFAULT_ORDER_BY, startDate: Optional[datetime.datetime] = None, endDate: Optional[datetime.datetime] = None): if page == 0: page = 1 offset = (page - 1) * limit - response = await CallTranscriptServices().get_call_transcripts(limit, offset) + response = await CallTranscriptServices().get_call_transcripts(limit, offset, search, orderBy, order, startDate, endDate) return ApiResponse(data=response, message="Call transcripts retrieved successfully") -@router.post("/bulk-download") -async def bulk_download_call_transcripts(key_ids: list[int], background_tasks: BackgroundTasks): + +@router.get("/{key_id}", dependencies=[Depends(auth_required)]) +async def download_call_transcript(key_id: str): + service = CallTranscriptServices() + response = await service.download_call_transcript(key_id) + return ApiResponse(data=response, message="Call transcript downloaded successfully") + + +@router.post("/") +async def create_call_transcript(data: CallTranscriptsCreate): + service = CallTranscriptServices() + await service.create_call_transcript(data) + return ApiResponse(data="OK", message="Call transcript created successfully") + + +@router.post("/bulk-download", dependencies=[Depends(auth_required)]) +async def bulk_download_call_transcripts( + key_ids: list[int], background_tasks: BackgroundTasks +): service = CallTranscriptServices() response = await service.bulk_download_call_transcripts(key_ids, background_tasks) - return response \ No newline at end of file + return response diff --git a/models/CallTranscripts.py b/models/CallTranscripts.py index ca834e0..4c3bb34 100644 --- a/models/CallTranscripts.py +++ b/models/CallTranscripts.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, DateTime from database import Base @@ -9,9 +9,10 @@ class CallTranscripts(Base, CustomBase): __tablename__ = "call_transcripts" id = Column(Integer, primary_key=True, index=True) - patient_name = Column(String) + patient_name = Column(String, nullable=True) patient_number = Column(String) call_duration = Column(String) - call_received_time = Column(String) + call_received_time = Column(DateTime(timezone=True)) transcript_key_id = Column(String) + clinic_id = Column(Integer, nullable=True, default=None) \ No newline at end of file diff --git a/schemas/BaseSchemas.py b/schemas/BaseSchemas.py index 1d98bb4..fa9869f 100644 --- a/schemas/BaseSchemas.py +++ b/schemas/BaseSchemas.py @@ -119,10 +119,10 @@ class ClinicDoctorBase(BaseModel): class CallTranscriptsBase(BaseModel): - patient_name:str + patient_name:Optional[str] = None patient_number:str call_duration:str - call_received_time:str + call_received_time:datetime transcript_key_id:str diff --git a/services/authService.py b/services/authService.py index 063a6a9..d3e8819 100644 --- a/services/authService.py +++ b/services/authService.py @@ -262,7 +262,7 @@ class AuthService: self.db.add(reset_password) self.db.commit() - reset_password_url = f"{self.url}/auth/reset-password?token={reset_password_token}" + reset_password_url = f"{self.url}auth/reset-password?token={reset_password_token}" self.email_service.send_reset_password_email(email, reset_password_url) diff --git a/services/callTranscripts.py b/services/callTranscripts.py index 038c5c0..9a75b02 100644 --- a/services/callTranscripts.py +++ b/services/callTranscripts.py @@ -1,3 +1,5 @@ +import datetime +from typing import Optional from fastapi import BackgroundTasks from sqlalchemy.orm import Session import tempfile @@ -6,7 +8,7 @@ import time from fastapi.responses import FileResponse import os from concurrent.futures import ThreadPoolExecutor, as_completed - +from sqlalchemy import desc from schemas.ResponseSchemas import CallTranscriptsResponse from database import get_db from models.CallTranscripts import CallTranscripts @@ -15,23 +17,44 @@ from services.s3Service import get_signed_url from interface.common_response import CommonResponse from loguru import logger +from schemas.CreateSchemas import CallTranscriptsCreate + from exceptions.db_exceptions import DBExceptionHandler class CallTranscriptServices: def __init__(self): self.db:Session = next(get_db()) self.logger = logger - - async def get_call_transcripts(self, limit:int, offset:int): + + async def create_call_transcript(self, data:CallTranscriptsCreate): try: - call_transcripts = self.db.query(CallTranscripts).limit(limit).offset(offset).all() + call_transcript = CallTranscripts(**data.model_dump()) + self.db.add(call_transcript) + self.db.commit() + return + except Exception as e: + DBExceptionHandler.handle_exception(e, context="creating call transcript") + finally: + self.db.close() + + async def get_call_transcripts(self, limit:int, offset:int, search: str = "", orderBy: str = "call_received_time", order: str = "ASC", startDate: Optional[datetime.datetime] = None, endDate: Optional[datetime.datetime] = None): + try: + query = self.db.query(CallTranscripts).order_by(desc(getattr(CallTranscripts, orderBy)) if order == "DESC" else getattr(CallTranscripts, orderBy)) + + if search: + query = query.filter(CallTranscripts.patient_number.contains(search)) + + if startDate and endDate: + query = query.filter(CallTranscripts.call_received_time.between(startDate, endDate)) + + call_transcripts = query.limit(limit).offset(offset).all() total = self.db.query(CallTranscripts).count() response = [CallTranscriptsResponse(**call_transcript.__dict__.copy()) for call_transcript in call_transcripts] for call_transcript in response: - call_transcript.transcript_key_id = get_signed_url(call_transcript.transcript_key_id) + call_transcript.transcript_key_id = await get_signed_url(call_transcript.transcript_key_id) return_response = CommonResponse(data=response, total=total) @@ -41,7 +64,7 @@ class CallTranscriptServices: finally: self.db.close() - def download_call_transcript(self, key_id: str): + async def download_call_transcript(self, key_id: str): try: call_transcript = self.db.query(CallTranscripts).filter(CallTranscripts.transcript_key_id == key_id).first() @@ -115,7 +138,7 @@ class CallTranscriptServices: download_info = [] for key in keys: # Generate signed URL for each key - url = get_signed_url(key) + url = await get_signed_url(key) # Determine filename (using key's basename or a formatted name) filename = os.path.basename(key) @@ -148,14 +171,14 @@ class CallTranscriptServices: zip_file.write(file_path, arcname=arcname) # Add cleanup task to run after response is sent - background_tasks.add_task(self.cleanup_temp_files, temp_dir, zip_path) + # background_tasks.add_task(self.cleanup_temp_files, temp_dir, zip_path) # Return the zip file as a response return FileResponse( path=zip_path, media_type="application/zip", filename="call_transcripts.zip", - background=background_tasks + # background=background_tasks ) except Exception as e: DBExceptionHandler.handle_exception(e, context="bulk downloading call transcripts") diff --git a/services/masterAppointmentServices.py b/services/masterAppointmentServices.py index 5c0b807..90f696a 100644 --- a/services/masterAppointmentServices.py +++ b/services/masterAppointmentServices.py @@ -14,10 +14,16 @@ class MasterAppointmentServices: self.logger = logger - async def get_master_appointment_types(self): + async def get_master_appointment_types(self, limit: int, offset: int, search: str): try: - appointment_types = self.db.query(MasterAppointmentTypes).all() - total= self.db.query(MasterAppointmentTypes).count() + query = self.db.query(MasterAppointmentTypes) + total= query.count() + + if search: + query = query.filter(MasterAppointmentTypes.type.contains(search)) + total = query.count() + + appointment_types = query.limit(limit).offset(offset).all() response = CommonResponse(data=[MasterAppointmentTypeResponse(**appointment_type.__dict__.copy()) for appointment_type in appointment_types], total=total) return response except Exception as e: diff --git a/services/s3Service.py b/services/s3Service.py index f60d2df..2ecfc48 100644 --- a/services/s3Service.py +++ b/services/s3Service.py @@ -110,8 +110,9 @@ async def get_signed_url(key: str) -> str: Params={ 'Bucket': s3_service.bucket_name, 'Key': key, + 'ResponseContentDisposition': 'attachment' }, - ExpiresIn=3600 # 1 hour + ExpiresIn=3600, # 1 hour ) return url except ClientError as e: