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
38 changes: 25 additions & 13 deletions FusionIIIT/applications/academic_information/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ def generate_xlsheet_api(request):

# OPTIMIZATION 7: Fast Excel generation with minimal formatting
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side

wb = Workbook()
ws = wb.active
Expand All @@ -520,6 +520,12 @@ def generate_xlsheet_api(request):
# Minimal header formatting (single operation)
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
thin_border = Border(
left=Side(style="thin"),
right=Side(style="thin"),
top=Side(style="thin"),
bottom=Side(style="thin"),
)

# Add title rows efficiently
ws.merge_cells('A1:G1')
Expand All @@ -544,18 +550,18 @@ def generate_xlsheet_api(request):
if course_instructor:
instructor_name = f"{course_instructor.instructor_id.id.user.first_name} {course_instructor.instructor_id.id.user.last_name}".strip()

# Course details
ws['A3'] = "Course No:"
ws['B3'] = course_info['code']
ws['A4'] = "Course Title:"
ws.merge_cells('B4:G4')
ws['B4'] = course_info['name']
ws['A5'] = "Instructor:"
ws.merge_cells('B5:G5')
ws['B5'] = instructor_name
ws['A6'] = "List Type:"
ws.merge_cells('B6:G6')
ws['B6'] = list_type_display
# Course details merged into single full-width cells (no borders on metadata rows).
ws.merge_cells('A3:G3')
ws['A3'] = f"Course No: {course_info['code']}"
ws.merge_cells('A4:G4')
ws['A4'] = f"Course Title: {course_info['name']}"
ws.merge_cells('A5:G5')
ws['A5'] = f"Instructor: {instructor_name}"
ws.merge_cells('A6:G6')
ws['A6'] = f"List Type: {list_type_display}"

for row in range(3, 7):
ws[f'A{row}'].alignment = Alignment(horizontal="left", vertical="center")

headers = ['Sl. No', 'Roll No', 'Name', 'Discipline', 'Email', 'Reg. Type', 'Signature']
for col, header in enumerate(headers, 1):
Expand All @@ -576,6 +582,12 @@ def generate_xlsheet_api(request):
]
ws.append(row_data)

# Add borders only to the student list table section (header + data rows).
last_table_row = ws.max_row
for row in range(8, last_table_row + 1):
for col in range(1, 8):
ws.cell(row=row, column=col).border = thin_border

from io import BytesIO
output = BytesIO()
wb.save(output)
Expand Down
35 changes: 33 additions & 2 deletions FusionIIIT/applications/examination/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,8 @@ def post(self, request):

