diff --git a/csc/admin.py b/csc/admin.py index 32ddbf4..342746e 100644 --- a/csc/admin.py +++ b/csc/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin -from .models import * +from .models import FossCategory,CertifiateCategories,CategoryCourses,CSCFossMdlCourses # Register your models here. admin.site.register(FossCategory) admin.site.register(CertifiateCategories) admin.site.register(CategoryCourses) +admin.site.register(CSCFossMdlCourses) diff --git a/csc/api/utility.py b/csc/api/utility.py index 696dc28..ef36bce 100644 --- a/csc/api/utility.py +++ b/csc/api/utility.py @@ -13,12 +13,14 @@ from csc.utils import is_user_vle, is_user_student from django.template.loader import render_to_string def send_pwd_mail(u): + print(u.username) pwd = ''.join(random.choices(string.ascii_letters,k=10)) - + print(pwd) u.set_password(pwd) u.save() from_email = getattr(settings, "NO_REPLY_MAIL", "no-reply@spoken-tutorial.org") + print(from_email) subject = "Login credentials for Spoken Tutorial - CSC" if is_user_student(u): message = f""" @@ -36,6 +38,7 @@ def send_pwd_mail(u): """ path = 'student_mail_template.html' if is_user_vle(u): + print('USER IS VLE') message = f""" Dear {u.get_full_name()}, @@ -69,6 +72,7 @@ def send_pwd_mail(u): fail_silently=False, html_message = html_content ) + print("***************mail send******") except Exception as e: print(e) print(f"Failed to send mail to user : {u.email}") diff --git a/csc/decorators.py b/csc/decorators.py index fffe633..3a38487 100644 --- a/csc/decorators.py +++ b/csc/decorators.py @@ -1,7 +1,7 @@ from email import message from django.contrib import messages from django.core.exceptions import PermissionDenied -from .utils import is_user_vle,is_user_student +from .utils import is_user_vle,is_user_student,is_csc_team_role # decorator def is_vle(view_func): @@ -24,3 +24,12 @@ def wrapper(request,*args,**kwargs): raise PermissionDenied() return wrapper +def is_csc_team(view_func): + def wrapper(request,*args,**kwargs): + # ToDo : Role for VLE in group + if is_csc_team_role(request.user): + return view_func(request,*args,**kwargs) + else: + messages.add_message(request,messages.INFO,'Access denied') + raise PermissionDenied() + return wrapper \ No newline at end of file diff --git a/csc/migrations/0017_auto_20221006_1045.py b/csc/migrations/0017_auto_20221006_1045.py new file mode 100644 index 0000000..962c17a --- /dev/null +++ b/csc/migrations/0017_auto_20221006_1045.py @@ -0,0 +1,39 @@ +# Generated by Django 3.0.3 on 2022-10-06 05:15 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('csc', '0016_auto_20220930_1304'), + ] + + operations = [ + migrations.RemoveField( + model_name='test', + name='test_name', + ), + migrations.AddField( + model_name='test', + name='participant_count', + field=models.IntegerField(default=10), + preserve_default=False, + ), + migrations.AddField( + model_name='test', + name='status', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='student', + name='date_of_registration', + field=models.DateField(default=datetime.date(2022, 10, 6)), + ), + migrations.AlterField( + model_name='test', + name='publish', + field=models.BooleanField(default=True), + ), + ] diff --git a/csc/migrations/0018_test_invigilator.py b/csc/migrations/0018_test_invigilator.py new file mode 100644 index 0000000..1cec19d --- /dev/null +++ b/csc/migrations/0018_test_invigilator.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.3 on 2022-10-06 06:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('csc', '0017_auto_20221006_1045'), + ] + + operations = [ + migrations.AddField( + model_name='test', + name='invigilator', + field=models.ManyToManyField(blank=True, null=True, to='csc.Invigilator'), + ), + ] diff --git a/csc/migrations/0019_auto_20221006_1214.py b/csc/migrations/0019_auto_20221006_1214.py new file mode 100644 index 0000000..d41d9d7 --- /dev/null +++ b/csc/migrations/0019_auto_20221006_1214.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.3 on 2022-10-06 06:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('csc', '0018_test_invigilator'), + ] + + operations = [ + migrations.AlterField( + model_name='test', + name='participant_count', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/csc/migrations/0020_auto_20221006_1632.py b/csc/migrations/0020_auto_20221006_1632.py new file mode 100644 index 0000000..e003884 --- /dev/null +++ b/csc/migrations/0020_auto_20221006_1632.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2022-10-06 11:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('csc', '0019_auto_20221006_1214'), + ] + + operations = [ + migrations.AlterField( + model_name='test', + name='vle', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='csc.VLE'), + ), + ] diff --git a/csc/migrations/0021_auto_20221102_1327.py b/csc/migrations/0021_auto_20221102_1327.py new file mode 100644 index 0000000..f169ae1 --- /dev/null +++ b/csc/migrations/0021_auto_20221102_1327.py @@ -0,0 +1,115 @@ +# Generated by Django 3.0.3 on 2022-11-02 07:57 + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('csc', '0020_auto_20221006_1632'), + ] + + operations = [ + migrations.CreateModel( + name='FOSSVLEView', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('foss', models.CharField(max_length=256)), + ('vle_id', models.IntegerField()), + ], + options={ + 'db_table': 'foss_vle', + 'managed': False, + }, + ), + migrations.RemoveField( + model_name='test', + name='note_invigilator', + ), + migrations.RemoveField( + model_name='test', + name='note_student', + ), + migrations.AddField( + model_name='invigilator', + name='password_mail_sent', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='student', + name='mdl_mail_sent', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='invigilator', + name='phone', + field=models.CharField(blank=True, max_length=32, null=True), + ), + migrations.AlterField( + model_name='invigilator', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invi', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='invigilator', + name='vle', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='invig', to='csc.VLE'), + preserve_default=False, + ), + migrations.AlterField( + model_name='student', + name='date_of_registration', + field=models.DateField(default=datetime.date(2022, 11, 2)), + ), + migrations.AlterField( + model_name='test', + name='status', + field=models.PositiveIntegerField(default=0), + ), + migrations.AlterUniqueTogether( + name='invigilator', + unique_together={('user', 'vle')}, + ), + migrations.CreateModel( + name='CSCFossMdlCourses', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('mdlcourse_id', models.PositiveIntegerField()), + ('mdlquiz_id', models.PositiveIntegerField()), + ('foss', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cscfoss', to='csc.FossCategory')), + ('testfoss', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='testfoss', to='csc.FossCategory')), + ], + ), + migrations.RemoveField( + model_name='invigilator', + name='added_by', + ), + migrations.RemoveField( + model_name='invigilator', + name='vle', + ), + migrations.CreateModel( + name='CSCTestAtttendance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('mdluser_id', models.PositiveIntegerField()), + ('mdlcourse_id', models.PositiveIntegerField(default=0)), + ('mdlquiz_id', models.PositiveIntegerField(default=0)), + ('mdlattempt_id', models.PositiveIntegerField(default=0)), + ('status', models.PositiveSmallIntegerField(default=0)), + ('mdlgrade', models.DecimalField(decimal_places=5, default=0.0, max_digits=12)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='csc.Student')), + ('test', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='csc.Test')), + ], + options={ + 'verbose_name': 'Test Attendance', + 'unique_together': {('test', 'mdluser_id')}, + }, + ), + ] diff --git a/csc/migrations/0022_invigilator_vle.py b/csc/migrations/0022_invigilator_vle.py new file mode 100644 index 0000000..84c0c76 --- /dev/null +++ b/csc/migrations/0022_invigilator_vle.py @@ -0,0 +1,20 @@ +# Generated by Django 3.0.3 on 2022-11-02 08:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('csc', '0021_auto_20221102_1327'), + ] + + operations = [ + migrations.AddField( + model_name='invigilator', + name='vle', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='invig', to='csc.VLE'), + preserve_default=False, + ), + ] diff --git a/csc/models.py b/csc/models.py index af67a53..028d4a1 100644 --- a/csc/models.py +++ b/csc/models.py @@ -1,5 +1,5 @@ - from __future__ import unicode_literals +from audioop import reverse from email.policy import default from django.db import models from django.contrib.auth.models import User @@ -9,7 +9,11 @@ from cms.models import State, District, City from django.forms import widgets from datetime import date +from django.template.defaultfilters import slugify +# from csc.utils import TEST_OPEN + +TEST_OPEN = 0 REJECTED = 0 APPROVED = 1 PROGRAMME_TYPE_CHOICES = Choices( @@ -159,7 +163,8 @@ class Student(models.Model): date_of_registration = models.DateField(default=date.today()) occupation = models.CharField(max_length=255,blank=True,null=True) category = models.CharField(max_length=255,blank=True,null=True) - + mdl_mail_sent = models.BooleanField(default=False) + class Student_certificate_course(models.Model): @@ -193,29 +198,38 @@ def __str__(self): class Invigilator(models.Model): - user = models.ForeignKey(User,on_delete=models.CASCADE) - phone = models.CharField(max_length=32) - vle = models.ManyToManyField(VLE) - added_by = models.ForeignKey(User,on_delete=models.CASCADE,related_name='added_by_user') + user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='invi') + phone = models.CharField(max_length=32,null=True,blank=True) + # vle = models.ManyToManyField(VLE) + # vle = models.ForeignKey(User,on_delete=models.CASCADE,related_name='invig') + vle = models.ForeignKey(VLE,on_delete=models.CASCADE,related_name='invig') + # added_by = models.ForeignKey(User,on_delete=models.CASCADE,related_name='added_by_user') created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) + password_mail_sent = models.BooleanField(default=False) def __str__(self): return f"{self.user.first_name} {self.user.last_name} - {self.user.email}" + class Meta: + unique_together = [['user', 'vle']] + class Test(models.Model): foss = models.ForeignKey(FossCategory,on_delete=models.CASCADE) + # mdlfoss = models.ForeignKey(FossCategory,on_delete=models.CASCADE) tdate = models.DateField() ttime = models.TimeField() - # invigilator = models.ManyToManyField(Invigilator,blank=True,null=True) - publish = models.BooleanField() + invigilator = models.ManyToManyField(Invigilator,blank=True,null=True) + publish = models.BooleanField(default=True) + vle = models.ForeignKey(VLE,on_delete=models.CASCADE,null=True,blank=True) + # note_student = models.TextField(blank=True,null=True) + # note_invigilator = models.TextField(blank=True,null=True) + status = models.PositiveIntegerField(default=TEST_OPEN)# + # test_name = models.CharField(max_length=252,blank=True,null=True) + participant_count = models.IntegerField(null=True,blank=True) slug = models.SlugField(max_length=40) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) - vle = models.ForeignKey(VLE,on_delete=models.CASCADE) - note_student = models.TextField(blank=True,null=True) - note_invigilator = models.TextField(blank=True,null=True) - test_name = models.CharField(max_length=252,blank=True,null=True) #ToDO : Add status ; to mark if completed or cancellled # class Meta: @@ -223,13 +237,19 @@ class Test(models.Model): # 'tdate':widgets.DateInput(attrs={'type': 'date'}) # } def get_absolute_url(self): - return f"{self.foss}" + # return f"{self.foss}" + return reverse("detail_test",kwargs={"slug": self.slug}) def __str__(self): - if self.test_name: - return self.test_name - else: - return f"{self.foss} - {self.tdate}" + return f"{self.foss} ".ljust(25,'-') + f" ( {self.tdate.strftime('%b %d')}, {self.ttime.strftime('%I:%M %p')} )" + + def __repr__(self): + return f"{self.foss} - {self.tdate}" + + def save(self, *args, **kwargs): # new + if not self.slug: + self.slug = slugify(self.id) + return super().save(*args, **kwargs) class InvigilationRequest(models.Model): invigilator = models.ForeignKey(Invigilator,on_delete=models.CASCADE) @@ -255,4 +275,53 @@ class TestRequest(models.Model): created = models.DateTimeField(auto_now_add=True) def __str__(self): - return f"{self.id}" \ No newline at end of file + return f"{self.id}" + +class CSCTestAtttendance(models.Model): + test = models.ForeignKey(Test, on_delete=models.PROTECT ) + student = models.ForeignKey(Student, on_delete=models.PROTECT ) + # mdluser_firstname = models.CharField(max_length = 100) + # mdluser_lastname = models.CharField(max_length = 100) + mdluser_id = models.PositiveIntegerField() + mdlcourse_id = models.PositiveIntegerField(default=0) + mdlquiz_id = models.PositiveIntegerField(default=0) + mdlattempt_id = models.PositiveIntegerField(default=0) + status = models.PositiveSmallIntegerField(default=0) + mdlgrade = models.DecimalField(max_digits=12, decimal_places=5, default=0.00) + created = models.DateTimeField(auto_now_add = True) + updated = models.DateTimeField(auto_now = True) + class Meta(object): + verbose_name = "Test Attendance" + unique_together = (("test", "mdluser_id")) + + +class CSCFossMdlCourses(models.Model): + foss = models.ForeignKey(FossCategory, on_delete=models.PROTECT, related_name='trainingfoss', null=True) + mdlcourse_id = models.PositiveIntegerField() + mdlquiz_id = models.PositiveIntegerField() + testfoss = models.ForeignKey(FossCategory, on_delete=models.PROTECT, related_name='testfoss', null=True) + + def __str__(self): + return self.foss.foss + + def __str__(self): + return self.student.user.email + self.test.foss.foss + + +class CSCFossMdlCourses(models.Model): + foss = models.ForeignKey(FossCategory, on_delete=models.PROTECT, related_name='cscfoss', null=True) + mdlcourse_id = models.PositiveIntegerField() + mdlquiz_id = models.PositiveIntegerField() + testfoss = models.ForeignKey(FossCategory, on_delete=models.PROTECT, related_name='testfoss', null=True) + + def __str__(self): + return self.foss.foss + +class FOSSVLEView(models.Model): + foss = models.CharField(max_length=256) + vle_id = models.IntegerField() + + class Meta: + managed = False + db_table = "foss_vle" + \ No newline at end of file diff --git a/csc/stats/__init__.py b/csc/stats/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csc/stats/urls.py b/csc/stats/urls.py new file mode 100644 index 0000000..e301bf9 --- /dev/null +++ b/csc/stats/urls.py @@ -0,0 +1,21 @@ +from django.urls import path +from django.urls import re_path + + +from csc.stats.views import * + + +urlpatterns = [ + path('',stats,name="stats_dashboard"), + path('ajax_stats/',ajax_stats,name="ajax_stats"), + path('ajax_csc_state_count/',get_csc_state_count,name="ajax_csc_state_count"), + path('ajax_vle_detail/',ajax_vle_detail,name="ajax_vle_detail"), + path('vle_stats/',VLEListView.as_view(),name="vle_stats"), + path('student_stats/',StudentListView.as_view(),name="student_stats"), + path('vle_report/',vle_report,name="vle_report"), + path('student_report/',student_report,name="student_report") + + + + +] \ No newline at end of file diff --git a/csc/stats/utility.py b/csc/stats/utility.py new file mode 100644 index 0000000..a212b8a --- /dev/null +++ b/csc/stats/utility.py @@ -0,0 +1,54 @@ +from django.db.models import Count,Q,F +from django.conf import settings + +from csc.models import CertifiateCategories, Student,Student_Foss,FossCategory, CSC, VLE + +TEST_VLE_COUNT=int(getattr(settings, "TEST_VLE_COUNT", 2)) +TEST_STUDENT_COUNT=int(getattr(settings, "TEST_STUDENT_COUNT", 1)) +TEST_VLE_EMAIL=getattr(settings, "TEST_VLE_EMAIL", ['ankitamk@gmail.com','roliimpex@gmail.com']) +TEST_STUDENT_EMAIL=getattr(settings, "TEST_STUDENT_EMAIL", ['kirti3192@gmail.com']) +TEST_CSC_ID=getattr(settings, "TEST_CSC_ID", [1,2]) + +qs_students = Student.objects.exclude(Q(user__email__in=TEST_STUDENT_EMAIL)) +qs_student_foss = Student_Foss.objects.exclude(Q(student__user__email__in=TEST_STUDENT_EMAIL)) +qs_csc = CSC.objects.exclude(Q(id__in=TEST_CSC_ID)) +qs_vle = VLE.objects.exclude(Q(user__email__in=TEST_VLE_EMAIL)) + + + + +def get_student_gender_stats(): + return qs_students.values('gender').annotate(count=Count('gender')).order_by('-count') + + +def get_student_category_stats(): + return qs_students.values('category').annotate(count=Count('category')).order_by('-count') + +def get_student_occupation_stats(): + return qs_students.values('occupation').annotate(count=Count('occupation')).order_by('-count') + +def get_student_certi_stats(): + return qs_student_foss.values('cert_category__code').annotate(count=Count('cert_category'),title=F('cert_category__title')).order_by('-count') + +def get_student_state_stats(): + return qs_csc.values('state').annotate(count=Count('state')).order_by('state') + +def get_student_foss_stats(start=0,end=len(qs_student_foss),type=''): + t = FossCategory.objects.filter(available_for_jio=1).order_by('foss') + indi = CertifiateCategories.objects.get(code='INDI') + if type: + q = [x['csc_foss__foss'] for x in qs_student_foss.filter(csc_foss__in=t,cert_category=indi).values('csc_foss__foss').annotate(count=Count('csc_foss__foss')).order_by('-count')] + else: + q = [x['csc_foss__foss'] for x in qs_student_foss.filter(csc_foss__in=t).values('csc_foss__foss').annotate(count=Count('csc_foss__foss')).order_by('-count')] + temp = [] + for x in range(start, end, 1): + try: + temp.append(q[x]) + except Exception as e: + # print(e) + pass + if type: + return qs_student_foss.filter(csc_foss__foss__in=temp,cert_category=indi).values('csc_foss__foss').annotate(count=Count('csc_foss__foss')).order_by('-count') + else: + return qs_student_foss.filter(csc_foss__foss__in=temp).values('csc_foss__foss').annotate(count=Count('csc_foss__foss')).order_by('-count') + \ No newline at end of file diff --git a/csc/stats/views.py b/csc/stats/views.py new file mode 100644 index 0000000..d0035f7 --- /dev/null +++ b/csc/stats/views.py @@ -0,0 +1,236 @@ +from django.shortcuts import render +from django.db.models import Count,F,Q +from django.http import JsonResponse +from django.views.generic import ListView +from django.views.decorators.csrf import csrf_exempt +from django.contrib.auth.decorators import login_required +from django.utils.decorators import method_decorator + +from django.contrib.auth.mixins import LoginRequiredMixin +from csc.models import CSC,VLE,Student, FossCategory, CertifiateCategories, Student_Foss +from cms.models import State, District +from csc.decorators import is_csc_team as dec_is_csc_team +from csc.utils import is_csc_team_role +from .utility import * + + +@login_required +@dec_is_csc_team +def stats(request): + context = {} + + total_vles = VLE.objects.count()-TEST_VLE_COUNT + total_students = Student.objects.count()-TEST_STUDENT_COUNT + total_foss = FossCategory.objects.filter(available_for_jio=True).count() + total_certificate_course = CertifiateCategories.objects.count() + context['total_vles'] = total_vles + context['total_students'] = total_students + context['total_foss'] = total_foss + context['total_certificate_course'] = total_certificate_course + + student_gender = get_student_gender_stats() + context['student_gender'] = student_gender + sct = get_student_certi_stats() + context['cert_count_tb'] = [x for x in sct] + + sft = get_student_foss_stats(0) + context['foss_count_tb'] = [x for x in sft] + csc_state = get_student_state_stats() + context['csc_state'] = csc_state + indi = CertifiateCategories.objects.get(code='INDI') + student_indi_foss=Student_Foss.objects.filter(cert_category=indi).values('csc_foss__foss').annotate(count=Count('csc_foss')).order_by('-count') + context['student_indi_foss'] = [x for x in student_indi_foss] + + return render(request, 'stats/stats.html', context) + + +def ajax_stats(request): + data = {} + + student_gender = get_student_gender_stats() + data['student_gender'] = [x for x in student_gender] + student_category = get_student_category_stats() + data['student_category'] = [x for x in student_category] + student_occupation = get_student_occupation_stats() + data['student_occupation'] = [x for x in student_occupation] + student_course=get_student_certi_stats() + data['student_course'] = [x for x in student_course] + + data['student_foss_1'] = [x for x in get_student_foss_stats(0,15)] + data['student_foss_2'] = [x for x in get_student_foss_stats(15,30)] + data['student_foss_3'] = [x for x in get_student_foss_stats(30,45)] + data['student_foss_4'] = [x for x in get_student_foss_stats(45,60)] + data['student_foss_5'] = [x for x in get_student_foss_stats(start=45)] + + csc_state = get_student_state_stats() + data['csc_state'] = [x for x in csc_state] + + data['student_indi_foss_1'] = [x for x in get_student_foss_stats(0,15,'indi')] + data['student_indi_foss_2'] = [x for x in get_student_foss_stats(15,30,'indi')] + data['student_indi_foss_3'] = [x for x in get_student_foss_stats(30,45,'indi')] + data['student_indi_foss_4'] = [x for x in get_student_foss_stats(45,60,'indi')] + data['student_indi_foss_5'] = [x for x in get_student_foss_stats(start=45,type='indi')] + + return JsonResponse(data) + +def get_csc_state_count(request): + data = {} + csc_state = CSC.objects.values('state').annotate(count=Count('state'),code=F('state')) + csc_state = [x for x in csc_state] + data['csc_state'] = csc_state + return JsonResponse(data) + +@method_decorator(login_required, name='dispatch') +class StudentListView(ListView): + paginate_by = 100 + model = Student + template_name = 'stats/student_stats.html' + + + def get_queryset(self): + raw = 'Select DATEDIFF(CURDATE(), `csc_student`.`dob`) from `csc_student` u where u.`id`=`csc_student`.id' + + qs = super().get_queryset() + qs = qs_students + # qs = qs.annotate(age2=find_age) + # qs = qs.annotate(age2=ExpressionWrapper(RawSQL(raw, ())/365),output_field=IntegerField()) + if self.request.GET.get('name'): + name = self.request.GET.get('name') + qs = qs.filter(Q(user__first_name__icontains=name)|Q(user__last_name__icontains=name)|Q(user__email__icontains=name)) + if self.request.GET.get('vle_name'): + name = self.request.GET.get('vle_name') + vles = VLE.objects.filter(Q(user__first_name__icontains=name)|Q(user__last_name__icontains=name)|Q(user__email__icontains=name)) + qs = qs.filter(vle_id__in=vles) + if self.request.GET.get('csc'): + csc = self.request.GET.get('csc') + csc = CSC.objects.get(csc_id=csc) + vle = VLE.objects.filter(csc=csc) + qs = qs.filter(vle_id__in=vle) + if self.request.GET.get('edu'): + edu = self.request.GET.get('edu') + qs = qs.filter(edu_qualification__icontains=edu) + if self.request.GET.get('state'): + state = self.request.GET.get('state') + state_obj = State.objects.get(name=state) + qs = qs.filter(state_id=state_obj.id) + if self.request.GET.get('district'): + district = self.request.GET.get('district') + district_obj = District.objects.get(name=district) + qs = qs.filter(district_id=district_obj.id) + + if self.request.GET.get('occupation'): + occupation = self.request.GET.get('occupation') + qs = qs.filter(occupation=occupation) + + return qs + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + edu = Student.objects.values('edu_qualification').distinct().order_by('edu_qualification') + context['edu'] = edu + context['states'] = State.objects.all().order_by('name') + context['districts'] = District.objects.all().order_by('name') + occupation = Student.objects.values('occupation').distinct().order_by('occupation') + context['occupation'] = occupation + query_str = self.request.GET + context['query_str'] = query_str + return context + + + +@method_decorator(login_required, name='dispatch') +class VLEListView(ListView): + paginate_by = 100 + model = VLE + template_name = 'stats/vle_stats.html' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['states'] = State.objects.all().order_by('name') + context['districts'] = District.objects.all().order_by('name') + query_str = self.request.GET + context['query_str'] = query_str + return context + def get_queryset(self): + qs = super().get_queryset() + qs = qs_vle + if self.request.GET.get('name'): + name = self.request.GET.get('name') + qs = qs.filter(Q(user__first_name__icontains=name)|Q(user__last_name__icontains=name)|Q(user__email__icontains=name)) + if self.request.GET.get('csc_id'): + csc_id = self.request.GET.get('csc_id') + qs = qs.filter(csc__csc_id=csc_id) + if self.request.GET.get('state'): + state = self.request.GET.get('state') + qs = qs.filter(csc__state=state) + if self.request.GET.get('district'): + district = self.request.GET.get('district') + qs = qs.filter(csc__district=district) + return qs + +def student_stats(request): + context = {} + return render(request, 'stats/student_stats.html', context) + + +def vle_stats(request): + context = {} + return render(request, 'stats/vle_stats.html', context) + +@csrf_exempt +def ajax_vle_detail(request): + data = {} + vle_id = request.GET.get('vle_id') + vle = VLE.objects.get(id=vle_id) + data['name'] = vle.user.get_full_name() + data['email'] = vle.user.email + data['phone'] = vle.phone + data['csc_id'] = vle.csc.csc_id + data['state'] = vle.csc.state + data['district'] = vle.csc.district + data['address'] = vle.csc.address + data['pin'] = vle.csc.pincode + data['registered_on'] = vle.user.date_joined + print(f"Student.objects.filter(vle_id=vle_id) ****** {Student.objects.filter(vle_id=vle_id)}") + data['total_students'] = Student.objects.filter(vle_id=vle_id).count() + data['fosses'] = [x for x in Student_Foss.objects.filter(student__vle_id=vle_id).values('csc_foss__foss').annotate(count=Count('csc_foss'))] + data['courses'] = [x for x in Student_Foss.objects.filter(student__vle_id=vle_id).values('cert_category__code','cert_category__title').annotate(count=Count('cert_category'))] + data['students'] = [x for x in Student.objects.filter(vle_id=vle_id).values('user__first_name', 'user__last_name','user__email')] + cert_category = CertifiateCategories.objects.get(code='INDI') + data['indi_fosses'] = [x for x in Student_Foss.objects.filter(student__vle_id=vle_id,cert_category=cert_category).values('csc_foss__foss').annotate(count=Count('csc_foss'))] + + return JsonResponse(data) + +@login_required +def student_report(request): + context = {} + context['total_students'] = Student.objects.count() + student_course=Student_Foss.objects.values('cert_category__code','cert_category__title').annotate(count=Count('cert_category')) + context['student_course'] = student_course + student_foss=Student_Foss.objects.values('csc_foss__foss').annotate(count=Count('csc_foss')).order_by('csc_foss') + context['student_foss'] = student_foss + student_gender = Student.objects.values('gender').annotate(count=Count('gender')) + context['student_gender'] = student_gender + student_occupation = Student.objects.values('occupation').annotate(count=Count('occupation')).order_by('occupation') + context['student_occupation'] = student_occupation + edu = Student.objects.values('edu_qualification').distinct().annotate(count=Count('edu_qualification')).order_by('edu_qualification') + context['edu'] = edu + student_category = Student.objects.values('category').annotate(count=Count('category')).order_by('category') + context['student_category'] = student_category + student_state = Student.objects.values('state__name').annotate(count=Count('state')).order_by('state') + context['student_state'] = student_state + student_district = Student.objects.values('district__name').annotate(count=Count('district')).order_by('district') + context['student_district'] = student_district + + return render(request, 'stats/student_report.html', context) + +@login_required +@dec_is_csc_team +def vle_report(request): + context = {} + csc_state = CSC.objects.values('state').annotate(count=Count('state')).order_by('state') + context['csc_state'] = csc_state + csc_district = CSC.objects.values('district').annotate(count=Count('district')).order_by('district') + context['csc_district'] = csc_district + + return render(request, 'stats/vle_report.html', context) \ No newline at end of file diff --git a/csc/student_urls.py b/csc/student_urls.py index 59a5820..a0b5f85 100644 --- a/csc/student_urls.py +++ b/csc/student_urls.py @@ -16,6 +16,10 @@ path('apply_for_test/', apply_for_test, name="apply_for_test"), + path('download_certificate/', download_certificate, name="download_certificate"), + + + ] \ No newline at end of file diff --git a/csc/student_views.py b/csc/student_views.py index d90f214..122c7a1 100644 --- a/csc/student_views.py +++ b/csc/student_views.py @@ -4,12 +4,14 @@ from django.urls import reverse from django.http import HttpResponseRedirect, JsonResponse from django.shortcuts import render -from .models import CertifiateCategories, Student, StudentTest, Student_Foss, Test, VLE, TestRequest, Vle_csc_foss,CategoryCourses,FossCategory,Student_certificate_course +from .models import CSCTestAtttendance, CertifiateCategories, Student, StudentTest, Student_Foss, Test, VLE, TestRequest, Vle_csc_foss,CategoryCourses,FossCategory,Student_certificate_course from .utils import upcoming_foss_tests,check_student_test_status from .student_forms import RaiseTestRequestForm from django.contrib.auth.decorators import login_required from .decorators import is_student from django.db.models import Q +from .utils import TEST_COMPLETED_BY_STUDENT +from django.conf import settings TEST_WAITING_PERIOD = 10 def student_tests(request): @@ -22,7 +24,7 @@ def student_tests(request): tests_all = [] test_status = {} for vle in vles: - tests_vle = Test.objects.filter(vle=vle,foss__in=[x.csc_foss.spoken_foss for x in studentFoss]) + tests_vle = Test.objects.filter(vle=vle,foss__in=[x.csc_foss.foss for x in studentFoss]) for test in tests_vle: tests_all.append(test) try: @@ -58,17 +60,22 @@ def student_dashboard(request): student = Student.objects.get(user=request.user) sf = Student_Foss.objects.filter(student=student) courses = [x.cert_category for x in sf] + indi = CertifiateCategories.objects.get(code='INDI') - fosses = [x.csc_foss.foss for x in Student_Foss.objects.filter(student=student,cert_category__code='INDI').order_by('csc_foss__foss')] + indi_fosses = [x.csc_foss.foss for x in Student_Foss.objects.filter(student=student,cert_category__code='INDI').order_by('csc_foss__foss')] + d = {} for course in courses: - d[course] = [x.foss.foss for x in CategoryCourses.objects.filter(certificate_category=course)] + d[course] = [x.foss for x in CategoryCourses.objects.filter(certificate_category=course)] try: d.pop(indi) except: pass + + context['student'] = student + context['CSC_ONLINE_TEST_URL'] = settings.CSC_ONLINE_TEST_URL context['courses'] = d - context['fosses'] = fosses + context['fosses'] = indi_fosses context['vle'] = student.vle_id.all()[0] return render(request,'csc/student_dashboard.html',context) @@ -110,47 +117,56 @@ def student_courses(request): def student_tests(request): context = {} student = Student.objects.get(user=request.user) - print(f"student.vle_id - {student.vle_id.all()}") - vles = VLE.objects.filter(id__in=[x.id for x in student.vle_id.all()]) - csc = [(x.csc.id,x.csc.csc_id, x.csc.city) for x in vles] - # if len(csc) == 1: - vle = VLE.objects.get(csc_id = csc[0]) - vle_csc_foss = Vle_csc_foss.objects.filter(vle=vle) - sf = Student_Foss.objects.filter(student=student,csc_foss__in = vle_csc_foss) + open_tests = CSCTestAtttendance.objects.filter(student=student,status=OPEN_TEST) + completed_tests = CSCTestAtttendance.objects.filter(student=student,status__in=[TEST_COMPLETED_BY_STUDENT]) + context['open_tests']=open_tests + context['completed_tests']=completed_tests + # print(f"student.vle_id - {student.vle_id.all()}") + # vles = VLE.objects.filter(id__in=[x.id for x in student.vle_id.all()]) + # csc = [(x.csc.id,x.csc.csc_id, x.csc.city) for x in vles] + # # if len(csc) == 1: + # vle = VLE.objects.get(csc_id = csc[0]) + # # vle_csc_foss = Vle_csc_foss.objects.filter(vle=vle) + # # sf = Student_Foss.objects.filter(student=student,csc_foss__in = vle_csc_foss) + # sf = Student_Foss.objects.filter(student=student) - # fosses = [(x.csc_foss.spoken_foss.foss,x.csc_foss.spoken_foss.id) for x in sf] - fosses = [x.csc_foss.spoken_foss for x in sf] - applied = [x.foss for x in TestRequest.objects.filter(student=student)] - print(f'fosses : {fosses}') - print(f'applied : {applied}') - available_foss = [] - for foss in fosses: - if not foss in applied: - available_foss.append((foss.foss,foss.id)) - context['fosses'] = available_foss + # # fosses = [(x.csc_foss.spoken_foss.foss,x.csc_foss.spoken_foss.id) for x in sf] + # fosses = [x.csc_foss for x in sf] + # applied = [x.foss for x in TestRequest.objects.filter(student=student)] + # print(f'fosses : {fosses}') + # print(f'applied : {applied}') + # available_foss = [] + # for foss in fosses: + # if not foss in applied: + # available_foss.append((foss.foss,foss.id)) + # context['fosses'] = available_foss - context['csc'] = csc - testReqs = TestRequest.objects.filter(student=student) - context['test_requests'] = testReqs + # context['csc'] = csc + # testReqs = TestRequest.objects.filter(student=student) + # context['test_requests'] = testReqs - student_foss = Student_Foss.objects.filter(student=student) - in_progress_foss_data = {} - for item in student_foss: - csc_foss = item.csc_foss - foss = csc_foss.spoken_foss - vle = csc_foss.vle + # student_foss = Student_Foss.objects.filter(student=student) + # in_progress_foss_data = {} + # for item in student_foss: + # csc_foss = item.csc_foss + # foss = csc_foss.foss + # vle = csc_foss.vle - upcoming_tests = upcoming_foss_tests(foss,vle) - applied = check_student_test_status(upcoming_tests,student) - print(f'before : {upcoming_tests}') - try: - upcoming_tests.remove(applied) - except Exception as e: - print(e) + # upcoming_tests = upcoming_foss_tests(foss,vle) + # applied = check_student_test_status(upcoming_tests,student) + # print(f'before : {upcoming_tests}') + # try: + # upcoming_tests.remove(applied) + # except Exception as e: + # print(e) - print(f'after : {upcoming_tests}') - in_progress_foss_data[foss] = {'applied' : applied, 'upcoming_tests' : upcoming_tests} + # print(f'after : {upcoming_tests}') + # in_progress_foss_data[foss] = {'applied' : applied, 'upcoming_tests' : upcoming_tests} - context['in_progress_foss_data'] = in_progress_foss_data + # context['in_progress_foss_data'] = in_progress_foss_data return render(request,'csc/student_tests.html',context) +def download_certificate(request): + data = {} + print(f"download_certificate ************************************ ") + return JsonResponse(data) diff --git a/csc/templates/csc/courses.html b/csc/templates/csc/courses.html index 1463fca..8cd5ab6 100644 --- a/csc/templates/csc/courses.html +++ b/csc/templates/csc/courses.html @@ -14,10 +14,7 @@ .nav-link{ color: var(--footer-color); } - .c-btn{ - background-color:#F19584!important; - border-color:#F19584; - } + {% endblock css %} diff --git a/csc/templates/csc/create_invigilator.html b/csc/templates/csc/create_invigilator.html new file mode 100644 index 0000000..9229d73 --- /dev/null +++ b/csc/templates/csc/create_invigilator.html @@ -0,0 +1,62 @@ +{% extends 'csc_base.html'%} +{% load static %} +{% load crispy_forms_tags %} +{% block css %} + + +{% endblock css %} + +{% block content %} +
+
+

