From 38ba63e9be99e13776286ced9a1655b1b91f4a9e Mon Sep 17 00:00:00 2001 From: ramG-reddy Date: Sun, 20 Oct 2024 11:37:22 +0530 Subject: [PATCH 01/44] fix: Resolved System check errors on latest_staging --- .../applications/central_mess/models.py | 2 +- FusionIIIT/applications/globals/models.py | 4 ++-- .../applications/hostel_management/urls.py | 2 +- FusionIIIT/applications/hr2/models.py | 20 ++++++++-------- FusionIIIT/applications/iwdModuleV2/models.py | 24 +++++++++---------- FusionIIIT/applications/iwdModuleV2/views.py | 6 ++--- .../research_procedures/models.py | 2 +- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/FusionIIIT/applications/central_mess/models.py b/FusionIIIT/applications/central_mess/models.py index ae96e8f82..3e6030952 100644 --- a/FusionIIIT/applications/central_mess/models.py +++ b/FusionIIIT/applications/central_mess/models.py @@ -154,7 +154,7 @@ class Payments(models.Model): amount_paid = models.IntegerField(default=0) payment_month = models.CharField(max_length=20, default=current_month) payment_year = models.IntegerField(default = current_year) - payment_date = models.DateField(default= datetime.date.today()) + payment_date = models.DateField(default= datetime.date.today) # class Meta: # unique_together = (('student_id', 'payment_date')) diff --git a/FusionIIIT/applications/globals/models.py b/FusionIIIT/applications/globals/models.py index bc95a3042..c4603a457 100644 --- a/FusionIIIT/applications/globals/models.py +++ b/FusionIIIT/applications/globals/models.py @@ -65,10 +65,10 @@ class Constants: ('academic', 'Academic Designation'), ('administrative', 'Administrative Designation'), ) - USER_STATUS = { + USER_STATUS = ( ("NEW", "NEW"), ("PRESENT", "PRESENT"), - } + ) class Designation(models.Model): diff --git a/FusionIIIT/applications/hostel_management/urls.py b/FusionIIIT/applications/hostel_management/urls.py index 95fcdeeb8..28c1915c2 100644 --- a/FusionIIIT/applications/hostel_management/urls.py +++ b/FusionIIIT/applications/hostel_management/urls.py @@ -12,7 +12,7 @@ path('admin/', admin.site.urls), #Home path('', views.hostel_view, name="hostel_view"), - path('/hello', views.hostel_view, name="hello"), + path('hello', views.hostel_view, name="hello"), #Notice Board path('notice_form/', views.notice_board, name="notice_board"), diff --git a/FusionIIIT/applications/hr2/models.py b/FusionIIIT/applications/hr2/models.py index 01ca223c7..e225ca076 100644 --- a/FusionIIIT/applications/hr2/models.py +++ b/FusionIIIT/applications/hr2/models.py @@ -78,7 +78,7 @@ class EmpConfidentialDetails(models.Model): table for employee confidential details """ extra_info = models.OneToOneField(ExtraInfo, on_delete=models.CASCADE) - aadhar_no = models.BigIntegerField(default=0, max_length=12, + aadhar_no = models.BigIntegerField(default=0, validators=[MaxValueValidator(999999999999),MinValueValidator(99999999999)]) maritial_status = models.CharField( @@ -149,7 +149,7 @@ class LTCform(models.Model): employeeId = models.IntegerField() name = models.CharField(max_length=100, null=True) blockYear = models.TextField() # - pfNo = models.IntegerField(max_length=50) + pfNo = models.IntegerField() basicPaySalary = models.IntegerField(null=True) designation = models.CharField(max_length=50) departmentInfo = models.CharField(max_length=50) @@ -181,12 +181,12 @@ class LTCform(models.Model): class CPDAAdvanceform(models.Model): id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(max_length=22, null=True) + employeeId = models.IntegerField(null=True) name = models.CharField(max_length=40,null=True) designation = models.CharField(max_length=40,null=True) - pfNo = models.IntegerField(max_length=30,null=True) + pfNo = models.IntegerField(null=True) purpose = models.TextField(max_length=40, null=True) - amountRequired = models.IntegerField(max_length=30,null=True) + amountRequired = models.IntegerField(null=True) advanceDueAdjustment = models.DecimalField(max_digits=10, decimal_places=2, null=True,blank=True) submissionDate = models.DateField(blank=True, null=True) @@ -202,11 +202,11 @@ class CPDAAdvanceform(models.Model): class LeaveForm(models.Model): id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(max_length=22,null=True) + employeeId = models.IntegerField(null=True) name = models.CharField(max_length=40,null=True) designation = models.CharField(max_length=40,null=True) submissionDate = models.DateField(blank=True, null=True) - pfNo = models.IntegerField(max_length=30,null=True) + pfNo = models.IntegerField(null=True) departmentInfo = models.CharField(max_length=40,null=True) natureOfLeave = models.TextField(max_length=40,null=True) leaveStartDate = models.DateField(blank=True, null=True) @@ -237,7 +237,7 @@ class LeaveBalance(models.Model): class Appraisalform(models.Model): id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(max_length=22,null=True) + employeeId = models.IntegerField(null=True) name = models.CharField(max_length=22) designation = models.CharField(max_length=50) disciplineInfo = models.CharField(max_length=22, null=True) @@ -273,10 +273,10 @@ class Appraisalform(models.Model): class CPDAReimbursementform(models.Model): id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(max_length=22,null=True) + employeeId = models.IntegerField(null=True) name = models.CharField(max_length=50) designation = models.CharField(max_length=50) - pfNo = models.IntegerField(max_length=20) + pfNo = models.IntegerField() advanceTaken = models.IntegerField() purpose = models.TextField() adjustmentSubmitted = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) diff --git a/FusionIIIT/applications/iwdModuleV2/models.py b/FusionIIIT/applications/iwdModuleV2/models.py index 9a3337987..9c79aa21c 100644 --- a/FusionIIIT/applications/iwdModuleV2/models.py +++ b/FusionIIIT/applications/iwdModuleV2/models.py @@ -9,7 +9,7 @@ class Projects(models.Model): class PageOneDetails(models.Model): - id = models.ForeignKey(Projects, on_delete=models.CASCADE, primary_key=True) + page_id = models.OneToOneField(Projects, on_delete=models.CASCADE, null=True) aESFile = models.FileField(null=True) dASA = models.DateField(null=True) nitNiqNo = models.IntegerField(null=True) @@ -31,7 +31,7 @@ class AESDetails(models.Model): class PageTwoDetails(models.Model): - id = models.ForeignKey(Projects, on_delete=models.CASCADE, primary_key=True) + page_id = models.OneToOneField(Projects, on_delete=models.CASCADE, null=True) corrigendum = models.FileField(null=True) addendum = models.FileField(null=True) preBidMeetingDetails = models.FileField(null=True) @@ -46,7 +46,7 @@ class PageTwoDetails(models.Model): class CorrigendumTable(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) issueDate = models.DateField() nitNo = models.IntegerField() name = models.CharField(max_length=200) @@ -59,7 +59,7 @@ class CorrigendumTable(models.Model): class Addendum(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) issueDate = models.DateField() nitNiqNo = models.IntegerField() name = models.CharField(max_length=200) @@ -68,7 +68,7 @@ class Addendum(models.Model): class PreBidDetails(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) sNo = models.CharField(max_length=200) nameOfParticipants = models.CharField(max_length=200) issuesRaised = models.CharField(max_length=200) @@ -76,7 +76,7 @@ class PreBidDetails(models.Model): class TechnicalBidDetails(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) sNo = models.CharField(max_length=200) requirements = models.CharField(max_length=200) @@ -88,7 +88,7 @@ class TechnicalBidContractorDetails(models.Model): class FinancialBidDetails(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) sNo = models.CharField(max_length=200) description = models.CharField(max_length=200) @@ -103,7 +103,7 @@ class FinancialContractorDetails(models.Model): class LetterOfIntentDetails(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) nitNiqNo = models.IntegerField() dateOfOpening = models.DateField() agency = models.CharField(max_length=200) @@ -112,7 +112,7 @@ class LetterOfIntentDetails(models.Model): class WorkOrderForm(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) issueDate = models.DateField() nitNiqNo = models.IntegerField() agency = models.CharField(max_length=200) @@ -127,7 +127,7 @@ class WorkOrderForm(models.Model): class Agreement(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) date = models.DateField() agencyName = models.CharField(max_length=200) workName = models.CharField(max_length=200) @@ -143,7 +143,7 @@ class Milestones(models.Model): class PageThreeDetails(models.Model): - id = models.ForeignKey(Projects, on_delete=models.CASCADE, primary_key=True) + page_id = models.OneToOneField(Projects, on_delete=models.CASCADE, null=True) extensionOfTime = models.FileField() actualCostOfBuilding = models.IntegerField() @@ -157,7 +157,7 @@ class ExtensionOfTimeDetails(models.Model): class NoOfTechnicalBidTimes(models.Model): - key = models.ForeignKey(Projects, on_delete=models.CASCADE, unique=True) + key = models.OneToOneField(Projects, on_delete=models.CASCADE) number = models.IntegerField() class Requests(models.Model): diff --git a/FusionIIIT/applications/iwdModuleV2/views.py b/FusionIIIT/applications/iwdModuleV2/views.py index 1f999ebd9..be7042499 100644 --- a/FusionIIIT/applications/iwdModuleV2/views.py +++ b/FusionIIIT/applications/iwdModuleV2/views.py @@ -360,13 +360,13 @@ def page1View(request): if request.POST: request.session['projectId'] = request.POST['id'] projectPageOne = PageOneDetails.objects.get( - id=Projects.objects.get(id=request.session['projectId'])) + id=Projects.objects.get(page_id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page1.html', {'x': projectPageOne}) def page2View(request): projectPageTwo = PageTwoDetails.objects.get( - id=Projects.objects.get(id=request.session['projectId'])) + id=Projects.objects.get(page_id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page2.html', {'x': projectPageTwo}) @@ -445,7 +445,7 @@ def milestoneView(request): def page3View(request): pageThreeDetails = PageThreeDetails.objects.get( - id=Projects.objects.get(id=request.session['projectId'])) + id=Projects.objects.get(page_id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page3.html', {'x': pageThreeDetails}) diff --git a/FusionIIIT/applications/research_procedures/models.py b/FusionIIIT/applications/research_procedures/models.py index a02aad5e3..3a653e374 100644 --- a/FusionIIIT/applications/research_procedures/models.py +++ b/FusionIIIT/applications/research_procedures/models.py @@ -77,7 +77,7 @@ class staff_allocations(models.Model): year=models.IntegerField() stipend=models.IntegerField() staff_type=models.CharField(max_length=100,default="research") - start_date=models.DateField(default=datetime.date.today()) #default=datetime.date.today() + start_date=models.DateField(default=datetime.date.today) end_date=models.DateField(null=True, blank=True) def __str__(self): From f4e062cc58f8069b43e853a2eff3c463e3f4dcdb Mon Sep 17 00:00:00 2001 From: ramG-reddy Date: Sun, 20 Oct 2024 11:41:37 +0530 Subject: [PATCH 02/44] chore: migrations for previous commit changes --- .../migrations/0002_auto_20241020_1126.py | 23 +++++ .../globals/migrations/0002_moduleaccess.py | 39 ++++++++ .../hr2/migrations/0002_auto_20241020_1126.py | 64 +++++++++++++ .../migrations/0002_auto_20241020_1126.py | 89 +++++++++++++++++++ .../migrations/0002_auto_20241020_1126.py | 40 +++++++++ .../migrations/0002_auto_20241020_1126.py | 19 ++++ 6 files changed, 274 insertions(+) create mode 100644 FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py create mode 100644 FusionIIIT/applications/globals/migrations/0002_moduleaccess.py create mode 100644 FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py create mode 100644 FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py create mode 100644 FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py create mode 100644 FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py diff --git a/FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py new file mode 100644 index 000000000..fa5bb79af --- /dev/null +++ b/FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1.5 on 2024-10-20 11:26 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('central_mess', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='payments', + name='payment_date', + field=models.DateField(default=datetime.date.today), + ), + migrations.AlterUniqueTogether( + name='payments', + unique_together=set(), + ), + ] diff --git a/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py b/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py new file mode 100644 index 000000000..75fd77a4f --- /dev/null +++ b/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py @@ -0,0 +1,39 @@ +# Generated by Django 3.1.5 on 2024-10-20 11:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('globals', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='ModuleAccess', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('designation', models.CharField(max_length=155)), + ('program_and_curriculum', models.BooleanField(default=False)), + ('course_registration', models.BooleanField(default=False)), + ('course_management', models.BooleanField(default=False)), + ('other_academics', models.BooleanField(default=False)), + ('spacs', models.BooleanField(default=False)), + ('department', models.BooleanField(default=False)), + ('examinations', models.BooleanField(default=False)), + ('hr', models.BooleanField(default=False)), + ('iwd', models.BooleanField(default=False)), + ('complaint_management', models.BooleanField(default=False)), + ('fts', models.BooleanField(default=False)), + ('purchase_and_store', models.BooleanField(default=False)), + ('rspc', models.BooleanField(default=False)), + ('hostel_management', models.BooleanField(default=False)), + ('mess_management', models.BooleanField(default=False)), + ('gymkhana', models.BooleanField(default=False)), + ('placement_cell', models.BooleanField(default=False)), + ('visitor_hostel', models.BooleanField(default=False)), + ('phc', models.BooleanField(default=False)), + ], + ), + ] diff --git a/FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py new file mode 100644 index 000000000..5b99015f7 --- /dev/null +++ b/FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py @@ -0,0 +1,64 @@ +# Generated by Django 3.1.5 on 2024-10-20 11:26 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hr2', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='appraisalform', + name='employeeId', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='cpdaadvanceform', + name='amountRequired', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='cpdaadvanceform', + name='employeeId', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='cpdaadvanceform', + name='pfNo', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='cpdareimbursementform', + name='employeeId', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='cpdareimbursementform', + name='pfNo', + field=models.IntegerField(), + ), + migrations.AlterField( + model_name='empconfidentialdetails', + name='aadhar_no', + field=models.BigIntegerField(default=0, validators=[django.core.validators.MaxValueValidator(999999999999), django.core.validators.MinValueValidator(99999999999)]), + ), + migrations.AlterField( + model_name='leaveform', + name='employeeId', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='leaveform', + name='pfNo', + field=models.IntegerField(null=True), + ), + migrations.AlterField( + model_name='ltcform', + name='pfNo', + field=models.IntegerField(), + ), + ] diff --git a/FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py new file mode 100644 index 000000000..cbadd4cd6 --- /dev/null +++ b/FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py @@ -0,0 +1,89 @@ +# Generated by Django 3.1.5 on 2024-10-20 11:26 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('iwdModuleV2', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='pageonedetails', + name='page_id', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AddField( + model_name='pagethreedetails', + name='page_id', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AddField( + model_name='pagetwodetails', + name='page_id', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='addendum', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='agreement', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='corrigendumtable', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='financialbiddetails', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='letterofintentdetails', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='nooftechnicalbidtimes', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='pageonedetails', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='pagethreedetails', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='pagetwodetails', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='prebiddetails', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='technicalbiddetails', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + migrations.AlterField( + model_name='workorderform', + name='key', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), + ), + ] diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py new file mode 100644 index 000000000..2e166954c --- /dev/null +++ b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py @@ -0,0 +1,40 @@ +# Generated by Django 3.1.5 on 2024-10-20 11:26 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('academic_information', '0001_initial'), + ('online_cms', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='attendance', + name='no_of_attendance', + field=models.IntegerField(default=1), + ), + migrations.AlterField( + model_name='attendance', + name='present', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='gradingscheme', + name='type_of_evaluation', + field=models.CharField(max_length=100), + ), + migrations.CreateModel( + name='StudentEvaluation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('marks', models.DecimalField(decimal_places=2, max_digits=10, null=True)), + ('total_marks', models.DecimalField(decimal_places=2, default=0, max_digits=10)), + ('evaluation_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_cms.gradingscheme')), + ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), + ], + ), + ] diff --git a/FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py new file mode 100644 index 000000000..e94863c75 --- /dev/null +++ b/FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.5 on 2024-10-20 11:26 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('research_procedures', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='staff_allocations', + name='start_date', + field=models.DateField(default=datetime.date.today), + ), + ] From 014a47d5b7fe4d4f1e4bac2ef557b6859849d8df Mon Sep 17 00:00:00 2001 From: grvup Date: Sun, 20 Oct 2024 11:46:16 +0530 Subject: [PATCH 03/44] Required changes in forms.py --- .../programme_curriculum/forms.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/FusionIIIT/applications/programme_curriculum/forms.py b/FusionIIIT/applications/programme_curriculum/forms.py index 394828004..d6c8a53e0 100644 --- a/FusionIIIT/applications/programme_curriculum/forms.py +++ b/FusionIIIT/applications/programme_curriculum/forms.py @@ -8,7 +8,7 @@ from django.contrib.auth.models import User from applications.globals.models import (DepartmentInfo, Designation,ExtraInfo, Faculty, HoldsDesignation) from applications.filetracking.sdk.methods import * - +from django.db.models import Q class ProgrammeForm(ModelForm): class Meta: @@ -38,6 +38,20 @@ class Meta: 'programmes': 'Link Programmes to this Disciplines', 'acronym' : 'Enter Acronym' } + def __init__(self, *args, **kwargs): + super(DisciplineForm, self).__init__(*args, **kwargs) + + # Get the current discipline instance + discipline = kwargs.get('instance', None) + + if discipline: + # Show programmes that are either unlinked or linked to the current discipline + self.fields['programmes'].queryset = Programme.objects.filter( + Q(discipline__isnull=True) | Q(discipline=discipline) + ) + else: + # Show only programmes that are unlinked (no discipline assigned) + self.fields['programmes'].queryset = Programme.objects.filter(discipline__isnull=True) class CurriculumForm(ModelForm): @@ -203,6 +217,18 @@ class Meta: 'year' : 'Batch Year', 'curriculum' : 'Select Curriculum For Batch Students', } + def __init__(self, *args, **kwargs): + super(BatchForm, self).__init__(*args, **kwargs) + + # Get the list of curriculum ids that are already assigned to batches (excluding NULL values) + assigned_curriculum_ids = Batch.objects.filter(curriculum__isnull=False).values_list('curriculum', flat=True) + + # Exclude curriculums already in use + available_curriculums = Curriculum.objects.exclude(id__in=assigned_curriculum_ids) + + # Add an empty option (blank choice) at the start of the curriculum choices + self.fields['curriculum'].queryset = available_curriculums + self.fields['curriculum'].empty_label = "Select Curriculum" # This adds a blank option with a label class CourseSlotForm(ModelForm): From 415d2fd32e61565b4417ec5c258e1cceb98f1c73 Mon Sep 17 00:00:00 2001 From: Darpan Mehta Date: Sun, 20 Oct 2024 11:48:41 +0530 Subject: [PATCH 04/44] Required changes in views and template files --- .../programme_curriculum/views.py | 30 ++++++++------ .../admin_view_semesters_of_a_curriculum.html | 40 ++++++++++++++----- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/FusionIIIT/applications/programme_curriculum/views.py b/FusionIIIT/applications/programme_curriculum/views.py index 4ea51e609..602bbc866 100644 --- a/FusionIIIT/applications/programme_curriculum/views.py +++ b/FusionIIIT/applications/programme_curriculum/views.py @@ -225,6 +225,7 @@ def view_a_courseslot(request, courseslot_id): elif 'hod' in request.session['currentDesignationSelected'].lower(): url+='faculty/' course_slot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) + notifs = request.user.notifications.all() return render(request, url+'view_a_courseslot.html', {'course_slot': course_slot,'notifications': notifs,}) @@ -438,7 +439,7 @@ def admin_view_semesters_of_a_curriculum(request, curriculum_id): transpose_semester_slots = list(zip(*semester_slots)) - all_batches = Batch.objects.filter(running_batch=True).exclude(curriculum=curriculum_id).order_by('year') + all_batches = Batch.objects.filter(running_batch=True, curriculum__isnull=True).order_by('year') return render(request, 'programme_curriculum/acad_admin/admin_view_semesters_of_a_curriculum.html', {'curriculum': curriculum, 'semesters': semesters, 'semester_slots': transpose_semester_slots, 'semester_credits': semester_credits, 'all_batches':all_batches}) @@ -924,29 +925,32 @@ def delete_courseslot(request, courseslot_id): return render(request, 'programme_curriculum/view_a_courseslot.html', {'course_slot': courseslot}) +# views.py def add_batch_form(request): - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + user_details = ExtraInfo.objects.get(user=request.user) + des = HoldsDesignation.objects.all().filter(user=request.user).first() + + if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) == "acadadmin" : + elif str(request.user) == "acadadmin": pass elif 'hod' in request.session['currentDesignationSelected'].lower(): return HttpResponseRedirect('/programme_curriculum/programmes/') - - curriculum_id = request.GET.get('curriculum_id', -1) - form = BatchForm(initial={'curriculum': curriculum_id}) - submitbutton= request.POST.get('Submit') + + # Explicitly setting curriculum to None or '' to prevent any default value + form = BatchForm(initial={'curriculum': None}) + + submitbutton = request.POST.get('Submit') if submitbutton: if request.method == 'POST': form = BatchForm(request.POST) if form.is_valid(): form.save() - messages.success(request, "Added Batch successful") + messages.success(request, "Added Batch successfully") return HttpResponseRedirect('/programme_curriculum/admin_batches/') - return render(request, 'programme_curriculum/acad_admin/add_batch_form.html',{'form':form, 'submitbutton': submitbutton}) - + + return render(request, 'programme_curriculum/acad_admin/add_batch_form.html', {'form': form, 'submitbutton': submitbutton}) + def edit_batch_form(request, batch_id): diff --git a/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_semesters_of_a_curriculum.html b/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_semesters_of_a_curriculum.html index 35f30d2d7..e41901709 100644 --- a/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_semesters_of_a_curriculum.html +++ b/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_semesters_of_a_curriculum.html @@ -25,7 +25,7 @@

{{ curriculum }}

Batches:     {% for batch in curriculum.batches.all %} - {{ batch }},     + {{ batch }} {% endfor %}

@@ -189,14 +189,30 @@

- -
NEW BATCH
- -
+
+ {% if curriculum.batches.count < 1 %} + + +
NEW BATCH
+ +
+ {% else %} + + +
BATCH ALREADY ATTACHED
+ +
+ {% endif %} +
+
@@ -228,6 +244,7 @@

+ {% if curriculum.batches.count < 1 %} + {% else %} + + {% endif %} From 7a9d5f092b08119bf2027283d8ccf0bb71f71645 Mon Sep 17 00:00:00 2001 From: grvup Date: Sun, 20 Oct 2024 12:44:40 +0530 Subject: [PATCH 05/44] changed files in templates --- .../faculty/view_semesters_of_a_curriculum.html | 2 +- .../programme_curriculum/view_semesters_of_a_curriculum.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FusionIIIT/templates/programme_curriculum/faculty/view_semesters_of_a_curriculum.html b/FusionIIIT/templates/programme_curriculum/faculty/view_semesters_of_a_curriculum.html index 2b8f7e5dc..78ab0b10b 100644 --- a/FusionIIIT/templates/programme_curriculum/faculty/view_semesters_of_a_curriculum.html +++ b/FusionIIIT/templates/programme_curriculum/faculty/view_semesters_of_a_curriculum.html @@ -25,7 +25,7 @@

{{ curriculum }}

Batches:     {% for batch in curriculum.batches.all %} - {{ batch }},     + {{ batch }} {% endfor %}

diff --git a/FusionIIIT/templates/programme_curriculum/view_semesters_of_a_curriculum.html b/FusionIIIT/templates/programme_curriculum/view_semesters_of_a_curriculum.html index f6e3bb580..37943388b 100644 --- a/FusionIIIT/templates/programme_curriculum/view_semesters_of_a_curriculum.html +++ b/FusionIIIT/templates/programme_curriculum/view_semesters_of_a_curriculum.html @@ -25,7 +25,7 @@

{{ curriculum }}

Batches:     {% for batch in curriculum.batches.all %} - {{ batch }},     + {{ batch }} {% endfor %}

From 891ac5c179f853334438a02a2b80f74d486b3eae Mon Sep 17 00:00:00 2001 From: Arghadeep Bosu Date: Mon, 21 Oct 2024 17:16:20 +0530 Subject: [PATCH 06/44] Changed Template Files: Co authored by Abhyuday Singh and Arghadeep Bosu --- .../examination/announcement_req.html | 4 +- .../examination/generate_transcript.html | 4 +- .../examination/generate_transcript_form.html | 4 +- .../generate_transcript_students.html | 4 +- .../examination/gradeSubmission.html | 139 +++++++++++----- .../examination/gradeSubmissionForm.html | 6 +- FusionIIIT/templates/examination/message.html | 4 +- .../templates/examination/messageProf.html | 70 ++++++++ FusionIIIT/templates/examination/submit.html | 4 +- .../templates/examination/submitGrade.html | 4 +- .../examination/submitGradesProf.html | 152 ++++++++++++++++++ .../examination/updateEntergrades.html | 13 +- 12 files changed, 345 insertions(+), 63 deletions(-) create mode 100644 FusionIIIT/templates/examination/messageProf.html create mode 100644 FusionIIIT/templates/examination/submitGradesProf.html diff --git a/FusionIIIT/templates/examination/announcement_req.html b/FusionIIIT/templates/examination/announcement_req.html index 99bb51e9c..d8482bf62 100644 --- a/FusionIIIT/templates/examination/announcement_req.html +++ b/FusionIIIT/templates/examination/announcement_req.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} diff --git a/FusionIIIT/templates/examination/generate_transcript.html b/FusionIIIT/templates/examination/generate_transcript.html index 6b1cf4a9e..58e012b06 100644 --- a/FusionIIIT/templates/examination/generate_transcript.html +++ b/FusionIIIT/templates/examination/generate_transcript.html @@ -54,9 +54,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/generate_transcript_form.html b/FusionIIIT/templates/examination/generate_transcript_form.html index e0bd04fae..229bd9331 100644 --- a/FusionIIIT/templates/examination/generate_transcript_form.html +++ b/FusionIIIT/templates/examination/generate_transcript_form.html @@ -8,9 +8,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/generate_transcript_students.html b/FusionIIIT/templates/examination/generate_transcript_students.html index 6bc1edd66..f7beb443c 100644 --- a/FusionIIIT/templates/examination/generate_transcript_students.html +++ b/FusionIIIT/templates/examination/generate_transcript_students.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/gradeSubmission.html b/FusionIIIT/templates/examination/gradeSubmission.html index c79b41b7f..6f163f4a5 100644 --- a/FusionIIIT/templates/examination/gradeSubmission.html +++ b/FusionIIIT/templates/examination/gradeSubmission.html @@ -2,17 +2,15 @@ {% block sidetabmenu %} @@ -50,43 +48,106 @@

Submit Results

+ {% comment %}
+
+ +
+ +
{% endcomment %} +
+ + +
- - -
+ + + - - - + -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/gradeSubmissionForm.html b/FusionIIIT/templates/examination/gradeSubmissionForm.html index 6a77bc472..7de9e2788 100644 --- a/FusionIIIT/templates/examination/gradeSubmissionForm.html +++ b/FusionIIIT/templates/examination/gradeSubmissionForm.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement @@ -223,4 +223,4 @@

SUBMIT STUDENT MARKS

window.location.href = "{% url 'examination:updateGrades' %}"; } -{% endblock %} \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/message.html b/FusionIIIT/templates/examination/message.html index 73ca5caa4..6df482710 100644 --- a/FusionIIIT/templates/examination/message.html +++ b/FusionIIIT/templates/examination/message.html @@ -42,9 +42,9 @@
Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/messageProf.html b/FusionIIIT/templates/examination/messageProf.html new file mode 100644 index 000000000..f63749792 --- /dev/null +++ b/FusionIIIT/templates/examination/messageProf.html @@ -0,0 +1,70 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + + +{% endblock %} + +{% block content %} +
+
+
+ {{message}} +
+ +

Please check back later or contact support for assistance.

+
+
+ +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/submit.html b/FusionIIIT/templates/examination/submit.html index 3964b7062..e85d9fbd0 100644 --- a/FusionIIIT/templates/examination/submit.html +++ b/FusionIIIT/templates/examination/submit.html @@ -9,9 +9,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/submitGrade.html b/FusionIIIT/templates/examination/submitGrade.html index e17d06330..817406be6 100644 --- a/FusionIIIT/templates/examination/submitGrade.html +++ b/FusionIIIT/templates/examination/submitGrade.html @@ -9,9 +9,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/submitGradesProf.html b/FusionIIIT/templates/examination/submitGradesProf.html new file mode 100644 index 000000000..7373a13e6 --- /dev/null +++ b/FusionIIIT/templates/examination/submitGradesProf.html @@ -0,0 +1,152 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + +{% endblock %} + +{% block content %} +

Submit Results

+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+ {% comment %}
+
+ +
+ +
{% endcomment %} +
+ +
+ + +
+
+ + + +
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/updateEntergrades.html b/FusionIIIT/templates/examination/updateEntergrades.html index 7e82cf370..15de505cb 100644 --- a/FusionIIIT/templates/examination/updateEntergrades.html +++ b/FusionIIIT/templates/examination/updateEntergrades.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement @@ -75,9 +75,7 @@

VERIFY STUDENT MARKS

Course ID semester - Total Marks - - + Remarks Grade @@ -89,7 +87,7 @@

VERIFY STUDENT MARKS

{{ registration.batch }} {{ registration.course_id }} {{ registration.semester }} - {{ registration.total_marks }} + {{ registration.remarks }} @@ -209,8 +207,9 @@

VERIFY STUDENT MARKS

