parent
287b6e5761
commit
2efc09cf20
|
|
@ -5,7 +5,7 @@ from fastapi.security import HTTPBearer
|
|||
# Import the security scheme
|
||||
bearer_scheme = HTTPBearer(scheme_name="Bearer Authentication")
|
||||
|
||||
from .endpoints import clinics, doctors, calender, appointments, patients, admin, auth, s3, users, clinicDoctor, dashboard, call_transcripts
|
||||
from .endpoints import clinics, doctors, calender, appointments, patients, admin, auth, s3, users, clinicDoctor, dashboard, call_transcripts, notifications
|
||||
|
||||
api_router = APIRouter()
|
||||
# api_router.include_router(twilio.router, prefix="/twilio")
|
||||
|
|
@ -37,3 +37,5 @@ api_router.include_router(clinicDoctor.router, prefix="/clinic-doctors", tags=["
|
|||
api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboard"], dependencies=[Depends(auth_required)])
|
||||
|
||||
api_router.include_router(call_transcripts.router, prefix="/call-transcripts", tags=["call-transcripts"], dependencies=[Depends(auth_required)])
|
||||
|
||||
api_router.include_router(notifications.router, prefix="/notifications", tags=["notifications"], dependencies=[Depends(auth_required)])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
from fastapi import APIRouter
|
||||
from utils.constants import DEFAULT_LIMIT, DEFAULT_PAGE
|
||||
from services.notificationServices import NotificationServices
|
||||
from schemas.ApiResponse import ApiResponse
|
||||
from fastapi import Request
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/")
|
||||
def get_notifications(request: Request, limit: int = DEFAULT_LIMIT, page: int = DEFAULT_PAGE):
|
||||
if page <0:
|
||||
page = 1
|
||||
|
||||
offset = (page - 1) * limit
|
||||
|
||||
notifications = NotificationServices().getNotifications(request.state.user["id"], limit, offset)
|
||||
return ApiResponse(data=notifications, message="Notifications retrieved successfully")
|
||||
|
||||
@router.delete("/")
|
||||
def delete_notification(notification_id: int):
|
||||
NotificationServices().deleteNotification(notification_id)
|
||||
return ApiResponse(data="OK", message="Notification deleted successfully")
|
||||
|
||||
@router.put("/")
|
||||
def update_notification_status(notification_id: int):
|
||||
NotificationServices().updateNotificationStatus(notification_id)
|
||||
return ApiResponse(data="OK", message="Notification status updated successfully")
|
||||
|
||||
@router.post("/")
|
||||
def send_notification(title: str, message: str, sender_id: int, receiver_id: int):
|
||||
NotificationServices().createNotification(title, message, sender_id, receiver_id)
|
||||
return ApiResponse(data="OK", message="Notification sent successfully")
|
||||
|
||||
@router.post("/fcm")
|
||||
def send_fcm_notification(req: Request, token: str):
|
||||
NotificationServices().createOrUpdateFCMToken(req.state.user["id"], token)
|
||||
return ApiResponse(data="OK", message="FCM Notification sent successfully")
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
"""notification table
|
||||
|
||||
Revision ID: ac71b9a4b040
|
||||
Revises: 0ce7107c1910
|
||||
Create Date: 2025-05-14 16:14:23.750891
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'ac71b9a4b040'
|
||||
down_revision: Union[str, None] = '0ce7107c1910'
|
||||
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! ###
|
||||
op.create_table('notifications',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(), nullable=True),
|
||||
sa.Column('message', sa.String(), nullable=True),
|
||||
sa.Column('is_read', sa.Boolean(), nullable=True),
|
||||
sa.Column('sender_id', sa.Integer(), nullable=False),
|
||||
sa.Column('receiver_id', sa.Integer(), nullable=False),
|
||||
sa.Column('create_time', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('update_time', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['receiver_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['sender_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_notifications_id'), 'notifications', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_notifications_receiver_id'), 'notifications', ['receiver_id'], unique=False)
|
||||
op.create_index(op.f('ix_notifications_sender_id'), 'notifications', ['sender_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_notifications_sender_id'), table_name='notifications')
|
||||
op.drop_index(op.f('ix_notifications_receiver_id'), table_name='notifications')
|
||||
op.drop_index(op.f('ix_notifications_id'), table_name='notifications')
|
||||
op.drop_table('notifications')
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
from sqlalchemy import Column, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy import ForeignKey
|
||||
|
||||
from database import Base
|
||||
from .CustomBase import CustomBase
|
||||
|
||||
|
||||
class Fcm(Base, CustomBase):
|
||||
__tablename__ = "fcm"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
token = Column(String)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||
user = relationship("Users", back_populates="fcm")
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from database import Base
|
||||
from .CustomBase import CustomBase
|
||||
|
||||
class Notifications(Base, CustomBase):
|
||||
__tablename__ = "notifications"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String)
|
||||
message = Column(String)
|
||||
is_read = Column(Boolean, default=False)
|
||||
|
||||
sender_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||
sender = relationship("Users", foreign_keys=[sender_id], back_populates="sent_notifications")
|
||||
|
||||
receiver_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||
receiver = relationship("Users", foreign_keys=[receiver_id], back_populates="received_notifications")
|
||||
|
|
@ -3,6 +3,7 @@ from database import Base
|
|||
from sqlalchemy import Enum
|
||||
from enums.enums import ClinicUserRoles, UserType
|
||||
from models.CustomBase import CustomBase
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
class Users(Base, CustomBase):
|
||||
__tablename__ = "users"
|
||||
|
|
@ -13,3 +14,10 @@ class Users(Base, CustomBase):
|
|||
clinicRole = Column(Enum(ClinicUserRoles), nullable=True)
|
||||
userType = Column(Enum(UserType), nullable=True)
|
||||
profile_pic = Column(String, nullable=True)
|
||||
|
||||
# Notification relationships
|
||||
sent_notifications = relationship("Notifications", foreign_keys="Notifications.sender_id", back_populates="sender")
|
||||
received_notifications = relationship("Notifications", foreign_keys="Notifications.receiver_id", back_populates="receiver")
|
||||
|
||||
# FCM relationships
|
||||
fcm = relationship("Fcm", back_populates="user")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ from .Calendar import Calenders
|
|||
from .AppointmentRelations import AppointmentRelations
|
||||
from .MasterAppointmentTypes import MasterAppointmentTypes
|
||||
from .ClinicDoctors import ClinicDoctors
|
||||
from .Notifications import Notifications
|
||||
from .CallTranscripts import CallTranscripts
|
||||
from .Fcm import Fcm
|
||||
|
||||
__all__ = [
|
||||
"Users",
|
||||
|
|
@ -18,4 +21,7 @@ __all__ = [
|
|||
"AppointmentRelations",
|
||||
"MasterAppointmentTypes",
|
||||
"ClinicDoctors",
|
||||
"Notifications",
|
||||
"CallTranscripts",
|
||||
"Fcm",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -84,4 +84,12 @@ class CallTranscriptsBase(BaseModel):
|
|||
patient_number:str
|
||||
call_duration:str
|
||||
call_received_time:str
|
||||
transcript_key_id:str
|
||||
transcript_key_id:str
|
||||
|
||||
|
||||
class NotificationBase(BaseModel):
|
||||
title: str
|
||||
message: str
|
||||
is_read: bool
|
||||
sender_id: int
|
||||
receiver_id: int
|
||||
|
|
|
|||
|
|
@ -45,3 +45,7 @@ class ClinicDoctorCreate(ClinicDoctorBase):
|
|||
|
||||
class CallTranscriptsCreate(CallTranscriptsBase):
|
||||
pass
|
||||
|
||||
|
||||
class NotificationCreate(NotificationBase):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -150,5 +150,14 @@ class CallTranscriptsResponse(CallTranscriptsBase):
|
|||
create_time: datetime
|
||||
update_time: datetime
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class NotificationResponse(NotificationBase):
|
||||
id: int
|
||||
create_time: datetime
|
||||
update_time: datetime
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
from logging import Logger
|
||||
from database import get_db
|
||||
from sqlalchemy.orm import Session
|
||||
from firebase_admin import messaging
|
||||
|
||||
from models import Notifications, Users, Fcm
|
||||
from interface.common_response import CommonResponse
|
||||
from schemas.ResponseSchemas import NotificationResponse
|
||||
from exceptions.resource_not_found_exception import ResourceNotFoundException
|
||||
|
||||
class NotificationServices:
|
||||
def __init__(self):
|
||||
self.db:Session = next(get_db())
|
||||
|
||||
def createNotification(self, title, message, sender_id, receiver_id):
|
||||
# validate sender and receiver
|
||||
sender = self.db.query(Users).filter(Users.id == sender_id).first()
|
||||
receiver = self.db.query(Users).filter(Users.id == receiver_id).first()
|
||||
|
||||
if not sender or not receiver:
|
||||
raise ResourceNotFoundException("Sender or receiver not found")
|
||||
|
||||
notification = Notifications(title=title, message=message, sender_id=sender_id, receiver_id=receiver_id)
|
||||
self.db.add(notification)
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def getNotifications(self, user_id, limit: int, offset: int):
|
||||
notifications = self.db.query(Notifications).filter(Notifications.receiver_id == user_id).limit(limit).offset(offset).all()
|
||||
total = self.db.query(Notifications).filter(Notifications.receiver_id == user_id).count()
|
||||
|
||||
response = CommonResponse(data=[NotificationResponse(**notification.__dict__.copy()) for notification in notifications], total=total)
|
||||
|
||||
return response
|
||||
|
||||
def deleteNotification(self, notification_id):
|
||||
notification = self.db.query(Notifications).filter(Notifications.id == notification_id).first()
|
||||
|
||||
if not notification:
|
||||
raise ResourceNotFoundException("Notification not found")
|
||||
|
||||
self.db.delete(notification)
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def updateNotificationStatus(self, notification_id):
|
||||
notification = self.db.query(Notifications).filter(Notifications.id == notification_id).first()
|
||||
|
||||
if not notification:
|
||||
raise ResourceNotFoundException("Notification not found")
|
||||
|
||||
notification.is_read = not notification.is_read
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def createOrUpdateFCMToken(self,user_id,token):
|
||||
fcm = self.db.query(Fcm).filter(Fcm.token == token).first()
|
||||
|
||||
if fcm is None:
|
||||
fcm = Fcm(user_id=user_id, token=token)
|
||||
self.db.add(fcm)
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
fcm.token = token
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def sendPushNotification(self, token:str, payload):
|
||||
try:
|
||||
message = messaging.Message(
|
||||
notification=messaging.Notification(
|
||||
title=payload["title"],
|
||||
body=payload["body"],
|
||||
),
|
||||
data={},
|
||||
token=token,
|
||||
)
|
||||
response = messaging.send(message)
|
||||
return response
|
||||
except:
|
||||
Logger.error("Failed to send push notification")
|
||||
Loading…
Reference in New Issue