Add Invigilator

+
+ {% if messages %} + + {% endif %} +
+ {% csrf_token %} + {{form|crispy}} + +
+
+
+ + +{% endblock %} + +{% block script %} + + + + + +{% endblock %} + diff --git a/csc/templates/csc/csc_base.html b/csc/templates/csc/csc_base.html index 6ee7597..54928b9 100644 --- a/csc/templates/csc/csc_base.html +++ b/csc/templates/csc/csc_base.html @@ -41,6 +41,24 @@ #sidebarMenu{ padding-top: 00; } + .c-btn{ + background-color:#F19584!important; + border-color:#F19584; + color:#fff; + } + .remove-btn{ + background:none; + border:none; + color:maroon; + } + .c-card-header{ + background: #004a91 !important; + color: #fff; + } + .sm-header{ + color:#013A6B!important; + font-weight:bold; + } {% block css %}{% endblock css %} diff --git a/csc/templates/csc/invigilator 2.html b/csc/templates/csc/invigilator 2.html new file mode 100644 index 0000000..5549d90 --- /dev/null +++ b/csc/templates/csc/invigilator 2.html @@ -0,0 +1,163 @@ +{% extends 'csc_base.html'%} +{% load static %} +{% load crispy_forms_tags %} +{% block css %} + + +{% endblock css %} + +{% block content %} +
+ {% if messages %} + + {% endif %} + + +
+{% endblock %} + +{% block script %} + + + + + +{% endblock %} + diff --git a/csc/templates/csc/invigilator.html b/csc/templates/csc/invigilator.html new file mode 100644 index 0000000..7eb7808 --- /dev/null +++ b/csc/templates/csc/invigilator.html @@ -0,0 +1,128 @@ +{% extends 'csc_base.html'%} +{% load static %} +{% load crispy_forms_tags %} +{% block css %} + + +{% endblock css %} + +{% block content %} +
+ + +
+{% endblock %} + +{% block script %} + + + + + +{% endblock %} + diff --git a/csc/templates/csc/invigilator_confirm_delete.html b/csc/templates/csc/invigilator_confirm_delete.html new file mode 100644 index 0000000..086771c --- /dev/null +++ b/csc/templates/csc/invigilator_confirm_delete.html @@ -0,0 +1,12 @@ +
{% csrf_token %} + + + + +

