Files
Student-Management-System-I…/app/firebase.py
T
2025-04-15 04:20:26 +05:45

223 lines
7.3 KiB
Python

import os
from django.http import HttpResponse, JsonResponse
from django.db import models
import firebase_admin
from firebase_admin import messaging, credentials
from student_management_system.settings import BASE_DIR
# --------------------------------------------------------------------
# FCM Device Model
# --------------------------------------------------------------------
class FCMDevice(models.Model):
"""Model to store Firebase Cloud Messaging device tokens"""
id = models.BigAutoField(primary_key=True)
token = models.TextField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
last_active = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
is_fallback = models.BooleanField(default=False) # Flag to identify fallback tokens
user_type = models.CharField(
max_length=20,
choices=[
("student", "Student"),
("parent", "Parent"),
("teacher", "Teacher"),
("admin", "Admin"),
("unknown", "Unknown"),
],
default="unknown",
) # Add user type field
def __str__(self):
return f"Device {self.id} ({self.token[:20]}...)"
class Meta:
verbose_name = "FCM Device"
verbose_name_plural = "FCM Devices"
ordering = ["-last_active"]
def deactivate(self):
"""Deactivate this device token"""
self.is_active = False
self.save()
# --------------------------------------------------------------------
# Token Management
# --------------------------------------------------------------------
def save_fcm_token(request, token):
"""Save FCM token to database"""
try:
# Check if this is a fallback token
is_fallback = token.startswith("fcm-token-") or token.startswith(
"fallback-token-"
)
# Determine user type if a user is authenticated
user_type = "unknown"
if hasattr(request, "user") and request.user.is_authenticated:
user_groups = request.user.groups.values_list("name", flat=True)
if "Student" in user_groups:
user_type = "student"
elif "Parent" in user_groups:
user_type = "parent"
elif "Teacher" in user_groups:
user_type = "teacher"
elif request.user.is_superuser:
user_type = "admin"
# Use update_or_create to avoid unnecessary deletes
device, created = FCMDevice.objects.update_or_create(
token=token,
defaults={
"is_fallback": is_fallback,
"is_active": True,
"user_type": user_type,
},
)
return JsonResponse({"status": "success"})
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)
# --------------------------------------------------------------------
# Firebase Configuration
# --------------------------------------------------------------------
def get_firebase_js():
"""Generate Firebase JavaScript configuration"""
firebase_config = {
"apiKey": os.getenv("FIREBASE_API_KEY", ""),
"authDomain": os.getenv("FIREBASE_AUTH_DOMAIN", ""),
"projectId": os.getenv("FIREBASE_PROJECT_ID", ""),
"storageBucket": os.getenv("FIREBASE_STORAGE_BUCKET", ""),
"messagingSenderId": os.getenv("FIREBASE_MESSAGING_SENDER_ID", ""),
"appId": os.getenv("FIREBASE_APP_ID", ""),
"measurementId": os.getenv("FIREBASE_MEASUREMENT_ID", ""),
}
data = """
importScripts('https://www.gstatic.com/firebasejs/11.0.2/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/11.0.2/firebase-messaging-compat.js');
firebase.initializeApp({
apiKey: "%s",
authDomain: "%s",
projectId: "%s",
storageBucket: "%s",
messagingSenderId: "%s",
appId: "%s",
measurementId: "%s"
});
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function(payload) {
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: payload.notification.icon || '/static/img/logo.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
""" % (
firebase_config["apiKey"],
firebase_config["authDomain"],
firebase_config["projectId"],
firebase_config["storageBucket"],
firebase_config["messagingSenderId"],
firebase_config["appId"],
firebase_config["measurementId"],
)
return HttpResponse(data, content_type="application/javascript")
# --------------------------------------------------------------------
# Firebase Admin Initialization
# --------------------------------------------------------------------
# Initialize Firebase Admin
firebase_app = None
try:
cert_path = os.path.join(BASE_DIR, "firebase-key.json")
if os.path.exists(cert_path):
cred = credentials.Certificate(cert_path)
firebase_app = firebase_admin.initialize_app(cred)
except Exception:
pass
# --------------------------------------------------------------------
# Push Notification Functions
# --------------------------------------------------------------------
def send_push_notification(title, message, tokens):
"""
Send push notification using Firebase Cloud Messaging
Returns a tuple of (success_count, failure_count, failed_tokens)
"""
if not firebase_app or not tokens:
return 0, len(tokens) if tokens else 0, tokens
if not isinstance(tokens, (list, tuple)):
tokens = list(tokens) # Convert QuerySet to list
success_count = 0
failure_count = 0
failed_tokens = []
# Get only active devices that are not fallback tokens
active_devices = FCMDevice.objects.filter(
token__in=tokens,
is_active=True,
is_fallback=False, # Skip fallback tokens for push notifications
)
# Count fallback tokens as "success" for reporting purposes
fallback_count = FCMDevice.objects.filter(
token__in=tokens, is_active=True, is_fallback=True
).count()
success_count += fallback_count
for device in active_devices:
try:
# Skip tokens that match our fallback pattern
if device.token.startswith("fcm-token-") or device.token.startswith(
"fallback-token-"
):
device.is_fallback = True
device.save()
continue
messaging.send(
messaging.Message(
notification=messaging.Notification(
title=title,
body=message,
),
token=str(device.token), # Ensure token is a string
)
)
success_count += 1
except messaging.UnregisteredError:
# Token is no longer valid, deactivate the device
device.deactivate()
failure_count += 1
failed_tokens.append(device.token)
except Exception:
failure_count += 1
failed_tokens.append(device.token)
return success_count, failure_count, failed_tokens