Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f9cfe26
Implemented Live server support
KanavCode Feb 5, 2026
df3cd8c
in Allowed Origin classlensfrontend.vercel.app Updated
KanavCode Feb 25, 2026
8e4962f
Merge branch 'main' of https://github.com/ClassLens-A/ClassLens
KanavCode Feb 25, 2026
09039bb
Merge branch 'main' of https://github.com/ClassLens-A/ClassLens
KanavCode Feb 25, 2026
b5dfb25
rabbitmq and start_server.bat
KanavCode May 7, 2026
7d72095
Readme
ClasslensCode May 7, 2026
80ab63d
Design change in DB and Backend
mannshah24 May 12, 2026
c4469f0
Done some changes to make the backend working
mannshah24 May 14, 2026
9914095
Update settings and requirements
Dhriti-5 May 15, 2026
b931391
Merge branch 'main' of https://github.com/mannshah24/ClassLensBack in…
Dhriti-5 May 15, 2026
399e43f
Backend attendance processing and API updates
Dhriti-5 May 19, 2026
2c077e9
Merge pull request #1 from mannshah24/dhriti-backend
Dhriti-5 May 19, 2026
1f88e8d
resolve the timestamp mismatch between class_session_time and attenda…
Dhriti-5 May 25, 2026
9f0a84f
create api endpoint for retriving attendance data at teacher-side
Dhriti-5 May 27, 2026
167618d
Merge pull request #2 from mannshah24/dhriti-backend
mannshah24 May 27, 2026
fdbcb90
cretae endpoint for updating face id and uploading attendance result …
Dhriti-5 May 29, 2026
db1a424
Update gitignore and remove local files from tracking
Dhriti-5 Jun 3, 2026
93515de
Merge pull request #3 from mannshah24/dhriti-backend
mannshah24 Jun 3, 2026
1378fcf
updated .gitignore
Dhriti-5 Jun 3, 2026
0250033
Merge pull request #4 from mannshah24/dhriti-backend
mannshah24 Jun 3, 2026
dd0f069
change view.py to calculate attendance statstics
Dhriti-5 Jun 4, 2026
d42ce85
Merge pull request #5 from mannshah24/dhriti-backend
Dhriti-5 Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,34 @@ gfpgan

# Firebase credentials (NEVER commit these!)
firebase-service-account.json
**/firebase-service-account.json
**/firebase-service-account.json


# Markdown docs except README
*.md
!README.md

# Local test/debug files
test_sync_endpoint.py

# Local schema/docs
New_schema
data-1778222711514.csv
API_ENDPOINTS.md
ARCHITECTURE_RULES.md
DATABASE_SCHEMA.md
db-chat.txt
DBMSUIS_V7_utf8.sql
DBMSUIS_V7.sql
RABBITMQ_MIGRATION.md
setup_backend.bat

# Python virtual env
.venv/

# Python cache
__pycache__/
*.pyc

# Environment files
.env
15 changes: 12 additions & 3 deletions ClassLens_DB/ClassLens_DB/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@
from celery import Celery
import environ #type:ignore
from pathlib import Path #type:ignore
from urllib.parse import quote_plus

BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
redis_url = env('REDIS_URL')
rabbitmq_url = env('RABBITMQ_URL')

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ClassLens_DB.settings')

# Build database result backend URL with proper password encoding
db_user = env("DB_USER")
db_password = quote_plus(env("DB_PASSWORD"))
db_host = env("DB_HOST")
db_port = env("DB_PORT")
db_name = env("DB_NAME")
db_result_backend = f'db+postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}'

app = Celery('ClassLens_DB')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
app.conf.update(
broker_url=redis_url,
result_backend=redis_url,
broker_url=rabbitmq_url,
result_backend=db_result_backend,
broker_connection_retry_on_startup=True,
)
54 changes: 43 additions & 11 deletions ClassLens_DB/ClassLens_DB/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pathlib import Path
import environ
import os
from urllib.parse import quote_plus

BASE_DIR = Path(__file__).resolve().parent.parent