Are you sure you want to delete "{{ object }}"?

+ + + + + +
\ No newline at end of file diff --git a/csc/templates/csc/invigilator_dashboard.html b/csc/templates/csc/invigilator_dashboard.html index 61d9953..b1163e8 100644 --- a/csc/templates/csc/invigilator_dashboard.html +++ b/csc/templates/csc/invigilator_dashboard.html @@ -1,4 +1,5 @@ {% extends 'csc_base.html' %} +{% load csc_tags %} {% block css %} +{% endblock css %} + +{% block content %} +
+
+

Invigilators

+
+ {% if messages %} + + {% endif %} +
+ + + + + + + + + + + + + {% for item in invigilators %} + + + + + + + + + {% endfor %} + +
#NameEmailPhoneEdit
{{forloop.counter}}{{item.user.get_full_name}}{{item.user.email}}{{item.phone}}
+
+ +
+
+ + +{% endblock %} + +{% block script %} + + + + + +{% endblock %} + diff --git a/csc/templates/csc/mark_attendance.html b/csc/templates/csc/mark_attendance.html index 479d145..593a158 100644 --- a/csc/templates/csc/mark_attendance.html +++ b/csc/templates/csc/mark_attendance.html @@ -29,7 +29,7 @@
- {{test.test_name|default_if_none:test.foss.foss | title }} + {{test.foss.foss | title }}
@@ -50,12 +50,14 @@

-

+

+
+ {% csrf_token %} @@ -69,21 +71,25 @@ - {% for item in students %} + {% for student,status in students %} - - + + + @@ -95,6 +101,7 @@
{{forloop.counter}} - {{item.student.user.get_full_name | title}}{{item.student.user.email}}{{item.student.phone}}{{student.user.get_full_name | title}}{{student.user.email}}{{student.phone}}
- {% if item.test_status %} + + + + + {% comment %} {% if item.test_status %} {% else %} - {% endif %} + {% endif %} {% endcomment %}
+
diff --git a/csc/templates/csc/sidebar.html b/csc/templates/csc/sidebar.html index b311fa2..fee61b3 100644 --- a/csc/templates/csc/sidebar.html +++ b/csc/templates/csc/sidebar.html @@ -2,17 +2,7 @@ {% load helper %}