parent
287b6e5761
commit
2efc09cf20
|
|
@ -5,7 +5,7 @@ from fastapi.security import HTTPBearer
|
||||||
# Import the security scheme
|
# Import the security scheme
|
||||||
bearer_scheme = HTTPBearer(scheme_name="Bearer Authentication")
|
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 = APIRouter()
|
||||||
# api_router.include_router(twilio.router, prefix="/twilio")
|
# 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(dashboard.router, prefix="/dashboard", tags=["dashboard"], dependencies=[Depends(auth_required)])
|
||||||
|
|
||||||
api_router.include_router(call_transcripts.router, prefix="/call-transcripts", tags=["call-transcripts"], dependencies=[Depends(auth_required)])
|
api_router.include_router(call_transcripts.router, prefix="/call-transcripts", tags=["call-transcripts"], 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 sqlalchemy import Enum
|
||||||
from enums.enums import ClinicUserRoles, UserType
|
from enums.enums import ClinicUserRoles, UserType
|
||||||
from models.CustomBase import CustomBase
|
from models.CustomBase import CustomBase
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
class Users(Base, CustomBase):
|
class Users(Base, CustomBase):
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
|
|
@ -13,3 +14,10 @@ class Users(Base, CustomBase):
|
||||||
clinicRole = Column(Enum(ClinicUserRoles), nullable=True)
|
clinicRole = Column(Enum(ClinicUserRoles), nullable=True)
|
||||||
userType = Column(Enum(UserType), nullable=True)
|
userType = Column(Enum(UserType), nullable=True)
|
||||||
profile_pic = Column(String, 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 .AppointmentRelations import AppointmentRelations
|
||||||
from .MasterAppointmentTypes import MasterAppointmentTypes
|
from .MasterAppointmentTypes import MasterAppointmentTypes
|
||||||
from .ClinicDoctors import ClinicDoctors
|
from .ClinicDoctors import ClinicDoctors
|
||||||
|
from .Notifications import Notifications
|
||||||
|
from .CallTranscripts import CallTranscripts
|
||||||
|
from .Fcm import Fcm
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Users",
|
"Users",
|
||||||
|
|
@ -18,4 +21,7 @@ __all__ = [
|
||||||
"AppointmentRelations",
|
"AppointmentRelations",
|
||||||
"MasterAppointmentTypes",
|
"MasterAppointmentTypes",
|
||||||
"ClinicDoctors",
|
"ClinicDoctors",
|
||||||
|
"Notifications",
|
||||||
|
"CallTranscripts",
|
||||||
|
"Fcm",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,11 @@ class CallTranscriptsBase(BaseModel):
|
||||||
call_duration:str
|
call_duration:str
|
||||||
call_received_time: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):
|
class CallTranscriptsCreate(CallTranscriptsBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationCreate(NotificationBase):
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -152,3 +152,12 @@ class CallTranscriptsResponse(CallTranscriptsBase):
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
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