Expand All @@ -26,16 +27,20 @@
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-x3zih9=91am0f(gkv&16y+n%7i1b91e-^mh&v*_r=_kz@y7t04"
SECRET_KEY = env("SECRET_KEY")

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = [
'172.25.13.31',
'172.16.141.247',
'172.26.12.236',
'10.0.2.2',
'14.139.121.110',
'localhost',
'127.0.0.1',
'172.25.13.31'
'10.0.3.2',
]


Expand All @@ -52,7 +57,6 @@
"DatabaseAdminApp",
"rest_framework",
'corsheaders',
'django_redis'
]

MIDDLEWARE = [
Expand Down Expand Up @@ -105,11 +109,8 @@

CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": env('REDIS_URL'),
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "classlens-local-cache",
Comment on lines +112 to +113
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mannshah24 LocMemCache is not suitable for multi-worker (like ours ) production deployments because each process maintains its own isolated in-memory cache.

The suggestion by copilot seems appropriate to use db as a cache.

First run this command python manage.py createcachetable classlens_cache and then apply the suggested changes by copilot.

}
}

Expand Down Expand Up @@ -164,8 +165,14 @@

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

CELERY_BROKER_URL = env('REDIS_URL')
CELERY_RESULT_BACKEND = env('REDIS_URL')
CELERY_BROKER_URL = env('RABBITMQ_URL')
CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND', default='db+postgresql://{0}:{1}@{2}:{3}/{4}'.format(
env("DB_USER"),
quote_plus(env("DB_PASSWORD")),
env("DB_HOST"),
env("DB_PORT"),
env("DB_NAME")
))

CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
Expand All @@ -182,4 +189,29 @@
EMAIL_USE_TLS = True # Use False if using SSL (port 465)
EMAIL_HOST_USER = env('EMAIL_HOST_USER') # Your email address
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD') # Your email password/app password
DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER')
DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER')

# Django REST Framework Configuration
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'Home.authentication.CustomAdminAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100,
}

# JWT Configuration
from datetime import timedelta

SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=24),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
}
20 changes: 20 additions & 0 deletions ClassLens_DB/ClassLens_DB/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,29 @@
from django.conf.urls.static import static
from DatabaseAdminApp import urls as db_admin_urls
from django.urls import include
from Home.views import health
from django.views.generic import RedirectView
from django.http import JsonResponse


def api_root(request):
return JsonResponse(
{
"message": "ClassLens API",
"routes": {
"health": "/api/health/",
"departments": "/api/getDepartments/",
"admin": "/api/admin/",
},
}
)

urlpatterns = [
path("admin/", admin.site.urls),
path("health/",health,name="health_check"),
path("api", RedirectView.as_view(url="/api/", permanent=False)),
path("api/", api_root, name="api_root"),
path("api/health/", health, name="api_health"),
path("api/", include(urls)),
path("api/", include(db_admin_urls)),
]
Expand Down
36 changes: 35 additions & 1 deletion ClassLens_DB/DatabaseAdminApp/models.py
Copy link
Copy Markdown
Contributor

@VyomShah28 VyomShah28 May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use of APIPaper, APIStudentAcademicInformation and APIStudentPartTerm.... ? What are the uses of this tables and it's data for our system ?

Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
from django.db import models

# Create your models here.

class APIEnrollment(models.Model):
"""Staging table for enrollment data - matches New_schema exactly."""
prn = models.BigIntegerField()
subject_code = models.CharField(max_length=100)
division = models.CharField(max_length=20)
year = models.IntegerField()

class Meta:
unique_together = ('prn', 'subject_code', 'division', 'year')

def __str__(self):
return f"PRN {self.prn} - {self.subject_code} Div {self.division}"


class APIPaper(models.Model):
"""Staging table for paper data - mirrors MSUIS API payloads. Matches New_schema exactly."""
msuis_id = models.BigIntegerField(primary_key=True)
paper_name = models.CharField(max_length=500)
paper_code = models.CharField(max_length=100)
raw_payload = models.JSONField()

def __str__(self):
return f"{self.paper_code} - {self.paper_name}"


