refactor: minor changes

This commit is contained in:
deepvasoya 2025-06-03 13:49:51 +05:30
parent 7558b5e2fe
commit f545a5b75b
6 changed files with 142 additions and 117 deletions

View File

@ -23,16 +23,12 @@ async def get_users(limit:int = DEFAULT_LIMIT, page:int = DEFAULT_PAGE, search:s
@router.get("/me") @router.get("/me")
async def get_user(request: Request): async def get_user(request: Request):
try:
user_id = request.state.user["id"] user_id = request.state.user["id"]
user = await UserServices().get_user(user_id) user = await UserServices().get_user(user_id)
return ApiResponse( return ApiResponse(
data=user, data=user,
message="User fetched successfully" message="User fetched successfully"
) )
except Exception as e:
logger.error(f"Error getting user: {str(e)}")
raise e
@router.get("/{user_id}") @router.get("/{user_id}")
async def get_user(request: Request, user_id: int): async def get_user(request: Request, user_id: int):

View File

@ -14,7 +14,7 @@ class Clinic(ClinicBase):
status: ClinicStatus status: ClinicStatus
class Config: class Config:
orm_mode = True from_attributes = True
class ClinicDoctorResponse(ClinicDoctorBase): class ClinicDoctorResponse(ClinicDoctorBase):
@ -43,9 +43,8 @@ class UserResponse(UserBase):
created_clinics: Optional[List[Clinic]] = None created_clinics: Optional[List[Clinic]] = None
class Config: class Config:
orm_mode = True
from_attributes = True from_attributes = True
allow_population_by_field_name = True populate_by_name = True
class Doctor(DoctorBase): class Doctor(DoctorBase):

View File

@ -38,7 +38,7 @@ class AuthService:
self.logger = logger self.logger = logger
async def login(self, data: AuthBase) -> str: async def login(self, data: AuthBase) -> str:
try:
# get user # get user
user = await self.user_service.get_user_by_email(data.email) user = await self.user_service.get_user_by_email(data.email)
@ -46,13 +46,20 @@ class AuthService:
if not verify_password(data.password, user.password): if not verify_password(data.password, user.password):
raise UnauthorizedException("Invalid credentials") raise UnauthorizedException("Invalid credentials")
# remove password from user dict user_dict = user.model_dump(
user_dict = user.__dict__.copy() exclude={"password": True, "created_clinics": True},
user_dict.pop("password", None) exclude_none=True,
mode="json"
)
# create token # create token
token = create_jwt_token(user_dict) token = create_jwt_token(user_dict)
return token return token
except Exception as e:
self.logger.error(f"Error logging in: {e}")
raise e
finally:
self.db.close()
async def register(self, user_data: UserCreate, background_tasks=None): async def register(self, user_data: UserCreate, background_tasks=None):
try: try:

View File

@ -5,6 +5,7 @@ from enum import Enum
from utils.constants import JWT_SECRET, JWT_ALGORITHM, JWT_EXPIRE_MINUTES from utils.constants import JWT_SECRET, JWT_ALGORITHM, JWT_EXPIRE_MINUTES
def create_jwt_token(data: dict): def create_jwt_token(data: dict):
try:
# Create a copy of the data and handle Enum and datetime serialization # Create a copy of the data and handle Enum and datetime serialization
to_encode = {} to_encode = {}
for key, value in data.items(): for key, value in data.items():
@ -21,6 +22,8 @@ def create_jwt_token(data: dict):
to_encode.update({"exp": expire.timestamp()}) # Use timestamp for expiration to_encode.update({"exp": expire.timestamp()}) # Use timestamp for expiration
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM) encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
return encoded_jwt return encoded_jwt
except Exception as e:
raise e
def verify_jwt_token(token: str): def verify_jwt_token(token: str):

View File

@ -245,7 +245,7 @@ class StripeServices:
session_data = { session_data = {
'customer': customer_id, 'customer': customer_id,
'payment_method_types': ['card'], "payment_method_types": ["card","au_becs_debit"],
'mode': 'subscription', 'mode': 'subscription',
'line_items': line_items, 'line_items': line_items,
'success_url': f"{self.redirect_url}auth/waiting", 'success_url': f"{self.redirect_url}auth/waiting",

View File

@ -4,7 +4,7 @@ from sqlalchemy.orm import Session
from database import get_db from database import get_db
from models.Users import Users from models.Users import Users
from exceptions.validation_exception import ValidationException from exceptions.validation_exception import ValidationException
from schemas.ResponseSchemas import UserResponse from schemas.ResponseSchemas import Clinic, UserResponse
from models import Clinics from models import Clinics
from enums.enums import ClinicStatus, UserType from enums.enums import ClinicStatus, UserType
from schemas.UpdateSchemas import UserUpdate from schemas.UpdateSchemas import UserUpdate
@ -20,6 +20,8 @@ from sqlalchemy.orm import joinedload
from services.emailService import EmailService from services.emailService import EmailService
from services.clinicServices import ClinicServices from services.clinicServices import ClinicServices
from services.dashboardService import DashboardService from services.dashboardService import DashboardService
class UserServices: class UserServices:
def __init__(self): def __init__(self):
self.db: Session = next(get_db()) self.db: Session = next(get_db())
@ -27,6 +29,7 @@ class UserServices:
self.stripe_service = StripeServices() self.stripe_service = StripeServices()
self.clinic_service = ClinicServices() self.clinic_service = ClinicServices()
self.dashboard_service = DashboardService() self.dashboard_service = DashboardService()
self.logger = logger
async def create_user(self, user_data: UserCreate, background_tasks=None): async def create_user(self, user_data: UserCreate, background_tasks=None):
@ -38,15 +41,11 @@ class UserServices:
user = user_data.user user = user_data.user
# Check if user with same username or email exists # Check if user with same username or email exists
existing_user = ( existing_user = (
self.db.query(Users) self.db.query(Users).filter(Users.email == user.email.lower()).first()
.filter(Users.email == user.email.lower())
.first()
) )
if existing_user: if existing_user:
raise ValidationException( raise ValidationException("User with same email already exists")
"User with same email already exists"
)
# Create a new user instance # Create a new user instance
new_user = Users( new_user = Users(
@ -55,7 +54,7 @@ class UserServices:
password=hash_password(user.password), password=hash_password(user.password),
clinicRole=user.clinicRole, clinicRole=user.clinicRole,
userType=user.userType, userType=user.userType,
mobile=user.mobile mobile=user.mobile,
) )
# Add user to database but don't commit yet # Add user to database but don't commit yet
@ -63,16 +62,20 @@ class UserServices:
self.db.flush() # Flush to get the user ID without committing self.db.flush() # Flush to get the user ID without committing
# Create stripe customer # Create stripe customer
stripe_customer = await self.stripe_service.create_customer(new_user.id, user.email, user.username) stripe_customer = await self.stripe_service.create_customer(
new_user.id, user.email, user.username
)
# Create stripe account # Create stripe account
stripe_account = await self.stripe_service.create_account(new_user.id, user.email, user.username, user.mobile) stripe_account = await self.stripe_service.create_account(
new_user.id, user.email, user.username, user.mobile
)
# Create stripe user # Create stripe user
stripe_user = StripeUsers( stripe_user = StripeUsers(
user_id=new_user.id, user_id=new_user.id,
customer_id=stripe_customer.id, customer_id=stripe_customer.id,
account_id=stripe_account.id account_id=stripe_account.id,
) )
self.db.add(stripe_user) self.db.add(stripe_user)
@ -81,8 +84,14 @@ class UserServices:
# cross verify domain, in db # cross verify domain, in db
# Convert to lowercase and keep only alphanumeric characters, hyphens, and underscores # Convert to lowercase and keep only alphanumeric characters, hyphens, and underscores
domain = ''.join(char for char in clinic.name.lower() if char.isalnum() or char == '-' or char == '_') domain = "".join(
existing_clinic = self.db.query(Clinics).filter(Clinics.domain == domain).first() char
for char in clinic.name.lower()
if char.isalnum() or char == "-" or char == "_"
)
existing_clinic = (
self.db.query(Clinics).filter(Clinics.domain == domain).first()
)
if existing_clinic: if existing_clinic:
# This will trigger rollback in the exception handler # This will trigger rollback in the exception handler
@ -114,7 +123,7 @@ class UserServices:
voice_model_gender=clinic.voice_model_gender, voice_model_gender=clinic.voice_model_gender,
scenarios=clinic.scenarios, scenarios=clinic.scenarios,
general_info=clinic.general_info, general_info=clinic.general_info,
status=ClinicStatus.PAYMENT_DUE, #TODO: change this to PAYMENT_DUE status=ClinicStatus.PAYMENT_DUE, # TODO: change this to PAYMENT_DUE
domain=domain, domain=domain,
creator_id=new_user.id, # Set the creator_id to link the clinic to the user who created it creator_id=new_user.id, # Set the creator_id to link the clinic to the user who created it
) )
@ -128,7 +137,7 @@ class UserServices:
clinic_id=new_clinic.id, clinic_id=new_clinic.id,
abn_doc_is_verified=None, abn_doc_is_verified=None,
contract_doc_is_verified=None, contract_doc_is_verified=None,
last_changed_by=new_user.id last_changed_by=new_user.id,
) )
# Add clinic files to database # Add clinic files to database
@ -138,7 +147,9 @@ class UserServices:
if background_tasks: if background_tasks:
background_tasks.add_task(self._send_emails_to_admins, clinic.email) background_tasks.add_task(self._send_emails_to_admins, clinic.email)
offer = await self.clinic_service.get_clinic_offer_by_clinic_email(clinic.email) offer = await self.clinic_service.get_clinic_offer_by_clinic_email(
clinic.email
)
signup_pricing = await self.dashboard_service.get_signup_pricing_master() signup_pricing = await self.dashboard_service.get_signup_pricing_master()
@ -146,15 +157,23 @@ class UserServices:
"setup_fees": signup_pricing.setup_fees, "setup_fees": signup_pricing.setup_fees,
"subscription_fees": signup_pricing.subscription_fees, "subscription_fees": signup_pricing.subscription_fees,
"per_call_charges": signup_pricing.per_call_charges, "per_call_charges": signup_pricing.per_call_charges,
"total": signup_pricing.setup_fees + signup_pricing.subscription_fees + signup_pricing.per_call_charges "total": signup_pricing.setup_fees
+ signup_pricing.subscription_fees
+ signup_pricing.per_call_charges,
} }
if offer: if offer:
fees_to_be["setup_fees"] = offer.setup_fees fees_to_be["setup_fees"] = offer.setup_fees
fees_to_be["per_call_charges"] = offer.per_call_charges fees_to_be["per_call_charges"] = offer.per_call_charges
fees_to_be["total"] = offer.setup_fees + fees_to_be["subscription_fees"] + offer.per_call_charges fees_to_be["total"] = (
offer.setup_fees
+ fees_to_be["subscription_fees"]
+ offer.per_call_charges
)
payment_link = await self.stripe_service.create_subscription_checkout(fees_to_be, new_clinic.id, stripe_account.id,stripe_customer.id) payment_link = await self.stripe_service.create_subscription_checkout(
fees_to_be, new_clinic.id, stripe_account.id, stripe_customer.id
)
self.db.commit() self.db.commit()
@ -166,7 +185,7 @@ class UserServices:
"clinicRole": new_user.clinicRole, "clinicRole": new_user.clinicRole,
"userType": new_user.userType, "userType": new_user.userType,
"mobile": new_user.mobile, "mobile": new_user.mobile,
"clinicId": new_clinic.id "clinicId": new_clinic.id,
} }
return { return {
@ -190,33 +209,22 @@ class UserServices:
finally: finally:
self.db.close() self.db.close()
async def get_user(self, user_id) -> UserResponse: async def get_user(self, user_id) -> UserResponse:
try: try:
# Query the user by ID and explicitly load the created clinics relationship # Query the user by ID and explicitly load the created clinics relationship
user = self.db.query(Users).options(joinedload(Users.created_clinics)).filter(Users.id == user_id).first() user = (
self.db.query(Users)
.options(joinedload(Users.created_clinics))
.filter(Users.id == user_id)
.first()
)
if not user: if not user:
logger.error("User not found") self.logger.error("User not found")
raise ResourceNotFoundException("User not found") raise ResourceNotFoundException("User not found")
# First convert the user to a dictionary
user_dict = {}
for column in user.__table__.columns:
user_dict[column.name] = getattr(user, column.name)
# Convert created clinics to dictionaries
if user.created_clinics:
clinics_list = []
for clinic in user.created_clinics:
clinic_dict = {}
for column in clinic.__table__.columns:
clinic_dict[column.name] = getattr(clinic, column.name)
clinics_list.append(clinic_dict)
user_dict['created_clinics'] = clinics_list
# Create the user response # Create the user response
user_response = UserResponse.model_validate(user_dict) user_response = UserResponse.model_validate(user)
# Return the response as a dictionary # Return the response as a dictionary
return user_response.model_dump() return user_response.model_dump()
@ -225,7 +233,7 @@ class UserServices:
finally: finally:
self.db.close() self.db.close()
async def get_users(self, limit:int, offset:int, search:str): async def get_users(self, limit: int, offset: int, search: str):
try: try:
query = self.db.query(Users) query = self.db.query(Users)
if search: if search:
@ -234,7 +242,7 @@ class UserServices:
Users.username.contains(search), Users.username.contains(search),
Users.email.contains(search), Users.email.contains(search),
Users.clinicRole.contains(search), Users.clinicRole.contains(search),
Users.userType.contains(search) Users.userType.contains(search),
) )
) )
@ -242,7 +250,10 @@ class UserServices:
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: except Exception as e:
@ -252,15 +263,17 @@ class UserServices:
async def get_user_by_email(self, email: str) -> UserResponse: async def get_user_by_email(self, email: str) -> UserResponse:
try: try:
user = self.db.query(Users).filter(Users.email == email.lower()).first() user = (
self.db.query(Users)
.filter(Users.email == email.lower())
.first()
)
if not user: if not user:
logger.error("User not found") self.logger.error("User not found")
raise ResourceNotFoundException("User not found") raise ResourceNotFoundException("User not found")
user_dict = user.__dict__.copy() user_response = UserResponse.model_validate(user)
user_response = UserResponse(**user_dict)
return user_response return user_response
except Exception as e: except Exception as e:
@ -268,24 +281,28 @@ class UserServices:
finally: finally:
self.db.close() self.db.close()
async def update_user(self, admin_id:int|None, user_id: int, user_data: UserUpdate) -> UserResponse: async def update_user(
self, admin_id: int | None, user_id: int, user_data: UserUpdate
) -> UserResponse:
try: try:
# Check admin authorization if admin_id is provided # Check admin authorization if admin_id is provided
if admin_id: if admin_id:
admin = self.db.query(Users).filter(Users.id == admin_id).first() admin = self.db.query(Users).filter(Users.id == admin_id).first()
if not admin: if not admin:
logger.error("Admin not found") self.logger.error("Admin not found")
raise ResourceNotFoundException("Admin not found") raise ResourceNotFoundException("Admin not found")
# Only check admin type if admin_id was provided # Only check admin type if admin_id was provided
if admin.userType != UserType.SUPER_ADMIN: if admin.userType != UserType.SUPER_ADMIN:
logger.error("User is not authorized to perform this action") self.logger.error("User is not authorized to perform this action")
raise UnauthorizedException("User is not authorized to perform this action") raise UnauthorizedException(
"User is not authorized to perform this action"
)
# Find the user to update # Find the user to update
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: if not user:
logger.error("User not found") self.logger.error("User not found")
raise ResourceNotFoundException("User not found") raise ResourceNotFoundException("User not found")
# Update only the fields that were provided # Update only the fields that were provided
@ -309,7 +326,7 @@ class UserServices:
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: if not user:
logger.error("User not found") self.logger.error("User not found")
raise ResourceNotFoundException("User not found") raise ResourceNotFoundException("User not found")
# Use the soft_delete method from CustomBase # Use the soft_delete method from CustomBase
@ -323,7 +340,11 @@ class UserServices:
async def get_super_admins(self): async def get_super_admins(self):
try: try:
return self.db.query(Users).filter(Users.userType == UserType.SUPER_ADMIN).all() return (
self.db.query(Users)
.filter(Users.userType == UserType.SUPER_ADMIN)
.all()
)
except Exception as e: except Exception as e:
DBExceptionHandler.handle_exception(e, context="getting super admins") DBExceptionHandler.handle_exception(e, context="getting super admins")
finally: finally:
@ -335,12 +356,11 @@ class UserServices:
admins = await self.get_super_admins() admins = await self.get_super_admins()
for admin in admins: for admin in admins:
self.email_service.send_new_clinic_email( self.email_service.send_new_clinic_email(
to_address=admin.email, to_address=admin.email, clinic_name=clinic_name
clinic_name=clinic_name
) )
except Exception as e: except Exception as e:
# Log the error but don't interrupt the main flow # Log the error but don't interrupt the main flow
logger.error(f"Error sending admin emails: {str(e)}") self.logger.error(f"Error sending admin emails: {str(e)}")
finally: finally:
self.db.close() self.db.close()
@ -349,7 +369,7 @@ class UserServices:
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: if not user:
logger.error("User not found") self.logger.error("User not found")
raise ResourceNotFoundException("User not found") raise ResourceNotFoundException("User not found")
return self.stripe_service.create_payment_link(user_id) return self.stripe_service.create_payment_link(user_id)