feat: clinic setup update

fix: migration issue
This commit is contained in:
deepvasoya 2025-05-13 17:01:48 +05:30
parent dabc7dd308
commit 13023e5913
12 changed files with 280 additions and 74 deletions

View File

@ -7,17 +7,17 @@ from services.clinicDoctorsServices import ClinicDoctorsServices
router = APIRouter() router = APIRouter()
@router.post("/clinic-doctor") @router.post("/")
def create_clinic_doctor(clinic_doctor: ClinicDoctorCreate): def create_clinic_doctor(clinic_doctor: ClinicDoctorCreate):
clinic_doctor = ClinicDoctorsServices().create_clinic_doctor(clinic_doctor) clinic_doctor = ClinicDoctorsServices().create_clinic_doctor(clinic_doctor)
return ApiResponse(data=clinic_doctor, message="Clinic doctor created successfully") return ApiResponse(data=clinic_doctor, message="Clinic doctor created successfully")
@router.put("/clinic-doctor/{clinic_doctor_id}") @router.put("/{clinic_doctor_id}")
def update_clinic_doctor(clinic_doctor_id: int, clinic_doctor: ClinicDoctorUpdate): def update_clinic_doctor(clinic_doctor_id: int, clinic_doctor: ClinicDoctorUpdate):
clinic_doctor = ClinicDoctorsServices().update_clinic_doctor(clinic_doctor) clinic_doctor = ClinicDoctorsServices().update_clinic_doctor(clinic_doctor_id, clinic_doctor)
return ApiResponse(data=clinic_doctor, message="Clinic doctor updated successfully") return ApiResponse(data=clinic_doctor, message="Clinic doctor updated successfully")
@router.delete("/clinic-doctor/{clinic_doctor_id}") @router.delete("/{clinic_doctor_id}")
def delete_clinic_doctor(clinic_doctor_id: int): def delete_clinic_doctor(clinic_doctor_id: int):
ClinicDoctorsServices().delete_clinic_doctor(clinic_doctor_id) ClinicDoctorsServices().delete_clinic_doctor(clinic_doctor_id)
return ApiResponse(data="OK", message="Clinic doctor deleted successfully") return ApiResponse(data="OK", message="Clinic doctor deleted successfully")

View File