class APIStudent(models.Model):
"""Staging table for student data - mirrors MSUIS API payloads. Matches New_schema exactly."""
prn = models.BigIntegerField(primary_key=True)
email_id = models.CharField(max_length=255, null=True, blank=True)
raw_payload = models.JSONField()
full_name = models.CharField(max_length=255, null=True, blank=True)

def __str__(self):
return str(self.prn)
78 changes: 74 additions & 4 deletions ClassLens_DB/DatabaseAdminApp/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# serializers.py

from django.db import transaction
from rest_framework import serializers
from Home.models import (
Department, Teacher, Student, Subject, SubjectFromDept,
StudentEnrollment, TeacherSubject, AdminUser
StudentEnrollment, TeacherSubject, AdminUser, Division
)
from Home.face_utils import extract_face_embedding
from .models import (
APIStudent,
APIPaper,
APIEnrollment,
)

class DepartmentSerializer(serializers.ModelSerializer):
Expand All @@ -21,13 +28,50 @@ class Meta:

class StudentSerializer(serializers.ModelSerializer):
department_name = serializers.CharField(source='department.name', read_only=True)
division_name = serializers.CharField(source='division.name', read_only=True)
photo = serializers.ImageField(write_only=True, required=False, allow_null=True)

class Meta:
model = Student
fields = ['id', 'prn', 'name', 'email', 'password_hash', 'year', 'department',
'department_name', 'face_embedding', 'notification_token']
'department_name', 'division', 'division_name', 'face_embedding', 'notification_token', 'photo']
extra_kwargs = {'password_hash': {'write_only': True}, 'face_embedding': {'write_only': True}}

def _apply_face_photo(self, instance, photo):
if photo is None:
return

embedding = extract_face_embedding(photo)
instance.face_embedding = [float(value) for value in embedding]

def create(self, validated_data):
photo = validated_data.pop('photo', None)
try:
with transaction.atomic():
student = super().create(validated_data)

if photo is not None:
self._apply_face_photo(student, photo)
student.save(update_fields=['face_embedding'])

return student
except ValueError as exc:
raise serializers.ValidationError({'photo': str(exc)}) from exc

def update(self, instance, validated_data):
photo = validated_data.pop('photo', None)
try:
with transaction.atomic():
student = super().update(instance, validated_data)

if photo is not None:
self._apply_face_photo(student, photo)
student.save(update_fields=['face_embedding'])

return student
except ValueError as exc:
raise serializers.ValidationError({'photo': str(exc)}) from exc

class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
Expand Down Expand Up @@ -56,10 +100,11 @@ class Meta:
class TeacherSubjectSerializer(serializers.ModelSerializer):
teacher_name = serializers.CharField(source='teacher_id.name', read_only=True)
subject_name = serializers.CharField(source='subject.name', read_only=True)
division_name = serializers.CharField(source='division.name', read_only=True)

class Meta:
model = TeacherSubject
fields = ['id', 'teacher_id', 'teacher_name', 'subject', 'subject_name']
fields = ['id', 'teacher_id', 'teacher_name', 'subject', 'subject_name', 'division', 'division_name']

# class AdminUserSerializer(serializers.ModelSerializer):
# password = serializers.CharField(write_only=True)
Expand Down Expand Up @@ -93,4 +138,29 @@ def create(self, validated_data):
)
user.set_password(validated_data['password'])
user.save()
return user
return user


class APIStudentSerializer(serializers.ModelSerializer):
class Meta:
model = APIStudent
fields = "__all__"


class APIPaperSerializer(serializers.ModelSerializer):
class Meta:
model = APIPaper
fields = "__all__"


class APIEnrollmentSerializer(serializers.ModelSerializer):
class Meta:
model = APIEnrollment
fields = "__all__"


class DivisionSerializer(serializers.ModelSerializer):
class Meta:
from Home.models import Division
model = Division
fields = ['id', 'department', 'year', 'name']
1 change: 1 addition & 0 deletions ClassLens_DB/DatabaseAdminApp/settings.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the vercel url in the env and then access it from there, don't hardcore the url in the code

Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
"https://classlensfrontend.vercel.app"
]
Loading