feat: call transcripts api

fix: other small fixes
This commit is contained in:
deepvasoya 2025-05-30 15:32:31 +05:30
parent 4df268c8ac
commit 0b6d2442a8
9 changed files with 89 additions and 30 deletions

View File

@ -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(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)]) api_router.include_router(notifications.router, prefix="/notifications", tags=["notifications"], dependencies=[Depends(auth_required)])

View File

@ -60,8 +60,15 @@ async def update_master_data(master_appointment_type_id: int, appointment_type:
@router.get("/master-data") @router.get("/master-data")
async def get_master_data(): async def get_master_data(
appointment_types = await MasterAppointmentServices().get_master_appointment_types() 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") return ApiResponse(data=appointment_types, message="Master data retrieved successfully")

View File

@ -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 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.ApiResponse import ApiResponse
from schemas.CreateSchemas import CallTranscriptsCreate
router = APIRouter() router = APIRouter()
@router.get("/") @router.get("/", dependencies=[Depends(auth_required)])
async def get_call_transcripts(limit:int = DEFAULT_LIMIT, page:int = DEFAULT_PAGE): 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: if page == 0:
page = 1 page = 1
offset = (page - 1) * limit 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") 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() service = CallTranscriptServices()
response = await service.bulk_download_call_transcripts(key_ids, background_tasks) response = await service.bulk_download_call_transcripts(key_ids, background_tasks)
return response return response

View File

@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, String from sqlalchemy import Column, Integer, String, DateTime
from database import Base from database import Base
@ -9,9 +9,10 @@ class CallTranscripts(Base, CustomBase):
__tablename__ = "call_transcripts" __tablename__ = "call_transcripts"
id = Column(Integer, primary_key=True, index=True) id = Column(Integer, primary_key=True, index=True)
patient_name = Column(String) patient_name = Column(String, nullable=True)
patient_number = Column(String) patient_number = Column(String)
call_duration = Column(String) call_duration = Column(String)
call_received_time = Column(String) call_received_time = Column(DateTime(timezone=True))
transcript_key_id = Column(String) transcript_key_id = Column(String)
clinic_id = Column(Integer, nullable=True, default=None)

View File

@ -119,10 +119,10 @@ class ClinicDoctorBase(BaseModel):
class CallTranscriptsBase(BaseModel): class CallTranscriptsBase(BaseModel):
patient_name:str patient_name:Optional[str] = None
patient_number:str patient_number:str
call_duration:str call_duration:str
call_received_time:str call_received_time:datetime
transcript_key_id:str transcript_key_id:str

View File

@ -262,7 +262,7 @@ class AuthService:
self.db.add(reset_password) self.db.add(reset_password)
self.db.commit() 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) self.email_service.send_reset_password_email(email, reset_password_url)

View File

@ -1,3 +1,5 @@
import datetime
from typing import Optional
from fastapi import BackgroundTasks from fastapi import BackgroundTasks
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
import tempfile import tempfile
@ -6,7 +8,7 @@ import time
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
import os import os
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from sqlalchemy import desc
from schemas.ResponseSchemas import CallTranscriptsResponse from schemas.ResponseSchemas import CallTranscriptsResponse
from database import get_db from database import get_db
from models.CallTranscripts import CallTranscripts from models.CallTranscripts import CallTranscripts
@ -15,23 +17,44 @@ from services.s3Service import get_signed_url
from interface.common_response import CommonResponse from interface.common_response import CommonResponse
from loguru import logger from loguru import logger
from schemas.CreateSchemas import CallTranscriptsCreate
from exceptions.db_exceptions import DBExceptionHandler from exceptions.db_exceptions import DBExceptionHandler
class CallTranscriptServices: class CallTranscriptServices:
def __init__(self): def __init__(self):
self.db:Session = next(get_db()) self.db:Session = next(get_db())
self.logger = logger self.logger = logger
async def get_call_transcripts(self, limit:int, offset:int): async def create_call_transcript(self, data:CallTranscriptsCreate):
try: 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() total = self.db.query(CallTranscripts).count()
response = [CallTranscriptsResponse(**call_transcript.__dict__.copy()) for call_transcript in call_transcripts] response = [CallTranscriptsResponse(**call_transcript.__dict__.copy()) for call_transcript in call_transcripts]
for call_transcript in response: 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) return_response = CommonResponse(data=response, total=total)
@ -41,7 +64,7 @@ class CallTranscriptServices:
finally: finally:
self.db.close() self.db.close()
def download_call_transcript(self, key_id: str): async def download_call_transcript(self, key_id: str):
try: try:
call_transcript = self.db.query(CallTranscripts).filter(CallTranscripts.transcript_key_id == key_id).first() call_transcript = self.db.query(CallTranscripts).filter(CallTranscripts.transcript_key_id == key_id).first()
@ -115,7 +138,7 @@ class CallTranscriptServices:
download_info = [] download_info = []
for key in keys: for key in keys:
# Generate signed URL for each key # 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) # Determine filename (using key's basename or a formatted name)
filename = os.path.basename(key) filename = os.path.basename(key)
@ -148,14 +171,14 @@ class CallTranscriptServices:
zip_file.write(file_path, arcname=arcname) zip_file.write(file_path, arcname=arcname)
# Add cleanup task to run after response is sent # 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 the zip file as a response
return FileResponse( return FileResponse(
path=zip_path, path=zip_path,
media_type="application/zip", media_type="application/zip",
filename="call_transcripts.zip", filename="call_transcripts.zip",
background=background_tasks # background=background_tasks
) )
except Exception as e: except Exception as e:
DBExceptionHandler.handle_exception(e, context="bulk downloading call transcripts") DBExceptionHandler.handle_exception(e, context="bulk downloading call transcripts")

View File

@ -14,10 +14,16 @@ class MasterAppointmentServices:
self.logger = logger self.logger = logger
async def get_master_appointment_types(self): async def get_master_appointment_types(self, limit: int, offset: int, search: str):
try: try:
appointment_types = self.db.query(MasterAppointmentTypes).all() query = self.db.query(MasterAppointmentTypes)
total= self.db.query(MasterAppointmentTypes).count() 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) response = CommonResponse(data=[MasterAppointmentTypeResponse(**appointment_type.__dict__.copy()) for appointment_type in appointment_types], total=total)
return response return response
except Exception as e: except Exception as e:

View File

@ -110,8 +110,9 @@ async def get_signed_url(key: str) -> str:
Params={ Params={
'Bucket': s3_service.bucket_name, 'Bucket': s3_service.bucket_name,
'Key': key, 'Key': key,
'ResponseContentDisposition': 'attachment'
}, },
ExpiresIn=3600 # 1 hour ExpiresIn=3600, # 1 hour
) )
return url return url
except ClientError as e: except ClientError as e: