diff --git a/database.py b/database.py index 15cd561..789298f 100644 --- a/database.py +++ b/database.py @@ -12,15 +12,10 @@ from sqlalchemy.exc import SQLAlchemyError engine = create_engine( os.getenv("DB_URL"), pool_pre_ping=True, - pool_size=2, # Reduced from 5 - max_overflow=3, # Reduced from 10 - pool_recycle=1800, # Reduced to 30 minutes - pool_timeout=30, # Add connection timeout - echo=False, # Disable in production - this uses memory + echo=True if os.getenv("IS_DEV") == "True" else False, # Disable in production - this uses memory connect_args={ "sslmode": "require" if os.getenv("IS_DEV") == "False" else "disable", "connect_timeout": 10, # Connection timeout - "command_timeout": 60, # Query timeout }, ) diff --git a/services/authService.py b/services/authService.py index 386778b..88d8723 100644 --- a/services/authService.py +++ b/services/authService.py @@ -26,7 +26,7 @@ from schemas.BaseSchemas import AuthBase, AuthOTP from exceptions.unauthorized_exception import UnauthorizedException from database import get_db - +from loguru import logger class AuthService: @@ -35,6 +35,7 @@ class AuthService: self.db = next(get_db()) self.email_service = EmailService() self.url = os.getenv("FRONTEND_URL") + self.logger = logger async def login(self, data: AuthBase) -> str: @@ -58,58 +59,75 @@ class AuthService: return response def blockEmailSNS(self, body: str): - # confirm subscription - if body["Type"] == "SubscriptionConfirmation": - urllib.request.urlopen(body["SubscribeURL"]) + try: + # confirm subscription + if body["Type"] == "SubscriptionConfirmation": + urllib.request.urlopen(body["SubscribeURL"]) - # disable automatic unsubscribe confirmation by activating subscription again - elif body["Type"] == "UnsubscribeConfirmation": - urllib.request.urlopen(body["SubscribeURL"]) + # disable automatic unsubscribe confirmation by activating subscription again + elif body["Type"] == "UnsubscribeConfirmation": + urllib.request.urlopen(body["SubscribeURL"]) - # handle bounce notifications only - elif body["Type"] == "Notification": - msg = json.loads(body["Message"]) + # handle bounce notifications only + elif body["Type"] == "Notification": + msg = json.loads(body["Message"]) - # check if msg contains notificationType - if "notificationType" not in msg: - return + # check if msg contains notificationType + if "notificationType" not in msg: + return - recepients = msg["bounce"]["bouncedRecipients"] + recepients = msg["bounce"]["bouncedRecipients"] - for recipient in recepients: - blockEmail = BlockedEmail(email=recipient["emailAddress"], reason=msg["notificationType"], severity=msg["bounce"]["bounceType"]) - self.db.add(blockEmail) - self.db.commit() + for recipient in recepients: + blockEmail = BlockedEmail(email=recipient["emailAddress"], reason=msg["notificationType"], severity=msg["bounce"]["bounceType"]) + self.db.add(blockEmail) + self.db.commit() - return "OK" + return "OK" + except Exception as e: + self.logger.error(f"Error blocking email: {e}") + raise + finally: + self.db.close() async def send_otp(self, email:str): - otp = generateOTP() - self.email_service.send_otp_email(email, otp) + try: + otp = generateOTP() + self.email_service.send_otp_email(email, otp) - # Create OTP record with proper datetime handling - expire_time = datetime.datetime.now() + datetime.timedelta(minutes=10) - otp_record = OTP(email=email, otp=otp, expireAt=expire_time) - self.db.add(otp_record) - self.db.commit() + # Create OTP record with proper datetime handling + expire_time = datetime.datetime.now() + datetime.timedelta(minutes=10) + otp_record = OTP(email=email, otp=otp, expireAt=expire_time) + self.db.add(otp_record) + self.db.commit() - return + return + except Exception as e: + self.logger.error(f"Error sending OTP: {e}") + raise + finally: + self.db.close() async def verify_otp(self, data: AuthOTP): - db_otp = self.db.query(OTP).filter(OTP.email == data.email, OTP.otp == data.otp).first() - if not db_otp: - raise ValidationException("Invalid OTP") - if db_otp.otp != data.otp: - raise ValidationException("Invalid OTP") - if db_otp.expireAt < datetime.datetime.now(): - raise ValidationException("OTP expired") + try: + db_otp = self.db.query(OTP).filter(OTP.email == data.email, OTP.otp == data.otp).first() + if not db_otp: + raise ValidationException("Invalid OTP") + if db_otp.otp != data.otp: + raise ValidationException("Invalid OTP") + if db_otp.expireAt < datetime.datetime.now(): + raise ValidationException("OTP expired") - # OTP is valid, delete it to prevent reuse - # self.db.delete(db_otp) - # self.db.commit() - - return + # OTP is valid, delete it to prevent reuse + self.db.delete(db_otp) + self.db.commit() + return + except Exception as e: + self.logger.error(f"Error verifying OTP: {e}") + raise + finally: + self.db.close() async def get_admins(self, user, limit:int, offset:int, search:str): try: @@ -138,112 +156,144 @@ class AuthService: return common_response except Exception as e: + self.logger.error(f"Error getting admins: {e}") raise e + finally: + self.db.close() async def create_super_admin(self, user, data: CreateSuperAdmin): + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("User is not authorized to perform this action") - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("User is not authorized to perform this action") + # password = "admin@123" + password = generate_secure_password() + hashed_password = hash_password(password) - # password = "admin@123" - password = generate_secure_password() - hashed_password = hash_password(password) - - # check if username and email are unique - existing_user = ( - self.db.query(Users) - .filter( - Users.email == data.email.lower(), + # check if username and email are unique + existing_user = ( + self.db.query(Users) + .filter( + Users.email == data.email.lower(), + ) + .first() ) - .first() - ) - if existing_user: - raise ValidationException("User with same email already exists") + if existing_user: + raise ValidationException("User with same email already exists") - user = Users( - username=data.username, - email=data.email.lower(), - password=hashed_password, - userType=UserType.SUPER_ADMIN, - ) + user = Users( + username=data.username, + email=data.email.lower(), + password=hashed_password, + userType=UserType.SUPER_ADMIN, + ) - - self.db.add(user) - self.db.commit() + + self.db.add(user) + self.db.commit() - LOGIN_URL = self.url + LOGIN_URL = self.url - # send email to user - self.email_service.send_new_admin_email(data.username, data.email, password, LOGIN_URL) + # send email to user + self.email_service.send_new_admin_email(data.username, data.email, password, LOGIN_URL) - return + return + except Exception as e: + self.logger.error(f"Error creating super admin: {e}") + raise e + finally: + self.db.close() async def update_super_admin(self, user, user_id: int, data: UpdateSuperAdmin): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("User is not authorized to perform this action") + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("User is not authorized to perform this action") - user = self.db.query(Users).filter(Users.id == user_id).first() + user = self.db.query(Users).filter(Users.id == user_id).first() - if not user: - raise ResourceNotFoundException("User not found") + if not user: + raise ResourceNotFoundException("User not found") - user.username = data.username.lower() + user.username = data.username.lower() - self.db.add(user) - self.db.commit() + self.db.add(user) + self.db.commit() - return + return + except Exception as e: + self.logger.error(f"Error updating super admin: {e}") + raise e + finally: + self.db.close() async def delete_super_admin(self, user, user_id: int): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("User is not authorized to perform this action") + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("User is not authorized to perform this action") - user = self.db.query(Users).filter(Users.id == user_id).first() + user = self.db.query(Users).filter(Users.id == user_id).first() - if not user: - raise ResourceNotFoundException("User not found") + if not user: + raise ResourceNotFoundException("User not found") - user.soft_delete(self.db) - - return + user.soft_delete(self.db) + return + except Exception as e: + self.logger.error(f"Error deleting super admin: {e}") + raise e + finally: + self.db.close() async def forget_password(self, email: str): - user = self.db.query(Users).filter(Users.email == email.lower()).first() + try: + user = self.db.query(Users).filter(Users.email == email.lower()).first() - if not user: - raise ResourceNotFoundException("User not found") + if not user: + raise ResourceNotFoundException("User not found") - # get reset password token - reset_password_token = generate_reset_password_token() + # get reset password token + reset_password_token = generate_reset_password_token() - reset_password = ResetPasswordTokens(email=email, token=reset_password_token) - self.db.add(reset_password) - self.db.commit() + reset_password = ResetPasswordTokens(email=email, token=reset_password_token) + 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) + self.email_service.send_reset_password_email(email, reset_password_url) - return + return + except Exception as e: + self.logger.error(f"Error forgetting password: {e}") + raise e + finally: + self.db.close() async def reset_password(self, token: str, password: str): - reset_password = self.db.query(ResetPasswordTokens).filter(ResetPasswordTokens.token == token).first() + try: + reset_password = self.db.query(ResetPasswordTokens).filter(ResetPasswordTokens.token == token).first() - if not reset_password: - raise ResourceNotFoundException("Reset password token not found") + if not reset_password: + raise ResourceNotFoundException("Reset password token not found") - user = self.db.query(Users).filter(Users.email == reset_password.email).first() + user = self.db.query(Users).filter(Users.email == reset_password.email).first() - if not user: - raise ResourceNotFoundException("User not found") + if not user: + raise ResourceNotFoundException("User not found") - user.password = hash_password(password) - self.db.delete(reset_password) - self.db.commit() + user.password = hash_password(password) + self.db.delete(reset_password) + self.db.commit() - return + return + except Exception as e: + self.logger.error(f"Error resetting password: {e}") + raise e + finally: + self.db.close() + \ No newline at end of file diff --git a/services/callTranscripts.py b/services/callTranscripts.py index 109eb1f..038c5c0 100644 --- a/services/callTranscripts.py +++ b/services/callTranscripts.py @@ -13,32 +13,46 @@ from models.CallTranscripts import CallTranscripts from exceptions.business_exception import BusinessValidationException from services.s3Service import get_signed_url from interface.common_response import CommonResponse +from loguru import logger + +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): - call_transcripts = self.db.query(CallTranscripts).limit(limit).offset(offset).all() + try: + call_transcripts = self.db.query(CallTranscripts).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: - call_transcript.transcript_key_id = get_signed_url(call_transcript.transcript_key_id) + for call_transcript in response: + call_transcript.transcript_key_id = get_signed_url(call_transcript.transcript_key_id) - return_response = CommonResponse(data=response, total=total) + return_response = CommonResponse(data=response, total=total) - return return_response + return return_response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting call transcripts") + finally: + self.db.close() def download_call_transcript(self, key_id: str): - call_transcript = self.db.query(CallTranscripts).filter(CallTranscripts.transcript_key_id == key_id).first() + try: + call_transcript = self.db.query(CallTranscripts).filter(CallTranscripts.transcript_key_id == key_id).first() - if not call_transcript: - raise BusinessValidationException("Call transcript not found!") + if not call_transcript: + raise BusinessValidationException("Call transcript not found!") - return get_signed_url(call_transcript.transcript_key_id) + return get_signed_url(call_transcript.transcript_key_id) + except Exception as e: + DBExceptionHandler.handle_exception(e, context="downloading call transcript") + finally: + self.db.close() def download_file(self, url: str, file_path: str) -> None: @@ -85,61 +99,65 @@ class CallTranscriptServices: print(f"Error during cleanup: {e}") async def bulk_download_call_transcripts(self, key_ids: list[int], background_tasks: BackgroundTasks): + try: + transcript_ids = self.db.query(CallTranscripts).filter(CallTranscripts.id.in_(key_ids)).all() - transcript_ids = self.db.query(CallTranscripts).filter(CallTranscripts.id.in_(key_ids)).all() + keys = [transcript.transcript_key_id for transcript in transcript_ids] - keys = [transcript.transcript_key_id for transcript in transcript_ids] - - if len(keys) < 1: - raise BusinessValidationException("No call transcripts found!") + if len(keys) < 1: + raise BusinessValidationException("No call transcripts found!") - temp_dir = tempfile.mkdtemp(prefix="call_transcripts_") - zip_path = os.path.join(temp_dir, "call_transcripts.zip") + temp_dir = tempfile.mkdtemp(prefix="call_transcripts_") + zip_path = os.path.join(temp_dir, "call_transcripts.zip") - # Prepare download information - download_info = [] - for key in keys: - # Generate signed URL for each key - url = get_signed_url(key) + # Prepare download information + download_info = [] + for key in keys: + # Generate signed URL for each key + url = get_signed_url(key) + + # Determine filename (using key's basename or a formatted name) + filename = os.path.basename(key) + file_path = os.path.join(temp_dir, filename) + download_info.append((url, file_path, filename)) - # Determine filename (using key's basename or a formatted name) - filename = os.path.basename(key) - file_path = os.path.join(temp_dir, filename) - download_info.append((url, file_path, filename)) - - # Use ThreadPoolExecutor for concurrent downloads - # Adjust max_workers based on your system capabilities and S3 rate limits - max_workers = min(32, len(download_info)) # Cap at 32 threads or number of files, whichever is smaller - - with ThreadPoolExecutor(max_workers=max_workers) as executor: - # Submit all download tasks - future_to_file = {executor.submit(self.download_file, url, file_path): (file_path, filename) - for url, file_path, filename in download_info} + # Use ThreadPoolExecutor for concurrent downloads + # Adjust max_workers based on your system capabilities and S3 rate limits + max_workers = min(32, len(download_info)) # Cap at 32 threads or number of files, whichever is smaller - # Collect results as they complete - file_paths = [] - for future in as_completed(future_to_file): - file_path, filename = future_to_file[future] - try: - future.result() # Get the result to catch any exceptions - file_paths.append((file_path, filename)) - except Exception as e: - print(f"Error downloading {filename}: {e}") - - # Create zip file from downloaded files - with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: - for file_path, arcname in file_paths: - if os.path.exists(file_path): - zip_file.write(file_path, arcname=arcname) + with ThreadPoolExecutor(max_workers=max_workers) as executor: + # Submit all download tasks + future_to_file = {executor.submit(self.download_file, url, file_path): (file_path, filename) + for url, file_path, filename in download_info} + + # Collect results as they complete + file_paths = [] + for future in as_completed(future_to_file): + file_path, filename = future_to_file[future] + try: + future.result() # Get the result to catch any exceptions + file_paths.append((file_path, filename)) + except Exception as e: + print(f"Error downloading {filename}: {e}") + + # Create zip file from downloaded files + with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: + for file_path, arcname in file_paths: + if os.path.exists(file_path): + 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) + # Add cleanup task to run after response is sent + 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 - ) \ No newline at end of file + # Return the zip file as a response + return FileResponse( + path=zip_path, + media_type="application/zip", + filename="call_transcripts.zip", + background=background_tasks + ) + except Exception as e: + DBExceptionHandler.handle_exception(e, context="bulk downloading call transcripts") + finally: + self.db.close() \ No newline at end of file diff --git a/services/clinicDoctorsServices.py b/services/clinicDoctorsServices.py index 7e589c8..6d30e63 100644 --- a/services/clinicDoctorsServices.py +++ b/services/clinicDoctorsServices.py @@ -80,6 +80,8 @@ class ClinicDoctorsServices: self.logger.error(e) self.db.rollback() raise e + finally: + self.db.close() async def update_clinic_doctor( self, user, clinic_doctor_id: int, clinic_doctor_data: ClinicDoctorUpdate @@ -155,6 +157,8 @@ class ClinicDoctorsServices: except Exception as e: self.db.rollback() raise e + finally: + self.db.close() async def delete_clinic_doctor(self, clinic_doctor_id: int): try: @@ -167,26 +171,32 @@ class ClinicDoctorsServices: self.db.commit() except Exception as e: raise e + finally: + self.db.close() async def get_doctor_status_count(self): - - # Query to count doctors by status - status_counts = ( - self.db.query( - ClinicDoctors.status, func.count(ClinicDoctors.id).label("count") + try: + # Query to count doctors by status + status_counts = ( + self.db.query( + ClinicDoctors.status, func.count(ClinicDoctors.id).label("count") + ) + .group_by(ClinicDoctors.status) + .all() ) - .group_by(ClinicDoctors.status) - .all() - ) - # Initialize result dictionary with all possible statuses set to 0 - result = {status.value: 0 for status in ClinicDoctorStatus} + # Initialize result dictionary with all possible statuses set to 0 + result = {status.value: 0 for status in ClinicDoctorStatus} - # Update with actual counts from the query - for status, count in status_counts: - result[status.value] = count + # Update with actual counts from the query + for status, count in status_counts: + result[status.value] = count - return result + return result + except Exception as e: + raise e + finally: + self.db.close() async def get_clinic_doctors(self, limit: int, offset: int, search: str = "", sort_by: str = DEFAULT_ORDER, sort_order: str = DEFAULT_ORDER_BY): try: @@ -258,3 +268,5 @@ class ClinicDoctorsServices: except Exception as e: self.logger.error(e) raise e + finally: + self.db.close() diff --git a/services/clinicServices.py b/services/clinicServices.py index 8378d63..60e40e4 100644 --- a/services/clinicServices.py +++ b/services/clinicServices.py @@ -14,325 +14,390 @@ from services.s3Service import get_file_key, get_signed_url from models import ClinicFileVerifications, ClinicOffers, Clinics, Users from schemas.BaseSchemas import ClinicFileVerificationBase, ClinicOffersBase from services.emailService import EmailService +from loguru import logger +from sqlalchemy import func +from exceptions.db_exceptions import DBExceptionHandler class ClinicServices: def __init__(self): self.db: Session = next(get_db()) self.email_service = EmailService() + self.logger = logger async def get_clinics(self, user, limit:int, offset:int, filter_type: Union[Literal["UNREGISTERED"], Literal["REGISTERED"]] = "UNREGISTERED", search:str = ""): + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("You are not authorized to get clinics") - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to get clinics") + clinics_query = self.db.query(Clinics).order_by(Clinics.update_time.desc()) - clinics_query = self.db.query(Clinics).order_by(Clinics.update_time.desc()) + if filter_type == "UNREGISTERED": + clinics_query = clinics_query.filter(not_(Clinics.status == ClinicStatus.ACTIVE),not_(Clinics.status == ClinicStatus.INACTIVE)) + elif filter_type == "REGISTERED": + clinics_query = clinics_query.filter(or_(Clinics.status == ClinicStatus.ACTIVE,Clinics.status == ClinicStatus.INACTIVE)) - if filter_type == "UNREGISTERED": - clinics_query = clinics_query.filter(not_(Clinics.status == ClinicStatus.ACTIVE),not_(Clinics.status == ClinicStatus.INACTIVE)) - elif filter_type == "REGISTERED": - clinics_query = clinics_query.filter(or_(Clinics.status == ClinicStatus.ACTIVE,Clinics.status == ClinicStatus.INACTIVE)) - - if search: - clinics_query = clinics_query.filter( - or_( - Clinics.name.contains(search), - Clinics.email.contains(search), - Clinics.phone.contains(search), - Clinics.address.contains(search) + if search: + clinics_query = clinics_query.filter( + or_( + Clinics.name.contains(search), + Clinics.email.contains(search), + Clinics.phone.contains(search), + Clinics.address.contains(search) + ) ) - ) - clinics = clinics_query.offset(offset).limit(limit).all() - - count_query = text(""" - SELECT - COUNT(*) as total, - COUNT(CASE WHEN status = 'ACTIVE' OR status = 'INACTIVE' THEN 1 END) as active, - COUNT(CASE WHEN status != 'ACTIVE' AND status != 'INACTIVE' THEN 1 END) as rejected - FROM clinics - """) - - result = self.db.execute(count_query).first() - - totalClinics = result.total or 0 - totalRegisteredClinics = result.active or 0 - totalRejectedClinics = result.rejected or 0 + clinics = clinics_query.offset(offset).limit(limit).all() + + count_query = text(""" + SELECT + COUNT(*) as total, + COUNT(CASE WHEN status = 'ACTIVE' OR status = 'INACTIVE' THEN 1 END) as active, + COUNT(CASE WHEN status != 'ACTIVE' AND status != 'INACTIVE' THEN 1 END) as rejected + FROM clinics + """) + + result = self.db.execute(count_query).first() + + totalClinics = result.total or 0 + totalRegisteredClinics = result.active or 0 + totalRejectedClinics = result.rejected or 0 - clinic_response = [Clinic(**clinic.__dict__.copy()) for clinic in clinics] + clinic_response = [Clinic(**clinic.__dict__.copy()) for clinic in clinics] - for clinic in clinic_response: - clinic.logo = await get_signed_url(clinic.logo) if clinic.logo else None + for clinic in clinic_response: + clinic.logo = await get_signed_url(clinic.logo) if clinic.logo else None - clinic_response_with_total = { - "clinics": clinic_response, - "totalRegisteredClinics": totalRegisteredClinics, - "totalRejectedClinics": totalRejectedClinics, - } + clinic_response_with_total = { + "clinics": clinic_response, + "totalRegisteredClinics": totalRegisteredClinics, + "totalRejectedClinics": totalRejectedClinics, + } - response = CommonResponse(data=clinic_response_with_total, total=totalClinics) + response = CommonResponse(data=clinic_response_with_total, total=totalClinics) - return response + return response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting clinics") + finally: + self.db.close() async def get_latest_clinic_id(self) -> int: - clinic = self.db.query(Clinics).order_by(Clinics.id.desc()).first() - return clinic.id if clinic else 0 + try: + clinic = self.db.query(Clinics).order_by(Clinics.id.desc()).first() + return clinic.id if clinic else 0 + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting latest clinic id") + finally: + self.db.close() async def get_clinic_by_id(self, clinic_id: int): - clinic = self.db.query(Clinics).options(joinedload(Clinics.creator)).filter(Clinics.id == clinic_id).first() + try: + clinic = self.db.query(Clinics).options(joinedload(Clinics.creator)).filter(Clinics.id == clinic_id).first() - if clinic is None: - raise ResourceNotFoundException("Clinic not found") + if clinic is None: + raise ResourceNotFoundException("Clinic not found") - clinic_response = Clinic(**clinic.__dict__.copy()) - - clinic_response.logo = await get_signed_url(clinic_response.logo) if clinic_response.logo else None - clinic_response.abn_doc = await get_signed_url(clinic_response.abn_doc) if clinic_response.abn_doc else None - clinic_response.contract_doc = await get_signed_url(clinic_response.contract_doc) if clinic_response.contract_doc else None + clinic_response = Clinic(**clinic.__dict__.copy()) + + clinic_response.logo = await get_signed_url(clinic_response.logo) if clinic_response.logo else None + clinic_response.abn_doc = await get_signed_url(clinic_response.abn_doc) if clinic_response.abn_doc else None + clinic_response.contract_doc = await get_signed_url(clinic_response.contract_doc) if clinic_response.contract_doc else None - clinicFiles = None - if(clinic.status != ClinicStatus.ACTIVE): - clinicFiles = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() + clinicFiles = None + if(clinic.status != ClinicStatus.ACTIVE): + clinicFiles = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() - clinic_resp = { - "clinic": clinic_response, - "creator": { - "name": clinic.creator.username, - "email": clinic.creator.email, - "phone": clinic.creator.mobile, - "designation": clinic.creator.clinicRole - }, - "clinic_files": await self.get_clinic_files(clinic_id), - "fileStatus": {"reason":clinicFiles.rejection_reason if clinicFiles else None}, - } + clinic_resp = { + "clinic": clinic_response, + "creator": { + "name": clinic.creator.username, + "email": clinic.creator.email, + "phone": clinic.creator.mobile, + "designation": clinic.creator.clinicRole + }, + "clinic_files": await self.get_clinic_files(clinic_id), + "fileStatus": {"reason":clinicFiles.rejection_reason if clinicFiles else None}, + } - return clinic_resp + return clinic_resp + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting clinic by id") + finally: + self.db.close() async def get_clinic_files(self, clinic_id: int): - clinic_files = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() + try: + clinic_files = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() - if clinic_files is None: - raise ResourceNotFoundException("Clinic not found") + if clinic_files is None: + raise ResourceNotFoundException("Clinic not found") - response = ClinicFileVerificationBase(**clinic_files.__dict__.copy()) + response = ClinicFileVerificationBase(**clinic_files.__dict__.copy()) - return response + return response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting clinic files") + finally: + self.db.close() async def update_clinic(self, user, clinic_id: int, clinic_data: ClinicUpdate): - clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() + try: + clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() - if clinic is None: - raise ResourceNotFoundException("Clinic not found") + if clinic is None: + raise ResourceNotFoundException("Clinic not found") - if clinic.creator_id != user["id"]: - raise UnauthorizedException("You are not authorized to update this clinic") + if clinic.creator_id != user["id"]: + raise UnauthorizedException("You are not authorized to update this clinic") - clinic_data.abn_doc = get_file_key(clinic_data.abn_doc) if clinic_data.abn_doc else None - clinic_data.contract_doc = get_file_key(clinic_data.contract_doc) if clinic_data.contract_doc else None - clinic_data.logo = get_file_key(clinic_data.logo) if clinic_data.logo else None + clinic_data.abn_doc = get_file_key(clinic_data.abn_doc) if clinic_data.abn_doc else None + clinic_data.contract_doc = get_file_key(clinic_data.contract_doc) if clinic_data.contract_doc else None + clinic_data.logo = get_file_key(clinic_data.logo) if clinic_data.logo else None - update_data = clinic_data.model_dump(exclude_unset=True) - - for key, value in update_data.items(): - setattr(clinic, key, value) + update_data = clinic_data.model_dump(exclude_unset=True) + + for key, value in update_data.items(): + setattr(clinic, key, value) - self.db.add(clinic) - self.db.commit() - self.db.refresh(clinic) + self.db.add(clinic) + self.db.commit() + self.db.refresh(clinic) - clinic_response = Clinic(**clinic.__dict__.copy()) - clinic_response.logo = get_signed_url(clinic_response.logo) if clinic_response.logo else None + clinic_response = Clinic(**clinic.__dict__.copy()) + clinic_response.logo = get_signed_url(clinic_response.logo) if clinic_response.logo else None - return clinic_response + return clinic_response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="updating clinic") + finally: + self.db.close() async def delete_clinic(self, clinic_id: int): - clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() + try: + clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() - if clinic is None: - raise ResourceNotFoundException("Clinic not found") + if clinic is None: + raise ResourceNotFoundException("Clinic not found") - clinic.soft_delete(self.db) + clinic.soft_delete(self.db) + except Exception as e: + DBExceptionHandler.handle_exception(e, context="deleting clinic") + finally: + self.db.close() async def get_clinic_count(self): - from sqlalchemy import func - - # Get total count - totalClinics = self.db.query(Clinics).count() - - # Get counts for specific statuses in a single query - status_counts = self.db.query( - Clinics.status, - func.count(Clinics.id).label('count') - ).filter( - Clinics.status.in_([ - ClinicStatus.ACTIVE, - ClinicStatus.UNDER_REVIEW, - ClinicStatus.REJECTED - ]) - ).group_by(Clinics.status).all() - - # Initialize counts with 0 - counts = { - "totalClinics": totalClinics, - "totalActiveClinics": 0, - "totalUnderReviewClinics": 0, - "totalRejectedClinics": 0 - } - - # Map status values to their respective count keys - status_to_key = { - ClinicStatus.ACTIVE: "totalActiveClinics", - ClinicStatus.UNDER_REVIEW: "totalUnderReviewClinics", - ClinicStatus.REJECTED: "totalRejectedClinics" - } - - # Update counts with actual values from the query - for status, count in status_counts: - key = status_to_key.get(status) - if key: - counts[key] = count - - return counts + try: + # Get total count + totalClinics = self.db.query(Clinics).count() + + # Get counts for specific statuses in a single query + status_counts = self.db.query( + Clinics.status, + func.count(Clinics.id).label('count') + ).filter( + Clinics.status.in_([ + ClinicStatus.ACTIVE, + ClinicStatus.UNDER_REVIEW, + ClinicStatus.REJECTED + ]) + ).group_by(Clinics.status).all() + + # Initialize counts with 0 + counts = { + "totalClinics": totalClinics, + "totalActiveClinics": 0, + "totalUnderReviewClinics": 0, + "totalRejectedClinics": 0 + } + + # Map status values to their respective count keys + status_to_key = { + ClinicStatus.ACTIVE: "totalActiveClinics", + ClinicStatus.UNDER_REVIEW: "totalUnderReviewClinics", + ClinicStatus.REJECTED: "totalRejectedClinics" + } + + # Update counts with actual values from the query + for status, count in status_counts: + key = status_to_key.get(status) + if key: + counts[key] = count + + return counts + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting clinic count") + finally: + self.db.close() async def update_clinic_status(self, user, clinic_id: int, status: ClinicStatus, documentStatus: Optional[dict] = None, rejection_reason: Optional[str] = None): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to update clinic status") + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("You are not authorized to update clinic status") - clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() + clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() - if clinic is None: - raise ResourceNotFoundException("Clinic not found") + if clinic is None: + raise ResourceNotFoundException("Clinic not found") - clinic.status = status - self.db.add(clinic) - self.db.commit() - self.db.refresh(clinic) - - if clinic.status == ClinicStatus.ACTIVE: - clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() - - clinic_file_verification.logo_is_verified = True - clinic_file_verification.abn_doc_is_verified = True - clinic_file_verification.contract_doc_is_verified = True - - self.db.add(clinic_file_verification) + clinic.status = status + self.db.add(clinic) self.db.commit() + self.db.refresh(clinic) - # send mail to user - self.email_service.send_apporve_clinic_email(clinic.email, clinic.name) + if clinic.status == ClinicStatus.ACTIVE: + clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() + + clinic_file_verification.logo_is_verified = True + clinic_file_verification.abn_doc_is_verified = True + clinic_file_verification.contract_doc_is_verified = True + + self.db.add(clinic_file_verification) + self.db.commit() + + # send mail to user + self.email_service.send_apporve_clinic_email(clinic.email, clinic.name) - if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: - clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() + if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: + clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() - if documentStatus and "LOGO" in documentStatus: - clinic_file_verification.logo_is_verified = documentStatus.get("LOGO") - clinic_file_verification.rejection_reason = rejection_reason + if documentStatus and "LOGO" in documentStatus: + clinic_file_verification.logo_is_verified = documentStatus.get("LOGO") + clinic_file_verification.rejection_reason = rejection_reason - if documentStatus and "ABN" in documentStatus: - clinic_file_verification.abn_doc_is_verified = documentStatus.get("ABN") - clinic_file_verification.rejection_reason = rejection_reason + if documentStatus and "ABN" in documentStatus: + clinic_file_verification.abn_doc_is_verified = documentStatus.get("ABN") + clinic_file_verification.rejection_reason = rejection_reason - if documentStatus and "CONTRACT" in documentStatus: - clinic_file_verification.contract_doc_is_verified = documentStatus.get("CONTRACT") - clinic_file_verification.rejection_reason = rejection_reason + if documentStatus and "CONTRACT" in documentStatus: + clinic_file_verification.contract_doc_is_verified = documentStatus.get("CONTRACT") + clinic_file_verification.rejection_reason = rejection_reason - self.db.add(clinic_file_verification) - self.db.commit() + self.db.add(clinic_file_verification) + self.db.commit() - # send mail to user - self.email_service.send_reject_clinic_email(clinic.email, clinic.name) + # send mail to user + self.email_service.send_reject_clinic_email(clinic.email, clinic.name) - # if rejected or under review then email to clinic creator - if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: - pass + # if rejected or under review then email to clinic creator + if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: + pass - # handle inactive status - if clinic.status == ClinicStatus.INACTIVE: - # get clinic creator - # clinic_creator = self.db.query(Users).options(joinedload(Users.created_clinics)).filter(Clinics.id == clinic.id).first() + # handle inactive status + if clinic.status == ClinicStatus.INACTIVE: + # get clinic creator + # clinic_creator = self.db.query(Users).options(joinedload(Users.created_clinics)).filter(Clinics.id == clinic.id).first() - # # block clinic creator - # clinic_creator.isBlocked = True - # self.db.add(clinic_creator) - # self.db.commit() - pass + # # block clinic creator + # clinic_creator.isBlocked = True + # self.db.add(clinic_creator) + # self.db.commit() + pass - return + return + except Exception as e: + DBExceptionHandler.handle_exception(e, context="updating clinic status") + finally: + self.db.close() async def get_clinic_offer_by_clinic_email(self, clinic_email: str): - clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.clinic_email == clinic_email).first() - return clinic_offer + try: + clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.clinic_email == clinic_email).first() + return clinic_offer + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting clinic offer by clinic email") + finally: + self.db.close() async def get_clinic_offers(self, user, limit:int, offset:int, search:str = ""): + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("You are not authorized to get clinic offers") - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to get clinic offers") + clinic_offers_query = self.db.query(ClinicOffers) - clinic_offers_query = self.db.query(ClinicOffers) - - total = clinic_offers_query.count() - - if search: - clinic_offers_query = clinic_offers_query.filter(ClinicOffers.clinic_email.contains(search)) total = clinic_offers_query.count() - clinic_offers = clinic_offers_query.limit(limit).offset(offset).all() + if search: + clinic_offers_query = clinic_offers_query.filter(ClinicOffers.clinic_email.contains(search)) + total = clinic_offers_query.count() - clinic_offers_response = [ClinicOfferResponse(**clinic_offer.__dict__.copy()) for clinic_offer in clinic_offers] + clinic_offers = clinic_offers_query.limit(limit).offset(offset).all() - response = CommonResponse(data=clinic_offers_response, total=total) + clinic_offers_response = [ClinicOfferResponse(**clinic_offer.__dict__.copy()) for clinic_offer in clinic_offers] - return response + response = CommonResponse(data=clinic_offers_response, total=total) + + return response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting clinic offers") + finally: + self.db.close() async def create_clinic_offer(self, user, clinic_offer_data: ClinicOffersBase): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to create clinic offer") + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("You are not authorized to create clinic offer") - existing_offer = self.db.query(ClinicOffers).filter(ClinicOffers.clinic_email == clinic_offer_data.clinic_email).first() + existing_offer = self.db.query(ClinicOffers).filter(ClinicOffers.clinic_email == clinic_offer_data.clinic_email).first() - if existing_offer: - raise ValidationException("Offer already exists for this clinic") + if existing_offer: + raise ValidationException("Offer already exists for this clinic") - clinic_offer = ClinicOffers(**clinic_offer_data.model_dump()) + clinic_offer = ClinicOffers(**clinic_offer_data.model_dump()) - self.db.add(clinic_offer) - self.db.commit() + self.db.add(clinic_offer) + self.db.commit() - return + return + except Exception as e: + DBExceptionHandler.handle_exception(e, context="creating clinic offer") + finally: + self.db.close() async def update_clinic_offer(self, user, clinic_offer_id: int, clinic_offer_data: ClinicOffersBase): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to update clinic offer") + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("You are not authorized to update clinic offer") - clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.id == clinic_offer_id).first() + clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.id == clinic_offer_id).first() - if clinic_offer is None: - raise ResourceNotFoundException("Clinic offer not found") + if clinic_offer is None: + raise ResourceNotFoundException("Clinic offer not found") - clinic_offer.setup_fees_waived = clinic_offer_data.setup_fees_waived - clinic_offer.special_offer_for_month = clinic_offer_data.special_offer_for_month + clinic_offer.setup_fees_waived = clinic_offer_data.setup_fees_waived + clinic_offer.special_offer_for_month = clinic_offer_data.special_offer_for_month - self.db.add(clinic_offer) - self.db.commit() + self.db.add(clinic_offer) + self.db.commit() - return + return + except Exception as e: + DBExceptionHandler.handle_exception(e, context="updating clinic offer") + finally: + self.db.close() async def delete_clinic_offer(self, user, clinic_offer_id: int): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to delete clinic offer") + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException("You are not authorized to delete clinic offer") - clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.id == clinic_offer_id).first() + clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.id == clinic_offer_id).first() - if clinic_offer is None: - raise ResourceNotFoundException("Clinic offer not found") + if clinic_offer is None: + raise ResourceNotFoundException("Clinic offer not found") - clinic_offer.soft_delete(self.db) + clinic_offer.soft_delete(self.db) - return \ No newline at end of file + return + except Exception as e: + DBExceptionHandler.handle_exception(e, context="deleting clinic offer") + finally: + self.db.close() \ No newline at end of file diff --git a/services/dashboardService.py b/services/dashboardService.py index 89ce454..2532b7b 100644 --- a/services/dashboardService.py +++ b/services/dashboardService.py @@ -7,12 +7,13 @@ from models.SignupPricingMaster import SignupPricingMaster from exceptions import UnauthorizedException from enums.enums import UserType from exceptions import ResourceNotFoundException - +from loguru import logger class DashboardService: def __init__(self): self.db = next(get_db()) self.clinicDoctorsServices = ClinicDoctorsServices() self.clinicServices = ClinicServices() + self.logger = logger async def get_dashboard_counts(self, isSuperAdmin: bool): if isSuperAdmin: @@ -25,50 +26,62 @@ class DashboardService: async def update_signup_pricing_master( self, user, pricing_data: SignupPricingMasterBase ): - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException( - "You are not authorized to update signup pricing master" - ) + try: + if user["userType"] != UserType.SUPER_ADMIN: + raise UnauthorizedException( + "You are not authorized to update signup pricing master" + ) - existing_pricing = self.db.query(SignupPricingMaster).first() + existing_pricing = self.db.query(SignupPricingMaster).first() - if existing_pricing is None: - # Create new record - new_pricing = SignupPricingMaster( - **pricing_data.model_dump() - ) - self.db.add(new_pricing) - self.db.commit() - self.db.refresh(new_pricing) + if existing_pricing is None: + # Create new record + new_pricing = SignupPricingMaster( + **pricing_data.model_dump() + ) + self.db.add(new_pricing) + self.db.commit() + self.db.refresh(new_pricing) - response = SignupPricingMasterResponse( - **new_pricing.__dict__.copy() - ) - - return response - else: - # Update existing record with values from the request - existing_pricing.setup_fees = pricing_data.setup_fees - existing_pricing.subscription_fees = pricing_data.subscription_fees - existing_pricing.per_call_charges = pricing_data.per_call_charges - - self.db.commit() - self.db.refresh(existing_pricing) - - response = SignupPricingMasterResponse( - **existing_pricing.__dict__.copy() - ) - - return response + response = SignupPricingMasterResponse( + **new_pricing.__dict__.copy() + ) + + return response + else: + # Update existing record with values from the request + existing_pricing.setup_fees = pricing_data.setup_fees + existing_pricing.subscription_fees = pricing_data.subscription_fees + existing_pricing.per_call_charges = pricing_data.per_call_charges + + self.db.commit() + self.db.refresh(existing_pricing) + + response = SignupPricingMasterResponse( + **existing_pricing.__dict__.copy() + ) + + return response + except Exception as e: + self.logger.error(f"Error updating signup pricing master: {e}") + raise e + finally: + self.db.close() async def get_signup_pricing_master(self): - signup_pricing_master = self.db.query(SignupPricingMaster).first() - if signup_pricing_master is None: - raise ResourceNotFoundException("Signup pricing master not found") + try: + signup_pricing_master = self.db.query(SignupPricingMaster).first() + if signup_pricing_master is None: + raise ResourceNotFoundException("Signup pricing master not found") - response = SignupPricingMasterResponse( - **signup_pricing_master.__dict__.copy() - ) + response = SignupPricingMasterResponse( + **signup_pricing_master.__dict__.copy() + ) - return response + return response + except Exception as e: + self.logger.error(f"Error getting signup pricing master: {e}") + raise e + finally: + self.db.close() diff --git a/services/masterAppointmentServices.py b/services/masterAppointmentServices.py index 817ffb8..12683b0 100644 --- a/services/masterAppointmentServices.py +++ b/services/masterAppointmentServices.py @@ -6,61 +6,86 @@ from schemas.BaseSchemas import MasterAppointmentTypeBase from exceptions import ResourceNotFoundException from interface.common_response import CommonResponse from schemas.ResponseSchemas import MasterAppointmentTypeResponse +from loguru import logger class MasterAppointmentServices: def __init__(self): self.db: Session = next(get_db()) + self.logger = logger async def get_master_appointment_types(self): - appointment_types = self.db.query(MasterAppointmentTypes).all() - total= self.db.query(MasterAppointmentTypes).count() - response = CommonResponse(data=[MasterAppointmentTypeResponse(**appointment_type.__dict__.copy()) for appointment_type in appointment_types], total=total) - return response + try: + appointment_types = self.db.query(MasterAppointmentTypes).all() + total= self.db.query(MasterAppointmentTypes).count() + response = CommonResponse(data=[MasterAppointmentTypeResponse(**appointment_type.__dict__.copy()) for appointment_type in appointment_types], total=total) + return response + except Exception as e: + self.logger.error(f"Error getting master appointment types: {e}") + raise e + finally: + self.db.close() def is_appointment_type_exists(self, appointment_type: MasterAppointmentTypeBase): existing_appointment_type = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.type == appointment_type.type.lower()).first() return existing_appointment_type async def create_master_appointment_type(self, appointment_type: MasterAppointmentTypeBase): + try: + # get existing appointment type + existing_appointment_type = self.is_appointment_type_exists(appointment_type) - # get existing appointment type - existing_appointment_type = self.is_appointment_type_exists(appointment_type) + if existing_appointment_type: + return - if existing_appointment_type: + appointment_type.type = appointment_type.type.lower() + + appointment_type = MasterAppointmentTypes(**appointment_type.model_dump()) + self.db.add(appointment_type) + self.db.commit() return - - appointment_type.type = appointment_type.type.lower() - - appointment_type = MasterAppointmentTypes(**appointment_type.model_dump()) - self.db.add(appointment_type) - self.db.commit() - return + except Exception as e: + self.logger.error(f"Error creating master appointment type: {e}") + raise e + finally: + self.db.close() async def delete_master_appointment_type(self, appointment_type_id: int): - appointment_type = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.id == appointment_type_id).first() + try: + appointment_type = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.id == appointment_type_id).first() - if appointment_type is None: - raise ResourceNotFoundException("Appointment type not found") + if appointment_type is None: + raise ResourceNotFoundException("Appointment type not found") - appointment_type.soft_delete(self.db) - self.db.commit() - return + appointment_type.soft_delete(self.db) + self.db.commit() + return + except Exception as e: + self.logger.error(f"Error deleting master appointment type: {e}") + raise e + finally: + self.db.close() async def update_master_appointment_type(self, appointment_type_id: int, appointment_type: MasterAppointmentTypeBase): - appointment_type_db = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.id == appointment_type_id).first() + try: + appointment_type_db = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.id == appointment_type_id).first() - if appointment_type_db is None: - raise ResourceNotFoundException("Appointment type not found") + if appointment_type_db is None: + raise ResourceNotFoundException("Appointment type not found") - # get existing appointment type - existing_appointment_type = self.is_appointment_type_exists(appointment_type) + # get existing appointment type + existing_appointment_type = self.is_appointment_type_exists(appointment_type) - if existing_appointment_type and existing_appointment_type.id != appointment_type_id: - raise ResourceNotFoundException("Appointment type already exists") + if existing_appointment_type and existing_appointment_type.id != appointment_type_id: + raise ResourceNotFoundException("Appointment type already exists") - appointment_type_db.type = appointment_type.type + appointment_type_db.type = appointment_type.type - self.db.add(appointment_type_db) - self.db.commit() - return \ No newline at end of file + self.db.add(appointment_type_db) + self.db.commit() + return + except Exception as e: + self.logger.error(f"Error updating master appointment type: {e}") + raise e + finally: + self.db.close() \ No newline at end of file diff --git a/services/stripeServices.py b/services/stripeServices.py index b31cbe0..50a1c5f 100644 --- a/services/stripeServices.py +++ b/services/stripeServices.py @@ -186,6 +186,8 @@ class StripeServices: except stripe.error.StripeError as e: self.logger.error(f"Error creating checkout session: {e}") raise + finally: + self.db.close() async def handle_webhook(self, request: Request): @@ -212,6 +214,8 @@ class StripeServices: except stripe.error.SignatureVerificationError as e: self.logger.error(f"Invalid signature: {e}") raise + finally: + self.db.close() async def update_payment_log(self, unique_clinic_id:str, clinic_id:int): try: @@ -227,4 +231,6 @@ class StripeServices: except Exception as e: self.logger.error(f"Error updating payment log: {e}") - raise \ No newline at end of file + raise + finally: + self.db.close() \ No newline at end of file diff --git a/services/userServices.py b/services/userServices.py index dede4d1..8e38d0d 100644 --- a/services/userServices.py +++ b/services/userServices.py @@ -173,6 +173,7 @@ class UserServices: DBExceptionHandler.handle_exception(e, context="creating user") finally: self.db.commit() + self.db.close() async def get_user(self, user_id) -> UserResponse: @@ -205,88 +206,113 @@ class UserServices: # Return the response as a dictionary return user_response.model_dump() except Exception as e: - # Use the centralized exception handler - from twillio.exceptions.db_exceptions import DBExceptionHandler DBExceptionHandler.handle_exception(e, context="getting user") + finally: + self.db.close() async def get_users(self, limit:int, offset:int, search:str): - query = self.db.query(Users) - if search: - query = query.filter( - or_( - Users.username.contains(search), - Users.email.contains(search), + try: + query = self.db.query(Users) + if search: + query = query.filter( + or_( + Users.username.contains(search), + Users.email.contains(search), Users.clinicRole.contains(search), Users.userType.contains(search) ) ) - users = query.limit(limit).offset(offset).all() + users = query.limit(limit).offset(offset).all() - total = self.db.query(Users).count() + total = self.db.query(Users).count() - response = CommonResponse(data=[UserResponse(**user.__dict__.copy()) for user in users], total=total) + response = CommonResponse(data=[UserResponse(**user.__dict__.copy()) for user in users], total=total) - return response + return response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting users") + finally: + self.db.close() async def get_user_by_email(self, email: str) -> UserResponse: - user = self.db.query(Users).filter(Users.email == email.lower()).first() + try: + user = self.db.query(Users).filter(Users.email == email.lower()).first() - if not user: - logger.error("User not found") - raise ResourceNotFoundException("User not found") + if not user: + logger.error("User not found") + raise ResourceNotFoundException("User not found") - user_dict = user.__dict__.copy() + user_dict = user.__dict__.copy() - user_response = UserResponse(**user_dict) + user_response = UserResponse(**user_dict) - return user_response + return user_response + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting user by email") + finally: + self.db.close() async def update_user(self, admin_id:int|None, user_id: int, user_data: UserUpdate) -> UserResponse: - # Check admin authorization if admin_id is provided - if admin_id: - admin = self.db.query(Users).filter(Users.id == admin_id).first() - if not admin: - logger.error("Admin not found") - raise ResourceNotFoundException("Admin not found") - - # Only check admin type if admin_id was provided - if admin.userType != UserType.SUPER_ADMIN: - logger.error("User is not authorized to perform this action") - raise UnauthorizedException("User is not authorized to perform this action") + try: + # Check admin authorization if admin_id is provided + if admin_id: + admin = self.db.query(Users).filter(Users.id == admin_id).first() + if not admin: + logger.error("Admin not found") + raise ResourceNotFoundException("Admin not found") + + # Only check admin type if admin_id was provided + if admin.userType != UserType.SUPER_ADMIN: + logger.error("User is not authorized to perform this action") + raise UnauthorizedException("User is not authorized to perform this action") - # Find the user to update - user = self.db.query(Users).filter(Users.id == user_id).first() - if not user: - logger.error("User not found") - raise ResourceNotFoundException("User not found") + # Find the user to update + user = self.db.query(Users).filter(Users.id == user_id).first() + if not user: + logger.error("User not found") + raise ResourceNotFoundException("User not found") - # Update only the fields that were provided - update_data = user_data.model_dump(exclude_unset=True) - for key, value in update_data.items(): - setattr(user, key, value) + # Update only the fields that were provided + update_data = user_data.model_dump(exclude_unset=True) + for key, value in update_data.items(): + setattr(user, key, value) - self.db.add(user) - self.db.commit() - self.db.refresh(user) - - # Return properly serialized response - return UserResponse.model_validate(user) + self.db.add(user) + self.db.commit() + self.db.refresh(user) + + # Return properly serialized response + return UserResponse.model_validate(user) + except Exception as e: + DBExceptionHandler.handle_exception(e, context="updating user") + finally: + self.db.close() async def delete_user(self, user_id: int): - user = self.db.query(Users).filter(Users.id == user_id).first() + try: + user = self.db.query(Users).filter(Users.id == user_id).first() - if not user: - logger.error("User not found") - raise ResourceNotFoundException("User not found") + if not user: + logger.error("User not found") + raise ResourceNotFoundException("User not found") - # Use the soft_delete method from CustomBase - user.soft_delete(self.db) - - return True + # Use the soft_delete method from CustomBase + user.soft_delete(self.db) + + return True + except Exception as e: + DBExceptionHandler.handle_exception(e, context="deleting user") + finally: + self.db.close() async def get_super_admins(self): - return self.db.query(Users).filter(Users.userType == UserType.SUPER_ADMIN).all() + try: + return self.db.query(Users).filter(Users.userType == UserType.SUPER_ADMIN).all() + except Exception as e: + DBExceptionHandler.handle_exception(e, context="getting super admins") + finally: + self.db.close() async def _send_emails_to_admins(self, clinic_name): """Helper method to send emails to all super admins""" @@ -300,12 +326,19 @@ class UserServices: except Exception as e: # Log the error but don't interrupt the main flow logger.error(f"Error sending admin emails: {str(e)}") + finally: + self.db.close() async def create_payment_link(self, user_id: int): - user = self.db.query(Users).filter(Users.id == user_id).first() + try: + user = self.db.query(Users).filter(Users.id == user_id).first() - if not user: - logger.error("User not found") - raise ResourceNotFoundException("User not found") + if not user: + logger.error("User not found") + raise ResourceNotFoundException("User not found") - return self.stripe_service.create_payment_link(user_id) \ No newline at end of file + return self.stripe_service.create_payment_link(user_id) + except Exception as e: + DBExceptionHandler.handle_exception(e, context="creating payment link") + finally: + self.db.close() \ No newline at end of file