parent
4df268c8ac
commit
0b6d2442a8
|
|
@ -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)])
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue