Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions FusionIIIT/Fusion/settings/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Test settings: build the test schema directly from the current models instead
# of replaying historical migrations. Several legacy migrations reference tables
# via raw SQL before those tables exist, so they cannot run against an empty
# database (the dev DB is loaded from a prod dump, not migrated from zero).
from Fusion.settings.development import * # noqa: F401,F403


class _DisableMigrations:
def __contains__(self, item):
return True

def __getitem__(self, item):
return None


MIGRATION_MODULES = _DisableMigrations()
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import base64
import pandas as pd
import openpyxl
import random
Expand All @@ -7,6 +8,7 @@
import sys
import os
from io import BytesIO
from django.core.files.base import ContentFile
from datetime import datetime, date
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
Expand Down Expand Up @@ -43,6 +45,31 @@
from django.contrib.auth.models import User
pass

def _decode_base64_image(data_url, name_prefix="image", max_kb=None):
"""
Convert a base64 data URL (data:image/png;base64,...) to a ContentFile for an
ImageField. Returns None for empty/invalid input, an already-stored path (so
re-saving an unchanged edit form leaves the existing image untouched), a
non-PNG/JPG type, or a payload larger than max_kb (server-side size guard).
"""
if not data_url or not isinstance(data_url, str) or ";base64," not in data_url:
return None
header, encoded = data_url.split(";base64,", 1)
try:
raw = base64.b64decode(encoded)
except Exception:
return None
if max_kb is not None and len(raw) > max_kb * 1024:
return None
ext = "png"
if "/" in header:
ext = (header.split("/")[-1].split(";")[0] or "png").lower()
if ext == "jpeg":
ext = "jpg"
if ext not in ("png", "jpg"):
return None
return ContentFile(raw, name="{}.{}".format(name_prefix, ext))

def parse_request_data(request, field_mappings=None):
"""
Helper function to parse request data from various formats with field mapping
Expand Down Expand Up @@ -1315,8 +1342,13 @@ def add_single_student(request):
dob = parse_date_flexible(data.get('date_of_birth'))

# Build shared kwargs used by both PhD and UG/PG models
_roll_for_file = str(student_data.get('roll_number') or data.get('rollNumber') or data.get('roll_number') or 'student')
_shared_kwargs = dict(
name=student_data.get('name') or data.get('name', ''),
hindi_name=data.get('hindi_name', '') or data.get('hindiName', ''),
aadhar_number=(data.get('aadhar_number') or data.get('aadharNumber') or data.get('aadharNo') or ''),
photo=_decode_base64_image(data.get('photo'), _roll_for_file + "_photo", max_kb=200),
signature=_decode_base64_image(data.get('signature'), _roll_for_file + "_sign", max_kb=30),
roll_number=student_data.get('roll_number') or data.get('rollNumber', '') or data.get('roll_number', ''),
institute_email=student_data.get('institute_email') or data.get('instituteEmail', '') or data.get('institute_email', ''),
father_name=data.get('father_name', ''),
Expand Down Expand Up @@ -3687,7 +3719,8 @@ def update_student(request, student_id):
pass

direct_fields = [
'name', 'gender', 'category', 'pwd', 'minority', 'address', 'state', 'branch', 'specialization',
'name', 'hindi_name', 'aadhar_number',
'gender', 'category', 'pwd', 'minority', 'address', 'state', 'branch', 'specialization',
'personal_email', 'parent_email', 'country', 'nationality',
'blood_group', 'blood_group_remarks', 'pwd_category', 'pwd_category_remarks',
'admission_mode', 'admission_mode_remarks', 'income_group', 'income'
Expand Down Expand Up @@ -3715,6 +3748,26 @@ def update_student(request, student_id):
except AttributeError:
pass

# Photo / signature: replace only when a new base64 image is sent; an
# unchanged edit form re-sends the existing file path, which is ignored.
for _img_field in ('photo', 'signature'):
if _img_field in data:
_suffix = 'photo' if _img_field == 'photo' else 'sign'
_decoded = _decode_base64_image(
data[_img_field],
"{}_{}".format(student.roll_number or student_id, _suffix),
max_kb=200 if _img_field == 'photo' else 30,
)
if _decoded is not None:
# Remove the previous file so replacements don't orphan on disk.
_old = getattr(student, _img_field, None)
if _old:
try:
_old.delete(save=False)
except Exception:
pass
setattr(student, _img_field, _decoded)

dob_value = data.get('dob') or data.get('dateOfBirth') or data.get('date_of_birth')
if dob_value:
try:
Expand Down Expand Up @@ -4434,6 +4487,13 @@ def get_batch_students(request, batch_id):
'mother_occupation': getattr(student, 'mother_occupation', ''),
'mother_mobile': getattr(student, 'mother_mobile', ''),
'aadhar_number': getattr(student, 'aadhar_number', ''),
'aadharNo': getattr(student, 'aadhar_number', ''),
'hindi_name': getattr(student, 'hindi_name', ''),
'hindiName': getattr(student, 'hindi_name', ''),
'photo': student.photo.url if getattr(student, 'photo', None) else '',
'signature': student.signature.url
if getattr(student, 'signature', None)
else '',

'allotted_category': getattr(student, 'allotted_category', ''),
'allotted_gender': getattr(student, 'allotted_gender', ''),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 3.1.5 on 2026-08-12 23:09

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('programme_curriculum', '0048_merge_20260805_2220'),
]

operations = [
migrations.AddField(
model_name='phdstudentbatchupload',
name='hindi_name',
field=models.CharField(blank=True, help_text='Full Name in Hindi', max_length=200, null=True),
),
migrations.AddField(
model_name='phdstudentbatchupload',
name='photo',
field=models.ImageField(blank=True, help_text='Passport photo (max 200KB)', null=True, upload_to='programme_curriculum/student_photos'),
),
migrations.AddField(
model_name='phdstudentbatchupload',
name='signature',
field=models.ImageField(blank=True, help_text='Signature image (max 30KB)', null=True, upload_to='programme_curriculum/student_signatures'),
),
migrations.AddField(
model_name='studentbatchupload',
name='hindi_name',
field=models.CharField(blank=True, help_text='Full Name in Hindi', max_length=200, null=True),
),
migrations.AddField(
model_name='studentbatchupload',
name='photo',
field=models.ImageField(blank=True, help_text='Passport photo (max 200KB)', null=True, upload_to='programme_curriculum/student_photos'),
),
migrations.AddField(
model_name='studentbatchupload',
name='signature',
field=models.ImageField(blank=True, help_text='Signature image (max 30KB)', null=True, upload_to='programme_curriculum/student_signatures'),
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ class StudentBatchUpload(models.Model):

# Personal information
name = models.CharField(max_length=200, help_text="Full Name")
hindi_name = models.CharField(max_length=200, blank=True, null=True, help_text="Full Name in Hindi")
father_name = models.CharField(max_length=200, help_text="Father's Name")
mother_name = models.CharField(max_length=200, help_text="Mother's Name")
gender = models.CharField(max_length=10, choices=GENDER_CHOICES)
Expand Down Expand Up @@ -203,7 +204,9 @@ class StudentBatchUpload(models.Model):
mother_mobile = models.CharField(max_length=15, blank=True, null=True)
parent_email = models.EmailField(blank=True, null=True, help_text="Parent Email ID")
aadhar_number = models.CharField(max_length=12, blank=True, null=True)

photo = models.ImageField(upload_to='programme_curriculum/student_photos', blank=True, null=True, help_text="Passport photo (max 200KB)")
signature = models.ImageField(upload_to='programme_curriculum/student_signatures', blank=True, null=True, help_text="Signature image (max 30KB)")

# Allotment details
allotted_category = models.CharField(max_length=50, blank=True, null=True)
allotted_gender = models.CharField(max_length=50, blank=True, null=True)
Expand Down Expand Up @@ -656,6 +659,7 @@ class PhdStudentBatchUpload(models.Model):

# ---- Personal information (col 4, 6, 7, 8, 9, 10, 11, 12) ----
name = models.CharField(max_length=200, help_text="Full Name (col: Name)")
hindi_name = models.CharField(max_length=200, blank=True, null=True, help_text="Full Name in Hindi")
discipline = models.CharField(max_length=200, help_text="Discipline / Branch (col: Discipline)")
admission_type = models.CharField(
max_length=100, choices=ADMISSION_TYPE_CHOICES, blank=True, null=True,
Expand Down Expand Up @@ -728,6 +732,8 @@ class PhdStudentBatchUpload(models.Model):
max_length=12, blank=True, null=True,
help_text="Aadhaar Number (12 digits)"
)
photo = models.ImageField(upload_to='programme_curriculum/student_photos', blank=True, null=True, help_text="Passport photo (max 200KB)")
signature = models.ImageField(upload_to='programme_curriculum/student_signatures', blank=True, null=True, help_text="Signature image (max 30KB)")

# ---- System / batch tracking fields ----
admission_semester = models.CharField(
Expand Down
Loading
Loading