function finalizeGrades() { // Submit the form document.querySelector('form').submit(); - + console.log("hyee") // Redirect to the URL that handles Excel download + alert('Course verification Successful please refresh the page') window.location.href = "{% url 'examination:submit' %}"; } From 792aa18975c9580d2fbe3cb6540c51efd5934a2b Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Mon, 21 Oct 2024 17:25:15 +0530 Subject: [PATCH 07/44] Configured Backend: Added new feature in Examination module and modified the Student_grades table in online_cms module --- FusionIIIT/applications/examination/urls.py | 5 +- FusionIIIT/applications/examination/views.py | 434 +++++++++++++++---- FusionIIIT/applications/online_cms/models.py | 3 +- 3 files changed, 344 insertions(+), 98 deletions(-) diff --git a/FusionIIIT/applications/examination/urls.py b/FusionIIIT/applications/examination/urls.py index 83789d49e..175fe9a8b 100644 --- a/FusionIIIT/applications/examination/urls.py +++ b/FusionIIIT/applications/examination/urls.py @@ -52,6 +52,9 @@ name='generate_transcript_form'),#new # Announcement url(r'announcement/', views.announcement, name='announcement'),#new - + path('upload_grades/',views.upload_grades,name='upload_grades'), + path('message/',views.show_message,name='message'), + path('submitGradesProf/',views.submitGradesProf,name='submitGradesProf'), + path('download_template/',views.download_template,name='download_template') ] diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py index e65f34b3e..381ad2a5c 100644 --- a/FusionIIIT/applications/examination/views.py +++ b/FusionIIIT/applications/examination/views.py @@ -3,6 +3,7 @@ from django.views.generic import View from django.http import HttpResponse import csv +import json from django.db.models import IntegerField from django.db.models.functions import Cast from django.db.models.query_utils import Q @@ -20,35 +21,46 @@ from django.contrib.auth.models import User from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from applications.academic_information.models import Spi, Student, Curriculum -from applications.globals.models import (Designation, ExtraInfo, - HoldsDesignation, Faculty) -from applications.eis.models import (faculty_about, emp_research_projects) +from applications.globals.models import ( + Designation, + ExtraInfo, + HoldsDesignation, + Faculty, +) +from applications.eis.models import faculty_about, emp_research_projects from applications.academic_information.models import Course from applications.academic_procedures.models import course_registration, Register from applications.programme_curriculum.filters import CourseFilter from notification.views import examination_notif from applications.department.models import SpecialRequest, Announcements -from applications.globals.models import (DepartmentInfo, Designation, - ExtraInfo, Faculty, HoldsDesignation) +from applications.globals.models import ( + DepartmentInfo, + Designation, + ExtraInfo, + Faculty, + HoldsDesignation, +) from jsonschema import validate from jsonschema.exceptions import ValidationError from django.shortcuts import render, redirect, HttpResponse from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status -from .models import hidden_grades +from .models import hidden_grades, grade from .forms import StudentGradeForm from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import hidden_grades, authentication from rest_framework.permissions import AllowAny -from applications.online_cms.models import (Student_grades) - +from applications.online_cms.models import Student_grades +from django.http import JsonResponse +import csv from applications.programme_curriculum.models import Course as Courses, CourseInstructor +from django.urls import reverse -@login_required(login_url='/accounts/login') +@login_required(login_url="/accounts/login") def exam(request): """ This function is used to Differenciate acadadmin and all other user. @@ -59,15 +71,19 @@ def exam(request): @variables: user_details - Gets the information about the logged in user. des - Gets the designation about the looged in user. - # """ + #""" user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.all().filter(user=request.user).first() - if str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor": - return HttpResponseRedirect('/examination/updateGrades/') + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return HttpResponseRedirect("/examination/submitGradesProf/") elif request.session.get("currentDesignationSelected") == "acadadmin": - return HttpResponseRedirect('/examination/updateGrades/') + return HttpResponseRedirect("/examination/updateGrades/") - return HttpResponseRedirect('/dashboard/') + return HttpResponseRedirect("/dashboard/") @login_required(login_url='/accounts/login') @@ -134,7 +150,7 @@ def browse_announcements(): me_ann = Announcements.objects.filter(department="ME") sm_ann = Announcements.objects.filter(department="SM") all_ann = Announcements.objects.filter(department="ALL") - + print(cse_ann) context = { "cse": cse_ann, "ece": ece_ann, @@ -302,7 +318,7 @@ def announcement(request): department = request.POST.get('department') ann_date = date.today() - obj1, created = Announcements.objects.get_or_create( + obj1 = Announcements.objects.get_or_create( maker_id=user_info, batch=batch, programme=programme, @@ -314,7 +330,8 @@ def announcement(request): recipients = User.objects.all() # Modify this query as per your requirements examination_notif(sender=usrnm, recipient=recipients, type=message) - + return render(request,'department/browse_announcements_staff.html') + print(user_info.user_type) context = browse_announcements() return render(request, 'examination/announcement_req.html', { "user_designation": user_info.user_type, @@ -560,46 +577,53 @@ def generate_transcript_form(request): return render(request, 'examination/generate_transcript_form.html', context) -@login_required(login_url='/accounts/login') +@login_required(login_url="/accounts/login") def updateGrades(request): - unique_course_ids = Student_grades.objects.values( - 'course_id').distinct() + unique_course_ids = Student_grades.objects.values("course_id").distinct() # Cast the course IDs to integers unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) + course_id_int=Cast("course_id", IntegerField()) + ) # Retrieve course names and course codes based on unique course IDs - print(unique_course_ids) + # print(unique_course_ids) courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) - unique_batch_ids = Student_grades.objects.values( - 'batch').distinct() + unique_batch_ids = Student_grades.objects.values("batch").distinct() context = { - 'courses_info': courses_info, - 'unique_batch_ids': unique_batch_ids, + "courses_info": courses_info, + "unique_batch_ids": unique_batch_ids, } - return render(request, '../templates/examination/submitGrade.html', context) + return render(request, "../templates/examination/submitGrade.html", context) def updateEntergrades(request): - course_id = request.GET.get('course') - semester_id = request.GET.get('semester') - batch = request.GET.get('batch') - + course_id = request.GET.get("course") + semester_id = request.GET.get("semester") + batch = request.GET.get("batch") course_present = Student_grades.objects.filter( - course_id=course_id, semester=semester_id, batch=batch) + course_id=course_id, semester=semester_id, batch=batch + ) - context = { - 'registrations': course_present - } + if not course_present: + context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} + return render(request, "../templates/examination/message.html", context) + + verification = course_present.first().verified + print(verification) + if verification: + context = {"message": "THIS COURSE IS VERIFIED"} + return render(request, "../templates/examination/message.html", context) - return render(request, '../templates/examination/updateEntergrades.html', context) + context = {"registrations": course_present} + return render(request, "../templates/examination/updateEntergrades.html", context) class moderate_student_grades(APIView): permission_classes = [AllowAny] @@ -619,6 +643,7 @@ def post(self, request): grade_of_student = Student_grades.objects.get( course_id=course_id, roll_no=student_id, semester=semester_id) grade_of_student.grade = grade + grade_of_student.verified = True grade_of_student.save() except Student_grades.DoesNotExist: # If the grade doesn't exist, create a new one @@ -634,119 +659,336 @@ def post(self, request): writer.writerow(['Student ID', 'Semester ID', 'Course ID', 'Grade']) for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): writer.writerow([student_id, semester_id, course_id, grade]) - + print("HELLO") return response return render(request, '../templates/examination/grades_updated.html', {}) -@login_required(login_url='/accounts/login') +@login_required(login_url="/accounts/login") def submitGrades(request): - unique_course_ids = course_registration.objects.values( - 'course_id').distinct() - working_years = course_registration.objects.values( - 'working_year').distinct() - - + unique_course_ids = course_registration.objects.values("course_id").distinct() + working_years = course_registration.objects.values("working_year").distinct() # Cast the course IDs to integers unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) + course_id_int=Cast("course_id", IntegerField()) + ) # Retrieve course names and course codes based on unique course IDs - print(unique_course_ids) + # print(unique_course_ids) courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) - context = { - 'courses_info': courses_info, - 'working_years': working_years - } + context = {"courses_info": courses_info, "working_years": working_years} - print(working_years) + # print(working_years) - return render(request, '../templates/examination/gradeSubmission.html', context) + return render(request, "../templates/examination/gradeSubmission.html", context) def submitEntergrades(request): - course_id = request.GET.get('course') - year = request.GET.get('year') + + course_id = request.GET.get("course") + year = request.GET.get("year") if year is None or not year.isdigit(): message = "YEAR SHOULD NOT BE NONE" - context = { - 'message': message - } + context = {"message": message} - return render(request, '../templates/examination/message.html', context) + return render(request, "../templates/examination/message.html", context) return HttpResponse("Invalid year parameter") # Handle invalid year parameter # You can return an error response or redirect the user to an error page courses_info = Courses.objects.get(id=course_id) - courses = Student_grades.objects.filter( - course_id=courses_info.id, year=year) + courses = Student_grades.objects.filter(course_id=courses_info.id, year=year) if courses: message = "THIS Course was Already Submitted" - context = { - 'message': message - } + context = {"message": message} - return render(request, '../templates/examination/message.html', context) + return render(request, "../templates/examination/message.html", context) students = course_registration.objects.filter( - course_id_id=course_id, working_year=year) + course_id_id=course_id, working_year=year + ) # print(students) - context = { - 'registrations': students, - 'curr_id': course_id, - 'year': year - } + context = {"registrations": students, "curr_id": course_id, "year": year} - return render(request, '../templates/examination/gradeSubmissionForm.html', context) + return render(request, "../templates/examination/gradeSubmissionForm.html", context) class submitEntergradesStoring(APIView): permission_classes = [AllowAny] def post(self, request): - student_ids = request.POST.getlist('student_ids[]') - batch_ids = request.POST.getlist('batch_ids[]') - course_ids = request.POST.getlist('course_ids[]') - semester_ids = request.POST.getlist('semester_ids[]') - year_ids = request.POST.getlist('year_ids[]') - marks = request.POST.getlist('marks[]') - grades = request.POST.getlist('grades[]') - - if len(student_ids) != len(batch_ids) != len(course_ids) != len(semester_ids) != len(year_ids) != len(marks) != len(grades): - return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) + student_ids = request.POST.getlist("student_ids[]") + batch_ids = request.POST.getlist("batch_ids[]") + course_ids = request.POST.getlist("course_ids[]") + semester_ids = request.POST.getlist("semester_ids[]") + year_ids = request.POST.getlist("year_ids[]") + marks = request.POST.getlist("marks[]") + Student_grades = request.POST.getlist("Student_grades[]") + + if ( + len(student_ids) + != len(batch_ids) + != len(course_ids) + != len(semester_ids) + != len(year_ids) + != len(marks) + != len(Student_grades) + ): + return Response( + {"error": "Invalid Student_grades data provided"}, + status=status.HTTP_400_BAD_REQUEST, + ) - for student_id, batch_id, course_id, semester_id, year_id, mark, grade in zip(student_ids, batch_ids, course_ids, semester_ids, year_ids, marks, grades): + for ( + student_id, + batch_id, + course_id, + semester_id, + year_id, + mark, + Student_grades, + ) in zip( + student_ids, + batch_ids, + course_ids, + semester_ids, + year_ids, + marks, + Student_grades, + ): # Create an instance of hidden_grades model and save the data try: grade_of_student = Student_grades.objects.get( - course_id=course_id, roll_no=student_id, semester=semester_id) + course_id=course_id, roll_no=student_id, semester=semester_id + ) except Student_grades.DoesNotExist: - # If the grade doesn't exist, create a new one + # If the Student_grades doesn't exist, create a new one course_instance = Courses.objects.get(id=course_id) student_grade = Student_grades.objects.create( - course_id=course_instance, roll_no=student_id, semester=semester_id, grade=grade, batch=batch_id, year=year_id, total_marks=mark) + course_id=course_instance, + roll_no=student_id, + semester=semester_id, + Student_grades=Student_grades, + batch=batch_id, + year=year_id, + total_marks=mark, + ) student_grade.save() - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="grades.csv"' + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = 'attachment; filename="Student_grades.csv"' # Write data to CSV writer = csv.writer(response) - writer.writerow(['student_id', 'batch_ids', 'course_id', - 'semester_id', 'year_ids', 'marks', 'grade']) - for student_id, batch_id, course_id, semester_id, year_id, mark, grade in zip(student_ids, batch_ids, course_ids, semester_ids, year_ids, marks, grades): - writer.writerow([student_id, batch_id, course_id, - semester_id, year_id, mark, grade]) + writer.writerow( + [ + "student_id", + "batch_ids", + "course_id", + "semester_id", + "year_ids", + "marks", + "Student_grades", + ] + ) + for ( + student_id, + batch_id, + course_id, + semester_id, + year_id, + mark, + Student_grades, + ) in zip( + student_ids, + batch_ids, + course_ids, + semester_ids, + year_ids, + marks, + Student_grades, + ): + writer.writerow( + [ + student_id, + batch_id, + course_id, + semester_id, + year_id, + mark, + Student_grades, + ] + ) return response - return render(request, '../templates/examination/grades_updated.html', {}) + return render(request, "../templates/examination/grades_updated.html", {}) + + +def upload_grades(request): + if request.method == "POST" and request.FILES.get("csv_file"): + csv_file = request.FILES["csv_file"] + + if not csv_file.name.endswith(".csv"): + return JsonResponse( + {"error": "Invalid file format. Please upload a CSV file."}, status=400 + ) + + course_id = request.POST.get("course_id") + academic_year = request.POST.get("academic_year") + # semester = request.POST.get('semester') + + if academic_year == "None" or not academic_year.isdigit(): + return JsonResponse( + {"error": "Academic year must be a valid number."}, status=400 + ) + + if not course_id or not academic_year: + return JsonResponse( + {"error": "Course ID and Academic Year are required."}, status=400 + ) + + courses_info = Courses.objects.get(id=course_id) + + courses = Student_grades.objects.filter( + course_id=courses_info.id, year=academic_year + ) + students = course_registration.objects.filter( + course_id_id=course_id, working_year=academic_year + ) + + if not students: + message = "NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + + if courses: + message = "THIS Course was Already Submitted" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + + semester = students.first().semester_id_id + + try: + # Parse the CSV file + decoded_file = csv_file.read().decode("utf-8").splitlines() + reader = csv.DictReader(decoded_file) + + required_columns = ["roll_no", "name", "grade", "remarks"] + if not all(column in reader.fieldnames for column in required_columns): + return JsonResponse( + { + "error": "CSV file must contain the following columns: roll_no, name, grade, remarks." + }, + status=400, + ) + + for row in reader: + roll_no = row["roll_no"] + grade = row["grade"] + remarks = row["remarks"] + batch_prefix = roll_no[:2] + batch = int(f"20{batch_prefix}") + + Student_grades.objects.create( + roll_no=roll_no, + grade=grade, + remarks=remarks, + course_id_id=course_id, + year=academic_year, + semester=semester, + batch=batch, + ) + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGradesProf", + } + ) + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGrades", + } + ) + + except Courses.DoesNotExist: + return JsonResponse({"error": "Invalid course ID."}, status=400) + + except Exception as e: + return JsonResponse({"error": f"An error occurred: {e}"}, status=500) + + return JsonResponse( + {"error": "Invalid request. Please upload a CSV file."}, status=400 + ) + + +def show_message(request): + message = request.GET.get("message", "Default message if none provided.") + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return render(request, "examination/messageProf.html", {"message": message}) + return render(request, "examination/message.html", {"message": message}) + + +@login_required(login_url="/accounts/login") +def submitGradesProf(request): + # print(request.user,1) + unique_course_ids = ( + CourseInstructor.objects.filter(instructor_id_id=request.user.username) + .values("course_id_id") + .distinct() + ) + # unique_course_ids = course_registration.objects.values( + # 'course_id').distinct() + working_years = course_registration.objects.values("working_year").distinct() + + # Cast the course IDs to integers + unique_course_ids = unique_course_ids.annotate( + course_id_int=Cast("course_id", IntegerField()) + ) + + # Retrieve course names and course codes based on unique course IDs + + print(unique_course_ids) + courses_info = Courses.objects.filter( + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) + + context = {"courses_info": courses_info, "working_years": working_years} + + print(working_years) + + return render(request, "../templates/examination/submitGradesProf.html", context) + + +def download_template(request): + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = f'attachment; filename="template.csv"' + writer = csv.writer(response) + writer.writerow(["roll_no", "name", "grade", "remarks"]) + + return response \ No newline at end of file diff --git a/FusionIIIT/applications/online_cms/models.py b/FusionIIIT/applications/online_cms/models.py index 3a63aca08..a10234c32 100644 --- a/FusionIIIT/applications/online_cms/models.py +++ b/FusionIIIT/applications/online_cms/models.py @@ -267,9 +267,10 @@ class Student_grades(models.Model): semester = models.IntegerField(default=1) year = models.IntegerField(default=2016) roll_no = models.TextField(max_length=2000) - total_marks = models.DecimalField(max_digits=10, decimal_places=2, default=0) grade = models.TextField(max_length=2000) batch = models.IntegerField(default=2021) + remarks = models.CharField(max_length=500,null=True) + verified = models.BooleanField(default=False) def __str__(self): return '{} - {}'.format(self.pk, self.course_id) From 463b839354fcef0ca210f23eb394bfe77df6beff Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Mon, 21 Oct 2024 17:29:09 +0530 Subject: [PATCH 08/44] added migration file for the attributes changed in online_cms module --- .../migrations/0002_auto_20241021_0331.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py new file mode 100644 index 000000000..0a441f642 --- /dev/null +++ b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py @@ -0,0 +1,37 @@ +# Generated by Django 3.1.5 on 2024-10-21 03:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('online_cms', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='student_grades', + name='total_marks', + ), + migrations.AddField( + model_name='student_grades', + name='remarks', + field=models.CharField(max_length=500, null=True), + ), + migrations.AddField( + model_name='student_grades', + name='verified', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='attendance', + name='present', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='gradingscheme', + name='type_of_evaluation', + field=models.CharField(max_length=100), + ), + ] From fc87af7a876ad18b7bed8ae18d68e35f9d3dbaf1 Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Mon, 21 Oct 2024 19:20:20 +0530 Subject: [PATCH 09/44] added dean actor and gave verification powers only to dean and not to acad admin --- FusionIIIT/applications/examination/urls.py | 5 +- FusionIIIT/applications/examination/views.py | 47 +++- .../examination/gradeSubmission.html | 5 +- .../examination/submitGradeDean.html | 113 +++++++++ .../examination/submitGradesProf.html | 5 +- .../examination/updateEntergrades.html | 9 +- .../examination/updateEntergradesDean.html | 219 ++++++++++++++++++ 7 files changed, 396 insertions(+), 7 deletions(-) create mode 100644 FusionIIIT/templates/examination/submitGradeDean.html create mode 100644 FusionIIIT/templates/examination/updateEntergradesDean.html diff --git a/FusionIIIT/applications/examination/urls.py b/FusionIIIT/applications/examination/urls.py index 175fe9a8b..63b6f56ff 100644 --- a/FusionIIIT/applications/examination/urls.py +++ b/FusionIIIT/applications/examination/urls.py @@ -55,6 +55,7 @@ path('upload_grades/',views.upload_grades,name='upload_grades'), path('message/',views.show_message,name='message'), path('submitGradesProf/',views.submitGradesProf,name='submitGradesProf'), - path('download_template/',views.download_template,name='download_template') - + path('download_template/',views.download_template,name='download_template'), + path('verifyGradesDean/',views.verifyGradesDean,name='verifyGradesDean'), + path('updateEntergradesDean/',views.updateEntergradesDean,name='updateEnterGradesDean') ] diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py index 381ad2a5c..14a426a1b 100644 --- a/FusionIIIT/applications/examination/views.py +++ b/FusionIIIT/applications/examination/views.py @@ -82,6 +82,8 @@ def exam(request): return HttpResponseRedirect("/examination/submitGradesProf/") elif request.session.get("currentDesignationSelected") == "acadadmin": return HttpResponseRedirect("/examination/updateGrades/") + elif request.session.get("currentDesignationSelected") == "Dean Academic": + return HttpResponseRedirect("/examination/verifyGradesDean/") return HttpResponseRedirect("/dashboard/") @@ -991,4 +993,47 @@ def download_template(request): writer = csv.writer(response) writer.writerow(["roll_no", "name", "grade", "remarks"]) - return response \ No newline at end of file + return response + + + +def verifyGradesDean(request): + unique_course_ids = Student_grades.objects.values("course_id").distinct() + + # Cast the course IDs to integers + unique_course_ids = unique_course_ids.annotate( + course_id_int=Cast("course_id", IntegerField()) + ) + + # Retrieve course names and course codes based on unique course IDs + + # print(unique_course_ids) + courses_info = Courses.objects.filter( + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) + + unique_batch_ids = Student_grades.objects.values("batch").distinct() + + context = { + "courses_info": courses_info, + "unique_batch_ids": unique_batch_ids, + } + + return render(request, "../templates/examination/submitGradeDean.html", context) + + +def updateEntergradesDean(request): + course_id = request.GET.get("course") + semester_id = request.GET.get("semester") + batch = request.GET.get("batch") + course_present = Student_grades.objects.filter( + course_id=course_id, semester=semester_id, batch=batch + ) + + if not course_present: + context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} + return render(request, "../templates/examination/message.html", context) + + context = {"registrations": course_present} + + return render(request, "../templates/examination/updateEntergradesDean.html", context) diff --git a/FusionIIIT/templates/examination/gradeSubmission.html b/FusionIIIT/templates/examination/gradeSubmission.html index 6f163f4a5..ac46c59c7 100644 --- a/FusionIIIT/templates/examination/gradeSubmission.html +++ b/FusionIIIT/templates/examination/gradeSubmission.html @@ -110,7 +110,10 @@

Submit Results

alert('Please select course, year, and upload a CSV file.'); return; } - + if (!confirm('Are you sure you want to submit?')) { + // If the user clicks "Cancel", do not proceed + return; + } var formData = new FormData(); formData.append('course_id', selectedCourse); diff --git a/FusionIIIT/templates/examination/submitGradeDean.html b/FusionIIIT/templates/examination/submitGradeDean.html new file mode 100644 index 000000000..d3d151581 --- /dev/null +++ b/FusionIIIT/templates/examination/submitGradeDean.html @@ -0,0 +1,113 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} +
+{% endblock %} + +{% block content %} +

Update Result

+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+ + + +
+ +
+ + + +{% endblock %} + diff --git a/FusionIIIT/templates/examination/submitGradesProf.html b/FusionIIIT/templates/examination/submitGradesProf.html index 7373a13e6..448064503 100644 --- a/FusionIIIT/templates/examination/submitGradesProf.html +++ b/FusionIIIT/templates/examination/submitGradesProf.html @@ -110,7 +110,10 @@

Submit Results

return; } - + if (!confirm('Are you sure you want to submit?')) { + // If the user clicks "Cancel", do not proceed + return; + } var formData = new FormData(); formData.append('course_id', selectedCourse); formData.append('academic_year', selectedYear); diff --git a/FusionIIIT/templates/examination/updateEntergrades.html b/FusionIIIT/templates/examination/updateEntergrades.html index 15de505cb..b44de523e 100644 --- a/FusionIIIT/templates/examination/updateEntergrades.html +++ b/FusionIIIT/templates/examination/updateEntergrades.html @@ -36,9 +36,11 @@ {% endblock %} @@ -88,12 +91,14 @@

VERIFY STUDENT MARKS

{{ registration.course_id }} {{ registration.semester }} {{ registration.remarks }} + + - + {% endfor %} diff --git a/FusionIIIT/templates/examination/updateEntergradesDean.html b/FusionIIIT/templates/examination/updateEntergradesDean.html new file mode 100644 index 000000000..3683d31a1 --- /dev/null +++ b/FusionIIIT/templates/examination/updateEntergradesDean.html @@ -0,0 +1,219 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + + +{% endblock %} + +{% block content %} +
+

VERIFY STUDENT MARKS

+ {% if registrations %} +
+ {% csrf_token %} +
+ + + + + + + + + + + + + {% for registration in registrations %} + + + + + + + + + + {% endfor %} + +
+ Student ID + + + batchCourse IDsemester + Remarks + Grade
{{ registration.roll_no }}{{ registration.batch }}{{ registration.course_id }}{{ registration.semester }}{{ registration.remarks }} + + + + +
+
+ {% comment %} {% endcomment %} + + +
+
+ +
+ {% else %} +
+
+ NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER +
+

Please check back later or contact support for assistance.

+
+ {% endif %} +
+ + + + + + +{% endblock %} \ No newline at end of file From d4da4bd6ca1888f04ca2f35e1ce836334227c3bd Mon Sep 17 00:00:00 2001 From: Akash Kumar Sah Date: Mon, 21 Oct 2024 20:01:00 +0530 Subject: [PATCH 10/44] feat(AC2): Added support for elective preregistration and corrected pre registration process --- .../applications/academic_procedures/views.py | 6 +- .../auto_pre_registration.html | 144 ++++++++++++------ 2 files changed, 99 insertions(+), 51 deletions(-) diff --git a/FusionIIIT/applications/academic_procedures/views.py b/FusionIIIT/applications/academic_procedures/views.py index 5b2ea0f20..0e17ef96d 100644 --- a/FusionIIIT/applications/academic_procedures/views.py +++ b/FusionIIIT/applications/academic_procedures/views.py @@ -1474,14 +1474,14 @@ def auto_pre_registration(request): course_slot_id = course_slot_id_for_model, priority = priority_of_current_course ) - f =FinalRegistration(student_id=current_user ,course_slot_id=course_slot_id_for_model , course_id=course_id_for_model ,semester_id=sem_id) - final_reg_curr.append(f) + # f =FinalRegistration(student_id=current_user ,course_slot_id=course_slot_id_for_model , course_id=course_id_for_model ,semester_id=sem_id) + # final_reg_curr.append(f) reg_curr.append(p) existing_entries.add(current_combination) try: InitialRegistration.objects.bulk_create(reg_curr) - FinalRegistration.objects.bulk_create(final_reg_curr) + # FinalRegistration.objects.bulk_create(final_reg_curr) registration_check = StudentRegistrationChecks( student_id = current_user, pre_registration_flag = True, diff --git a/FusionIIIT/templates/academic_procedures/auto_pre_registration.html b/FusionIIIT/templates/academic_procedures/auto_pre_registration.html index b9129ca23..033c468fe 100644 --- a/FusionIIIT/templates/academic_procedures/auto_pre_registration.html +++ b/FusionIIIT/templates/academic_procedures/auto_pre_registration.html @@ -95,6 +95,7 @@ Course Code Course Name Credits + Priority @@ -121,6 +122,12 @@
{% endfor %} + + {% for course in courses %} + {{course.priority}} +
+ {% endfor %} + {% endfor %} @@ -144,6 +151,7 @@ Slot type Semester Credits + Priority Select @@ -152,34 +160,57 @@ {% for course_slot in next_sem_registration_courses %} {% if course_slot.type == "Swayam" %} - {% with "x"|ljust:swayam_courses_count as dummy_range %} - {% for _ in dummy_range %} - - - {{ forloop.counter0|add:forloop.parentloop.counter0|add:"+1" }}. - {{course_slot.name}} - {{course_slot.type}} - {{next_sem.semester_no}} - - - {{course_slot.courses.all.0.credit}} - -
- -
- - - - {% endfor %} - {% endwith %} + + + {{ forloop.counter }}. + {{course_slot.name}} + {{course_slot.type}} + {{next_sem.semester_no}} + + + {{course_slot.courses.all.0.credit}} + +
+ +
+ + + + {% elif course_slot.type == "Optional Elective" %} + {% for course in course_slot.courses.all %} + + + {{ forloop.parentloop.counter }}. + {{ course_slot.name }} + {{ course_slot.type }} + {{ next_sem.semester_no }} + + + {{ course.credit }} + {{ forloop.counter }} + +
+ +
+ + + {% endfor %} {% else %} 1) { - var selectedOption = dropdown.querySelector('select').value; + var selectedOption = selectElement.value; // console.log(dropdown) if (selectedOption !== "NULL") { var selectedCourseName = dropdown.querySelector('select option:checked').text; @@ -405,17 +433,37 @@ var courseSlotId = dropdown.querySelector('select').getAttribute("name").split('-')[1]; if (confirm("Are you sure you want to register for " + selectedCourseName + "?")) { // handleDropdownChangePreRegistrationEvent = false; - addCourse(dropdown , courseSlotId, function(response) { - if(response.message === 'Course not added because seats are full!'){ - // console.log(selectedOptionElement) - selectedOptionElement.remove(); - }else{ - // console.log("credits : " + credit); - credit += selectedCourseCredit; - dropdown.classList.add("disabled-dropdown"); - // alert(response.message); + // addCourse(dropdown , courseSlotId, function(response) { + // if(response.message === 'Course not added because seats are full!'){ + // // console.log(selectedOptionElement) + // selectedOptionElement.remove(); + // }else{ + // // console.log("credits : " + credit); + // credit += selectedCourseCredit; + // dropdown.classList.add("disabled-dropdown"); + // // alert(response.message); + // } + // }); + var suffix = selectedOption.split('-')[1]; + var prefix = selectedOption.split('-')[0]; + console.log(suffix); + var allSelects = document.querySelectorAll('select[name="' + selectElement.getAttribute("name") + '"]'); + // console.log(allSelects); + allSelects.forEach(function (currentSelectElement) { + // Skip the current dropdown where the selection was made + if (currentSelectElement !== selectElement) { + var optionToDisable = currentSelectElement.querySelector('option[value$="-' + suffix + '"]'); + console.log(currentSelectElement, optionToDisable); + if (optionToDisable) { + optionToDisable.remove(); + } } }); + if (prefix == '1') { + credit += selectedCourseCredit; + // console.log(credit); + } + dropdown.classList.add("disabled-dropdown"); } else { // Reset the dropdown to its initial state if user cancels dropdown.querySelector('select').selectedIndex = 0; From 7d72e85e68a7629f6ec38e5e91ada0022d61d86a Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Mon, 21 Oct 2024 20:33:06 +0530 Subject: [PATCH 11/44] Examination Module completed: Dean academic added, resubmission power can be given to faculty by dean and acadadmin can only read and verify the data, revoked him of write access --- FusionIIIT/applications/examination/urls.py | 4 +- FusionIIIT/applications/examination/views.py | 119 +++++++++++++++++- FusionIIIT/applications/online_cms/models.py | 2 +- .../examination/submitGradesProf.html | 2 +- .../examination/updateEntergradesDean.html | 7 ++ 5 files changed, 130 insertions(+), 4 deletions(-) diff --git a/FusionIIIT/applications/examination/urls.py b/FusionIIIT/applications/examination/urls.py index 63b6f56ff..caae0dc67 100644 --- a/FusionIIIT/applications/examination/urls.py +++ b/FusionIIIT/applications/examination/urls.py @@ -57,5 +57,7 @@ path('submitGradesProf/',views.submitGradesProf,name='submitGradesProf'), path('download_template/',views.download_template,name='download_template'), path('verifyGradesDean/',views.verifyGradesDean,name='verifyGradesDean'), - path('updateEntergradesDean/',views.updateEntergradesDean,name='updateEnterGradesDean') + path('updateEntergradesDean/',views.updateEntergradesDean,name='updateEnterGradesDean'), + path('upload_grades_prof/',views.upload_grades_prof,name='upload_grades_prof'), + ] diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py index 14a426a1b..cc813c4c1 100644 --- a/FusionIIIT/applications/examination/views.py +++ b/FusionIIIT/applications/examination/views.py @@ -635,6 +635,7 @@ def post(self, request): semester_ids = request.POST.getlist('semester_ids[]') course_ids = request.POST.getlist('course_ids[]') grades = request.POST.getlist('grades[]') + allow_resubmission = request.POST.get('allow_resubmission', 'NO') if len(student_ids) != len(semester_ids) != len(course_ids) != len(grades): return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) @@ -646,6 +647,8 @@ def post(self, request): course_id=course_id, roll_no=student_id, semester=semester_id) grade_of_student.grade = grade grade_of_student.verified = True + if allow_resubmission == 'YES': + grade_of_student.reSubmit = True grade_of_student.save() except Student_grades.DoesNotExist: # If the grade doesn't exist, create a new one @@ -998,7 +1001,7 @@ def download_template(request): def verifyGradesDean(request): - unique_course_ids = Student_grades.objects.values("course_id").distinct() + unique_course_ids = Student_grades.objects.filter(verified=True).values("course_id").distinct() # Cast the course IDs to integers unique_course_ids = unique_course_ids.annotate( @@ -1037,3 +1040,117 @@ def updateEntergradesDean(request): context = {"registrations": course_present} return render(request, "../templates/examination/updateEntergradesDean.html", context) + + + +def upload_grades_prof(request): + if request.method == "POST" and request.FILES.get("csv_file"): + csv_file = request.FILES["csv_file"] + + if not csv_file.name.endswith(".csv"): + return JsonResponse( + {"error": "Invalid file format. Please upload a CSV file."}, status=400 + ) + + course_id = request.POST.get("course_id") + academic_year = request.POST.get("academic_year") + # semester = request.POST.get('semester') + + if academic_year == "None" or not academic_year.isdigit(): + return JsonResponse( + {"error": "Academic year must be a valid number."}, status=400 + ) + + if not course_id or not academic_year: + return JsonResponse( + {"error": "Course ID and Academic Year are required."}, status=400 + ) + + courses_info = Courses.objects.get(id=course_id) + + courses = Student_grades.objects.filter( + course_id=courses_info.id, year=academic_year + ) + students = course_registration.objects.filter( + course_id_id=course_id, working_year=academic_year + ) + + if not students: + message = "NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + print(courses.first().reSubmit) + if courses and not courses.first().reSubmit: + + message = "THIS Course was Already Submitted" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + + semester = students.first().semester_id_id + + try: + # Parse the CSV file + decoded_file = csv_file.read().decode("utf-8").splitlines() + reader = csv.DictReader(decoded_file) + + required_columns = ["roll_no", "name", "grade", "remarks"] + if not all(column in reader.fieldnames for column in required_columns): + return JsonResponse( + { + "error": "CSV file must contain the following columns: roll_no, name, grade, remarks." + }, + status=400, + ) + + for row in reader: + roll_no = row["roll_no"] + grade = row["grade"] + remarks = row["remarks"] + batch_prefix = roll_no[:2] + batch = int(f"20{batch_prefix}") + reSubmit=False + Student_grades.objects.update_or_create( + roll_no=roll_no, + course_id_id=course_id, + year=academic_year, + semester=semester, + # Fields that will be updated if a match is found + defaults={ + 'grade': grade, + 'remarks': remarks, + 'reSubmit': reSubmit, + } + ) + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGradesProf", + } + ) + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGrades", + } + ) + + except Courses.DoesNotExist: + return JsonResponse({"error": "Invalid course ID."}, status=400) + + except Exception as e: + return JsonResponse({"error": f"An error occurred: {e}"}, status=500) + + return JsonResponse( + {"error": "Invalid request. Please upload a CSV file."}, status=400 + ) + diff --git a/FusionIIIT/applications/online_cms/models.py b/FusionIIIT/applications/online_cms/models.py index a10234c32..98d68b24c 100644 --- a/FusionIIIT/applications/online_cms/models.py +++ b/FusionIIIT/applications/online_cms/models.py @@ -271,7 +271,7 @@ class Student_grades(models.Model): batch = models.IntegerField(default=2021) remarks = models.CharField(max_length=500,null=True) verified = models.BooleanField(default=False) - + reSubmit = models.BooleanField(default=True) def __str__(self): return '{} - {}'.format(self.pk, self.course_id) diff --git a/FusionIIIT/templates/examination/submitGradesProf.html b/FusionIIIT/templates/examination/submitGradesProf.html index 448064503..c7c41d18a 100644 --- a/FusionIIIT/templates/examination/submitGradesProf.html +++ b/FusionIIIT/templates/examination/submitGradesProf.html @@ -122,7 +122,7 @@

Submit Results

$.ajax({ - url: '/examination/upload_grades/', + url: '/examination/upload_grades_prof/', type: 'POST', data: formData, processData: false, diff --git a/FusionIIIT/templates/examination/updateEntergradesDean.html b/FusionIIIT/templates/examination/updateEntergradesDean.html index 3683d31a1..d4296040d 100644 --- a/FusionIIIT/templates/examination/updateEntergradesDean.html +++ b/FusionIIIT/templates/examination/updateEntergradesDean.html @@ -103,6 +103,13 @@

VERIFY STUDENT MARKS

+
+ + +
{% comment %} {% endcomment %} From a6438bc43a3af7eb49adbc87cb5a634b63968409 Mon Sep 17 00:00:00 2001 From: Abhyuday Singh <129726315+Lone24wolf@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:55:56 +0530 Subject: [PATCH 12/44] AC-7 module feature requests (#1634) * Changed Template Files: Co authored by Abhyuday Singh and Arghadeep Bosu * Configured Backend: Added new feature in Examination module and modified the Student_grades table in online_cms module * added migration file for the attributes changed in online_cms module * added dean actor and gave verification powers only to dean and not to acad admin * Examination Module completed: Dean academic added, resubmission power can be given to faculty by dean and acadadmin can only read and verify the data, revoked him of write access --------- Co-authored-by: Arghadeep Bosu --- FusionIIIT/applications/examination/urls.py | 8 +- FusionIIIT/applications/examination/views.py | 596 +++++++++++++++--- .../migrations/0002_auto_20241021_0331.py | 37 ++ FusionIIIT/applications/online_cms/models.py | 5 +- .../examination/announcement_req.html | 4 +- .../examination/generate_transcript.html | 4 +- .../examination/generate_transcript_form.html | 4 +- .../generate_transcript_students.html | 4 +- .../examination/gradeSubmission.html | 142 +++-- .../examination/gradeSubmissionForm.html | 6 +- FusionIIIT/templates/examination/message.html | 4 +- .../templates/examination/messageProf.html | 70 ++ FusionIIIT/templates/examination/submit.html | 4 +- .../templates/examination/submitGrade.html | 4 +- .../examination/submitGradeDean.html | 113 ++++ .../examination/submitGradesProf.html | 155 +++++ .../examination/updateEntergrades.html | 22 +- .../examination/updateEntergradesDean.html | 226 +++++++ 18 files changed, 1244 insertions(+), 164 deletions(-) create mode 100644 FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py create mode 100644 FusionIIIT/templates/examination/messageProf.html create mode 100644 FusionIIIT/templates/examination/submitGradeDean.html create mode 100644 FusionIIIT/templates/examination/submitGradesProf.html create mode 100644 FusionIIIT/templates/examination/updateEntergradesDean.html diff --git a/FusionIIIT/applications/examination/urls.py b/FusionIIIT/applications/examination/urls.py index 83789d49e..caae0dc67 100644 --- a/FusionIIIT/applications/examination/urls.py +++ b/FusionIIIT/applications/examination/urls.py @@ -52,6 +52,12 @@ name='generate_transcript_form'),#new # Announcement url(r'announcement/', views.announcement, name='announcement'),#new - + path('upload_grades/',views.upload_grades,name='upload_grades'), + path('message/',views.show_message,name='message'), + path('submitGradesProf/',views.submitGradesProf,name='submitGradesProf'), + path('download_template/',views.download_template,name='download_template'), + path('verifyGradesDean/',views.verifyGradesDean,name='verifyGradesDean'), + path('updateEntergradesDean/',views.updateEntergradesDean,name='updateEnterGradesDean'), + path('upload_grades_prof/',views.upload_grades_prof,name='upload_grades_prof'), ] diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py index e65f34b3e..cc813c4c1 100644 --- a/FusionIIIT/applications/examination/views.py +++ b/FusionIIIT/applications/examination/views.py @@ -3,6 +3,7 @@ from django.views.generic import View from django.http import HttpResponse import csv +import json from django.db.models import IntegerField from django.db.models.functions import Cast from django.db.models.query_utils import Q @@ -20,35 +21,46 @@ from django.contrib.auth.models import User from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from applications.academic_information.models import Spi, Student, Curriculum -from applications.globals.models import (Designation, ExtraInfo, - HoldsDesignation, Faculty) -from applications.eis.models import (faculty_about, emp_research_projects) +from applications.globals.models import ( + Designation, + ExtraInfo, + HoldsDesignation, + Faculty, +) +from applications.eis.models import faculty_about, emp_research_projects from applications.academic_information.models import Course from applications.academic_procedures.models import course_registration, Register from applications.programme_curriculum.filters import CourseFilter from notification.views import examination_notif from applications.department.models import SpecialRequest, Announcements -from applications.globals.models import (DepartmentInfo, Designation, - ExtraInfo, Faculty, HoldsDesignation) +from applications.globals.models import ( + DepartmentInfo, + Designation, + ExtraInfo, + Faculty, + HoldsDesignation, +) from jsonschema import validate from jsonschema.exceptions import ValidationError from django.shortcuts import render, redirect, HttpResponse from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status -from .models import hidden_grades +from .models import hidden_grades, grade from .forms import StudentGradeForm from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import hidden_grades, authentication from rest_framework.permissions import AllowAny -from applications.online_cms.models import (Student_grades) - +from applications.online_cms.models import Student_grades +from django.http import JsonResponse +import csv from applications.programme_curriculum.models import Course as Courses, CourseInstructor +from django.urls import reverse -@login_required(login_url='/accounts/login') +@login_required(login_url="/accounts/login") def exam(request): """ This function is used to Differenciate acadadmin and all other user. @@ -59,15 +71,21 @@ def exam(request): @variables: user_details - Gets the information about the logged in user. des - Gets the designation about the looged in user. - # """ + #""" user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.all().filter(user=request.user).first() - if str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor": - return HttpResponseRedirect('/examination/updateGrades/') + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return HttpResponseRedirect("/examination/submitGradesProf/") elif request.session.get("currentDesignationSelected") == "acadadmin": - return HttpResponseRedirect('/examination/updateGrades/') + return HttpResponseRedirect("/examination/updateGrades/") + elif request.session.get("currentDesignationSelected") == "Dean Academic": + return HttpResponseRedirect("/examination/verifyGradesDean/") - return HttpResponseRedirect('/dashboard/') + return HttpResponseRedirect("/dashboard/") @login_required(login_url='/accounts/login') @@ -134,7 +152,7 @@ def browse_announcements(): me_ann = Announcements.objects.filter(department="ME") sm_ann = Announcements.objects.filter(department="SM") all_ann = Announcements.objects.filter(department="ALL") - + print(cse_ann) context = { "cse": cse_ann, "ece": ece_ann, @@ -302,7 +320,7 @@ def announcement(request): department = request.POST.get('department') ann_date = date.today() - obj1, created = Announcements.objects.get_or_create( + obj1 = Announcements.objects.get_or_create( maker_id=user_info, batch=batch, programme=programme, @@ -314,7 +332,8 @@ def announcement(request): recipients = User.objects.all() # Modify this query as per your requirements examination_notif(sender=usrnm, recipient=recipients, type=message) - + return render(request,'department/browse_announcements_staff.html') + print(user_info.user_type) context = browse_announcements() return render(request, 'examination/announcement_req.html', { "user_designation": user_info.user_type, @@ -560,46 +579,53 @@ def generate_transcript_form(request): return render(request, 'examination/generate_transcript_form.html', context) -@login_required(login_url='/accounts/login') +@login_required(login_url="/accounts/login") def updateGrades(request): - unique_course_ids = Student_grades.objects.values( - 'course_id').distinct() + unique_course_ids = Student_grades.objects.values("course_id").distinct() # Cast the course IDs to integers unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) + course_id_int=Cast("course_id", IntegerField()) + ) # Retrieve course names and course codes based on unique course IDs - print(unique_course_ids) + # print(unique_course_ids) courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) - unique_batch_ids = Student_grades.objects.values( - 'batch').distinct() + unique_batch_ids = Student_grades.objects.values("batch").distinct() context = { - 'courses_info': courses_info, - 'unique_batch_ids': unique_batch_ids, + "courses_info": courses_info, + "unique_batch_ids": unique_batch_ids, } - return render(request, '../templates/examination/submitGrade.html', context) + return render(request, "../templates/examination/submitGrade.html", context) def updateEntergrades(request): - course_id = request.GET.get('course') - semester_id = request.GET.get('semester') - batch = request.GET.get('batch') - + course_id = request.GET.get("course") + semester_id = request.GET.get("semester") + batch = request.GET.get("batch") course_present = Student_grades.objects.filter( - course_id=course_id, semester=semester_id, batch=batch) + course_id=course_id, semester=semester_id, batch=batch + ) - context = { - 'registrations': course_present - } + if not course_present: + context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} + return render(request, "../templates/examination/message.html", context) + + verification = course_present.first().verified + print(verification) + if verification: + context = {"message": "THIS COURSE IS VERIFIED"} + return render(request, "../templates/examination/message.html", context) - return render(request, '../templates/examination/updateEntergrades.html', context) + context = {"registrations": course_present} + return render(request, "../templates/examination/updateEntergrades.html", context) class moderate_student_grades(APIView): permission_classes = [AllowAny] @@ -609,6 +635,7 @@ def post(self, request): semester_ids = request.POST.getlist('semester_ids[]') course_ids = request.POST.getlist('course_ids[]') grades = request.POST.getlist('grades[]') + allow_resubmission = request.POST.get('allow_resubmission', 'NO') if len(student_ids) != len(semester_ids) != len(course_ids) != len(grades): return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) @@ -619,6 +646,9 @@ def post(self, request): grade_of_student = Student_grades.objects.get( course_id=course_id, roll_no=student_id, semester=semester_id) grade_of_student.grade = grade + grade_of_student.verified = True + if allow_resubmission == 'YES': + grade_of_student.reSubmit = True grade_of_student.save() except Student_grades.DoesNotExist: # If the grade doesn't exist, create a new one @@ -634,119 +664,493 @@ def post(self, request): writer.writerow(['Student ID', 'Semester ID', 'Course ID', 'Grade']) for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): writer.writerow([student_id, semester_id, course_id, grade]) - + print("HELLO") return response return render(request, '../templates/examination/grades_updated.html', {}) -@login_required(login_url='/accounts/login') +@login_required(login_url="/accounts/login") def submitGrades(request): - unique_course_ids = course_registration.objects.values( - 'course_id').distinct() - working_years = course_registration.objects.values( - 'working_year').distinct() - - + unique_course_ids = course_registration.objects.values("course_id").distinct() + working_years = course_registration.objects.values("working_year").distinct() # Cast the course IDs to integers unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) + course_id_int=Cast("course_id", IntegerField()) + ) # Retrieve course names and course codes based on unique course IDs - print(unique_course_ids) + # print(unique_course_ids) courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) - context = { - 'courses_info': courses_info, - 'working_years': working_years - } + context = {"courses_info": courses_info, "working_years": working_years} - print(working_years) + # print(working_years) - return render(request, '../templates/examination/gradeSubmission.html', context) + return render(request, "../templates/examination/gradeSubmission.html", context) def submitEntergrades(request): - course_id = request.GET.get('course') - year = request.GET.get('year') + + course_id = request.GET.get("course") + year = request.GET.get("year") if year is None or not year.isdigit(): message = "YEAR SHOULD NOT BE NONE" - context = { - 'message': message - } + context = {"message": message} - return render(request, '../templates/examination/message.html', context) + return render(request, "../templates/examination/message.html", context) return HttpResponse("Invalid year parameter") # Handle invalid year parameter # You can return an error response or redirect the user to an error page courses_info = Courses.objects.get(id=course_id) - courses = Student_grades.objects.filter( - course_id=courses_info.id, year=year) + courses = Student_grades.objects.filter(course_id=courses_info.id, year=year) if courses: message = "THIS Course was Already Submitted" - context = { - 'message': message - } + context = {"message": message} - return render(request, '../templates/examination/message.html', context) + return render(request, "../templates/examination/message.html", context) students = course_registration.objects.filter( - course_id_id=course_id, working_year=year) + course_id_id=course_id, working_year=year + ) # print(students) - context = { - 'registrations': students, - 'curr_id': course_id, - 'year': year - } + context = {"registrations": students, "curr_id": course_id, "year": year} - return render(request, '../templates/examination/gradeSubmissionForm.html', context) + return render(request, "../templates/examination/gradeSubmissionForm.html", context) class submitEntergradesStoring(APIView): permission_classes = [AllowAny] def post(self, request): - student_ids = request.POST.getlist('student_ids[]') - batch_ids = request.POST.getlist('batch_ids[]') - course_ids = request.POST.getlist('course_ids[]') - semester_ids = request.POST.getlist('semester_ids[]') - year_ids = request.POST.getlist('year_ids[]') - marks = request.POST.getlist('marks[]') - grades = request.POST.getlist('grades[]') - - if len(student_ids) != len(batch_ids) != len(course_ids) != len(semester_ids) != len(year_ids) != len(marks) != len(grades): - return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) + student_ids = request.POST.getlist("student_ids[]") + batch_ids = request.POST.getlist("batch_ids[]") + course_ids = request.POST.getlist("course_ids[]") + semester_ids = request.POST.getlist("semester_ids[]") + year_ids = request.POST.getlist("year_ids[]") + marks = request.POST.getlist("marks[]") + Student_grades = request.POST.getlist("Student_grades[]") + + if ( + len(student_ids) + != len(batch_ids) + != len(course_ids) + != len(semester_ids) + != len(year_ids) + != len(marks) + != len(Student_grades) + ): + return Response( + {"error": "Invalid Student_grades data provided"}, + status=status.HTTP_400_BAD_REQUEST, + ) - for student_id, batch_id, course_id, semester_id, year_id, mark, grade in zip(student_ids, batch_ids, course_ids, semester_ids, year_ids, marks, grades): + for ( + student_id, + batch_id, + course_id, + semester_id, + year_id, + mark, + Student_grades, + ) in zip( + student_ids, + batch_ids, + course_ids, + semester_ids, + year_ids, + marks, + Student_grades, + ): # Create an instance of hidden_grades model and save the data try: grade_of_student = Student_grades.objects.get( - course_id=course_id, roll_no=student_id, semester=semester_id) + course_id=course_id, roll_no=student_id, semester=semester_id + ) except Student_grades.DoesNotExist: - # If the grade doesn't exist, create a new one + # If the Student_grades doesn't exist, create a new one course_instance = Courses.objects.get(id=course_id) student_grade = Student_grades.objects.create( - course_id=course_instance, roll_no=student_id, semester=semester_id, grade=grade, batch=batch_id, year=year_id, total_marks=mark) + course_id=course_instance, + roll_no=student_id, + semester=semester_id, + Student_grades=Student_grades, + batch=batch_id, + year=year_id, + total_marks=mark, + ) student_grade.save() - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="grades.csv"' + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = 'attachment; filename="Student_grades.csv"' # Write data to CSV writer = csv.writer(response) - writer.writerow(['student_id', 'batch_ids', 'course_id', - 'semester_id', 'year_ids', 'marks', 'grade']) - for student_id, batch_id, course_id, semester_id, year_id, mark, grade in zip(student_ids, batch_ids, course_ids, semester_ids, year_ids, marks, grades): - writer.writerow([student_id, batch_id, course_id, - semester_id, year_id, mark, grade]) + writer.writerow( + [ + "student_id", + "batch_ids", + "course_id", + "semester_id", + "year_ids", + "marks", + "Student_grades", + ] + ) + for ( + student_id, + batch_id, + course_id, + semester_id, + year_id, + mark, + Student_grades, + ) in zip( + student_ids, + batch_ids, + course_ids, + semester_ids, + year_ids, + marks, + Student_grades, + ): + writer.writerow( + [ + student_id, + batch_id, + course_id, + semester_id, + year_id, + mark, + Student_grades, + ] + ) return response - return render(request, '../templates/examination/grades_updated.html', {}) + return render(request, "../templates/examination/grades_updated.html", {}) + + +def upload_grades(request): + if request.method == "POST" and request.FILES.get("csv_file"): + csv_file = request.FILES["csv_file"] + + if not csv_file.name.endswith(".csv"): + return JsonResponse( + {"error": "Invalid file format. Please upload a CSV file."}, status=400 + ) + + course_id = request.POST.get("course_id") + academic_year = request.POST.get("academic_year") + # semester = request.POST.get('semester') + + if academic_year == "None" or not academic_year.isdigit(): + return JsonResponse( + {"error": "Academic year must be a valid number."}, status=400 + ) + + if not course_id or not academic_year: + return JsonResponse( + {"error": "Course ID and Academic Year are required."}, status=400 + ) + + courses_info = Courses.objects.get(id=course_id) + + courses = Student_grades.objects.filter( + course_id=courses_info.id, year=academic_year + ) + students = course_registration.objects.filter( + course_id_id=course_id, working_year=academic_year + ) + + if not students: + message = "NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + + if courses: + message = "THIS Course was Already Submitted" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + + semester = students.first().semester_id_id + + try: + # Parse the CSV file + decoded_file = csv_file.read().decode("utf-8").splitlines() + reader = csv.DictReader(decoded_file) + + required_columns = ["roll_no", "name", "grade", "remarks"] + if not all(column in reader.fieldnames for column in required_columns): + return JsonResponse( + { + "error": "CSV file must contain the following columns: roll_no, name, grade, remarks." + }, + status=400, + ) + + for row in reader: + roll_no = row["roll_no"] + grade = row["grade"] + remarks = row["remarks"] + batch_prefix = roll_no[:2] + batch = int(f"20{batch_prefix}") + + Student_grades.objects.create( + roll_no=roll_no, + grade=grade, + remarks=remarks, + course_id_id=course_id, + year=academic_year, + semester=semester, + batch=batch, + ) + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGradesProf", + } + ) + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGrades", + } + ) + + except Courses.DoesNotExist: + return JsonResponse({"error": "Invalid course ID."}, status=400) + + except Exception as e: + return JsonResponse({"error": f"An error occurred: {e}"}, status=500) + + return JsonResponse( + {"error": "Invalid request. Please upload a CSV file."}, status=400 + ) + + +def show_message(request): + message = request.GET.get("message", "Default message if none provided.") + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return render(request, "examination/messageProf.html", {"message": message}) + return render(request, "examination/message.html", {"message": message}) + + +@login_required(login_url="/accounts/login") +def submitGradesProf(request): + # print(request.user,1) + unique_course_ids = ( + CourseInstructor.objects.filter(instructor_id_id=request.user.username) + .values("course_id_id") + .distinct() + ) + # unique_course_ids = course_registration.objects.values( + # 'course_id').distinct() + working_years = course_registration.objects.values("working_year").distinct() + + # Cast the course IDs to integers + unique_course_ids = unique_course_ids.annotate( + course_id_int=Cast("course_id", IntegerField()) + ) + + # Retrieve course names and course codes based on unique course IDs + + print(unique_course_ids) + courses_info = Courses.objects.filter( + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) + + context = {"courses_info": courses_info, "working_years": working_years} + + print(working_years) + + return render(request, "../templates/examination/submitGradesProf.html", context) + + +def download_template(request): + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = f'attachment; filename="template.csv"' + writer = csv.writer(response) + writer.writerow(["roll_no", "name", "grade", "remarks"]) + + return response + + + +def verifyGradesDean(request): + unique_course_ids = Student_grades.objects.filter(verified=True).values("course_id").distinct() + + # Cast the course IDs to integers + unique_course_ids = unique_course_ids.annotate( + course_id_int=Cast("course_id", IntegerField()) + ) + + # Retrieve course names and course codes based on unique course IDs + + # print(unique_course_ids) + courses_info = Courses.objects.filter( + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) + + unique_batch_ids = Student_grades.objects.values("batch").distinct() + + context = { + "courses_info": courses_info, + "unique_batch_ids": unique_batch_ids, + } + + return render(request, "../templates/examination/submitGradeDean.html", context) + + +def updateEntergradesDean(request): + course_id = request.GET.get("course") + semester_id = request.GET.get("semester") + batch = request.GET.get("batch") + course_present = Student_grades.objects.filter( + course_id=course_id, semester=semester_id, batch=batch + ) + + if not course_present: + context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} + return render(request, "../templates/examination/message.html", context) + + context = {"registrations": course_present} + + return render(request, "../templates/examination/updateEntergradesDean.html", context) + + + +def upload_grades_prof(request): + if request.method == "POST" and request.FILES.get("csv_file"): + csv_file = request.FILES["csv_file"] + + if not csv_file.name.endswith(".csv"): + return JsonResponse( + {"error": "Invalid file format. Please upload a CSV file."}, status=400 + ) + + course_id = request.POST.get("course_id") + academic_year = request.POST.get("academic_year") + # semester = request.POST.get('semester') + + if academic_year == "None" or not academic_year.isdigit(): + return JsonResponse( + {"error": "Academic year must be a valid number."}, status=400 + ) + + if not course_id or not academic_year: + return JsonResponse( + {"error": "Course ID and Academic Year are required."}, status=400 + ) + + courses_info = Courses.objects.get(id=course_id) + + courses = Student_grades.objects.filter( + course_id=courses_info.id, year=academic_year + ) + students = course_registration.objects.filter( + course_id_id=course_id, working_year=academic_year + ) + + if not students: + message = "NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + print(courses.first().reSubmit) + if courses and not courses.first().reSubmit: + + message = "THIS Course was Already Submitted" + redirect_url = reverse("examination:message") + f"?message={message}" + return JsonResponse( + {"error": message, "redirect_url": redirect_url}, status=400 + ) + + semester = students.first().semester_id_id + + try: + # Parse the CSV file + decoded_file = csv_file.read().decode("utf-8").splitlines() + reader = csv.DictReader(decoded_file) + + required_columns = ["roll_no", "name", "grade", "remarks"] + if not all(column in reader.fieldnames for column in required_columns): + return JsonResponse( + { + "error": "CSV file must contain the following columns: roll_no, name, grade, remarks." + }, + status=400, + ) + + for row in reader: + roll_no = row["roll_no"] + grade = row["grade"] + remarks = row["remarks"] + batch_prefix = roll_no[:2] + batch = int(f"20{batch_prefix}") + reSubmit=False + Student_grades.objects.update_or_create( + roll_no=roll_no, + course_id_id=course_id, + year=academic_year, + semester=semester, + # Fields that will be updated if a match is found + defaults={ + 'grade': grade, + 'remarks': remarks, + 'reSubmit': reSubmit, + } + ) + des = request.session.get("currentDesignationSelected") + if ( + str(des) == "Associate Professor" + or str(des) == "Professor" + or str(des) == "Assistant Professor" + ): + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGradesProf", + } + ) + return JsonResponse( + { + "message": "Grades uploaded successfully.", + "redirect_url": "/examination/submitGrades", + } + ) + + except Courses.DoesNotExist: + return JsonResponse({"error": "Invalid course ID."}, status=400) + + except Exception as e: + return JsonResponse({"error": f"An error occurred: {e}"}, status=500) + + return JsonResponse( + {"error": "Invalid request. Please upload a CSV file."}, status=400 + ) + diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py new file mode 100644 index 000000000..0a441f642 --- /dev/null +++ b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py @@ -0,0 +1,37 @@ +# Generated by Django 3.1.5 on 2024-10-21 03:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('online_cms', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='student_grades', + name='total_marks', + ), + migrations.AddField( + model_name='student_grades', + name='remarks', + field=models.CharField(max_length=500, null=True), + ), + migrations.AddField( + model_name='student_grades', + name='verified', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='attendance', + name='present', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='gradingscheme', + name='type_of_evaluation', + field=models.CharField(max_length=100), + ), + ] diff --git a/FusionIIIT/applications/online_cms/models.py b/FusionIIIT/applications/online_cms/models.py index 3a63aca08..98d68b24c 100644 --- a/FusionIIIT/applications/online_cms/models.py +++ b/FusionIIIT/applications/online_cms/models.py @@ -267,10 +267,11 @@ class Student_grades(models.Model): semester = models.IntegerField(default=1) year = models.IntegerField(default=2016) roll_no = models.TextField(max_length=2000) - total_marks = models.DecimalField(max_digits=10, decimal_places=2, default=0) grade = models.TextField(max_length=2000) batch = models.IntegerField(default=2021) - + remarks = models.CharField(max_length=500,null=True) + verified = models.BooleanField(default=False) + reSubmit = models.BooleanField(default=True) def __str__(self): return '{} - {}'.format(self.pk, self.course_id) diff --git a/FusionIIIT/templates/examination/announcement_req.html b/FusionIIIT/templates/examination/announcement_req.html index 99bb51e9c..d8482bf62 100644 --- a/FusionIIIT/templates/examination/announcement_req.html +++ b/FusionIIIT/templates/examination/announcement_req.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} diff --git a/FusionIIIT/templates/examination/generate_transcript.html b/FusionIIIT/templates/examination/generate_transcript.html index 6b1cf4a9e..58e012b06 100644 --- a/FusionIIIT/templates/examination/generate_transcript.html +++ b/FusionIIIT/templates/examination/generate_transcript.html @@ -54,9 +54,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/generate_transcript_form.html b/FusionIIIT/templates/examination/generate_transcript_form.html index e0bd04fae..229bd9331 100644 --- a/FusionIIIT/templates/examination/generate_transcript_form.html +++ b/FusionIIIT/templates/examination/generate_transcript_form.html @@ -8,9 +8,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/generate_transcript_students.html b/FusionIIIT/templates/examination/generate_transcript_students.html index 6bc1edd66..f7beb443c 100644 --- a/FusionIIIT/templates/examination/generate_transcript_students.html +++ b/FusionIIIT/templates/examination/generate_transcript_students.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/gradeSubmission.html b/FusionIIIT/templates/examination/gradeSubmission.html index c79b41b7f..ac46c59c7 100644 --- a/FusionIIIT/templates/examination/gradeSubmission.html +++ b/FusionIIIT/templates/examination/gradeSubmission.html @@ -2,17 +2,15 @@ {% block sidetabmenu %} @@ -50,43 +48,109 @@

Submit Results

+ {% comment %}
+
+ +
+ +
{% endcomment %} +
+ + +
- - -
+ + + - - - + -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/gradeSubmissionForm.html b/FusionIIIT/templates/examination/gradeSubmissionForm.html index 6a77bc472..7de9e2788 100644 --- a/FusionIIIT/templates/examination/gradeSubmissionForm.html +++ b/FusionIIIT/templates/examination/gradeSubmissionForm.html @@ -42,9 +42,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement @@ -223,4 +223,4 @@

SUBMIT STUDENT MARKS

window.location.href = "{% url 'examination:updateGrades' %}"; } -{% endblock %} \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/message.html b/FusionIIIT/templates/examination/message.html index 73ca5caa4..6df482710 100644 --- a/FusionIIIT/templates/examination/message.html +++ b/FusionIIIT/templates/examination/message.html @@ -42,9 +42,9 @@
Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/messageProf.html b/FusionIIIT/templates/examination/messageProf.html new file mode 100644 index 000000000..f63749792 --- /dev/null +++ b/FusionIIIT/templates/examination/messageProf.html @@ -0,0 +1,70 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + + +{% endblock %} + +{% block content %} +
+
+
+ {{message}} +
+ +

Please check back later or contact support for assistance.

+
+
+ +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/submit.html b/FusionIIIT/templates/examination/submit.html index 3964b7062..e85d9fbd0 100644 --- a/FusionIIIT/templates/examination/submit.html +++ b/FusionIIIT/templates/examination/submit.html @@ -9,9 +9,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/submitGrade.html b/FusionIIIT/templates/examination/submitGrade.html index e17d06330..817406be6 100644 --- a/FusionIIIT/templates/examination/submitGrade.html +++ b/FusionIIIT/templates/examination/submitGrade.html @@ -9,9 +9,9 @@ Verify - Authenticate Course + {% comment %} Authenticate Course - + {% endcomment %} Announcement diff --git a/FusionIIIT/templates/examination/submitGradeDean.html b/FusionIIIT/templates/examination/submitGradeDean.html new file mode 100644 index 000000000..d3d151581 --- /dev/null +++ b/FusionIIIT/templates/examination/submitGradeDean.html @@ -0,0 +1,113 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + +{% endblock %} + +{% block content %} +

Update Result

+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+ + + +
+ +
+ + + +{% endblock %} + diff --git a/FusionIIIT/templates/examination/submitGradesProf.html b/FusionIIIT/templates/examination/submitGradesProf.html new file mode 100644 index 000000000..c7c41d18a --- /dev/null +++ b/FusionIIIT/templates/examination/submitGradesProf.html @@ -0,0 +1,155 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + +{% endblock %} + +{% block content %} +

Submit Results

+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+ {% comment %}
+
+ +
+ +
{% endcomment %} +
+ +
+ + +
+
+ + + +
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/updateEntergrades.html b/FusionIIIT/templates/examination/updateEntergrades.html index 7e82cf370..b44de523e 100644 --- a/FusionIIIT/templates/examination/updateEntergrades.html +++ b/FusionIIIT/templates/examination/updateEntergrades.html @@ -36,23 +36,26 @@ {% endblock %} @@ -75,9 +78,7 @@

VERIFY STUDENT MARKS

Course ID semester - Total Marks - - + Remarks Grade @@ -89,13 +90,15 @@

VERIFY STUDENT MARKS

{{ registration.batch }} {{ registration.course_id }} {{ registration.semester }} - {{ registration.total_marks }} + {{ registration.remarks }} + + - + {% endfor %} @@ -209,8 +212,9 @@

VERIFY STUDENT MARKS

function finalizeGrades() { // Submit the form document.querySelector('form').submit(); - + console.log("hyee") // Redirect to the URL that handles Excel download + alert('Course verification Successful please refresh the page') window.location.href = "{% url 'examination:submit' %}"; } diff --git a/FusionIIIT/templates/examination/updateEntergradesDean.html b/FusionIIIT/templates/examination/updateEntergradesDean.html new file mode 100644 index 000000000..d4296040d --- /dev/null +++ b/FusionIIIT/templates/examination/updateEntergradesDean.html @@ -0,0 +1,226 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + + +{% endblock %} + +{% block content %} +
+

VERIFY STUDENT MARKS

+ {% if registrations %} +
+ {% csrf_token %} +
+ + + + + + + + + + + + + {% for registration in registrations %} + + + + + + + + + + {% endfor %} + +
+ Student ID + + + batchCourse IDsemester + Remarks + Grade
{{ registration.roll_no }}{{ registration.batch }}{{ registration.course_id }}{{ registration.semester }}{{ registration.remarks }} + + + + +
+
+
+ + +
+ {% comment %} {% endcomment %} + + +
+
+ +
+ {% else %} +
+
+ NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER +
+

Please check back later or contact support for assistance.

+
+ {% endif %} +
+ + + + + + +{% endblock %} \ No newline at end of file From 922bb824fedb791f73bab47641a30e836d76a558 Mon Sep 17 00:00:00 2001 From: grvup Date: Wed, 23 Oct 2024 10:02:56 +0530 Subject: [PATCH 13/44] fix: resolved migrations error --- .../migrations/0002_auto_20241021_0331.py | 37 ------------------- ...020_1126.py => 0002_auto_20241023_1002.py} | 21 ++++++++++- 2 files changed, 20 insertions(+), 38 deletions(-) delete mode 100644 FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py rename FusionIIIT/applications/online_cms/migrations/{0002_auto_20241020_1126.py => 0002_auto_20241023_1002.py} (68%) diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py deleted file mode 100644 index 0a441f642..000000000 --- a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-21 03:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('online_cms', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='student_grades', - name='total_marks', - ), - migrations.AddField( - model_name='student_grades', - name='remarks', - field=models.CharField(max_length=500, null=True), - ), - migrations.AddField( - model_name='student_grades', - name='verified', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='attendance', - name='present', - field=models.IntegerField(default=0), - ), - migrations.AlterField( - model_name='gradingscheme', - name='type_of_evaluation', - field=models.CharField(max_length=100), - ), - ] diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py similarity index 68% rename from FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py rename to FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py index 2e166954c..659201064 100644 --- a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py +++ b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 +# Generated by Django 3.1.5 on 2024-10-23 10:02 from django.db import migrations, models import django.db.models.deletion @@ -12,11 +12,30 @@ class Migration(migrations.Migration): ] operations = [ + migrations.RemoveField( + model_name='student_grades', + name='total_marks', + ), migrations.AddField( model_name='attendance', name='no_of_attendance', field=models.IntegerField(default=1), ), + migrations.AddField( + model_name='student_grades', + name='reSubmit', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='student_grades', + name='remarks', + field=models.CharField(max_length=500, null=True), + ), + migrations.AddField( + model_name='student_grades', + name='verified', + field=models.BooleanField(default=False), + ), migrations.AlterField( model_name='attendance', name='present', From b2612e2fedbe44cce968f0f70f3b9ab6d5d7051e Mon Sep 17 00:00:00 2001 From: grvup Date: Wed, 23 Oct 2024 10:02:56 +0530 Subject: [PATCH 14/44] fix: resolved migrations error --- .../migrations/0002_auto_20241021_0331.py | 37 ------------------- ...020_1126.py => 0002_auto_20241023_1002.py} | 21 ++++++++++- 2 files changed, 20 insertions(+), 38 deletions(-) delete mode 100644 FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py rename FusionIIIT/applications/online_cms/migrations/{0002_auto_20241020_1126.py => 0002_auto_20241023_1002.py} (68%) diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py deleted file mode 100644 index 0a441f642..000000000 --- a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-21 03:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('online_cms', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='student_grades', - name='total_marks', - ), - migrations.AddField( - model_name='student_grades', - name='remarks', - field=models.CharField(max_length=500, null=True), - ), - migrations.AddField( - model_name='student_grades', - name='verified', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='attendance', - name='present', - field=models.IntegerField(default=0), - ), - migrations.AlterField( - model_name='gradingscheme', - name='type_of_evaluation', - field=models.CharField(max_length=100), - ), - ] diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py similarity index 68% rename from FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py rename to FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py index 2e166954c..659201064 100644 --- a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241020_1126.py +++ b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 +# Generated by Django 3.1.5 on 2024-10-23 10:02 from django.db import migrations, models import django.db.models.deletion @@ -12,11 +12,30 @@ class Migration(migrations.Migration): ] operations = [ + migrations.RemoveField( + model_name='student_grades', + name='total_marks', + ), migrations.AddField( model_name='attendance', name='no_of_attendance', field=models.IntegerField(default=1), ), + migrations.AddField( + model_name='student_grades', + name='reSubmit', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='student_grades', + name='remarks', + field=models.CharField(max_length=500, null=True), + ), + migrations.AddField( + model_name='student_grades', + name='verified', + field=models.BooleanField(default=False), + ), migrations.AlterField( model_name='attendance', name='present', From 080a9a3778a25846604d882bb310769e6fbc8cbe Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Wed, 23 Oct 2024 21:26:13 +0530 Subject: [PATCH 15/44] Created Validation Page for Dean --- FusionIIIT/applications/examination/urls.py | 3 + FusionIIIT/applications/examination/views.py | 122 ++++++++++- .../examination/gradeSubmission.html | 2 +- .../examination/submitGradeDean.html | 6 +- .../examination/updateEntergrades.html | 4 + .../examination/updateEntergradesDean.html | 7 +- .../templates/examination/validation.html | 78 +++++++ .../examination/validationSubmit.html | 192 ++++++++++++++++++ 8 files changed, 402 insertions(+), 12 deletions(-) create mode 100644 FusionIIIT/templates/examination/validation.html create mode 100644 FusionIIIT/templates/examination/validationSubmit.html diff --git a/FusionIIIT/applications/examination/urls.py b/FusionIIIT/applications/examination/urls.py index caae0dc67..cf8a12637 100644 --- a/FusionIIIT/applications/examination/urls.py +++ b/FusionIIIT/applications/examination/urls.py @@ -59,5 +59,8 @@ path('verifyGradesDean/',views.verifyGradesDean,name='verifyGradesDean'), path('updateEntergradesDean/',views.updateEntergradesDean,name='updateEnterGradesDean'), path('upload_grades_prof/',views.upload_grades_prof,name='upload_grades_prof'), + path('validateDean/',views.validateDean,name='validateDean'), + path('validateDeanSubmit/',views.validateDeanSubmit,name='validateDeanSubmit'), + ] diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py index cc813c4c1..6d2f67491 100644 --- a/FusionIIIT/applications/examination/views.py +++ b/FusionIIIT/applications/examination/views.py @@ -636,7 +636,7 @@ def post(self, request): course_ids = request.POST.getlist('course_ids[]') grades = request.POST.getlist('grades[]') allow_resubmission = request.POST.get('allow_resubmission', 'NO') - + if len(student_ids) != len(semester_ids) != len(course_ids) != len(grades): return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) @@ -970,7 +970,7 @@ def submitGradesProf(request): # unique_course_ids = course_registration.objects.values( # 'course_id').distinct() working_years = course_registration.objects.values("working_year").distinct() - + course_ids_final=course_registration.objects.filter() # Cast the course IDs to integers unique_course_ids = unique_course_ids.annotate( course_id_int=Cast("course_id", IntegerField()) @@ -1154,3 +1154,121 @@ def upload_grades_prof(request): {"error": "Invalid request. Please upload a CSV file."}, status=400 ) +def validateDean(request): + unique_course_ids = Student_grades.objects.filter(verified=True).values("course_id").distinct() + + # Cast the course IDs to integers + unique_course_ids = unique_course_ids.annotate( + course_id_int=Cast("course_id", IntegerField()) + ) + + # Retrieve course names and course codes based on unique course IDs + + # print(unique_course_ids) + courses_info = Courses.objects.filter( + id__in=unique_course_ids.values_list("course_id_int", flat=True) + ) + working_years = course_registration.objects.values("working_year").distinct() + + unique_batch_ids = Student_grades.objects.values("batch").distinct() + + context = {"courses_info": courses_info, "working_years": working_years} + + return render(request, "../templates/examination/validation.html", context) + + +def validateDeanSubmit(request): + if request.method == "POST" and request.FILES.get("csv_file"): + csv_file = request.FILES["csv_file"] + + if not csv_file.name.endswith(".csv"): + return JsonResponse( + {"error": "Invalid file format. Please upload a CSV file."}, status=400 + ) + + course_id = request.POST.get("course") + academic_year = request.POST.get("year") + # semester = request.POST.get('semester') + print(academic_year) + if academic_year is None or not academic_year.isdigit(): + return JsonResponse( + {"error": "Academic year must be a valid number."}, status=400 + ) + + if not course_id or not academic_year: + return JsonResponse( + {"error": "Course ID and Academic Year are required."}, status=400 + ) + + # courses_info = Courses.objects.get(id=course_id) + + # courses = Student_grades.objects.filter( + # course_id=courses_info.id, year=academic_year + # ) + students = course_registration.objects.filter( + course_id_id=course_id, working_year=academic_year + ) + + + + try: + # Parse the CSV file + decoded_file = csv_file.read().decode("utf-8").splitlines() + reader = csv.DictReader(decoded_file) + + required_columns = ["roll_no", "name", "grade", "remarks"] + if not all(column in reader.fieldnames for column in required_columns): + return JsonResponse( + { + "error": "CSV file must contain the following columns: roll_no, name, grade, remarks." + }, + status=400, + ) + semester = students.first().semester_id_id + mismatch=[] + for row in reader: + roll_no = row["roll_no"] + grade = row["grade"] + remarks = row["remarks"] + batch_prefix = roll_no[:2] + batch = int(f"20{batch_prefix}") + Student_grades.objects.filter( + roll_no=roll_no, + course_id_id=course_id, + year=academic_year, + batch=batch, + ) + student_grade = Student_grades.objects.get( + roll_no=roll_no, + course_id_id=course_id, + year=academic_year, + batch=batch + ) + if student_grade.grade != grade: + mismatch.append({ + "roll_no": roll_no, + "csv_grade": grade, + "db_grade": student_grade.grade, + "remarks": remarks, + "batch": batch, + "semester": semester, + "course_id": course_id, + }) + if not mismatch: + message = "There Are no Mismatches" + context = { + "message":message, + } + return render(request, "../templates/examination/message.html", context) + context = { + "mismatch": mismatch, + } + return render(request, "../templates/examination/validationSubmit.html", context) + + except Exception as e: + + error_message = f"An error occurred while processing the file: {str(e)}" + context = { + "message": error_message, + } + return render(request, "../templates/examination/message.html", context) diff --git a/FusionIIIT/templates/examination/gradeSubmission.html b/FusionIIIT/templates/examination/gradeSubmission.html index ac46c59c7..76145649d 100644 --- a/FusionIIIT/templates/examination/gradeSubmission.html +++ b/FusionIIIT/templates/examination/gradeSubmission.html @@ -110,7 +110,7 @@

Submit Results

alert('Please select course, year, and upload a CSV file.'); return; } - if (!confirm('Are you sure you want to submit?')) { + if (!confirm('Are you sure you want to submit? This cannot be Undone')) { // If the user clicks "Cancel", do not proceed return; } diff --git a/FusionIIIT/templates/examination/submitGradeDean.html b/FusionIIIT/templates/examination/submitGradeDean.html index d3d151581..f9819cde5 100644 --- a/FusionIIIT/templates/examination/submitGradeDean.html +++ b/FusionIIIT/templates/examination/submitGradeDean.html @@ -6,12 +6,12 @@ Submit {% endcomment %} - Verify + Verify - {% comment %} Authenticate Course + Validate - {% endcomment %} + {% comment %} Announcement diff --git a/FusionIIIT/templates/examination/updateEntergrades.html b/FusionIIIT/templates/examination/updateEntergrades.html index b44de523e..858d63050 100644 --- a/FusionIIIT/templates/examination/updateEntergrades.html +++ b/FusionIIIT/templates/examination/updateEntergrades.html @@ -211,6 +211,10 @@

VERIFY STUDENT MARKS

function finalizeGrades() { // Submit the form + //if (!confirm('Are you sure you want to submit? This cannot be Undone')) { + // If the user clicks "Cancel", do not proceed + // return; + // } document.querySelector('form').submit(); console.log("hyee") // Redirect to the URL that handles Excel download diff --git a/FusionIIIT/templates/examination/updateEntergradesDean.html b/FusionIIIT/templates/examination/updateEntergradesDean.html index d4296040d..83008cd87 100644 --- a/FusionIIIT/templates/examination/updateEntergradesDean.html +++ b/FusionIIIT/templates/examination/updateEntergradesDean.html @@ -48,14 +48,9 @@
{% endcomment %} - {% if 'acadadmin' == request.user.extrainfo.user_type %} - Announcement - - - Generate Transcript + Validate - {% endif %} {% endblock %} diff --git a/FusionIIIT/templates/examination/validation.html b/FusionIIIT/templates/examination/validation.html new file mode 100644 index 000000000..d0b491394 --- /dev/null +++ b/FusionIIIT/templates/examination/validation.html @@ -0,0 +1,78 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + +{% endblock %} + +{% block content %} +

Validate Results

+
+ +
+ {% csrf_token %} +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+ + +
+ +
+ +
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/examination/validationSubmit.html b/FusionIIIT/templates/examination/validationSubmit.html new file mode 100644 index 000000000..039cff75a --- /dev/null +++ b/FusionIIIT/templates/examination/validationSubmit.html @@ -0,0 +1,192 @@ +{% extends 'examination/base.html' %} + +{% block sidetabmenu %} + + +{% endblock %} + +{% block content %} +
+

MISMATCHES IN STUDENT MARKS

+ {% if mismatch %} +
+ {% csrf_token %} +
+ + + + + + + + + + + + + + + {% for registration in mismatch %} + + + + + + + + + + {% endfor %} + +
+ Student ID + + + batchCourse IDsemester + Remarks + Grade in DBGrade in CSV
{{ registration.roll_no }}{{ registration.batch }}{{ registration.course_id }}{{ registration.semester }}{{ registration.remarks }}{{ registration.db_grade }}{{ registration.csv_grade }}
+
+ {% comment %} {% endcomment %} + {% else %} +
+
+ NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER +
+

Please check back later or contact support for assistance.

+
+ {% endif %} +
+ + + + + + +{% endblock %} \ No newline at end of file From f8c95af39202d1dfb97a3cea896f722259485a00 Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Wed, 23 Oct 2024 21:42:21 +0530 Subject: [PATCH 16/44] kuldeep's suggestions --- .../migrations/0002_auto_20241021_0331.py | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py deleted file mode 100644 index 0a441f642..000000000 --- a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241021_0331.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-21 03:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('online_cms', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='student_grades', - name='total_marks', - ), - migrations.AddField( - model_name='student_grades', - name='remarks', - field=models.CharField(max_length=500, null=True), - ), - migrations.AddField( - model_name='student_grades', - name='verified', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='attendance', - name='present', - field=models.IntegerField(default=0), - ), - migrations.AlterField( - model_name='gradingscheme', - name='type_of_evaluation', - field=models.CharField(max_length=100), - ), - ] From a9b48d3a26e955302e50731d6203a4ae738692d5 Mon Sep 17 00:00:00 2001 From: Abhyuday Singh Date: Thu, 24 Oct 2024 02:52:16 +0530 Subject: [PATCH 17/44] Bugs fixed in redirecting of pages --- FusionIIIT/applications/examination/views.py | 42 +++++++------ FusionIIIT/templates/examination/message.html | 7 +-- .../templates/examination/messageDean.html | 63 +++++++++++++++++++ .../templates/examination/validation.html | 2 +- .../examination/validationSubmit.html | 2 +- 5 files changed, 90 insertions(+), 26 deletions(-) create mode 100644 FusionIIIT/templates/examination/messageDean.html diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py index 6d2f67491..b4d8cdb94 100644 --- a/FusionIIIT/applications/examination/views.py +++ b/FusionIIIT/applications/examination/views.py @@ -1035,7 +1035,7 @@ def updateEntergradesDean(request): if not course_present: context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} - return render(request, "../templates/examination/message.html", context) + return render(request, "../templates/examination/messageDean.html", context) context = {"registrations": course_present} @@ -1182,23 +1182,29 @@ def validateDeanSubmit(request): csv_file = request.FILES["csv_file"] if not csv_file.name.endswith(".csv"): - return JsonResponse( - {"error": "Invalid file format. Please upload a CSV file."}, status=400 - ) + message = "Please Submit a csv file " + context = { + "message":message, + } + return render(request, "../templates/examination/messageDean.html", context) course_id = request.POST.get("course") academic_year = request.POST.get("year") # semester = request.POST.get('semester') print(academic_year) if academic_year is None or not academic_year.isdigit(): - return JsonResponse( - {"error": "Academic year must be a valid number."}, status=400 - ) + message = "Academic Year must be a number" + context = { + "message":message, + } + return render(request, "../templates/examination/messageDean.html", context) if not course_id or not academic_year: - return JsonResponse( - {"error": "Course ID and Academic Year are required."}, status=400 - ) + message = "Course and Academic year are required" + context = { + "message":message, + } + return render(request, "../templates/examination/messageDean.html", context) # courses_info = Courses.objects.get(id=course_id) @@ -1218,12 +1224,12 @@ def validateDeanSubmit(request): required_columns = ["roll_no", "name", "grade", "remarks"] if not all(column in reader.fieldnames for column in required_columns): - return JsonResponse( - { - "error": "CSV file must contain the following columns: roll_no, name, grade, remarks." - }, - status=400, - ) + message = "CSV file must contain the following columns: roll_no, name, grade, remarks." + context = { + "message":message, + } + return render(request, "../templates/examination/messageDean.html", context) + semester = students.first().semester_id_id mismatch=[] for row in reader: @@ -1259,7 +1265,7 @@ def validateDeanSubmit(request): context = { "message":message, } - return render(request, "../templates/examination/message.html", context) + return render(request, "../templates/examination/messageDean.html", context) context = { "mismatch": mismatch, } @@ -1271,4 +1277,4 @@ def validateDeanSubmit(request): context = { "message": error_message, } - return render(request, "../templates/examination/message.html", context) + return render(request, "../templates/examination/messageDean.html", context) diff --git a/FusionIIIT/templates/examination/message.html b/FusionIIIT/templates/examination/message.html index 6df482710..13c2132e8 100644 --- a/FusionIIIT/templates/examination/message.html +++ b/FusionIIIT/templates/examination/message.html @@ -36,17 +36,12 @@ diff --git a/FusionIIIT/templates/examination/validationSubmit.html b/FusionIIIT/templates/examination/validationSubmit.html index 039cff75a..06e43c4aa 100644 --- a/FusionIIIT/templates/examination/validationSubmit.html +++ b/FusionIIIT/templates/examination/validationSubmit.html @@ -44,7 +44,7 @@ {% endcomment %} - Validate + Validate
From c0bc0078967f76bb3ca1778d5edb64269fab2082 Mon Sep 17 00:00:00 2001 From: grvup Date: Wed, 23 Oct 2024 15:52:22 +0530 Subject: [PATCH 18/44] implemented logic for adding course Instructor to courses --- .../programme_curriculum/forms.py | 10 +++++- .../applications/programme_curriculum/urls.py | 1 + .../programme_curriculum/views.py | 18 ++++++++-- .../acad_admin/add_course_instructor.html | 33 +++++++++++++++++++ .../acad_admin/common.html | 3 ++ 5 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 FusionIIIT/templates/programme_curriculum/acad_admin/add_course_instructor.html diff --git a/FusionIIIT/applications/programme_curriculum/forms.py b/FusionIIIT/applications/programme_curriculum/forms.py index d6c8a53e0..38d6b9e11 100644 --- a/FusionIIIT/applications/programme_curriculum/forms.py +++ b/FusionIIIT/applications/programme_curriculum/forms.py @@ -3,7 +3,7 @@ from django.forms import ModelForm, widgets from django.forms import Form, ValidationError from django.forms.models import ModelChoiceField -from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES,NewProposalFile,Proposal_Tracking +from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES,NewProposalFile,Proposal_Tracking, CourseInstructor from django.utils.translation import gettext_lazy as _ from django.contrib.auth.models import User from applications.globals.models import (DepartmentInfo, Designation,ExtraInfo, Faculty, HoldsDesignation) @@ -382,6 +382,14 @@ def clean(self): return self.cleaned_data + +class CourseInstructorForm(forms.ModelForm): + course_id = forms.ModelChoiceField(queryset=Course.objects.all(), label="Select Course", empty_label="Choose a course") + instructor_id = forms.ModelChoiceField(queryset=ExtraInfo.objects.filter(user_type='faculty'), label="Select Instructor", empty_label="Choose an instructor") + batch_id = forms.ModelChoiceField(queryset=Batch.objects.all(), label="Select Batch", empty_label="Choose a batch") + class Meta: + model = CourseInstructor + fields = ['course_id', 'instructor_id', 'batch_id'] # def sed(self): diff --git a/FusionIIIT/applications/programme_curriculum/urls.py b/FusionIIIT/applications/programme_curriculum/urls.py index 4c06b2b1b..8f0dc51b8 100644 --- a/FusionIIIT/applications/programme_curriculum/urls.py +++ b/FusionIIIT/applications/programme_curriculum/urls.py @@ -39,6 +39,7 @@ path('admin_add_courseslot/', views.add_courseslot_form, name='add_courseslot_form'), path('admin_add_course/', views.add_course_form, name='add_course_form'), path('admin_add_batch/', views.add_batch_form, name='add_batch_form'), + path('admin_add_course_instructor/', views.add_course_instructor, name='add_course_instructor'), path('admin_update_course//', views.update_course_form, name='update_course_form'), path('admin_edit_curriculum//', views.edit_curriculum_form, name='edit_curriculum_form'), diff --git a/FusionIIIT/applications/programme_curriculum/views.py b/FusionIIIT/applications/programme_curriculum/views.py index 602bbc866..36c88adae 100644 --- a/FusionIIIT/applications/programme_curriculum/views.py +++ b/FusionIIIT/applications/programme_curriculum/views.py @@ -7,7 +7,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot,NewProposalFile,Proposal_Tracking -from .forms import ProgrammeForm, DisciplineForm, CurriculumForm, SemesterForm, CourseForm, BatchForm, CourseSlotForm, ReplicateCurriculumForm,NewCourseProposalFile,CourseProposalTrackingFile +from .forms import ProgrammeForm, DisciplineForm, CurriculumForm, SemesterForm, CourseForm, BatchForm, CourseSlotForm, ReplicateCurriculumForm,NewCourseProposalFile,CourseProposalTrackingFile, CourseInstructorForm from .filters import CourseFilter, BatchFilter, CurriculumFilter from django.db import IntegrityError from django.utils import timezone @@ -1621,4 +1621,18 @@ def file_unarchive(request,FileId): file = get_object_or_404(NewProposalFile,Q(id = FileId)) file.is_archive=False file.save() - return HttpResponseRedirect('/programme_curriculum/view_course_proposal_forms/') \ No newline at end of file + return HttpResponseRedirect('/programme_curriculum/view_course_proposal_forms/') + +@login_required(login_url='/accounts/login') +def add_course_instructor(request): + if request.session['currentDesignationSelected'] == "acadadmin": + if request.method == 'POST': + form = CourseInstructorForm(request.POST) + if form.is_valid(): + form.save() # Save the form data to the database + return redirect('/programme_curriculum/') # Redirect to a success page after saving + else: + form = CourseInstructorForm() + + return render(request, 'programme_curriculum/acad_admin/add_course_instructor.html', {'form': form}) + return HttpResponseRedirect('/programme_curriculum/') \ No newline at end of file diff --git a/FusionIIIT/templates/programme_curriculum/acad_admin/add_course_instructor.html b/FusionIIIT/templates/programme_curriculum/acad_admin/add_course_instructor.html new file mode 100644 index 000000000..6dd337fcc --- /dev/null +++ b/FusionIIIT/templates/programme_curriculum/acad_admin/add_course_instructor.html @@ -0,0 +1,33 @@ +{% extends 'programme_curriculum/acad_admin/common.html' %} +{% block content %} +
+

Course Instructor Form

+
+ + {% if form.errors %} + + {% endif %} +
+ {% csrf_token %} + {{ form.as_p }} + +
+
+ +{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/programme_curriculum/acad_admin/common.html b/FusionIIIT/templates/programme_curriculum/acad_admin/common.html index c02082f22..52c2e3710 100644 --- a/FusionIIIT/templates/programme_curriculum/acad_admin/common.html +++ b/FusionIIIT/templates/programme_curriculum/acad_admin/common.html @@ -51,6 +51,9 @@ Courses + Add Course Instructor + + {% endblock %} {% comment %}The Tab-Menu ends here!{% endcomment %} From 0c55dfc0a546064609bbef9276f43a1b4133d04c Mon Sep 17 00:00:00 2001 From: grvup Date: Wed, 23 Oct 2024 18:34:52 +0530 Subject: [PATCH 19/44] feat[ac-1]: implemented logic for updating course instructor --- .../programme_curriculum/filters.py | 15 +++- .../applications/programme_curriculum/urls.py | 2 + .../programme_curriculum/views.py | 69 +++++++++++++++- .../admin_view_all_course_instructor.html | 80 +++++++++++++++++++ .../acad_admin/common.html | 2 +- 5 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_all_course_instructor.html diff --git a/FusionIIIT/applications/programme_curriculum/filters.py b/FusionIIIT/applications/programme_curriculum/filters.py index d1da0cca5..93a0da69e 100644 --- a/FusionIIIT/applications/programme_curriculum/filters.py +++ b/FusionIIIT/applications/programme_curriculum/filters.py @@ -1,6 +1,6 @@ import django_filters from django import forms -from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES +from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES,CourseInstructor class CourseFilter(django_filters.FilterSet): class Meta: @@ -40,4 +40,15 @@ class Meta: 'name' : forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,}), 'name' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,})), 'code' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Version','max_length': 10,})), - } \ No newline at end of file + } + +class CourseInstructorFilter(django_filters.FilterSet): + course_id = django_filters.CharFilter(field_name='course_id__code', lookup_expr='icontains', label='Course Code') # Course code filter + instructor_id = django_filters.CharFilter(field_name='instructor_id__id', lookup_expr='icontains', label='Instructor Name') # Assuming 'username' field in related faculty model + batch_id = django_filters.CharFilter(field_name='batch_id__name', lookup_expr='icontains', label='Programme') # Batch name, adjust if necessary + year = django_filters.NumberFilter(field_name='batch_id__year', lookup_expr='exact', label='Year') # Year filter + discipline_acronym = django_filters.CharFilter(field_name='batch_id__discipline__acronym', lookup_expr='icontains', label='Branch') # Discipline acronym filter + + class Meta: + model = CourseInstructor + fields = ['course_id', 'instructor_id', 'batch_id', 'year', 'discipline_acronym'] diff --git a/FusionIIIT/applications/programme_curriculum/urls.py b/FusionIIIT/applications/programme_curriculum/urls.py index 8f0dc51b8..d89953767 100644 --- a/FusionIIIT/applications/programme_curriculum/urls.py +++ b/FusionIIIT/applications/programme_curriculum/urls.py @@ -31,6 +31,7 @@ path('admin_course//', views.admin_view_a_course, name='admin_view_a_course'), path('admin_disciplines/', views.admin_view_all_discplines, name='admin_view_all_discplines'), path('admin_batches/', views.admin_view_all_batches, name='admin_view_all_batches'), + path('admin_instructor/',views.admin_view_all_course_instructor,name='admin_view_all_course_instructor'), path('admin_add_programme/', views.add_programme_form, name='add_programme_form'), path('admin_add_discipline/', views.add_discipline_form, name='add_discipline_form'), @@ -41,6 +42,7 @@ path('admin_add_batch/', views.add_batch_form, name='add_batch_form'), path('admin_add_course_instructor/', views.add_course_instructor, name='add_course_instructor'), + path('admin_update_course_instructor//', views.update_course_instructor_form, name='update_course_instructor_form'), path('admin_update_course//', views.update_course_form, name='update_course_form'), path('admin_edit_curriculum//', views.edit_curriculum_form, name='edit_curriculum_form'), path('admin_edit_programme//', views.edit_programme_form, name='edit_programme_form'), diff --git a/FusionIIIT/applications/programme_curriculum/views.py b/FusionIIIT/applications/programme_curriculum/views.py index 36c88adae..dda6eeb38 100644 --- a/FusionIIIT/applications/programme_curriculum/views.py +++ b/FusionIIIT/applications/programme_curriculum/views.py @@ -6,9 +6,9 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User -from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot,NewProposalFile,Proposal_Tracking +from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot,NewProposalFile,Proposal_Tracking,CourseInstructor from .forms import ProgrammeForm, DisciplineForm, CurriculumForm, SemesterForm, CourseForm, BatchForm, CourseSlotForm, ReplicateCurriculumForm,NewCourseProposalFile,CourseProposalTrackingFile, CourseInstructorForm -from .filters import CourseFilter, BatchFilter, CurriculumFilter +from .filters import CourseFilter, BatchFilter, CurriculumFilter,CourseInstructorFilter from django.db import IntegrityError from django.utils import timezone @@ -17,6 +17,33 @@ from applications.globals.models import (DepartmentInfo, Designation,ExtraInfo, Faculty, HoldsDesignation) # ------------module-functions---------------# + + + +@login_required(login_url='/accounts/login') +def admin_view_all_course_instructor(request): + # Fetch all records from the CourseInstructor table + course_instructors = CourseInstructor.objects.all() + + # Passing the data to the template + context = { + 'course_instructors': course_instructors, + } + + return render(request, 'programme_curriculum/acad_admin/admin_view_all_course_instructor.html', context) +@login_required(login_url='/accounts/login') +def update_course_instructor_form(request, id): + + instructor = get_object_or_404(CourseInstructor, id=id) + course_instructors = CourseInstructor.objects.all() + + # Passing the data to the template + context = { + 'course_instructors': course_instructors, + } + # Handle the update logic here + return render(request, 'programme_curriculum/acad_admin/admin_view_all_course_instructor.html', context) + @login_required(login_url='/accounts/login') def programme_curriculum(request): """ @@ -1635,4 +1662,42 @@ def add_course_instructor(request): form = CourseInstructorForm() return render(request, 'programme_curriculum/acad_admin/add_course_instructor.html', {'form': form}) + return HttpResponseRedirect('/programme_curriculum/') + +@login_required(login_url='/accounts/login') +def admin_view_all_course_instructor(request): + if request.session.get('currentDesignationSelected') == "acadadmin": + course_instructors = CourseInstructor.objects.all() + + # Apply filtering + course_instructor_filter = CourseInstructorFilter(request.GET, queryset=course_instructors) + filtered_course_instructors = course_instructor_filter.qs + + return render(request, 'programme_curriculum/acad_admin/admin_view_all_course_instructor.html', { + 'course_instructors': filtered_course_instructors, + 'courseinstructorfilter': course_instructor_filter + }) + + return HttpResponseRedirect('/programme_curriculum/') + +@login_required(login_url='/accounts/login') +def update_course_instructor_form(request, instructor_id): + + if request.session.get('currentDesignationSelected') == "acadadmin": + # Retrieve the CourseInstructor object or return 404 if not found + course_instructor = get_object_or_404(CourseInstructor, id=instructor_id) + + if request.method == 'POST': + # Bind the form to the POST data for validation and save + form = CourseInstructorForm(request.POST, instance=course_instructor) + if form.is_valid(): + form.save() # Save the updated data to the database + return redirect('/programme_curriculum/') # Redirect after successful update + else: + # Create the form with existing data (pre-populated) + form = CourseInstructorForm(instance=course_instructor) + + return render(request, 'programme_curriculum/acad_admin/add_course_instructor.html', {'form': form, 'instructor': course_instructor}) + + # Redirect to the main page if the user is not 'acadadmin' return HttpResponseRedirect('/programme_curriculum/') \ No newline at end of file diff --git a/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_all_course_instructor.html b/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_all_course_instructor.html new file mode 100644 index 000000000..ffabc68d3 --- /dev/null +++ b/FusionIIIT/templates/programme_curriculum/acad_admin/admin_view_all_course_instructor.html @@ -0,0 +1,80 @@ +{% extends 'programme_curriculum/acad_admin/common.html' %} +{% block content %} + +
+
+
+ + + + + + + + + + + + + + {% for instructor in course_instructors %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +

Course Code

Course Name

Course Version

Instructor

Batch

Year

{{ instructor.course_id.code }}{{ instructor.course_id.name }}{{ instructor.course_id.version }}{{ instructor.instructor_id.id }}{{ instructor.batch_id.name }}   {{ instructor.batch_id.discipline.acronym }}{{ instructor.batch_id.year }} + +
EDIT
+ +
+
No Course Instructors Available
+
+
+
+{% endblock %} + +{% block rightcontent %} +
+
+
+
+ +
ADD COURSE INSTRUCTOR
+ +
+
FILTER SEARCH
+
+ {{ courseinstructorfilter.form }} +
+ +
+
+
+{% endblock %} diff --git a/FusionIIIT/templates/programme_curriculum/acad_admin/common.html b/FusionIIIT/templates/programme_curriculum/acad_admin/common.html index 52c2e3710..d6fd581f2 100644 --- a/FusionIIIT/templates/programme_curriculum/acad_admin/common.html +++ b/FusionIIIT/templates/programme_curriculum/acad_admin/common.html @@ -51,7 +51,7 @@ Courses - Add Course Instructor + Course Instructor From 0e6b1f0e8f306b300f5ce105913779f643b47e45 Mon Sep 17 00:00:00 2001 From: ramGopal <129493911+ramG-reddy@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:38:29 +0530 Subject: [PATCH 20/44] AC-2 essential changes (#1640) * Implemented suggested changes in AC-2 - Important Validations - Random elective allocation algo implementation in utils - New page for starting allocation process * migrations for previous changes * field changes in random allocation algo and start allocation form * Implemented the preregistration algorithm * Added allocated course viewing page - fixed bugs in algo * Restricted scope of the algo to only Open electives --------- Co-authored-by: ChallaBharadwajReddy --- .../academic_information/utils.py | 135 ++++++++++++++ .../academic_information/views.py | 10 +- .../academic_procedures/models.py | 4 +- .../migrations/0002_course_max_seats.py | 18 ++ .../programme_curriculum/models.py | 2 +- .../auto_pre_registration.html | 5 +- FusionIIIT/templates/ais/ais.html | 10 + .../ais/start_elective_allocation.html | 173 ++++++++++++++++++ 8 files changed, 351 insertions(+), 6 deletions(-) create mode 100644 FusionIIIT/applications/academic_information/utils.py create mode 100644 FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py create mode 100644 FusionIIIT/templates/ais/start_elective_allocation.html diff --git a/FusionIIIT/applications/academic_information/utils.py b/FusionIIIT/applications/academic_information/utils.py new file mode 100644 index 000000000..2fed73044 --- /dev/null +++ b/FusionIIIT/applications/academic_information/utils.py @@ -0,0 +1,135 @@ +from applications.academic_information.models import (Calendar, Student,Curriculum_Instructor, Curriculum, + Student_attendance) +from ..academic_procedures.models import (BranchChange, CoursesMtech, InitialRegistration, StudentRegistrationChecks, + Register, Thesis, FinalRegistration, ThesisTopicProcess, + Constants, FeePayments, TeachingCreditRegistration, SemesterMarks, + MarkSubmissionCheck, Dues,AssistantshipClaim, MTechGraduateSeminarReport, + PhDProgressExamination,CourseRequested, course_registration, MessDue, Assistantship_status , backlog_course,) + +from applications.programme_curriculum.models import(Course,CourseSlot,Batch,Semester) +from django.http import HttpResponse, JsonResponse +from django.utils import timezone +from django.core import serializers +from django.db.models import Q +import datetime +import random +from django.db import transaction +time = timezone.now() +def check_for_registration_complete (request): + batch = int(request.POST.get('batch')) + sem = int(request.POST.get('sem')) + year = request.POST.get('year') + + + date = time.date() + + try: + + pre_registration_date = Calendar.objects.all().filter(description=f"Pre Registration {sem} {year}").first() + prd_start_date = pre_registration_date.from_date + prd_end_date = pre_registration_date.to_date + + if date=prd_start_date and date<=prd_end_date: + return JsonResponse({'status':-1 , "message":"registration is under process"}) + + if course_registration.objects.filter(Q(semester_id__semester_no = sem) & Q(student_id__batch = batch)).exists() : + return JsonResponse({'status':2,"message":"courses already allocated"}) + + return JsonResponse({"status":1 , "message" : "courses not yet allocated"}) + except : + return JsonResponse({"status":-3, "message" : "No such registration found"}) + +@transaction.atomic +def random_algo(batch,sem,year,course_slot) : + unique_course = InitialRegistration.objects.filter(Q(semester_id__semester_no = sem) & Q( course_slot_id__name = course_slot ) & Q(student_id__batch = batch)).values_list('course_id',flat=True).distinct() + max_seats={} + seats_alloted = {} + present_priority = {} + next_priority = {} + total_seats = 0 + for course in unique_course : + max_seats[course] = Course.objects.get(id=course).max_seats + total_seats+=max_seats[course] + seats_alloted[course] = 0 + present_priority[course] = [] + next_priority[course] = [] + + priority_1 = InitialRegistration.objects.filter(Q(semester_id__semester_no = sem) & Q( course_slot_id__name = course_slot ) & Q(student_id__batch = batch) & Q(priority=1)) + rem=len(priority_1) + if rem > total_seats : + return -1 + + for p in priority_1 : + present_priority[p.course_id.id].append([p.student_id.id.id,p.course_slot_id.id]) + with transaction.atomic() : + p_priority = 1 + while rem > 0 : + for course in present_priority : + while(len(present_priority[course])) : + random_student_selected = random.choice(present_priority[course]) + + present_priority[course].remove(random_student_selected) + + if seats_alloted[course] < max_seats[course] : + stud = Student.objects.get(id__id = random_student_selected[0]) + curriculum_object = Student.objects.get(id__id = random_student_selected[0]).batch_id.curriculum + course_object = Course.objects.get(id=course) + course_slot_object = CourseSlot.objects.get(id = random_student_selected[1]) + semester_object = Semester.objects.get(Q(semester_no = sem) & Q(curriculum = curriculum_object)) + course_registration.objects.create( + student_id = stud, + working_year = year, + semester_id = semester_object, + course_id = course_object, + course_slot_id = course_slot_object + ) + seats_alloted[course] += 1 + rem-=1 + else : + next = InitialRegistration.objects.get(Q(student_id__id__id = random_student_selected[0]) & Q( course_slot_id__name = course_slot ) & Q(semester_id__semester_no = sem) & Q(student_id__batch = batch) & Q(priority=p_priority+1)) + next_priority[next.course_id.id].append([next.student_id.id.id,next.course_slot_id.id]) + p_priority+=1 + present_priority = next_priority + next_priority = {course : [] for course in unique_course} + + return 1 + +@transaction.atomic +def allocate(request) : + batch = request.POST.get('batch') + sem = request.POST.get('sem') + year = request.POST.get('year') + unique_course_slot = InitialRegistration.objects.filter(Q(semester_id__semester_no = sem) & Q(student_id__batch = batch)).values_list('course_slot_id',flat=True).distinct() + unique_course_name = [] + try: + with transaction.atomic() : + for course_slot in unique_course_slot : + course_slot_object = CourseSlot.objects.get(id=course_slot) + if course_slot_object.type == "Professional Elective": # Runs only for open elective course slots + if course_slot_object.name not in unique_course_name: + stat = random_algo(batch,sem,year,course_slot_object.name) + unique_course_name.append(course_slot_object.name) + if(stat == -1) : + raise Exception("seats not enough for course_slot"+str(course_slot_object.name)) + + return JsonResponse({'status': 1 , 'message' : "course allocation successful"}) + except: + return JsonResponse({'status': -1 , 'message' : "seats not enough for course_slot"+str(course_slot_object.name) }) + + +def view_alloted_course(request) : + batch = request.POST.get('batch') + sem = request.POST.get('sem') + year = request.POST.get('year') + course = request.POST.get('course') + + registrations = course_registration.objects.filter(Q(student_id__batch = batch) & Q(working_year = year) & Q(semester_id__semester_no = sem) & Q(course_id__code = course)) + return_list = [] + for registration in registrations: + obj = { + 'student':registration.student_id.id.id + } + return_list.append(obj) + return JsonResponse({'status':1 , 'student_list':return_list }) \ No newline at end of file diff --git a/FusionIIIT/applications/academic_information/views.py b/FusionIIIT/applications/academic_information/views.py index 2fab71e2f..f4ef54b5f 100755 --- a/FusionIIIT/applications/academic_information/views.py +++ b/FusionIIIT/applications/academic_information/views.py @@ -31,6 +31,7 @@ from applications.academic_procedures.views import acad_proced_global_context , get_sem_courses from applications.programme_curriculum.models import Batch from django.db.models import Q +from .utils import check_for_registration_complete,allocate,view_alloted_course @login_required @@ -214,7 +215,14 @@ def homepage(request): """ if user_check(request): return HttpResponseRedirect('/academic-procedures/') - + + if request.method == "POST": + if 'check_allocation' in request.POST : + return check_for_registration_complete(request) + if 'start_allocation' in request.POST : + return allocate(request) + if 'view_allocation' in request.POST : + return view_alloted_course(request) context = get_context(request) return render(request, "ais/ais.html", context) diff --git a/FusionIIIT/applications/academic_procedures/models.py b/FusionIIIT/applications/academic_procedures/models.py index 2015a1ee8..6c1083dda 100644 --- a/FusionIIIT/applications/academic_procedures/models.py +++ b/FusionIIIT/applications/academic_procedures/models.py @@ -605,8 +605,7 @@ class InitialRegistration(models.Model): class Meta: db_table = 'InitialRegistration' - - + class FinalRegistration(models.Model): ''' Current Purpose : stores information regarding the process of final(complete) registration of a student for a course @@ -690,7 +689,6 @@ def __str__(self): class Meta: db_table = 'course_registration' - class backlog_course(models.Model): ''' Current Purpose : stores information regarding the backlog courses of a student (purpose is unclear and is open to interpretations) diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py b/FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py new file mode 100644 index 000000000..75700de21 --- /dev/null +++ b/FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.5 on 2024-10-23 00:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('programme_curriculum', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='max_seats', + field=models.IntegerField(default=90), + ), + ] diff --git a/FusionIIIT/applications/programme_curriculum/models.py b/FusionIIIT/applications/programme_curriculum/models.py index 655994c1c..b09482669 100644 --- a/FusionIIIT/applications/programme_curriculum/models.py +++ b/FusionIIIT/applications/programme_curriculum/models.py @@ -261,7 +261,7 @@ class Course(models.Model): working_course = models.BooleanField(default=True) disciplines = models.ManyToManyField(Discipline, blank=True) latest_version = models.BooleanField(default=True) - + max_seats = models.IntegerField(default=90) class Meta: unique_together = ('code', 'version') diff --git a/FusionIIIT/templates/academic_procedures/auto_pre_registration.html b/FusionIIIT/templates/academic_procedures/auto_pre_registration.html index 033c468fe..fcf496fcf 100644 --- a/FusionIIIT/templates/academic_procedures/auto_pre_registration.html +++ b/FusionIIIT/templates/academic_procedures/auto_pre_registration.html @@ -247,7 +247,7 @@
- +
@@ -291,6 +291,8 @@ var inp = document.getElementsByClassName("Prechoices"); var count = 0; + document.getElementById('register-button').style.display = 'none'; + let course_slots = document.getElementsByName("course_slot"); let credits = document.getElementsByClassName("credit_values"); @@ -303,6 +305,7 @@ // // console.log("course_priority-" + course_slot_id , course_priority[j].value ) if ((!(course_slot_type.startsWith("Optional") && course_slot_type != "Optional Elective") && !course_slot_type.startsWith("Swayam") )&& course_priority[j].value == "NULL") { alert("Please select all choices for " + parseInt(i + 1) + " " + course_slot_type + " slot"); + document.getElementById('register-button').style = "text-allign:center;"; return false; } let selected_course = course_priority[j].value.split("-"); diff --git a/FusionIIIT/templates/ais/ais.html b/FusionIIIT/templates/ais/ais.html index d749b0270..7e248a2ed 100755 --- a/FusionIIIT/templates/ais/ais.html +++ b/FusionIIIT/templates/ais/ais.html @@ -111,6 +111,10 @@ Student Dashboard + + Start elective assignment + + + -
-
- -
- +
+ +
- {% comment %}
-
- -
- -
{% endcomment %}
-
- - +
+ +
-
- - +
+
-
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/FusionIIIT/templates/examination/submitGrade.html b/FusionIIIT/templates/examination/submitGrade.html index 817406be6..dfa741599 100644 --- a/FusionIIIT/templates/examination/submitGrade.html +++ b/FusionIIIT/templates/examination/submitGrade.html @@ -67,15 +67,15 @@

Update Result

- +
-