# Define a fill style for header cells: light grey background.
header_fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
zero_spi_fill = PatternFill(start_color="B30016", end_color="B30016", fill_type="solid")
low_spi_fill = PatternFill(start_color="F8F40F", end_color="F8F40F", fill_type="solid")
thin_border = Border(
left=Side(style="thin"), right=Side(style="thin"),
top=Side(style="thin"), bottom=Side(style="thin")
Expand Down Expand Up @@ -1415,13 +1417,19 @@ def post(self, request):
cell.font = Font(bold=True)
cell.fill = header_fill

# Ensure full header rows (1 to 4) are highlighted.
cell = ws.cell(row=1, column=col_idx+6)
cell.value = "WARNING"
cell.alignment = Alignment(horizontal="center", vertical="center")
cell.font = Font(bold=True)
cell.fill = header_fill

max_col = ws.max_column
for row in range(1, 5):
for col in range(1, max_col + 1):
cell = ws.cell(row=row, column=col)
cell.fill = header_fill
cell.border = thin_border
total_columns = ws.max_column

# Fill in student rows, starting from row 5.
row_idx = 5
Expand Down Expand Up @@ -1501,10 +1509,33 @@ def post(self, request):
ws.cell(row=row_idx, column=col_ptr+3).value = TU
ws.cell(row=row_idx, column=col_ptr+4).value = SP
ws.cell(row=row_idx, column=col_ptr+5).value = TP
for c in [col_ptr, col_ptr+1]:
ws.cell(row=row_idx, column=col_ptr+6).value = ""
for c in [col_ptr, col_ptr+1, col_ptr+2, col_ptr+3, col_ptr+4, col_ptr+5, col_ptr+6]:
ws.cell(row=row_idx, column=c).alignment = Alignment(horizontal="center", vertical="center")

# Highlight rows based on SPI and write warning in dedicated warning column.
try:
spi_numeric = float(spi_val)
except (TypeError, ValueError):
spi_numeric = None

if spi_numeric is not None and spi_numeric == 0:
for c in range(1, total_columns + 1):
ws.cell(row=row_idx, column=c).fill = zero_spi_fill
ws.cell(row=row_idx, column=col_ptr+6).value = ""
elif spi_numeric is not None and spi_numeric < 5:
for c in range(1, total_columns + 1):
ws.cell(row=row_idx, column=c).fill = low_spi_fill
ws.cell(row=row_idx, column=col_ptr+6).value = "WARNING"

row_idx += 1

# Apply borders on all populated cells (headers + student rows).
last_data_row = row_idx - 1
for row in range(1, last_data_row + 1):
for col in range(1, total_columns + 1):
ws.cell(row=row, column=col).border = thin_border

response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename="student_grades.xlsx"'
wb.save(response)
Expand Down
1 change: 1 addition & 0 deletions FusionIIIT/applications/programme_curriculum/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
path('admin_list_students/', views_student_management.list_students, name='admin_list_students'),

# Individual student CRUD
path('student_my_info/', views_student_management.student_my_info, name='student_my_info'),
path('student/<int:student_id>/', views_student_management.get_student, name='get_student'),
path('student/<int:student_id>/update/', views_student_management.update_student, name='update_student'),
path('student/<int:student_id>/delete/', views_student_management.delete_student, name='delete_student'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
from django.db.models import Q, Count
from django.conf import settings
from django.utils import timezone
from rest_framework.decorators import api_view, permission_classes
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework.response import Response
from rest_framework import status

Expand Down Expand Up @@ -5131,3 +5132,82 @@ def ensure_default_curriculum_exists(discipline_obj, programme_name):
# Log the error but don't raise it to avoid breaking the student reporting process
pass
return None

@api_view(['GET'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def student_my_info(request):
"""
Returns the authenticated student's programme type and linked curriculum IDs.
"""
try:
acad_student = (
AcademicStudent.objects
.select_related('batch_id__curriculum__programme', 'id__department')
.get(id__user=request.user)
)
except AcademicStudent.DoesNotExist:
return Response(
{'error': 'No student record found for this user. Contact the academic office.'},
status=status.HTTP_400_BAD_REQUEST,
)

programme_category = None
curriculum_ids = []

if acad_student.batch_id and acad_student.batch_id.curriculum_id:
curriculum_ids = [acad_student.batch_id.curriculum_id]
programme_category = acad_student.batch_id.curriculum.programme.category

if not programme_category:
programme_category = (
Programme.objects
.filter(name=acad_student.programme)
.values_list('category', flat=True)
.first()
)

if not programme_category:
return Response(
{
'error': (
'Programme type could not be determined. '
'Your batch or curriculum may not be properly linked. '
'Contact the academic office.'
)
},
status=status.HTTP_400_BAD_REQUEST,
)

if not acad_student.batch:
return Response(
{'error': 'Batch year is not set on your student record. Contact the academic office.'},
status=status.HTTP_400_BAD_REQUEST,
)

branch = (
acad_student.id.department.name
if acad_student.id_id and acad_student.id.department
else None
)
if not branch:
return Response(
{'error': 'Department/branch is not assigned to your student record. Contact the academic office.'},
status=status.HTTP_400_BAD_REQUEST,
)

if not curriculum_ids:
return Response(
{'error': 'No curriculum is linked to your batch yet. Contact the academic office.'},
status=status.HTTP_400_BAD_REQUEST,
)

return Response(
{
'programme_type': programme_category.lower(),
'year': acad_student.batch,
'branch': branch,
'curriculum_ids': curriculum_ids,
},
status=status.HTTP_200_OK,
)
Loading