@ -12,7 +12,11 @@ from schemas.CreateSchemas import ClinicCreate
from schemas.UpdateSchemas import ClinicUpdate from schemas.UpdateSchemas import ClinicUpdate
from models.Clinics import Clinics from models.Clinics import Clinics
# services
from services.clinicServices import ClinicServices
# Constants # Constants
from schemas.ApiResponse import ApiResponse
from utils.constants import DEFAULT_SKIP, DEFAULT_LIMIT from utils.constants import DEFAULT_SKIP, DEFAULT_LIMIT
router = APIRouter() router = APIRouter()
@ -20,56 +24,28 @@ router = APIRouter()
@router.get("/", response_model=List[Clinic]) @router.get("/", response_model=List[Clinic])
async def get_clinics( async def get_clinics(
skip: int = DEFAULT_SKIP, limit: int = DEFAULT_LIMIT, db: Session = Depends(get_db) skip: int = DEFAULT_SKIP, limit: int = DEFAULT_LIMIT
): ):
clinics = db.query(Clinics).offset(skip).limit(limit).all() clinics = ClinicServices().get_clinics(skip, limit)
return clinics return ApiResponse(data=clinics, message="Clinics retrieved successfully", status_code=status.HTTP_200_OK)
@router.get("/{clinic_id}", response_model=ClinicWithDoctors) @router.get("/{clinic_id}")
async def get_clinic(clinic_id: int, db: Session = Depends(get_db)): async def get_clinic(clinic_id: int):
db_clinic = db.query(Clinics).where(Clinics.id == clinic_id).first() clinic = ClinicServices().get_clinic_by_id(clinic_id)
if db_clinic is None: return ApiResponse(data=clinic, message="Clinic retrieved successfully", status_code=status.HTTP_200_OK)
raise HTTPException(status_code=404, detail="Clinic not found")
return db_clinic
@router.post("/", response_model=Clinic, status_code=status.HTTP_201_CREATED)
async def create_clinic(clinic: ClinicCreate, db: Session = Depends(get_db)):
try:
db_clinic = Clinics(**clinic.model_dump())
db.add(db_clinic)
db.commit()
db.refresh(db_clinic)
return db_clinic
except Exception as e:
db.rollback()
raise HTTPException(
status_code=500,
detail=str(e.__cause__),
) from e
@router.put("/{clinic_id}", response_model=Clinic) @router.put("/{clinic_id}", response_model=Clinic)
async def update_clinic( async def update_clinic(
clinic_id: int, clinic: ClinicUpdate, db: Session = Depends(get_db) clinic_id: int, clinic: ClinicUpdate
): ):
db_clinic = db.query(Clinics).filter(Clinics.id == clinic_id).first() clinic = ClinicServices().update_clinic(clinic_id, clinic)
if db_clinic is None: return ApiResponse(data=clinic, message="Clinic updated successfully", status_code=status.HTTP_200_OK)
raise HTTPException(status_code=404, detail="Clinic not found")
update_data = clinic.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(db_clinic, key, value)
db.commit()
db.refresh(db_clinic)
return db_clinic
@router.delete("/{clinic_id}", status_code=status.HTTP_204_NO_CONTENT) @router.delete("/{clinic_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_clinic(clinic_id: int, db: Session = Depends(get_db)): async def delete_clinic(clinic_id: int):
db_clinic = db.query(Clinics).where(Clinics.id == clinic_id).first() db_clinic = db.query(Clinics).where(Clinics.id == clinic_id).first()
if db_clinic is None: if db_clinic is None:
raise HTTPException(status_code=404, detail="Clinic not found") raise HTTPException(status_code=404, detail="Clinic not found")

View File

@ -0,0 +1,127 @@
"""fix enum clinic doctor
Revision ID: 0ce7107c1910
Revises: a3a9b7d17bdd
Create Date: 2025-05-13 12:20:49.384154
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from sqlalchemy import text
# revision identifiers, used by Alembic.
revision: str = '0ce7107c1910'
down_revision: Union[str, None] = 'a3a9b7d17bdd'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
# Check if the enum type exists before creating it
conn = op.get_bind()
result = conn.execute(text("SELECT EXISTS(SELECT 1 FROM pg_type WHERE typname = 'clinicdoctortype')"))
enum_exists = result.scalar()
if not enum_exists:
# Create the new enum type if it doesn't exist
op.execute(text("CREATE TYPE clinicdoctortype AS ENUM ('DOCTOR', 'NURSE')"))
# Check if the new_role column already exists
inspector = sa.inspect(conn)
columns = inspector.get_columns('clinic_doctors')
column_names = [column['name'] for column in columns]
if 'new_role' not in column_names:
# Add a temporary column with the new type
op.add_column('clinic_doctors', sa.Column('new_role', postgresql.ENUM('DOCTOR', 'NURSE', name='clinicdoctortype'), nullable=True))
# Update the temporary column with values based on the old column
op.execute(text("UPDATE clinic_doctors SET new_role = 'DOCTOR' WHERE role = 'DIRECTOR'"))
op.execute(text("UPDATE clinic_doctors SET new_role = 'NURSE' WHERE role = 'PRACTICE_MANAGER'"))
# Drop the old column and rename the new one
op.drop_column('clinic_doctors', 'role')
op.alter_column('clinic_doctors', 'new_role', new_column_name='role')
# We need to handle the users table that depends on the clinicuserroles enum
# First check if the users table has a clinicRole column that uses the enum
has_clinic_role = False
try:
user_columns = inspector.get_columns('users')
for column in user_columns:
if column['name'] == 'clinicRole':
has_clinic_role = True
break
except:
# Table might not exist
pass
if has_clinic_role:
# We need to update the users table to not use the enum before dropping it
# First, create a temporary column with a string type
op.add_column('users', sa.Column('temp_clinic_role', sa.String(), nullable=True))
# Copy the values - use double quotes to preserve case sensitivity in PostgreSQL
op.execute(text('UPDATE users SET temp_clinic_role = "clinicRole"::text'))
# Drop the old column and rename the new one
op.drop_column('users', 'clinicRole')
op.alter_column('users', 'temp_clinic_role', new_column_name='clinicRole')
# Now we can safely drop the old enum type
op.execute(text("DROP TYPE IF EXISTS clinicuserroles"))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
# Create the old enum type
op.execute(text("CREATE TYPE clinicuserroles AS ENUM ('DIRECTOR', 'PRACTICE_MANAGER')"))
# Add a temporary column with the old type
op.add_column('clinic_doctors', sa.Column('old_role', postgresql.ENUM('DIRECTOR', 'PRACTICE_MANAGER', name='clinicuserroles'), nullable=True))
# Update the temporary column with values based on the new column
op.execute(text("UPDATE clinic_doctors SET old_role = 'DIRECTOR' WHERE role = 'DOCTOR'"))
op.execute(text("UPDATE clinic_doctors SET old_role = 'PRACTICE_MANAGER' WHERE role = 'NURSE'"))
# Drop the new column and rename the old one
op.drop_column('clinic_doctors', 'role')
op.alter_column('clinic_doctors', 'old_role', new_column_name='role')
# If we modified the users table in the upgrade, we need to restore it
conn = op.get_bind()
inspector = sa.inspect(conn)
has_clinic_role = False
try:
user_columns = inspector.get_columns('users')
for column in user_columns:
if column['name'] == 'clinicRole':
has_clinic_role = True
break
except:
pass
if has_clinic_role:
# Create a temporary column with the enum type
op.add_column('users', sa.Column('temp_clinic_role', postgresql.ENUM('DIRECTOR', 'PRACTICE_MANAGER', name='clinicuserroles'), nullable=True))
# Copy the values (with appropriate conversion) - use double quotes to preserve case sensitivity
op.execute(text('UPDATE users SET temp_clinic_role = "clinicRole"::clinicuserroles'))
# Drop the old column and rename the new one
op.drop_column('users', 'clinicRole')
op.alter_column('users', 'temp_clinic_role', new_column_name='clinicRole')
# Drop the new enum type
op.execute(text("DROP TYPE IF EXISTS clinicdoctortype"))
# ### end Alembic commands ###
# ### end Alembic commands ###

View File

@ -1,6 +1,6 @@
from sqlalchemy import Column, Enum, Integer, String, ForeignKey,Table from sqlalchemy import Column, Enum, Integer, String, ForeignKey,Table
from database import Base from database import Base
from enums.enums import ClinicUserRoles, ClinicDoctorStatus from enums.enums import ClinicDoctorType, ClinicDoctorStatus
from .CustomBase import CustomBase from .CustomBase import CustomBase
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@ -9,9 +9,10 @@ class ClinicDoctors(Base, CustomBase):
id = Column(Integer, primary_key=True, index=True) id = Column(Integer, primary_key=True, index=True)
name = Column(String) name = Column(String)
role = Column(Enum(ClinicUserRoles)) role = Column(Enum(ClinicDoctorType))
status = Column(Enum(ClinicDoctorStatus)) status = Column(Enum(ClinicDoctorStatus))
appointmentRelations = relationship("AppointmentRelations", back_populates="clinicDoctors") appointmentRelations = relationship("AppointmentRelations", back_populates="clinicDoctors")
clinic_id = Column(Integer, ForeignKey("clinics.id")) clinic_id = Column(Integer, ForeignKey("clinics.id"))
clinic = relationship("Clinics", back_populates="clinicDoctors") clinic = relationship("Clinics", back_populates="clinicDoctors")

View File

@ -10,4 +10,3 @@ class MasterAppointmentTypes(Base, CustomBase):
type = Column(String) type = Column(String)
appointmentRelations = relationship("AppointmentRelations", back_populates="masterAppointmentTypes") appointmentRelations = relationship("AppointmentRelations", back_populates="masterAppointmentTypes")
clinicDoctors = relationship("ClinicDoctors", back_populates="masterAppointmentTypes")

View File

@ -76,3 +76,4 @@ class ClinicDoctorBase(BaseModel):
name: str name: str
role: ClinicDoctorType role: ClinicDoctorType
status: ClinicDoctorStatus status: ClinicDoctorStatus
clinic_id: int

View File

@ -136,7 +136,7 @@ class AppointmentDetailed(AppointmentSchema):
patient: Patient patient: Patient
class ClinicDoctor(ClinicDoctorBase): class ClinicDoctorResponse(ClinicDoctorBase):
id: int id: int
create_time: datetime create_time: datetime
update_time: datetime update_time: datetime

View File

@ -1,12 +1,32 @@
from .BaseSchemas import * from .BaseSchemas import *
from enums.enums import ClinicStatus, Integration
# Update schemas (all fields optional for partial updates) # Update schemas (all fields optional for partial updates)
class ClinicUpdate(BaseModel): class ClinicUpdate(BaseModel):
name: Optional[str] = None name: Optional[str] = None
address: Optional[str] = None address: Optional[str] = None
phone: Optional[str] = None phone: Optional[str] = None
email: Optional[EmailStr] = None status: Optional[ClinicStatus] = None
integration: Optional[Integration] = None
pms_id: Optional[str] = None
practice_name: Optional[str] = None
logo: Optional[str] = None
country: Optional[str] = None
postal_code: Optional[str] = None
city: Optional[str] = None
state: Optional[str] = None
abn_doc: Optional[str] = None
abn_number: Optional[str] = None
contract_doc: Optional[str] = None
clinic_phone: Optional[str] = None
is_clinic_phone_enabled: Optional[bool] = True
other_info: Optional[str] = None
greeting_msg: Optional[str] = None
voice_model: Optional[str] = None
voice_model_provider: Optional[str] = None
voice_model_gender: Optional[str] = None
scenarios: Optional[str] = None
general_info: Optional[str] = None
class DoctorUpdate(BaseModel): class DoctorUpdate(BaseModel):

View File

@ -1,21 +1,49 @@
from schemas.CreateSchemas import ClinicDoctorCreate from schemas.CreateSchemas import ClinicDoctorCreate
from schemas.UpdateSchemas import ClinicDoctorUpdate from schemas.UpdateSchemas import ClinicDoctorUpdate
from schemas.ResponseSchemas import ClinicDoctorResponse
from database import get_db from database import get_db
from models import ClinicDoctors from models import ClinicDoctors
from sqlalchemy.orm import Session
from services.clinicServices import ClinicServices
from exceptions import ResourceNotFoundException
class ClinicDoctorsServices: class ClinicDoctorsServices:
def __init__(self): def __init__(self):
self.db = next(get_db()) self.db: Session = next(get_db())
self.clinic_services = ClinicServices()
def create_clinic_doctor(self, clinic_doctor: ClinicDoctorCreate): def create_clinic_doctor(self, clinic_doctor: ClinicDoctorCreate) -> ClinicDoctorResponse:
clinic_doctor = ClinicDoctors(**clinic_doctor.dict())
# check if clinic exists
self.clinic_services.get_clinic_by_id(clinic_doctor.clinic_id)
clinic_doctor = ClinicDoctors(**clinic_doctor.model_dump())
self.db.add(clinic_doctor) self.db.add(clinic_doctor)
self.db.commit() self.db.commit()
self.db.refresh(clinic_doctor) self.db.refresh(clinic_doctor)
return clinic_doctor return ClinicDoctorResponse(**clinic_doctor.__dict__.copy())
def update_clinic_doctor(self, clinic_doctor_id: int, clinic_doctor: ClinicDoctorUpdate): def update_clinic_doctor(self, clinic_doctor_id: int, clinic_doctor_data: ClinicDoctorUpdate) -> ClinicDoctorResponse:
pass # check if clinic doctor exists
clinic_doctor = self.db.query(ClinicDoctors).filter(ClinicDoctors.id == clinic_doctor_id).first()
if clinic_doctor is None:
raise ResourceNotFoundException("Clinic doctor not found")
if clinic_doctor_data.clinic_id != clinic_doctor.clinic_id:
# check if clinic exists
self.clinic_services.get_clinic_by_id(clinic_doctor_data.clinic_id)
# Update the existing object with new values
update_data = clinic_doctor_data.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(clinic_doctor, key, value)
self.db.add(clinic_doctor)
self.db.commit()
self.db.refresh(clinic_doctor)
return ClinicDoctorResponse(**clinic_doctor.__dict__.copy())
def delete_clinic_doctor(self, clinic_doctor_id: int): def delete_clinic_doctor(self, clinic_doctor_id: int):
pass clinic_doctor = self.db.query(ClinicDoctors).filter(ClinicDoctors.id == clinic_doctor_id).first()
self.db.delete(clinic_doctor)
self.db.commit()

View File

@ -0,0 +1,52 @@
from database import get_db
from sqlalchemy.orm import Session
from models import Clinics
from schemas.UpdateSchemas import ClinicUpdate
from schemas.ResponseSchemas import Clinic
from typing import List
from exceptions import ResourceNotFoundException
class ClinicServices:
def __init__(self):
self.db: Session = next(get_db())
def get_clinics(self, limit:int, offset:int) -> List[Clinic]:
clinics = self.db.query(Clinics).limit(limit).offset(offset).all()
clinic_response = [Clinic(**clinic.__dict__.copy()) for clinic in clinics]
return clinic_response
def get_clinic_by_id(self, clinic_id: int) -> Clinic:
clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first()
if clinic is None:
raise ResourceNotFoundException("Clinic not found")
clinic_response = Clinic(**clinic.__dict__.copy())
return clinic_response
def update_clinic(self, clinic_id: int, clinic_data: ClinicUpdate):
clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first()
if clinic is None:
raise ResourceNotFoundException("Clinic not found")
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)
return Clinic(**clinic.__dict__.copy())
def delete_clinic(self, clinic_id: int):
clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first()
if clinic is None:
raise ResourceNotFoundException("Clinic not found")
self.db.delete(clinic)
self.db.commit()

View File

@ -6,7 +6,7 @@ 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 UserResponse
from models import Clinics from models import Clinics
from enums.enums import ClinicStatus from enums.enums import ClinicStatus, UserType
from schemas.UpdateSchemas import UserUpdate from schemas.UpdateSchemas import UserUpdate
from exceptions.unauthorized_exception import UnauthorizedException from exceptions.unauthorized_exception import UnauthorizedException
from utils.password_utils import hash_password from utils.password_utils import hash_password
@ -150,34 +150,36 @@ class UserServices:
return user_response return user_response
def update_user(self, admin_id:int|None, user_id: int, user_data: UserUpdate): 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: 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") logger.error("Admin not found")
raise ResourceNotFoundException("Admin not found") raise ResourceNotFoundException("Admin not found")
if admin.userType != UserType.ADMIN: # 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") 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
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") logger.error("User not found")
raise ResourceNotFoundException("User not found") raise ResourceNotFoundException("User not found")
user.username = user_data.username # Update only the fields that were provided
user.clinicRole = user_data.clinicRole update_data = user_data.model_dump(exclude_unset=True)
user.userType = user_data.userType for key, value in update_data.items():
user.profile_pic = user_data.profile_pic setattr(user, key, value)
self.db.add(user) self.db.add(user)
self.db.commit() self.db.commit()
self.db.refresh(user)
return user # Return properly serialized response
return UserResponse.model_validate(user)
def delete_user(self, user_id: int): def delete_user(self, user_id: int):
user = self.db.query(Users).filter(Users.id == user_id).first() user = self.db.query(Users).filter(Users.id == user_id).first()