diff --git a/cdcontent/__init__.py b/cdcontent/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/cdcontent/admin.py b/cdcontent/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/cdcontent/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/cdcontent/apps.py b/cdcontent/apps.py
new file mode 100644
index 0000000..91e1491
--- /dev/null
+++ b/cdcontent/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class CdcontentConfig(AppConfig):
+ name = 'cdcontent'
diff --git a/cdcontent/forms.py b/cdcontent/forms.py
new file mode 100644
index 0000000..0cb1770
--- /dev/null
+++ b/cdcontent/forms.py
@@ -0,0 +1,55 @@
+# Third Party Stuff
+from django import forms
+from django.db.models import Q
+
+# Spoken Tutorial Stuff
+from .models import TutorialResource
+from csc.models import FossCategory
+import json
+def jsonify(data):
+ return json.loads(data.replace("u'", "'").replace("'", '"'))
+
+class CDContentForm(forms.Form):
+ healthfosslist = list(FossCategory.objects.filter(show_on_homepage = 0, foss__contains='Health').values_list('id','foss'))
+
+ foss_list = list(TutorialResource.objects.filter(Q(status = 1)|Q(status = 2), tutorial_detail__foss__show_on_homepage = 1).values_list('tutorial_detail__foss_id', 'tutorial_detail__foss__foss').order_by('tutorial_detail__foss__foss').distinct())+healthfosslist
+ foss_list.insert(0, ('', 'Select FOSS Category'))
+
+ foss_category = forms.ChoiceField(
+ choices = foss_list,
+ required = True,
+ error_messages = {'required':'FOSS category field is required.'}
+ )
+ level = forms.ChoiceField(
+ choices = [('', 'Select Level'), (0, 'All'), (1, 'Basic'), (2, 'Intermediate'), (3, 'Advanced')],
+ required = True,
+ error_messages = {'required':'Level field is required.'}
+ )
+ language = forms.MultipleChoiceField(
+ required = True,
+ error_messages = {'required':'Languages field is required.'},
+ choices = [('', 'Select Languages')]
+ )
+ selected_foss = forms.CharField(
+ required = True,
+ error_messages = {'required': 'Add atleast one foss and language, before pressing "Create ZIP file" button'},
+ widget=forms.HiddenInput()
+ )
+
+ def __init__(self, *args, **kwargs):
+ super(CDContentForm, self).__init__(*args, **kwargs)
+ #self.fields['language'].choices = ['nothing']
+ if args:
+ print("args out ",args)
+ if ('foss_category' in args[0]) and ('level' in args[0]):
+ print("args in ",args[0])
+ if args[0]['foss_category'] and args[0]['foss_category'] != '' and args[0]['foss_category'] != 'None':
+ try:
+ tmp_level = int(args[0]['level'])
+ except:
+ tmp_level = ''
+ if tmp_level:
+ lang_recs = list(TutorialResource.objects.filter(Q(status = 1)|Q(status = 2), tutorial_detail__foss_id = int(args[0]['foss_category']), tutorial_detail__level_id = int(tmp_level)).values_list('language_id', 'language__name').order_by('language__name').distinct())
+ else:
+ lang_recs = list(TutorialResource.objects.filter(Q(status = 1)|Q(status = 2), tutorial_detail__foss_id = int(args[0]['foss_category'])).values_list('language_id', 'language__name').order_by('language__name').distinct())
+ self.fields['language'].choices = lang_recs
diff --git a/cdcontent/migrations/0001_initial.py b/cdcontent/migrations/0001_initial.py
new file mode 100644
index 0000000..8a6c886
--- /dev/null
+++ b/cdcontent/migrations/0001_initial.py
@@ -0,0 +1,186 @@
+# Generated by Django 3.0.3 on 2022-09-28 05:34
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Answer',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uid', models.IntegerField()),
+ ('body', models.TextField()),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_modified', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'db_table': 'website_answer',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='FossCategory',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('foss', models.CharField(max_length=255, unique=True)),
+ ('description', models.TextField()),
+ ('status', models.BooleanField(max_length=2)),
+ ('is_learners_allowed', models.BooleanField(default=0, max_length=2)),
+ ('is_translation_allowed', models.BooleanField(default=0, max_length=2)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('show_on_homepage', models.PositiveSmallIntegerField(default=0, help_text='0:Series, 1:Display on home page, 2:Archived')),
+ ('available_for_nasscom', models.BooleanField(default=True, help_text='If unchecked, this foss will not be available for nasscom')),
+ ('available_for_jio', models.BooleanField(default=True, help_text='If unchecked, this foss will not be available for jio and spoken-tutorial.in')),
+ ('csc_dca_programme', models.BooleanField(default=True, help_text='If unchecked, this foss will not be available for csc-dca programme')),
+ ],
+ options={
+ 'verbose_name': 'FOSS',
+ 'verbose_name_plural': 'FOSSes',
+ 'db_table': 'creation_fosscategory',
+ 'ordering': ('foss',),
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='FossSuperCategory',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255, unique=True)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'verbose_name': 'FOSS Category',
+ 'verbose_name_plural': 'FOSS Categories',
+ 'db_table': 'creation_fosssupercategory',
+ 'ordering': ('name',),
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Language',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255, unique=True)),
+ ('code', models.CharField(default='en', max_length=10)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'db_table': 'creation_language',
+ 'ordering': ('name',),
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Question',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uid', models.IntegerField()),
+ ('category', models.CharField(max_length=200)),
+ ('tutorial', models.CharField(max_length=200)),
+ ('minute_range', models.CharField(max_length=10)),
+ ('second_range', models.CharField(max_length=10)),
+ ('title', models.CharField(max_length=200)),
+ ('body', models.TextField()),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_modified', models.DateTimeField(auto_now=True)),
+ ('views', models.IntegerField(default=1)),
+ ('status', models.IntegerField(default=1)),
+ ],
+ options={
+ 'db_table': 'website_question',
+ 'get_latest_by': 'date_created',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TutorialCommonContent',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('slide', models.CharField(max_length=255)),
+ ('slide_status', models.PositiveSmallIntegerField(default=0)),
+ ('code', models.CharField(max_length=255)),
+ ('code_status', models.PositiveSmallIntegerField(default=0)),
+ ('assignment', models.CharField(max_length=255)),
+ ('assignment_status', models.PositiveSmallIntegerField(default=0)),
+ ('prerequisite_status', models.PositiveSmallIntegerField(default=0)),
+ ('additional_material', models.CharField(blank=True, max_length=255, null=True)),
+ ('additional_material_status', models.PositiveSmallIntegerField(default=0)),
+ ('keyword', models.TextField()),
+ ('keyword_status', models.PositiveSmallIntegerField(default=0)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'verbose_name': 'Tutorial Common Content',
+ 'db_table': 'creation_tutorialcommoncontent',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TutorialDetail',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('tutorial', models.CharField(max_length=255)),
+ ('order', models.IntegerField()),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'verbose_name': 'Tutorial Detail',
+ 'db_table': 'creation_tutorialdetail',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TutorialResource',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('outline', models.TextField()),
+ ('outline_status', models.PositiveSmallIntegerField(default=0)),
+ ('script', models.URLField(max_length=255)),
+ ('script_status', models.PositiveSmallIntegerField(default=0)),
+ ('timed_script', models.URLField(max_length=255)),
+ ('video', models.CharField(max_length=255)),
+ ('video_id', models.CharField(blank=True, default=None, max_length=255, null=True)),
+ ('playlist_item_id', models.CharField(blank=True, default=None, max_length=255, null=True)),
+ ('video_thumbnail_time', models.TimeField(default='00:00:00')),
+ ('video_status', models.PositiveSmallIntegerField(default=0)),
+ ('status', models.PositiveSmallIntegerField(default=0)),
+ ('version', models.PositiveSmallIntegerField(default=0)),
+ ('hit_count', models.PositiveIntegerField(default=0)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('publish_at', models.DateTimeField(null=True)),
+ ('submissiondate', models.DateTimeField(default=datetime.datetime(2000, 1, 2, 12, 0))),
+ ('assignment_status', models.PositiveSmallIntegerField(default=0)),
+ ('extension_status', models.PositiveIntegerField(default=0)),
+ ],
+ options={
+ 'db_table': 'creation_tutorialresource',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Level',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('level', models.CharField(max_length=255)),
+ ('code', models.CharField(max_length=10)),
+ ],
+ options={
+ 'verbose_name': 'Tutorial Level',
+ 'db_table': 'creation_level',
+ },
+ ),
+ ]
diff --git a/cdcontent/migrations/__init__.py b/cdcontent/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/cdcontent/models.py b/cdcontent/models.py
new file mode 100644
index 0000000..b9c0839
--- /dev/null
+++ b/cdcontent/models.py
@@ -0,0 +1,221 @@
+from django.db import models
+from django.contrib.auth.models import User
+from csc.models import FossCategory
+import datetime
+
+# Create your models here.
+class Level(models.Model):
+ level = models.CharField(max_length=255)
+ code = models.CharField(max_length=10)
+
+ class Meta(object):
+ verbose_name = 'Tutorial Level'
+ db_table = 'creation_level'
+
+
+ def __str__(self):
+ return self.level
+
+class FossSuperCategory(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ class Meta(object):
+ verbose_name = 'FOSS Category'
+ verbose_name_plural = 'FOSS Categories'
+ ordering = ('name',)
+ managed = False
+ db_table = 'creation_fosssupercategory'
+
+ def __str__(self):
+ return self.name
+
+class FossCategory(models.Model):
+ foss = models.CharField(unique=True, max_length=255)
+ description = models.TextField()
+ status = models.BooleanField(max_length=2)
+ is_learners_allowed = models.BooleanField(max_length=2,default=0 )
+ is_translation_allowed = models.BooleanField(max_length=2, default=0)
+ # user = models.ForeignKey(User, on_delete=models.PROTECT )
+ category = models.ManyToManyField(FossSuperCategory)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+ show_on_homepage = models.PositiveSmallIntegerField(default=0, help_text ='0:Series, 1:Display on home page, 2:Archived')
+ available_for_nasscom = models.BooleanField(default=True, help_text ='If unchecked, this foss will not be available for nasscom' )
+ available_for_jio = models.BooleanField(default=True, help_text ='If unchecked, this foss will not be available for jio and spoken-tutorial.in' )
+ csc_dca_programme = models.BooleanField(default=True, help_text ='If unchecked, this foss will not be available for csc-dca programme' )
+ class Meta(object):
+ verbose_name = 'FOSS'
+ verbose_name_plural = 'FOSSes'
+ ordering = ('foss', )
+ managed = False
+ db_table = 'creation_fosscategory'
+
+ def __str__(self):
+ return self.foss
+
+class TutorialDetail(models.Model):
+ foss = models.ForeignKey(FossCategory, on_delete=models.PROTECT )
+ tutorial = models.CharField(max_length=255)
+ level = models.ForeignKey(Level, on_delete=models.PROTECT )
+ order = models.IntegerField()
+ user = models.ForeignKey(User, on_delete=models.PROTECT )
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ class Meta(object):
+ verbose_name = 'Tutorial Detail'
+ unique_together = (('foss', 'tutorial', 'level'),)
+ managed = False
+ db_table = 'creation_tutorialdetail'
+
+ def __str__(self):
+ return self.tutorial
+class TutorialCommonContent(models.Model):
+ tutorial_detail = models.OneToOneField(
+ TutorialDetail, related_name='tutorial_detail',on_delete=models.PROTECT)
+ slide = models.CharField(max_length=255)
+ slide_user = models.ForeignKey(User, related_name='slides', on_delete=models.PROTECT )
+ slide_status = models.PositiveSmallIntegerField(default=0)
+
+ code = models.CharField(max_length=255)
+ code_user = models.ForeignKey(User, related_name='codes', on_delete=models.PROTECT )
+ code_status = models.PositiveSmallIntegerField(default=0)
+
+ assignment = models.CharField(max_length=255)
+ assignment_user = models.ForeignKey(User, related_name='assignments', on_delete=models.PROTECT )
+ assignment_status = models.PositiveSmallIntegerField(default=0)
+
+ prerequisite = models.ForeignKey(
+ TutorialDetail, related_name='prerequisite', blank=True, null=True, on_delete=models.PROTECT )
+ prerequisite_user = models.ForeignKey(User, related_name='prerequisite', on_delete=models.PROTECT )
+ prerequisite_status = models.PositiveSmallIntegerField(default=0)
+
+ additional_material = models.CharField(
+ max_length=255, blank=True, null=True)
+ additional_material_user = models.ForeignKey(
+ User, related_name='additional_material', null=True, default=None, on_delete=models.PROTECT )
+ additional_material_status = models.PositiveSmallIntegerField(default=0)
+
+ keyword = models.TextField()
+ keyword_user = models.ForeignKey(User, related_name='keywords', on_delete=models.PROTECT )
+ keyword_status = models.PositiveSmallIntegerField(default=0)
+
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ class Meta(object):
+ verbose_name = 'Tutorial Common Content'
+ managed = False
+ db_table = 'creation_tutorialcommoncontent'
+
+
+ def keyword_as_list(self):
+ return self.keyword.split(',')
+
+class Language(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ user = models.ForeignKey(User, on_delete=models.PROTECT )
+ code = models.CharField(max_length=10, default='en')
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ class Meta(object):
+ ordering = ('name',)
+ managed = False
+ db_table = 'creation_language'
+
+ def __str__(self):
+ return self.name
+
+class TutorialResource(models.Model):
+ tutorial_detail = models.ForeignKey(TutorialDetail, on_delete=models.PROTECT )
+ common_content = models.ForeignKey(TutorialCommonContent, on_delete=models.PROTECT )
+ language = models.ForeignKey(Language, on_delete=models.PROTECT )
+
+ outline = models.TextField()
+ outline_user = models.ForeignKey(User, related_name='outlines', on_delete=models.PROTECT )
+ outline_status = models.PositiveSmallIntegerField(default=0)
+
+ script = models.URLField(max_length=255)
+ script_user = models.ForeignKey(User, related_name='scripts', on_delete=models.PROTECT )
+ script_status = models.PositiveSmallIntegerField(default=0)
+ timed_script = models.URLField(max_length=255)
+
+ video = models.CharField(max_length=255)
+ video_id = models.CharField(
+ max_length=255, null=True, blank=True, default=None)
+ playlist_item_id = models.CharField(
+ max_length=255, null=True, blank=True, default=None)
+ video_thumbnail_time = models.TimeField(default='00:00:00')
+ video_user = models.ForeignKey(User, related_name='videos', on_delete=models.PROTECT )
+ video_status = models.PositiveSmallIntegerField(default=0)
+ status = models.PositiveSmallIntegerField(default=0)
+ version = models.PositiveSmallIntegerField(default=0)
+ hit_count = models.PositiveIntegerField(default=0)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+ publish_at = models.DateTimeField(null=True)
+ # the last submission date for the tutorial
+ submissiondate = models.DateTimeField(default=datetime.datetime(2000, 1, 2, 12, 00))
+ # 0 - Not Assigned to anyone , 1 - Assigned & work in progress , 2 - Completed (= published / PR )
+ assignment_status = models.PositiveSmallIntegerField(default=0)
+ # 0 - Not Extended , 1 - Extended , 2 - Tutorial Terminated from user
+ extension_status = models.PositiveIntegerField(default=0)
+
+ class Meta:
+ unique_together = ('tutorial_detail', 'language',)
+ managed = False
+ db_table = 'creation_tutorialresource'
+
+ def get_absolute_url(self):
+ from django.urls import reverse
+ return reverse('watch_tutorial', args=[self.tutorial_detail.foss.foss, self.tutorial_detail.tutorial, self.language])
+
+
+
+
+# Forums models
+
+class Question(models.Model):
+ uid = models.IntegerField()
+ category = models.CharField(max_length=200)
+ tutorial = models.CharField(max_length=200)
+ minute_range = models.CharField(max_length=10)
+ second_range = models.CharField(max_length=10)
+ title = models.CharField(max_length=200)
+ body = models.TextField()
+ date_created = models.DateTimeField(auto_now_add=True)
+ date_modified = models.DateTimeField(auto_now=True)
+ views = models.IntegerField(default=1)
+ status = models.IntegerField(default=1)
+ # votes = models.IntegerField(default=0)
+
+ def get_slugified_title(self):
+ return self.title.replace(' ', '-')
+
+ def user(self):
+ user = User.objects.get(id=self.uid)
+ return user.username
+
+ class Meta(object):
+ db_table = 'website_question'
+ get_latest_by = "date_created"
+ managed = False
+
+class Answer(models.Model):
+ uid = models.IntegerField()
+ question = models.ForeignKey(Question, on_delete=models.PROTECT )
+ body = models.TextField()
+ date_created = models.DateTimeField(auto_now_add=True)
+ date_modified = models.DateTimeField(auto_now=True)
+ # votes = models.IntegerField(default=0)
+
+ def user(self):
+ user = User.objects.get(id=self.uid)
+ return user.username
+
+ class Meta(object):
+ db_table = 'website_answer'
+ managed = False
\ No newline at end of file
diff --git a/cdcontent/templates/cdcontent/answer_to_question.html b/cdcontent/templates/cdcontent/answer_to_question.html
new file mode 100644
index 0000000..15fb259
--- /dev/null
+++ b/cdcontent/templates/cdcontent/answer_to_question.html
@@ -0,0 +1,185 @@
+
+
+
+
+
+ {% block title %}
+ Spoken Tutorial Forums
+ {% endblock %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% block content %}
+
+{% if answer %}
+
Answers:
+
+
+
+{% for ans in answer %}
+
+
+
+
+
+ {{ ans.body|safe }}
+
+
+
+
+
+ {{ ans.date_created|date:"d-m-y" }}, {{ ans.date_created|time }}
+
+
+
+
+ {{ ans.user }}
+
+
+
+
+
+
+
+
+
+
+{% endfor %}
+{% else %}
+
No answers yet
+ {% endif %}
+
Click here to reply/comment online
+ {% endblock %}
+
+
+
+
+
+
+
diff --git a/cdcontent/templates/cdcontent/cdcontent_home.html b/cdcontent/templates/cdcontent/cdcontent_home.html
new file mode 100644
index 0000000..a6d9d73
--- /dev/null
+++ b/cdcontent/templates/cdcontent/cdcontent_home.html
@@ -0,0 +1,102 @@
+{% extends 'csc_base.html'%}
+{% load widget_tweaks %}
+{% load static %}
+{% block content %}
+
+
+
+
+{% endblock %}
+
+
+{% block script %}
+
+
+
+
+
+
+
+{% endblock %}
+
diff --git a/cdcontent/templates/cdcontent/home.html b/cdcontent/templates/cdcontent/home.html
new file mode 100644
index 0000000..6def7fd
--- /dev/null
+++ b/cdcontent/templates/cdcontent/home.html
@@ -0,0 +1,281 @@
+{% load cdcontentdata %}
+
+
+
+
+ Search Tutorials | spoken-tutorial.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Search Tutorials
+
+
+
+
+ Select Language for Side-by-Side method video
+ {% for language in languages %}
+ {{ language }}
+ {% endfor %}
+
+
+
+
+
+
+
+ Spoken Tutorial Forums:
+
+
+
+
+
+
+ Spoken Tutorial Supplementary Material:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cdcontent/templates/cdcontent/index.html b/cdcontent/templates/cdcontent/index.html
new file mode 100644
index 0000000..a95b08e
--- /dev/null
+++ b/cdcontent/templates/cdcontent/index.html
@@ -0,0 +1,9 @@
+
+
+ Search Tutorials | spoken-tutorial.org
+
+
+
+
+
+
diff --git a/cdcontent/templates/cdcontent/tutorial_search.html b/cdcontent/templates/cdcontent/tutorial_search.html
new file mode 100644
index 0000000..7db9b9a
--- /dev/null
+++ b/cdcontent/templates/cdcontent/tutorial_search.html
@@ -0,0 +1,172 @@
+{% load cdcontentdata %}
+
+
+
+
+ Search Tutorials | spoken-tutorial.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Search Tutorials
+
+
+ {% for record in collection %}
+
+
+
+
+
+
+
+
Foss: {{ record.tutorial_detail.foss }} - {{ record.language }}
+
Outline: {{ record.outline|len_cutter:200 }}
+
+
+
+
+
{{ record.tutorial_detail.level.level }}
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cdcontent/templates/cdcontent/watch_tutorial.html b/cdcontent/templates/cdcontent/watch_tutorial.html
new file mode 100644
index 0000000..349b15f
--- /dev/null
+++ b/cdcontent/templates/cdcontent/watch_tutorial.html
@@ -0,0 +1,684 @@
+{% load cdcontentdata %}
+
+
+
+
+ {{ tr_rec.tutorial_detail.tutorial }} - {{ tr_rec.language.name }} | spoken-tutorial.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ tr_rec.tutorial_detail.tutorial }} - {{ tr_rec.language.name }}
+
+
+
+
+
+ {{ tr_rec|get_srt_files|safe }}
+
+
+
+
+
+
+
+
+ {% with link=tr_rec.tutorial_detail.foss|cd_instruction_sheet:tr_rec.language %}
+ {% if link %}
+ Instruction Sheet
+ {% endif %}
+ {% endwith %}
+
+ {% with link=tr_rec.tutorial_detail.foss|cd_installation_sheet:tr_rec.language %}
+ {% if link %}
+ Installation Sheet
+ {% endif %}
+ {% endwith %}
+
+
Outline: {{ tr_rec.outline }}
+
+
+
+ {% if tr_rec.common_content.assignment_status and tr_rec.common_content.assignment_status != 6 %}
+
+ {% endif %}
+ {% if tr_rec.common_content.code_status and tr_rec.common_content.code_status != 6 %}
+
+ {% endif %}
+ {% if tr_rec.common_content.additional_material_status and tr_rec.common_content.additional_material_status != 6 %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cdcontent/templatetags/cdcontentdata.py b/cdcontent/templatetags/cdcontentdata.py
new file mode 100644
index 0000000..64da400
--- /dev/null
+++ b/cdcontent/templatetags/cdcontentdata.py
@@ -0,0 +1,81 @@
+from django import template
+import zipfile
+import os
+from django.conf import settings
+
+register = template.Library()
+def get_zip_content(path):
+ file_names = None
+ try:
+ zf = zipfile.ZipFile(path, 'r')
+ file_names = zf.namelist()
+ return file_names
+ except Exception as e:
+ return False
+
+def get_review_status_list(key):
+ status_list = ['Pending', 'Waiting for Admin Review', 'Waiting for Domain Review', 'Waiting for Quality Review', 'Accepted', 'Need Improvement', 'Not Required']
+ return status_list[key]
+
+def get_review_status_symbol(key):
+ status_list = ['fa fa-1 fa-minus-circle review-pending-upload', 'fa fa-1 fa-check-circle review-admin-review', 'fa fa-1 fa-check-circle review-domain-review', 'fa fa-1 fa-check-circle review-quality-review', 'fa fa-1 fa-check-circle review-accepted', 'fa fa-1 fa-times-circle review-pending-upload', 'fa fa-1 fa-ban review-accepted']
+ return status_list[key]
+
+def get_srt_files(tr):
+ data = ''
+ k = tr.video.rfind(".")
+ new_srtfile = tr.video[:k] + '.srt'
+ if tr.language.name != 'English':
+ if os.path.isfile(settings.MEDIA_ROOT + 'videos/' + str(tr.tutorial_detail.foss_id) + '/' + str(tr.tutorial_detail_id) + '/' + new_srtfile.replace(tr.language.name, 'English')):
+ data += ' '
+ if os.path.isfile(settings.MEDIA_ROOT + 'videos/' + str(tr.tutorial_detail.foss_id) + '/' + str(tr.tutorial_detail_id) + '/' + new_srtfile):
+ data += ' '
+ return data
+
+def cd_instruction_sheet(foss, lang):
+ file_path = settings.MEDIA_ROOT + 'videos/' + str(foss.id) + '/' + foss.foss.replace(' ', '-') + '-Instruction-Sheet-' + lang.name + '.pdf'
+ if lang.name != 'English':
+ if os.path.isfile(file_path):
+ file_path = '../' + foss.foss.replace(' ', '-') + '-Instruction-Sheet-' + lang.name + '.pdf'
+ return file_path
+
+
+ file_path = settings.MEDIA_ROOT + 'videos/' + str(foss.id) + '/' + foss.foss.replace(' ', '-') + '-Instruction-Sheet-English.pdf'
+ if os.path.isfile(file_path):
+ file_path = '../' + foss.foss.replace(' ', '-') + '-Instruction-Sheet-English.pdf'
+ return file_path
+ return False
+
+def cd_installation_sheet(foss, lang):
+ file_path = settings.MEDIA_ROOT + 'videos/' + str(foss.id) + '/' + foss.foss.replace(' ', '-') + '-Installation-Sheet-' + lang.name + '.pdf'
+ if lang.name != 'English':
+ if os.path.isfile(file_path):
+ file_path = '../' + foss.foss.replace(' ', '-') + '-Installation-Sheet-' + lang.name + '.pdf'
+ return file_path
+
+ file_path = settings.MEDIA_ROOT + 'videos/' + str(foss.id) + '/' + foss.foss.replace(' ', '-') + '-Installation-Sheet-English.pdf'
+ if os.path.isfile(file_path):
+ file_path = '../' + foss.foss.replace(' ', '-') + '-Installation-Sheet-English.pdf'
+ return file_path
+ return False
+
+def get_foss_name(foss, key):
+ return foss[key]['foss']
+
+def len_cutter(srting, limit):
+ return srting[:limit] + (srting[limit:] and '..')
+
+def get_lang_details(foss, key):
+ data = ''
+ for lang_key, lang_detail in list(foss[key]['langs'].items()):
+ data += '' + lang_detail + ' '
+ return data
+register.filter('get_srt_files', get_srt_files)
+register.filter('cd_instruction_sheet', cd_instruction_sheet)
+register.filter('cd_installation_sheet', cd_installation_sheet)
+register.filter('get_review_status_symbol', get_review_status_symbol)
+register.filter('get_review_status_list', get_review_status_list)
+register.filter('get_zip_content', get_zip_content)
+register.filter('get_foss_name', get_foss_name)
+register.filter('len_cutter', len_cutter)
+register.filter('get_lang_details', get_lang_details)
\ No newline at end of file
diff --git a/cdcontent/tests.py b/cdcontent/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/cdcontent/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/cdcontent/urls.py b/cdcontent/urls.py
new file mode 100644
index 0000000..b57076a
--- /dev/null
+++ b/cdcontent/urls.py
@@ -0,0 +1,12 @@
+# Third Party Stuff
+from django.conf.urls import url
+from .views import *
+app_name = 'cdcontent'
+urlpatterns = [
+ # Main pages dispatcher
+ url(r'^$', home, name="cdcontenthome"),
+ url(r'^ajax-fill-languages/$', ajax_fill_languages, name="ajax_fill_languages"),
+ url(r'^ajax-add-foss/$', ajax_add_foss, name="ajax_add_foss"),
+ url(r'^ajax-show-added-foss/$', ajax_show_added_foss, name="ajax_show_added_foss"),
+ url(r'^create_cd_download/(\w+)/', internal_computation, name="create_cd_download"),
+]
\ No newline at end of file
diff --git a/cdcontent/views.py b/cdcontent/views.py
new file mode 100644
index 0000000..00fe693
--- /dev/null
+++ b/cdcontent/views.py
@@ -0,0 +1,528 @@
+
+# Standard Library
+from builtins import str
+import json
+import os
+import zipfile
+from datetime import datetime,date
+
+# Third Party Stuff
+from django.conf import settings
+
+from django.db.models import Q
+from django.http import HttpResponse
+from django.shortcuts import render
+from django.views.decorators.csrf import csrf_protect, csrf_exempt
+from django.template.context_processors import csrf
+
+from django.contrib.auth.decorators import login_required
+# Spoken Tutorial Stuff
+from cdcontent.forms import *
+from csc.decorators import is_vle
+from .models import Language, FossCategory,Level,Question,Answer
+# Create your views here.
+def zipdir(src_path, dst_path, archive):
+ for root, dirs, dir_files in os.walk(src_path):
+ for dir_file in dir_files:
+ archive.write(os.path.join(root, dir_file), os.path.join(dst_path, dir_file))
+
+
+def humansize(nbytes):
+ suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
+
+ if nbytes == 0:
+ return '0 B'
+
+ i = 0
+
+ while nbytes >= 1024 and i < len(suffixes) - 1:
+ nbytes /= 1024.
+ i += 1
+
+ f = ('%.1f' % nbytes).rstrip('0').rstrip('.')
+ return '%s %s' % (f, suffixes[i])
+
+
+def add_sheets(archive, foss, lang):
+ instruction_file = 'videos/{}/{}-Instruction-Sheet-{}.pdf'.format(foss.id,
+ foss.foss.replace(' ', '-'),
+ lang.name)
+
+ installation_file = 'videos/{}/{}-Installation-Sheet-{}.pdf'.format(foss.id,
+ foss.foss.replace(' ', '-'),
+ lang.name)
+ instruction_file_path = '{}{}'.format(settings.MEDIA_ROOT, instruction_file)
+ installation_file_path = '{}{}'.format(settings.MEDIA_ROOT, installation_file)
+
+ if os.path.isfile(instruction_file_path):
+ new_file_path = 'spoken/{}'.format(instruction_file)
+ archive.write(instruction_file_path, new_file_path)
+
+ if os.path.isfile(installation_file_path):
+ new_file_path = 'spoken/{}'.format(installation_file)
+ archive.write(installation_file_path, new_file_path)
+
+
+def get_all_foss_details(selectedfoss):
+ all_foss_details = {}
+
+ for key, values in list(selectedfoss.items()):
+ foss_rec = FossCategory.objects.get(pk=key)
+
+ if not all_foss_details.get(foss_rec.id, None):
+ all_foss_details[foss_rec.id] = {}
+
+ all_foss_details[foss_rec.id]['foss'] = foss_rec.foss
+
+ if not all_foss_details[foss_rec.id].get('langs', None):
+ all_foss_details[foss_rec.id]['langs'] = {}
+
+ for value in values[0]:
+ language = Language.objects.get(pk=value)
+ all_foss_details[foss_rec.id]['langs'][language.id] = language.name
+ return all_foss_details
+
+
+def add_side_by_side_tutorials(archive, languages):
+ languages.add('English')
+ available_langs = set()
+
+ for language in languages:
+ filepath = '{}/videos/32/714/Side-by-Side-Method-{}.ogv'.format(settings.MEDIA_ROOT, language)
+
+ if os.path.isfile(filepath):
+ available_langs.add(language)
+ archive.write(filepath, 'spoken/videos/Side-by-Side-Method-{}.ogv'.format(language))
+
+ return available_langs
+
+def add_forum_video(archive):
+ filepath = '{}/videos/32/1450/Spoken-Tutorial-Forums-English.ogv'.format(settings.MEDIA_ROOT)
+ if os.path.isfile(filepath):
+ archive.write(filepath, 'spoken/videos/Spoken-Tutorial-Forums-English.ogv')
+
+def add_suplementary_video(archive):
+ filepath = '{}/videos/32/1537/Spoken-Tutorial-Supplementary-Material-English.ogv'.format(settings.MEDIA_ROOT)
+ if os.path.isfile(filepath):
+ archive.write(filepath, 'spoken/videos/Spoken-Tutorial-Supplementary-Material-English.ogv')
+
+
+def get_static_files():
+ return {
+ '/static/spoken/css/bootstrap.min.css': 'spoken/includes/css/bootstrap.min.css',
+ '/static/spoken/css/font-awesome.min.css': 'spoken/includes/css/font-awesome.min.css',
+ '/static/spoken/css/main.css': 'spoken/includes/css/main.css',
+ '/static/spoken/css/video-js.min.css': 'spoken/includes/css/video-js.min.css',
+ '/static/spoken/images/favicon.ico': 'spoken/includes/images/favicon.ico',
+ '/static/spoken/images/logo.png': 'spoken/includes/images/logo.png',
+ '/static/spoken/js/jquery-1.11.0.min.js': 'spoken/includes/js/jquery-1.11.0.min.js',
+ '/static/spoken/js/bootstrap.min.js': 'spoken/includes/js/bootstrap.min.js',
+ '/static/spoken/js/video.js': 'spoken/includes/js/video.js',
+ '/static/spoken/images/thumb-even.png': 'spoken/includes/images/thumb-even.png',
+ '/static/spoken/images/Basic.png': 'spoken/includes/images/Basic.png',
+ '/static/spoken/images/Intermediate.png': 'spoken/includes/images/Intermediate.png',
+ '/static/spoken/images/Advanced.png': 'spoken/includes/images/Advanced.png',
+ '/static/cdcontent/templates/readme.txt': 'spoken/README.txt',
+ '/static/cdcontent/templates/index.html': 'spoken/index.html',
+ '/static/forum_website/css/bootstrap.min.css': 'spoken/includes/css/bootstrap_forum.min.css',
+ '/static/forum_website/css/main.css': 'spoken/includes/css/main_forum.css',
+ '/static/forum_website/css/nice-bar.css': 'spoken/includes/css/nice-bar.css',
+ '/static/forum_website/css/theme.blue.css': 'spoken/includes/css/theme.blue.css',
+ '/static/forum_website/slick/slick.css': 'spoken/includes/css/slick.css',
+ '/static/forum_website/images/cc-logo-88x31.png': 'spoken/includes/images/cc-logo-88x31.png'
+
+ }
+
+
+def calculate_directory_size(dir_path):
+ folder_size = 0.0
+
+ try:
+ if os.path.isdir(dir_path):
+ for (path, dirs, files) in os.walk(dir_path):
+ for file_name in files:
+ filename = os.path.join(path, file_name)
+ folder_size += os.path.getsize(filename)
+ except Exception as e:
+ folder_size = 0.0
+ print(e)
+
+ return folder_size
+
+
+def calculate_static_file_size():
+ fsize = 0.0
+
+ try:
+ static_files = get_static_files()
+ dir_path = '{}/static/spoken/fonts'.format(settings.BASE_DIR)
+
+ for key, value in list(static_files.items()):
+ filepath = '{}{}'.format(settings.BASE_DIR, key)
+
+ if os.path.isfile(filepath):
+ fsize += os.path.getsize(filepath)
+
+ fsize += calculate_directory_size(dir_path)
+ except Exception as e:
+ fsize = 0.0
+ print(e)
+
+ return fsize
+
+
+def add_static_files(archive):
+ zipdir(settings.BASE_DIR + '/static/spoken/fonts', 'spoken/includes/fonts/', archive)
+ static_files = get_static_files()
+
+ for key, value in list(static_files.items()):
+ filepath = '{}{}'.format(settings.BASE_DIR, key)
+
+ if os.path.isfile(filepath):
+ archive.write(filepath, value)
+
+
+def convert_template_to_html_file(archive, filename, request, template, ctx):
+ try:
+ html = render(request,template, ctx).content
+ except Exception as e:
+ print(e)
+
+ html_string = html.decode('utf-8')
+ html_string = html_string.replace('Content-Type: text/html; charset=utf-8', '').strip("\n")
+ archive.writestr(filename, html_string)
+
+
+def collect_common_files(tr_rec, common_files):
+ common_files_path = 'videos/{}/{}/resources'.format(tr_rec.tutorial_detail.foss_id, tr_rec.tutorial_detail_id)
+
+ # if tr_rec.common_content.slide_status > 0:
+ # common_files.add('{}/{}'.format(common_files_path, tr_rec.common_content.slide))
+
+ if tr_rec.common_content.assignment_status > 0 and tr_rec.common_content.assignment_status != 6:
+ common_files.add('{}/{}'.format(common_files_path, tr_rec.common_content.assignment))
+
+ if tr_rec.common_content.code_status > 0 and tr_rec.common_content.code_status != 6:
+ common_files.add('{}/{}'.format(common_files_path, tr_rec.common_content.code))
+
+ if tr_rec.common_content.additional_material_status > 0 and tr_rec.common_content.additional_material_status != 6:
+ common_files.add('{}/{}'.format(common_files_path, tr_rec.common_content.additional_material))
+
+
+
+def add_common_files(archive, common_files):
+ for filepath in common_files:
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)):
+ archive.write(os.path.join(settings.MEDIA_ROOT, filepath), 'spoken/' + filepath)
+
+
+def add_srt_file(archive, tr_rec, filepath, eng_flag, srt_files):
+ ptr = filepath.rfind(".")
+ filepath = filepath[:ptr] + '.srt'
+
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)):
+ archive.write(os.path.join(settings.MEDIA_ROOT, filepath), 'spoken/' + filepath)
+
+ if eng_flag:
+ filepath = 'videos/{}/{}/{}-English.srt'.format(tr_rec.tutorial_detail.foss_id, tr_rec.tutorial_detail_id,
+ tr_rec.tutorial_detail.tutorial.replace(' ', '-'))
+
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)) and filepath not in srt_files:
+ srt_files.add(filepath)
+ archive.write(os.path.join(settings.MEDIA_ROOT, filepath), 'spoken/' + filepath)
+
+
+def internal_computation(request, user_type):
+ zipfile_name = '{}.zip'.format(datetime.now().strftime('%Y%m%d%H%M%S%f'))
+ # file_obj = open('{}cdimage/{}'.format(settings.MEDIA_ROOT, zipfile_name), 'wb')
+ zip_file_name = os.path.join(settings.MEDIA_ROOT, 'cdimage',zipfile_name)
+
+
+
+ file_obj = open(zip_file_name, 'wb')
+ archive = zipfile.ZipFile(file_obj, 'w', zipfile.ZIP_DEFLATED, allowZip64=True)
+ try:
+ selectedfoss = json.loads(request.POST.get('selected_foss', {}))
+ except:
+ selectedfoss = request.POST.get('selected_foss', {})
+ all_foss_details = get_all_foss_details(selectedfoss)
+ eng_rec = Language.objects.get(name="English")
+ languages = set()
+
+ for key, values in list(selectedfoss.items()):
+ foss_rec = FossCategory.objects.get(pk=key)
+ level = int(values[1])
+ eng_flag = True
+ srt_files = set()
+ common_files = set()
+
+ if str(eng_rec.id) in values[0]:
+ eng_flag = False
+
+ t_resource_qs = TutorialResource.objects.filter(Q(status=1) | Q(status=2),
+ tutorial_detail__foss_id=key)
+
+ if level:
+ t_resource_qs = t_resource_qs.filter(tutorial_detail__level_id=level)
+ for value in values[0]:
+ language = Language.objects.get(pk=value)
+ add_sheets(archive, foss_rec, language)
+
+ tr_recs = t_resource_qs.filter(language_id=value).order_by(
+ 'tutorial_detail__level', 'tutorial_detail__order', 'language__name')
+
+ languages.add(language.name)
+
+ for rec in tr_recs:
+ filepath = 'videos/{}/{}/{}'.format(key, rec.tutorial_detail_id, rec.video)
+ # get list of questions of a particular tutorial
+ question_s = Question.objects.filter(category=foss_rec.foss.replace(' ','-'),tutorial=rec.tutorial_detail.tutorial.replace(' ','-')).order_by('-date_created')
+
+
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)):
+ archive.write(os.path.join(settings.MEDIA_ROOT, filepath), 'spoken/' + filepath)
+
+ # add srt file to archive
+ add_srt_file(archive, rec, filepath, eng_flag, srt_files)
+
+ # collect common files
+ collect_common_files(rec, common_files)
+
+ tutorial_path = '{}/{}/'.format(rec.tutorial_detail.foss_id, rec.tutorial_detail_id)
+ filepath = 'spoken/videos/{}show-video-{}.html'.format(tutorial_path, rec.language.name)
+ ctx = {'tr_rec': rec, 'tr_recs': tr_recs,
+ 'media_path': settings.MEDIA_ROOT, 'tutorial_path': tutorial_path,'question_s': question_s}
+
+ # convert_template_to_html_file(archive, filepath, request,
+ # "cdcontent/templates/watch_tutorial.html", ctx)
+ convert_template_to_html_file(archive, filepath, request,
+ "cdcontent/watch_tutorial.html", ctx)
+ # for each question find the answers
+ for question in question_s:
+ answer=Answer.objects.filter(question=question)
+ ctx = {'question': question, 'answer': answer}
+ filepath = 'spoken/videos/' + str(foss_rec.id) + '/' + str(rec.tutorial_detail_id) + '/answer-to-question-' + str(question.id) + '.html'
+ #ToDo
+ # convert_template_to_html_file(archive, filepath, request, "cdcontent/templates/answer_to_question.html", ctx)
+ convert_template_to_html_file(archive, filepath, request, "cdcontent/answer_to_question.html", ctx)
+
+
+
+
+ filepath = 'spoken/videos/' + str(foss_rec.id) + '/list-videos-' + language.name + '.html'
+ ctx = {'collection': tr_recs, 'foss_details': all_foss_details,
+ 'foss': foss_rec.id, 'lang': language.id}
+ #ToDo
+ # convert_template_to_html_file(archive, filepath, request,"cdcontent/templates/tutorial_search.html", ctx)
+ convert_template_to_html_file(archive, filepath, request,"cdcontent/tutorial_search.html", ctx)
+
+ # add common files for current foss
+ add_common_files(archive, common_files)
+
+ # add side-by-side tutorials for selected languages
+ languages = add_side_by_side_tutorials(archive, languages)
+ add_forum_video(archive)
+ add_suplementary_video(archive)
+
+ ctx = {'foss_details': all_foss_details, 'foss':'' ,
+ 'lang': '', 'languages': languages}
+ #ToDo
+ # convert_template_to_html_file(archive, 'spoken/videos/home.html', request,"cdcontent/templates/home.html", ctx)
+ convert_template_to_html_file(archive, 'spoken/videos/home.html', request,"cdcontent/home.html", ctx)
+
+ # add all required static files to archive
+ add_static_files(archive)
+ archive.close()
+
+ file_obj.close()
+ file_path= os.path.join(settings.MEDIA_ROOT, 'cdimage',zipfile_name)
+
+ # file_path= os.path.dirname(os.path.realpath(__file__))+'/../media/cdimage/{}'.format(zipfile_name)
+ # file_path= os.path.dirname(os.path.realpath(__file__))+'/../media/cdimage/{}'.format(zipfile_name)
+
+ # file_path1= os.path.join()
+ response = HttpResponse(open(file_path, 'rb'), content_type='application/zip')
+ return zipfile_name
+ if user_type == 'paid':
+ return zipfile_name
+ elif user_type == 'general':
+ return response
+
+# @csrf_exempt
+@login_required
+@is_vle
+def home(request):
+ if request.method == 'POST':
+ form = CDContentForm(request.POST)
+ if form.is_valid():
+ try:
+ zipfile_name = internal_computation(request, 'paid')
+ # wrapper = FileWrapper(temp)
+ # response = HttpResponse(wrapper, content_type='application/zip')
+ # response['Content-Disposition'] = 'attachment; filename=spoken-tutorial-cdcontent.zip'
+ # response['Content-Length'] = temp.tell()
+ # return response
+ context = {'path': '/media/cdimage/{}'.format(zipfile_name), 'status': True}
+ except Exception as e:
+ print(e)
+ context = {'path': '', 'status': False}
+ return HttpResponse(json.dumps(context), content_type='application/json')
+ else:
+ form = CDContentForm()
+ # states = State.objects.order_by('name')
+
+ context = {'form': form,}
+ context.update(csrf(request))
+ return render(request, "cdcontent/cdcontent_home.html", context)
+
+@csrf_exempt
+def ajax_fill_languages(request):
+ data = ''
+ fossid = request.POST.get('foss', '')
+ levelid = int(request.POST.get('level', 0))
+ if fossid:
+ if levelid:
+ lang_recs = TutorialResource.objects.filter(
+ Q(status=1) | Q(status=2), tutorial_detail__foss_id=fossid,
+ tutorial_detail__level_id=levelid).values_list(
+ 'language_id', 'language__name').order_by('language__name').distinct()
+ else:
+ lang_recs = TutorialResource.objects.filter(
+ Q(status=1) | Q(status=2), tutorial_detail__foss_id=fossid).values_list(
+ 'language_id', 'language__name').order_by('language__name').distinct()
+ for row in lang_recs:
+ data = data + '' + row[1] + ' '
+
+ return HttpResponse(json.dumps(data), content_type='application/json')
+
+
+@csrf_exempt
+def ajax_add_foss(request):
+ foss = request.POST.get('foss', '')
+ level = int(request.POST.get('level', 0))
+ selectedfoss = {}
+ try:
+ langs = json.loads(request.POST.get('langs', []))
+ except:
+ langs = []
+ try:
+ selectedfoss = json.loads(request.POST.get('selectedfoss', ''))
+ except:
+ pass
+ if foss and langs:
+ selectedfoss[foss] = [langs, level]
+ data = json.dumps(selectedfoss)
+ return HttpResponse(json.dumps(data), content_type='application/json')
+
+
+@csrf_exempt
+def ajax_show_added_foss(request):
+ try:
+ tmp = json.loads(request.POST.get('selectedfoss', {}))
+ except:
+ tmp = {}
+
+
+ data = ''
+ fsize_total = 0.0
+ languages = set()
+ eng_rec = Language.objects.get(name="English")
+
+ for key, values in list(tmp.items()):
+ langs_list = list(values[0])
+ foss, level = FossCategory.objects.get(pk=key), int(values[1])
+ langs = ', '.join(list(
+ Language.objects.filter(id__in=list(values[0])).order_by('name').values_list('name', flat=True)))
+ if level:
+ tr_recs = TutorialResource.objects.filter(Q(status=1) | Q(
+ status=2), tutorial_detail__foss=foss, tutorial_detail__level_id=level, language_id__in=langs_list)
+ level_txt = Level.objects.get(pk=level)
+ else:
+ tr_recs = TutorialResource.objects.filter(Q(status=1) | Q(
+ status=2), tutorial_detail__foss=foss, language_id__in=langs_list)
+ level_txt = 'All'
+
+ fsize = 0.0
+ eng_flag = True
+ srt_files = set()
+ common_files = set()
+
+ if str(eng_rec.id) in langs_list:
+ eng_flag = False
+
+ for rec in tr_recs:
+ try:
+ languages.add(rec.language.name)
+ # calculate video size
+ filepath = 'videos/{}/{}/{}'.format(foss.id, rec.tutorial_detail_id, rec.video)
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)):
+ fsize += os.path.getsize(os.path.join(settings.MEDIA_ROOT, filepath))
+ # calculate str file size
+ ptr = filepath.rfind(".")
+ filepath = filepath[:ptr] + '.srt'
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)):
+ fsize += os.path.getsize(os.path.join(settings.MEDIA_ROOT, filepath))
+ if eng_flag:
+ filepath = 'videos/{}/{}/{}-English.srt'.format(
+ key, rec.tutorial_detail_id, rec.tutorial_detail.tutorial.replace(' ', '-'))
+ if os.path.isfile(os.path.join(settings.MEDIA_ROOT, filepath)) and filepath not in srt_files:
+ fsize += os.path.getsize(os.path.join(settings.MEDIA_ROOT, filepath))
+ # append common files path to list
+ common_files_path = '{}/videos/{}/{}/resources'.format(settings.MEDIA_ROOT, key,
+ rec.tutorial_detail_id)
+ # if rec.common_content.slide_status > 0:
+ # common_files.add('{}/{}'.format(common_files_path, rec.common_content.slide))
+
+ if rec.common_content.assignment_status > 0 and rec.common_content.assignment_status != 6:
+ common_files.add('{}/{}'.format(common_files_path, rec.common_content.assignment))
+
+ if rec.common_content.code_status > 0 and rec.common_content.code_status != 6:
+ common_files.add('{}/{}'.format(common_files_path, rec.common_content.code))
+
+ if rec.common_content.additional_material_status > 0 and rec.common_content.additional_material_status != 6:
+ common_files.add('{}/{}'.format(common_files_path, rec.common_content.additional_material))
+ except Exception:
+ continue
+
+ # calculate common files size
+ for filepath in common_files:
+ if os.path.isfile(filepath):
+ fsize += os.path.getsize(filepath)
+ fsize_total += fsize
+
+
+ data += '{} {} {} {} '.format(foss.foss,level_txt, langs, humansize(fsize),foss.id)
+
+ fsize = 0.0
+ languages.add(eng_rec.name)
+
+ # calculate size for side-by-side tutorials
+ for language in languages:
+ filepath = '{}/videos/32/714/Side-by-Side-Method-{}.ogv'.format(settings.MEDIA_ROOT, language)
+
+ if os.path.isfile(filepath):
+ fsize += os.path.getsize(filepath)
+
+ # calculate size for forum video
+ filepath = '{}/videos/32/1450/Spoken-Tutorial-Forums-English.ogv'.format(settings.MEDIA_ROOT)
+ if os.path.isfile(filepath):
+ fsize += os.path.getsize(filepath)
+
+ # calculate size for suplementary video
+ filepath = '{}/videos/32/1537/Spoken-Tutorial-Supplementary-Material-English.ogv'.format(settings.MEDIA_ROOT)
+ if os.path.isfile(filepath):
+ fsize += os.path.getsize(filepath)
+
+ # calculate static file size
+ fsize += calculate_static_file_size()
+
+ fsize_total += fsize
+ data += 'Extra files {} '.format(humansize(fsize))
+
+ #check if user is registered
+ # user_details = check_user_details(request,fsize_total )
+ #ToDo
+ user_details = ['RP',2000]
+
+ output = {0: data, 1: humansize(fsize_total), 2:user_details}
+ return HttpResponse(json.dumps(output), content_type='application/json')
diff --git a/cms/__init__.py b/cms/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/cms/admin.py b/cms/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/cms/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/cms/api/__init__.py b/cms/api/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/cms/api/serializers.py b/cms/api/serializers.py
new file mode 100644
index 0000000..87ae09e
--- /dev/null
+++ b/cms/api/serializers.py
@@ -0,0 +1,21 @@
+from rest_framework import serializers
+
+from cms.models import State,District
+
+class StateSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = State
+ fields = ["id","name"]
+ readonly = ["id", "name"]
+
+class DistrictSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = District
+ fields = ["id","name","state"]
+ readonly = ["id","name","state"]
+
+class DistrictDetailSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = District
+ fields = ["id","name"]
+ readonly = ["id","name"]
\ No newline at end of file
diff --git a/cms/api/urls.py b/cms/api/urls.py
new file mode 100644
index 0000000..daf1b0a
--- /dev/null
+++ b/cms/api/urls.py
@@ -0,0 +1,18 @@
+from django.urls import path
+from django.urls import re_path
+
+from rest_framework import permissions
+
+from drf_yasg.views import get_schema_view
+from drf_yasg import openapi
+
+
+import os
+
+from cms.api.views import StateList,DistrictList,DistrictDetail
+
+urlpatterns = [
+ path('states/',StateList.as_view(),name="state_list"),
+ path('districts/',DistrictDetail.as_view(),name="state_districts"),
+ path('districts/',DistrictList.as_view(),name="district_list"),
+]
\ No newline at end of file
diff --git a/cms/api/views.py b/cms/api/views.py
new file mode 100644
index 0000000..b454cf7
--- /dev/null
+++ b/cms/api/views.py
@@ -0,0 +1,33 @@
+from rest_framework import generics
+from rest_framework.authentication import TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+
+from cms.api.serializers import StateSerializer,DistrictSerializer,DistrictDetailSerializer
+from cms.models import State,District
+
+class StateList(generics.ListAPIView):
+ # permission_classes = [IsAuthenticated]
+ # authentication_classes = [TokenAuthentication]
+ queryset = State.objects.all()
+ serializer_class = StateSerializer
+
+class DistrictList(generics.ListAPIView):
+ # permission_classes = [IsAuthenticated]
+ # authentication_classes = [TokenAuthentication]
+ queryset = District.objects.all()
+ serializer_class = DistrictSerializer
+
+
+class DistrictDetail(generics.ListAPIView):
+ lookup_field = "state_id"
+ serializer_class =DistrictDetailSerializer
+
+ def get_queryset(self):
+
+ state_id = self.kwargs['state_id']
+ print(f"state_id ******> {state_id}")
+ print(f"Count : {District.objects.filter(state_id=state_id).count()}")
+ return District.objects.filter(state_id=state_id)
+
+
+
\ No newline at end of file
diff --git a/cms/apps.py b/cms/apps.py
new file mode 100644
index 0000000..7ef3fea
--- /dev/null
+++ b/cms/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class CmsConfig(AppConfig):
+ name = 'cms'
diff --git a/cms/migrations/0001_initial.py b/cms/migrations/0001_initial.py
new file mode 100644
index 0000000..6e8e376
--- /dev/null
+++ b/cms/migrations/0001_initial.py
@@ -0,0 +1,60 @@
+# Generated by Django 3.0.3 on 2022-07-18 08:40
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='State',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('code', models.CharField(max_length=3)),
+ ('name', models.CharField(max_length=50)),
+ ('slug', models.CharField(max_length=100)),
+ ('latitude', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
+ ('longtitude', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
+ ('img_map_area', models.TextField()),
+ ('has_map', models.BooleanField(default=1)),
+ ('created', models.DateTimeField(auto_now_add=True, null=True)),
+ ('updated', models.DateTimeField(auto_now=True, null=True)),
+ ],
+ options={
+ 'unique_together': {('code', 'name')},
+ },
+ ),
+ migrations.CreateModel(
+ name='District',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('code', models.CharField(max_length=3)),
+ ('name', models.CharField(max_length=200)),
+ ('created', models.DateTimeField(auto_now_add=True, null=True)),
+ ('updated', models.DateTimeField(auto_now=True, null=True)),
+ ('state', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cms.State')),
+ ],
+ options={
+ 'unique_together': {('state', 'code', 'name')},
+ },
+ ),
+ migrations.CreateModel(
+ name='City',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200)),
+ ('created', models.DateTimeField(auto_now_add=True, null=True)),
+ ('updated', models.DateTimeField(auto_now=True, null=True)),
+ ('state', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cms.State')),
+ ],
+ options={
+ 'unique_together': {('name', 'state')},
+ },
+ ),
+ ]
diff --git a/cms/migrations/__init__.py b/cms/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/cms/models.py b/cms/models.py
new file mode 100644
index 0000000..a6d0e08
--- /dev/null
+++ b/cms/models.py
@@ -0,0 +1,54 @@
+from django.db import models
+
+# Create your models here.
+class State(models.Model):
+ code = models.CharField(max_length=3)
+ name = models.CharField(max_length=50)
+ slug = models.CharField(max_length = 100)
+ latitude = models.DecimalField(
+ null=True,
+ max_digits=10,
+ decimal_places=4,
+ blank=True
+ )
+ longtitude = models.DecimalField(
+ null=True,
+ max_digits=10,
+ decimal_places=4,
+ blank=True
+ )
+ img_map_area = models.TextField()
+ has_map = models.BooleanField(default=1)
+ created = models.DateTimeField(auto_now_add = True, null=True)
+ updated = models.DateTimeField(auto_now = True, null=True)
+
+ def __str__(self):
+ return self.name
+
+ class Meta(object):
+ unique_together = (("code","name"),)
+
+class District(models.Model):
+ state = models.ForeignKey(State, on_delete=models.PROTECT )
+ code = models.CharField(max_length=3)
+ name = models.CharField(max_length=200)
+ created = models.DateTimeField(auto_now_add = True, null=True)
+ updated = models.DateTimeField(auto_now = True, null=True)
+
+ def __str__(self):
+ return self.name
+
+ class Meta(object):
+ unique_together = (("state", "code","name"),)
+
+class City(models.Model):
+ state = models.ForeignKey(State, on_delete=models.PROTECT )
+ name = models.CharField(max_length=200)
+ created = models.DateTimeField(auto_now_add = True, null=True)
+ updated = models.DateTimeField(auto_now = True, null=True)
+
+ def __str__(self):
+ return self.name
+
+ class Meta(object):
+ unique_together = (("name","state"),)
\ No newline at end of file
diff --git a/cms/tests.py b/cms/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/cms/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/cms/urls.py b/cms/urls.py
new file mode 100644
index 0000000..09465e6
--- /dev/null
+++ b/cms/urls.py
@@ -0,0 +1,11 @@
+from django import views
+from csc.vle_views import CSCLogin
+from .views import *
+from django.urls import path
+
+app_name = 'csc'
+urlpatterns = [
+
+ path('load_district/', load_district, name="load_district"),
+ path('load_city/', load_city, name="load_city"),
+]
\ No newline at end of file
diff --git a/cms/views.py b/cms/views.py
new file mode 100644
index 0000000..e6492eb
--- /dev/null
+++ b/cms/views.py
@@ -0,0 +1,21 @@
+from django.shortcuts import render
+from django.views.decorators.csrf import csrf_exempt
+from .models import District,City
+from django.http import JsonResponse
+from django.views.generic.edit import CreateView
+# Create your views here.
+@csrf_exempt
+def load_district(request):
+ id = request.POST.get('id',0)
+ districts = District.objects.filter(state_id=id).values('id','name')
+ district_lst = [item for item in districts]
+ data = {'districts':district_lst}
+ return JsonResponse(data)
+
+@csrf_exempt
+def load_city(request):
+ id = request.POST.get('id',0)
+ cities = City.objects.filter(state_id=id).values('id','name')
+ data = {'cities':list(cities)}
+ return JsonResponse(data)
+
diff --git a/csc/admin.py b/csc/admin.py
index 8c38f3f..342746e 100644
--- a/csc/admin.py
+++ b/csc/admin.py
@@ -1,3 +1,8 @@
from django.contrib import admin
-
+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/ajax.py b/csc/ajax.py
new file mode 100644
index 0000000..1da1168
--- /dev/null
+++ b/csc/ajax.py
@@ -0,0 +1,85 @@
+from .models import Student, Student_Foss
+from django.http import JsonResponse
+from django.views.decorators.csrf import csrf_exempt
+from .models import VLE,Vle_csc_foss,Student_Foss,TestRequest, CSC,StudentTest, Test
+
+@csrf_exempt
+def get_foss_from_csc(request):
+ student = Student.objects.get(user = request.user)
+
+ csc = request.POST['csc']
+ vle = VLE.objects.get(csc_id = csc)
+ print(csc)
+ vle_csc_foss = Vle_csc_foss.objects.filter(vle=vle)
+ sf = Student_Foss.objects.filter(student=student,csc_foss__in = vle_csc_foss)
+
+ # 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))
+ # data = {}
+ # for foss in fosses:
+ # data[foss[1]] = fos
+ print(f'available_foss : {available_foss}')
+ print(fosses)
+ return JsonResponse({'fosses' : available_foss})
+
+
+@csrf_exempt
+def raise_test_request(request):
+ student = Student.objects.get(user = request.user)
+ csc = request.POST['csc']
+ foss = request.POST['foss']
+ foss_id = request.POST['foss_id']
+ vle = VLE.objects.get(csc_id = csc)
+
+ try:
+ TestRequest.objects.get(student=student,vle=vle,foss_id=foss_id)
+ csc_obj = CSC.objects.get(id=csc)
+ msg = f'Test request already submitted for {foss} in csc center - {csc_obj.city} [id: {csc_obj.csc_id}].'
+
+ return JsonResponse({'msg' : msg})
+ except TestRequest.DoesNotExist:
+ TestRequest.objects.create(student=student,vle=vle,foss_id=foss_id,status=0)
+ msg = f"Test request raised for {foss} !"
+ return JsonResponse({'msg' : msg})
+@csrf_exempt
+def apply_for_test(request):
+ context = {}
+ test_id = request.POST.get('test_id')
+ student = Student.objects.get(user=request.user)
+
+ try:
+ StudentTest.objects.create(student=student,test_id=test_id,status=0)
+ print("Ceated succesfully ")
+ msg = f"appied !"
+ except Exception as e:
+ msg = f"faied !"
+
+
+ return JsonResponse({'msg' : msg})
+
+@csrf_exempt
+def ajax_mark_attendance(request):
+ data = {}
+ print(request.POST)
+ test_id = request.POST.get('test_id')
+ students = request.POST.getlist('students[]')
+ st = StudentTest.objects.filter(test_id=test_id,student_id__in=students)
+ for item in st:
+ item.test_status = 1
+ item.save()
+ # StudentTest.objects.bulk_update(st,'test_status')
+ st = [x.student for x in StudentTest.objects.filter(test_id=test_id)]
+ total_enrolled = len(st)
+ attending = StudentTest.objects.filter(test_status=1).count()
+ pending = total_enrolled - attending
+ data['total_enrolled'] = total_enrolled
+ data['attending'] = attending
+ data['pending'] = pending
+ return JsonResponse(data)
\ No newline at end of file
diff --git a/csc/api/__init__.py b/csc/api/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/csc/api/serializers.py b/csc/api/serializers.py
new file mode 100644
index 0000000..7ac862c
--- /dev/null
+++ b/csc/api/serializers.py
@@ -0,0 +1,242 @@
+from functools import partial
+from django.contrib.auth.models import User, Group
+
+
+from rest_framework import serializers
+
+from csc.models import Student, Student_certificate_course,CertifiateCategories, VLE, CSC, Transaction,CategoryCourses,Student_Foss
+from csc.api.utility import send_pwd_mail,map_foss_to_student
+
+from csc.utils import getFirstName,getLastName
+
+from django.contrib.auth.hashers import make_password
+import random
+import string
+class CertifiateCategoriesSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = CertifiateCategories
+ fields = ["category_cert","code","title"]
+
+class StudentCertificateCourseSerializer(serializers.ModelSerializer):
+ cert_category = serializers.SlugRelatedField(
+ slug_field='code',
+ queryset=CertifiateCategories.objects.all()
+ )
+ class Meta:
+ model = Student_certificate_course
+ fields = ["cert_category","programme_starting_date"]
+
+class UserForVLESerializer(serializers.ModelSerializer):
+ class Meta:
+ model = User
+ fields = ["email"]
+
+class VLESerializer(serializers.ModelSerializer):
+ user = UserForVLESerializer()
+ class Meta:
+ model = VLE
+ fields = ["user"]
+
+class UserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = User
+ fields = ["first_name","last_name","email"]
+
+
+class StudentSerializer(serializers.ModelSerializer):
+ user = UserSerializer()
+ certificate_course = StudentCertificateCourseSerializer(many=True)
+ vle_id = VLESerializer(many=True)
+
+ class Meta:
+ model = Student
+ fields = ["certificate_course","user","gender","dob","phone","edu_qualification","vle_id","state","city","district","pincode","address","date_of_registration","category","occupation"]
+
+
+ def create(self, validated_data):
+ user = validated_data.pop('user')
+ user['username'] = user['email']
+
+ try:
+ User.objects.create(**user)
+ except (Exception):
+ raise serializers.ValidationError(f"Request Failed : User with email {user['email']} already exists.")
+ u = User.objects.get(email=user['email'])
+ course_data = validated_data.pop('certificate_course')
+ vle_ids = validated_data.pop('vle_id')
+ vle_email = vle_ids[0]['user']['email']
+ validated_data['user_id'] = u.id # validated_data updated
+ student = Student.objects.create(**validated_data)
+ try:
+ vle_spk = VLE.objects.get(user__email=vle_email)
+ student.vle_id.add(vle_spk.id)
+ except User.DoesNotExist:
+ raise serializers.ValidationError(f"User with email {vle_email} does not exists.")
+ for data in course_data:
+ Student_certificate_course.objects.create(student=student, **data)
+ student_group = Group.objects.get(name='STUDENT')
+ student_group.user_set.add(u)
+ send_pwd_mail(u)
+ #map courses
+ map_foss_to_student(student)
+
+ return student
+
+ def update(self,instance,validated_data):
+ student = instance
+ user_obj = student.user
+ has_user = 'user' in validated_data
+ has_courses = 'certificate_course' in validated_data
+ has_vle_ids = 'vle_id' in validated_data
+ user = []
+ course_data = []
+ vle_ids = []
+ if has_user : user = validated_data.pop('user')
+ if has_courses : course_data = validated_data.pop('certificate_course')
+ if has_vle_ids : vle_ids = validated_data.pop('vle_id')
+
+ instance = super(StudentSerializer,self).update(instance,validated_data)
+ existing_courses = [x.cert_category for x in student.certificate_course.all()]
+
+ for data in course_data:
+ if data['cert_category'] in existing_courses:
+ continue
+ Student_certificate_course.objects.create(student=student, **data)
+ map_foss_to_student(instance,fdate=data['programme_starting_date'])
+
+ if has_user:
+ s = UserSerializer(user_obj,data=user)
+ if s.is_valid():
+ s.save()
+ # if 'email' in user:
+ # send_pwd_mail(user_obj)
+ if "email" in user:
+ user_obj.username = user['email']
+ user_obj.email = user['email']
+ user_obj.save()
+ send_pwd_mail(user_obj)
+ for item in vle_ids:
+ student.vle_id.add(*vle_ids)
+
+ return instance
+
+
+ def validate_certificate_course(self, value):
+ if not value:
+ raise serializers.ValidationError("certificate_course field cannot be empty")
+ courses = []
+ for item in value:
+ if item['cert_category'] in courses:
+ raise serializers.ValidationError(f"Duplicate entry for {item['cert_category']}")
+ else:
+ courses.append(item['cert_category'])
+ return value
+
+ def validate_vle_id(self, value):
+ vle_email = value[0]['user']['email']
+ if not User.objects.filter(email=vle_email).exists():
+ raise serializers.ValidationError(f"VLE with email {vle_email} does not exist.")
+
+ return value
+
+
+class VLEUserSerializer(serializers.ModelSerializer):
+ full_name = serializers.SerializerMethodField('get_full_name')
+ name = serializers.CharField(write_only = True)
+
+ def get_full_name(self, user):
+ return f"{user.first_name} {user.last_name}"
+
+ class Meta:
+ model = User
+ fields = ['email','full_name','name']
+
+class CSCSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = CSC
+ fields = ['csc_id','institute','state','city','district','block','address','pincode','plan']
+
+class TransactionSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Transaction
+ fields = ['transcdate']
+
+class VLECSCSerializer(serializers.ModelSerializer):
+ csc = CSCSerializer()
+ user = VLEUserSerializer()
+ transaction_date = TransactionSerializer(many=True)
+ class Meta:
+ model = VLE
+ fields = ['csc','user','phone','transaction_date']
+
+ def create(self, validated_data):
+ user = {}
+ userdata = validated_data.pop('user')
+ user['username'] = userdata['email']
+ user['email'] = userdata['email']
+ user['first_name'] = getFirstName(userdata['name'])
+ user['last_name'] = getLastName(userdata['name'])
+ try:
+ User.objects.create(**user)
+ except Exception as e:
+ raise serializers.ValidationError(f"Request Failed : User with email {user['email']} already exists.")
+ u = User.objects.get(email=user['email'])
+ csc_data = validated_data.pop('csc')
+ s = CSCSerializer(data=csc_data)
+ s.is_valid(raise_exception=True)
+ obj = s.save()
+ transaction_data = validated_data.pop('transaction_date')
+ validated_data['user_id'] = u.id # validated_data updated
+ validated_data['csc_id'] = obj.id # validated_data updated
+ vle = VLE.objects.create(**validated_data)
+ vle_group = Group.objects.get(name='VLE')
+ vle_group.user_set.add(u)
+ t = Transaction.objects.create(vle=vle,csc=obj,transcdate=transaction_data[0]['transcdate'])
+ send_pwd_mail(u)
+ return vle
+
+ def validate_user(self, value):
+ try:
+ vle_email = value['email']
+ if User.objects.filter(email=vle_email).exists():
+ raise serializers.ValidationError(f"VLE with email {vle_email} already exist.")
+ except Exception as e:
+ print(e)
+
+ return value
+
+ def update(self,instance,validated_data):
+ vle = instance
+ has_user = 'user' in validated_data
+ has_transaction = 'transaction_date' in validated_data
+ has_csc = 'csc' in validated_data
+
+ if has_csc : csc_data = validated_data.pop('csc')
+ if has_transaction : transaction_data = validated_data.pop('transaction_date')[0]
+ if has_user : user_data = validated_data.pop('user')
+ instance = super(VLECSCSerializer,self).update(instance,validated_data)
+ if has_csc:
+ c = CSCSerializer(instance.csc,csc_data,partial=True)
+ c.is_valid(raise_exception=True)
+ c.save()
+ if has_transaction :
+ t = Transaction.objects.create(vle=instance,csc=instance.csc,transcdate=transaction_data['transcdate'])
+ if has_user:
+ user_obj = instance.user
+ if "email" in user_data:
+ user_obj.username = user_data['email']
+ user_obj.email = user_data['email']
+ user_obj.save()
+ send_pwd_mail(user_obj)
+
+ if "name" in user_data:
+ user_obj.first_name = getFirstName(user_data['name'])
+ user_obj.last_name = getLastName(user_data['name'])
+
+ user_obj.save()
+ # u = VLEUserSerializer(instance.user,user_data,partial=True)
+ # u.is_valid(raise_exception=True)
+ # u.save()
+
+ return instance
\ No newline at end of file
diff --git a/csc/api/urls.py b/csc/api/urls.py
new file mode 100644
index 0000000..b392c8d
--- /dev/null
+++ b/csc/api/urls.py
@@ -0,0 +1,45 @@
+from django.urls import path
+from django.urls import re_path
+
+from rest_framework import permissions
+
+from drf_yasg.views import get_schema_view
+from drf_yasg import openapi
+
+
+import os
+
+from csc.api.views import StudentListCreate,SutdentDetail,VLEListCreate,VLEDetail,studentTest
+from rest_framework_swagger.views import get_swagger_view
+schema_view = get_swagger_view(title='Pastebin API')
+
+schema_view = get_schema_view(
+ openapi.Info(
+ title="Spoken Tutorial - CSC API",
+ default_version="v1",
+ description="API for student data",
+ ),
+ url=f"http://127.0.0.1:8000/csc/",
+ public=True,
+ permission_classes=[permissions.AllowAny],
+)
+
+urlpatterns = [
+ path('students/',StudentListCreate.as_view(),name="create_list_student"),
+ path('students/',SutdentDetail.as_view(),name="detail_student"),
+ path('vles/',VLEListCreate.as_view(),name="create_list_student"),
+ path('vles/',VLEDetail.as_view(),name="detail_student"),
+ path('tests/students/',studentTest,name="student_test"),
+ # path('',schema_view)
+]
+
+
+urlpatterns += [
+ re_path(
+ r"^swagger(?P\.json|\.yaml)$",
+ schema_view.with_ui("swagger", cache_timeout=0),
+ name="schema-swagger-ui",
+ ),
+ re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
+
+]
\ No newline at end of file
diff --git a/csc/api/utility.py b/csc/api/utility.py
new file mode 100644
index 0000000..ef36bce
--- /dev/null
+++ b/csc/api/utility.py
@@ -0,0 +1,90 @@
+from datetime import datetime
+from email import message
+from urllib import request
+
+
+from django.contrib.auth.models import User
+from django.core.mail import send_mail
+from django.conf import settings
+
+import random, string
+from csc.models import Student_certificate_course,CategoryCourses,Student_Foss
+from django.db import IntegrityError
+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"""
+ Dear {u.get_full_name()},
+ Thank you for registering under Spoken Tutorial(IIT Bombay) courses.
+ Below are the login details for Spoken Tutorial Dashboard.
+ Link to Login: https://spoken-tutorial.in/login/
+
+ username : {u.username}
+ password : {pwd}
+
+ Thanks & Regards,
+ Team,
+ Spoken Tutorial
+ """
+ path = 'student_mail_template.html'
+ if is_user_vle(u):
+ print('USER IS VLE')
+ message = f"""
+ Dear {u.get_full_name()},
+
+ Welcome to IIT Bombay Spoken Tutorial Program. We are happy to be partnered with CSC Academy to
+ empower youth from all over the country via VLEs.
+ Please use the below Login details for the Spoken Tutorial Dashboard:
+ Link to Login: https://spoken-tutorial.in/login/
+
+ username : {u.username}
+ password : {pwd}
+
+ Please click the following training link to know the process of
+ Student Registration Instructions : https://docs.google.com/document/d/1z8-s4sSl7viPqJ8WAFeeNmoJUVLRPv2L9jLOrfN6ln0/edit?usp=sharing
+ Course Allotment Instructions : https://docs.google.com/document/d/1Mv23iijOVuS6eCcHCgYKbbxopjk_SkSfExXW-61G2AQ/edit?usp=sharing
+
+ In case of any query, please feel free to contact at animation-hackathon@cscacademy.org.
+
+ Thanks & Regards,
+ Team,
+ Spoken Tutorial
+ """
+ path = 'vle_mail_template.html'
+ html_content = render_to_string(path, {'full_name':u.get_full_name(),'username':u.username,'password':pwd})
+ try:
+ print(f"/n/nSending mail ; username,pwd : {u.username},{pwd}".ljust(40,'*'))
+ send_mail(
+ subject,
+ message,
+ from_email,
+ [u.email],
+ 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}")
+
+def map_foss_to_student(student,fdate=datetime.today()):
+ cert_courses = Student_certificate_course.objects.filter(student=student)
+ print(f"\n\ncert_courses ***** {cert_courses}\n")
+ l = []
+ for course in cert_courses:
+ fosses = CategoryCourses.objects.filter(certificate_category_id=course.cert_category_id).values('foss')
+ for foss in fosses:
+ try:
+ Student_Foss.objects.create(student=student,cert_category=course.cert_category,csc_foss_id=foss['foss'],foss_start_date=fdate)
+ except IntegrityError as e:
+ print(e)
\ No newline at end of file
diff --git a/csc/api/views.py b/csc/api/views.py
new file mode 100644
index 0000000..264b258
--- /dev/null
+++ b/csc/api/views.py
@@ -0,0 +1,80 @@
+from rest_framework import generics,status
+from rest_framework.authentication import TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from django.http import JsonResponse,HttpResponse
+
+from csc.api.serializers import StudentSerializer,VLECSCSerializer
+from csc.models import Student, VLE, Student_Foss,CategoryCourses,CSCTestAtttendance
+from csc.utils import TEST_COMPLETED_BY_STUDENT,PASS_GRADE
+import json
+
+class StudentListCreate(generics.ListCreateAPIView):
+ permission_classes = [IsAuthenticated]
+ authentication_classes = [TokenAuthentication]
+ queryset = Student.objects.all()
+ serializer_class = StudentSerializer
+
+class SutdentDetail(generics.RetrieveUpdateAPIView):
+ permission_classes = [IsAuthenticated]
+ authentication_classes = [TokenAuthentication]
+ lookup_field = "user__email"
+ queryset = Student.objects.all()
+ serializer_class = StudentSerializer
+
+class VLEListCreate(generics.ListCreateAPIView):
+ permission_classes = [IsAuthenticated]
+ authentication_classes = [TokenAuthentication]
+ queryset = VLE.objects.all()
+ serializer_class = VLECSCSerializer
+
+class VLEDetail(generics.RetrieveUpdateAPIView):
+ permission_classes = [IsAuthenticated]
+ authentication_classes = [TokenAuthentication]
+ lookup_field = "user__email"
+ queryset = VLE.objects.all()
+ serializer_class = VLECSCSerializer
+
+def studentTest(request,user__email):
+ d = {}
+ d['email'] = user__email
+ try:
+ student = Student.objects.get(user__email=user__email)
+ courses = set()
+ cert_categories = [courses.add(x.cert_category) for x in Student_Foss.objects.filter(student=student)]
+ scores = {}
+ ta = CSCTestAtttendance.objects.filter(student=student,status__gte=TEST_COMPLETED_BY_STUDENT).order_by('-updated')
+ if ta:
+ last_test_date = ta[0].updated.date()
+ d['last_updated'] = last_test_date
+ else:
+ d['last_updated'] = None
+ # else:
+ # d['last_updated'] = 'No test taken'
+ for item in ta:
+ scores[item.test.foss.foss] = item.mdlgrade
+ for course in courses:
+ d[course.code] = {
+ 'title' : course.title
+ }
+ fosses = [x.foss for x in CategoryCourses.objects.filter(certificate_category=course)]
+ ta_temp = CSCTestAtttendance.objects.filter(student=student,status__gte=TEST_COMPLETED_BY_STUDENT,test__foss__in=fosses).order_by('-updated')
+ if ta_temp:
+ d[course.code]['last_test_date'] = ta_temp[0].updated.date()
+ else:
+ d[course.code]['last_test_date'] = None
+ temp = dict()
+ for foss in fosses:
+ temp[foss.foss] = scores.get(foss.foss,'NA')
+
+ d[course.code]['foss'] = temp
+ b = [ x == 'NA' or float(x) < PASS_GRADE for x in temp.values()]
+ if True in b:
+ d[course.code]['status'] = 0
+ else:
+ d[course.code]['status'] = 1
+ except Student.DoesNotExist as e:
+ return HttpResponse(status=404)
+ return JsonResponse(d)
+
+
\ No newline at end of file
diff --git a/csc/backends.py b/csc/backends.py
new file mode 100644
index 0000000..ee3e8ad
--- /dev/null
+++ b/csc/backends.py
@@ -0,0 +1,62 @@
+
+from urllib import request
+from django.contrib.auth.backends import ModelBackend
+from django.contrib.auth.models import User
+from django.db.models import Q
+from django.contrib.auth.hashers import check_password
+from .models import VLE, CSC
+from datetime import datetime as dt
+from datetime import timedelta
+import requests
+from django.conf import settings
+from .cron import add_vle,add_transaction
+from django.contrib import messages
+
+class CSCBackend(ModelBackend):
+ def authenticate(self, request, username=None, password=None):
+ try:
+ user = User.objects.get(Q(username=username) | Q(email=username))
+ pwd_exists = user.password
+ if pwd_exists:
+ pwd_valid = check_password(password, pwd_exists)
+ if pwd_valid:
+ return user
+
+ except:
+ user = check_updated_vle(username,request)
+ return user
+ return None
+
+
+def check_updated_vle(username,request):
+ last_update_date = VLE.objects.order_by('user__date_joined').last().user.date_joined
+ delta_date = last_update_date - timedelta(2)
+ payload = {'date':delta_date}
+ url = getattr(settings, "URL_FETCH_VLE", "http://exam.cscacademy.org/shareiitbombayspokentutorial")
+ response = requests.get(url,params=payload)
+ if response.status_code == 200:
+ data = response.json()
+ data = data['req_data']
+ emails = [x['email'] for x in data]
+
+ for item in data:
+ if username == item['email']:
+ try:
+ csc = CSC.objects.get(csc_id=item['csc_id'])
+ except CSC.DoesNotExist:
+ print(f"csc - {item['csc_id']} does not exists")
+ CSC.objects.create(
+ csc_id=item.get('csc_id'),institute=item.get('institute',''),state=item.get('state',''),
+ city=item.get('city',''),district=item.get('district',''),block=item.get('block',''),
+ address=item.get('address',''),pincode=item.get('pincode',''),plan=item.get('plan',''),
+ activation_status=1
+ )
+ csc = CSC.objects.get(csc_id=item.get('csc_id'))
+ add_vle(item,csc)
+ vle = VLE.objects.get(user__email=username)
+ add_transaction(vle,csc,item['transcdate'])
+ # messages.add_message(request,messages.INFO,'hello')
+ return vle.user
+ else:
+ # messages.add_message(request,messages.INFO,'hello')
+ return None
diff --git a/csc/cron.py b/csc/cron.py
index 208e9b1..fedb5be 100644
--- a/csc/cron.py
+++ b/csc/cron.py
@@ -1,3 +1,4 @@
+from datetime import datetime
from django.db import IntegrityError
import requests
from django.conf import settings
@@ -10,6 +11,9 @@
from django.db.models import Q
# import utils as u
from django.core.mail import send_mail
+from django.contrib.auth.models import Group
+from .utils import get_tenure_end_date
+from django.template.loader import render_to_string
def update_vle_data(): #CRON TASK
url = getattr(settings, "URL_FETCH_VLE", "http://exam.cscacademy.org/shareiitbombayspokentutorial")
@@ -66,6 +70,7 @@ def update_vle_data(): #CRON TASK
address=item.get('address',''),pincode=item.get('pincode',''),plan=item.get('plan',''),
activation_status=1
)
+ csc = CSC.objects.get(csc_id=item.get('csc_id'))
print(f"csc - {item['csc_id']} created")
csc_created.append(f"{item['csc_id']} ({item['email']})")
add_vle(item,csc)
@@ -86,22 +91,36 @@ def send_password_mail(user,password):
subject = "Login credentials for Spoken Tutorial :"
from_email = getattr(settings, "NO_REPLY_MAIL", "no-reply@spoken-tutorial.org")
to_email = user.email
- message = """
- Your login information for Spoken Tutorial is :
- username : {to_email}
- password : {password}
- Login link : "https://spoken-tutorial.in/login/"
+ message = f"""
+ Dear {user.get_full_name()},
+
+ Welcome to IIT Bombay Spoken Tutorial Program. We are happy to be partnered with CSC Academy to
+ empower youth from all over the country via VLEs.
+ Please use the below Login details for the Spoken Tutorial Dashboard:
+ Link to Login: https://spoken-tutorial.in/login/
- Best Wishes,
- Admin
- Spoken Tutorials
- IIT Bombay.
- """
+ username : {user.username}
+ password : {password}
+
+ Please click the following training link to know the process of
+ Student Registration Instructions : https://docs.google.com/document/d/1z8-s4sSl7viPqJ8WAFeeNmoJUVLRPv2L9jLOrfN6ln0/edit?usp=sharing
+ Course Allotment Instructions : https://docs.google.com/document/d/1Mv23iijOVuS6eCcHCgYKbbxopjk_SkSfExXW-61G2AQ/edit?usp=sharing
+
+ In case of any query, please feel free to contact at animation-hackathon@cscacademy.org.
+
+ Thanks & Regards,
+ Team,
+ Spoken Tutorial
+ """
+ path = 'vle_mail_template.html'
+ html_content = render_to_string(path, {'full_name':user.get_full_name(),'username':user.username,'password':password})
try:
- print(f"sending mail .....{to_email},{password}")
- # send_mail(subject,message,from_email,to_email,fail_silently=False)
- except:
+ print(f"sending mails .....{to_email},{password}")
+ send_mail(subject,message,from_email,[to_email],fail_silently=False,html_message=html_content)
+ print(f"mail sent success")
+ except Exception as e:
print("Error in sending mail")
+ print(e)
def send_log_mail(message):
@@ -109,7 +128,7 @@ def send_log_mail(message):
from_email = getattr(settings, "NO_REPLY_MAIL", "no-reply@spoken-tutorial.org")
to_email = "web-query@spoken-tutorial.org"
try:
- # send_mail(subject,message,from_email,to_email,fail_silently=False)
+ send_mail(subject,message,from_email,to_email,fail_silently=False)
print(f"sending mail .....{to_email},{message}")
except:
print("Unable to send csc update mail to web-team")
@@ -138,7 +157,9 @@ def add_vle(item,csc):
email=item['email'],is_staff=0,is_active=1
)
print(f"user for vle {item.get('email')} created")
+ # IMPORTANT ToDo: REMOVE static pwd
password = ''.join([ random.choice(string.ascii_letters+string.digits) for x in range(8)])
+
enc_password = make_password(password)
user.password = enc_password
user.save()
@@ -148,13 +169,20 @@ def add_vle(item,csc):
vle = VLE.objects.create(
csc=csc,user=user,phone=item['phone'],status=1
)
+ vle_group = Group.objects.get(name='VLE')
+ vle_group.user_set.add(user)
print(f"created vle .... {user}")
except IntegrityError as e:
print(f"vle already exists ....")
vle = VLE.objects.get(Q(csc=csc) and Q(user=user))
tdate = item.get('transcdate').split()[0]
+
+ tdate = datetime.strptime(tdate,'%Y-%m-%d')
+ print(f"tdate ************************ {tdate}")
try:
- transaction = Transaction.objects.create(vle=vle,csc=csc,transcdate=tdate,tenure=None,tenure_end_date=None)
+ tenure_end_date = get_tenure_end_date(tdate)
+ print(f"**tenure_end_date : {tenure_end_date}")
+ transaction = Transaction.objects.create(vle=vle,csc=csc,transcdate=tdate,tenure=None,tenure_end_date=tenure_end_date)
except Exception as e:
print(e)
diff --git a/csc/decorators.py b/csc/decorators.py
index 9afdfcb..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 *
+from .utils import is_user_vle,is_user_student,is_csc_team_role
# decorator
def is_vle(view_func):
@@ -14,3 +14,22 @@ def wrapper(request,*args,**kwargs):
raise PermissionDenied()
return wrapper
+def is_student(view_func):
+ def wrapper(request,*args,**kwargs):
+ # ToDo : Role for STUDENT in group
+ if is_user_student(request.user):
+ return view_func(request,*args,**kwargs)
+ else:
+ messages.add_message(request,messages.INFO,'Access denied')
+ 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/0004_auto_20220726_1601.py b/csc/migrations/0004_auto_20220726_1601.py
new file mode 100644
index 0000000..d7b0af5
--- /dev/null
+++ b/csc/migrations/0004_auto_20220726_1601.py
@@ -0,0 +1,140 @@
+# Generated by Django 3.0.3 on 2022-07-26 10:31
+
+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),
+ ('cms', '0001_initial'),
+ ('csc', '0003_auto_20220601_2308'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='FossCategory',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('foss', models.CharField(max_length=255, unique=True)),
+ ('description', models.TextField()),
+ ('status', models.BooleanField(max_length=2)),
+ ('is_learners_allowed', models.BooleanField(default=0, max_length=2)),
+ ('is_translation_allowed', models.BooleanField(default=0, max_length=2)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('show_on_homepage', models.PositiveSmallIntegerField(default=0, help_text='0:Series, 1:Display on home page, 2:Archived')),
+ ('available_for_nasscom', models.BooleanField(default=True, help_text='If unchecked, this foss will not be available for nasscom')),
+ ('available_for_jio', models.BooleanField(default=True, help_text='If unchecked, this foss will not be available for jio and spoken-tutorial.in')),
+ ('csc_dca_programme', models.BooleanField(default=True, help_text='If unchecked, this foss will not be available for csc-dca programme')),
+ ],
+ options={
+ 'verbose_name': 'FOSS',
+ 'verbose_name_plural': 'FOSSes',
+ 'ordering': ('foss',),
+ },
+ ),
+ migrations.CreateModel(
+ name='FossSuperCategory',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255, unique=True)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'verbose_name': 'FOSS Category',
+ 'verbose_name_plural': 'FOSS Categories',
+ 'ordering': ('name',),
+ },
+ ),
+ migrations.CreateModel(
+ name='Student',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('student_id', models.CharField(max_length=255)),
+ ('gender', models.CharField(blank=True, max_length=10)),
+ ('dob', models.DateField(blank=True)),
+ ('phone', models.CharField(blank=True, max_length=32)),
+ ('edu_qualification', models.CharField(blank=True, max_length=255)),
+ ('pincode', models.CharField(blank=True, max_length=6)),
+ ('address', models.CharField(blank=True, max_length=255)),
+ ('date_of_registration', models.DateField()),
+ ('city', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cms.City')),
+ ('district', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cms.District')),
+ ('state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cms.State')),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ('vle_id', models.ManyToManyField(to='csc.VLE')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Vle_csc_foss',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('programme_type', models.CharField(choices=[('', '-- None --'), ('dca', 'DCA Programme'), ('individual', 'Individual Course')], max_length=100)),
+ ('created', models.DateField(blank=True, null=True)),
+ ('updated', models.DateField(auto_now=True, null=True)),
+ ('spoken_foss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.FossCategory')),
+ ('vle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.VLE')),
+ ],
+ options={
+ 'unique_together': {('spoken_foss', 'programme_type')},
+ },
+ ),
+ migrations.CreateModel(
+ name='Test',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('tdate', models.DateField()),
+ ('ttime', models.TimeField()),
+ ('publish', models.BooleanField()),
+ ('slug', models.SlugField(max_length=40)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('note_student', models.TextField(blank=True, null=True)),
+ ('note_invigilator', models.TextField(blank=True, null=True)),
+ ('test_name', models.CharField(blank=True, max_length=252, null=True)),
+ ('foss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.FossCategory')),
+ ('vle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.VLE')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Invigilator',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('phone', models.CharField(max_length=32)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('added_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='added_by_user', to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ('vle', models.ManyToManyField(to='csc.VLE')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='InvigilationRequest',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField()),
+ ('invigilator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Invigilator')),
+ ('test', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Test')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='fosscategory',
+ name='category',
+ field=models.ManyToManyField(to='csc.FossSuperCategory'),
+ ),
+ migrations.CreateModel(
+ name='Student_Foss',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('csc_foss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Vle_csc_foss')),
+ ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Student')),
+ ],
+ options={
+ 'unique_together': {('student', 'csc_foss')},
+ },
+ ),
+ ]
diff --git a/csc/migrations/0005_studenttest.py b/csc/migrations/0005_studenttest.py
new file mode 100644
index 0000000..75fb817
--- /dev/null
+++ b/csc/migrations/0005_studenttest.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.0.3 on 2022-07-27 05:51
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0004_auto_20220726_1601'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='StudentTest',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField()),
+ ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Student')),
+ ('test', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Test')),
+ ],
+ ),
+ ]
diff --git a/csc/migrations/0006_auto_20220901_1138.py b/csc/migrations/0006_auto_20220901_1138.py
new file mode 100644
index 0000000..209e5e0
--- /dev/null
+++ b/csc/migrations/0006_auto_20220901_1138.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.0.3 on 2022-09-01 06:08
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0005_studenttest'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='studenttest',
+ name='test_status',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AlterUniqueTogether(
+ name='studenttest',
+ unique_together={('student', 'test')},
+ ),
+ migrations.CreateModel(
+ name='TestRequest',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField()),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('foss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.FossCategory')),
+ ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Student')),
+ ('vle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.VLE')),
+ ],
+ ),
+ ]
diff --git a/csc/migrations/0007_categorycourses_certifiatecategories.py b/csc/migrations/0007_categorycourses_certifiatecategories.py
new file mode 100644
index 0000000..518b6cb
--- /dev/null
+++ b/csc/migrations/0007_categorycourses_certifiatecategories.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.0.3 on 2022-09-07 12:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0006_auto_20220901_1138'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='CertifiateCategories',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('code', models.CharField(max_length=100, unique=True)),
+ ('title', models.CharField(max_length=255, unique=True)),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CategoryCourses',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('certificate_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.CertifiateCategories')),
+ ('foss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.FossCategory')),
+ ],
+ ),
+ ]
diff --git a/csc/migrations/0008_auto_20220912_1719.py b/csc/migrations/0008_auto_20220912_1719.py
new file mode 100644
index 0000000..3ab02b2
--- /dev/null
+++ b/csc/migrations/0008_auto_20220912_1719.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.0.3 on 2022-09-12 11:49
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0007_categorycourses_certifiatecategories'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='student_foss',
+ name='csc_foss',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.FossCategory'),
+ ),
+ migrations.CreateModel(
+ name='Student_certificate_course',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('programme_starting_date', models.DateField(blank=True, null=True)),
+ ('created', models.DateField(blank=True, null=True)),
+ ('updated', models.DateField(auto_now=True, null=True)),
+ ('cert_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.CertifiateCategories')),
+ ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='csc.Student')),
+ ],
+ options={
+ 'unique_together': {('student', 'cert_category')},
+ },
+ ),
+ ]
diff --git a/csc/migrations/0009_auto_20220915_1344.py b/csc/migrations/0009_auto_20220915_1344.py
new file mode 100644
index 0000000..8e7e918
--- /dev/null
+++ b/csc/migrations/0009_auto_20220915_1344.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.3 on 2022-09-15 08:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0008_auto_20220912_1719'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='student_certificate_course',
+ name='student',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_cert', to='csc.Student'),
+ ),
+ ]
diff --git a/csc/migrations/0010_auto_20220915_1402.py b/csc/migrations/0010_auto_20220915_1402.py
new file mode 100644
index 0000000..6b65e2d
--- /dev/null
+++ b/csc/migrations/0010_auto_20220915_1402.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.3 on 2022-09-15 08:32
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0009_auto_20220915_1344'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='student_certificate_course',
+ name='cert_category',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='category_cert', to='csc.CertifiateCategories'),
+ ),
+ ]
diff --git a/csc/migrations/0011_auto_20220915_1445.py b/csc/migrations/0011_auto_20220915_1445.py
new file mode 100644
index 0000000..599aae9
--- /dev/null
+++ b/csc/migrations/0011_auto_20220915_1445.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.3 on 2022-09-15 09:15
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0010_auto_20220915_1402'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='student_certificate_course',
+ name='student',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='certificate_course', to='csc.Student'),
+ ),
+ ]
diff --git a/csc/migrations/0012_auto_20220920_0805.py b/csc/migrations/0012_auto_20220920_0805.py
new file mode 100644
index 0000000..284d7c3
--- /dev/null
+++ b/csc/migrations/0012_auto_20220920_0805.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.0.3 on 2022-09-20 02:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0011_auto_20220915_1445'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='student',
+ name='category',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='student',
+ name='occupation',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ ]
diff --git a/csc/migrations/0013_auto_20220927_0026.py b/csc/migrations/0013_auto_20220927_0026.py
new file mode 100644
index 0000000..f007d7e
--- /dev/null
+++ b/csc/migrations/0013_auto_20220927_0026.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.0.3 on 2022-09-26 18:56
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0012_auto_20220920_0805'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='student_foss',
+ name='student_cert_course',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='csc.Student_certificate_course'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='student_foss',
+ unique_together={('student', 'csc_foss', 'student_cert_course')},
+ ),
+ ]
diff --git a/csc/migrations/0013_auto_20220927_0844.py b/csc/migrations/0013_auto_20220927_0844.py
new file mode 100644
index 0000000..c17a7fe
--- /dev/null
+++ b/csc/migrations/0013_auto_20220927_0844.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.3 on 2022-09-27 03:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0012_auto_20220920_0805'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='transaction',
+ name='vle',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transaction_date', to='csc.VLE'),
+ ),
+ ]
diff --git a/csc/migrations/0014_auto_20220928_1104.py b/csc/migrations/0014_auto_20220928_1104.py
new file mode 100644
index 0000000..4c75fab
--- /dev/null
+++ b/csc/migrations/0014_auto_20220928_1104.py
@@ -0,0 +1,32 @@
+# Generated by Django 3.0.3 on 2022-09-28 05:34
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0013_auto_20220927_0026'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='student_foss',
+ name='cert_category',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='csc.CertifiateCategories'),
+ ),
+ migrations.AddField(
+ model_name='student_foss',
+ name='foss_start_date',
+ field=models.DateField(blank=True, null=True),
+ ),
+ migrations.AlterUniqueTogether(
+ name='student_foss',
+ unique_together={('student', 'csc_foss', 'cert_category')},
+ ),
+ migrations.RemoveField(
+ model_name='student_foss',
+ name='student_cert_course',
+ ),
+ ]
diff --git a/csc/migrations/0015_merge_20220929_0012.py b/csc/migrations/0015_merge_20220929_0012.py
new file mode 100644
index 0000000..b741c47
--- /dev/null
+++ b/csc/migrations/0015_merge_20220929_0012.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.0.3 on 2022-09-28 18:42
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0013_auto_20220927_0844'),
+ ('csc', '0014_auto_20220928_1104'),
+ ]
+
+ operations = [
+ ]
diff --git a/csc/migrations/0016_auto_20220930_1304.py b/csc/migrations/0016_auto_20220930_1304.py
new file mode 100644
index 0000000..c89a1e1
--- /dev/null
+++ b/csc/migrations/0016_auto_20220930_1304.py
@@ -0,0 +1,74 @@
+# Generated by Django 3.0.3 on 2022-09-30 07:34
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0015_merge_20220929_0012'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='csc',
+ name='address',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AlterField(
+ model_name='csc',
+ name='block',
+ field=models.CharField(blank=True, max_length=100, null=True),
+ ),
+ migrations.AlterField(
+ model_name='csc',
+ name='city',
+ field=models.CharField(blank=True, max_length=100, null=True),
+ ),
+ migrations.AlterField(
+ model_name='csc',
+ name='institute',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AlterField(
+ model_name='csc',
+ name='pincode',
+ field=models.CharField(blank=True, max_length=6, null=True),
+ ),
+ migrations.AlterField(
+ model_name='csc',
+ name='plan',
+ field=models.CharField(max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='student',
+ name='address',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AlterField(
+ model_name='student',
+ name='date_of_registration',
+ field=models.DateField(default=datetime.date(2022, 9, 30)),
+ ),
+ migrations.AlterField(
+ model_name='student',
+ name='dob',
+ field=models.DateField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='student',
+ name='edu_qualification',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AlterField(
+ model_name='student',
+ name='gender',
+ field=models.CharField(blank=True, max_length=10, null=True),
+ ),
+ migrations.AlterField(
+ model_name='student',
+ name='pincode',
+ field=models.CharField(blank=True, max_length=6, null=True),
+ ),
+ ]
diff --git a/csc/migrations/0017_auto_20221104_1204.py b/csc/migrations/0017_auto_20221104_1204.py
new file mode 100644
index 0000000..06bd6fd
--- /dev/null
+++ b/csc/migrations/0017_auto_20221104_1204.py
@@ -0,0 +1,105 @@
+# Generated by Django 3.0.3 on 2022-11-04 06:34
+
+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', '0016_auto_20220930_1304'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='test',
+ name='test_name',
+ ),
+ 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.AddField(
+ model_name='test',
+ name='invigilator',
+ field=models.ManyToManyField(blank=True, null=True, to='csc.Invigilator'),
+ ),
+ migrations.AddField(
+ model_name='test',
+ name='participant_count',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='test',
+ name='status',
+ field=models.PositiveIntegerField(default=0),
+ ),
+ 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.AlterField(
+ model_name='student',
+ name='date_of_registration',
+ field=models.DateField(default=datetime.date(2022, 11, 4)),
+ ),
+ migrations.AlterField(
+ model_name='test',
+ name='publish',
+ field=models.BooleanField(default=True),
+ ),
+ migrations.AlterField(
+ model_name='test',
+ name='vle',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='csc.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.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/0018_auto_20221121_1727.py b/csc/migrations/0018_auto_20221121_1727.py
new file mode 100644
index 0000000..a6c0b43
--- /dev/null
+++ b/csc/migrations/0018_auto_20221121_1727.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.3 on 2022-11-21 11:57
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0017_auto_20221104_1204'),
+ ]
+
+ operations = [
+ migrations.AlterUniqueTogether(
+ name='csctestatttendance',
+ unique_together={('mdlcourse_id', 'mdluser_id')},
+ ),
+ ]
diff --git a/csc/migrations/0019_auto_20221121_1831.py b/csc/migrations/0019_auto_20221121_1831.py
new file mode 100644
index 0000000..c8c6dcb
--- /dev/null
+++ b/csc/migrations/0019_auto_20221121_1831.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.3 on 2022-11-21 13:01
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('csc', '0018_auto_20221121_1727'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='student',
+ name='dca_count',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/csc/models.py b/csc/models.py
index a9cc687..af27673 100644
--- a/csc/models.py
+++ b/csc/models.py
@@ -1,24 +1,103 @@
-
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
from django.forms import ChoiceField
+from spokenlogin.models import *
+from model_utils import Choices
+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(
+ ('', ('-- None --')),('dca', ('DCA Programme')), ('individual', ('Individual Course'))
+ )
+
+
+class FossSuperCategory(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ class Meta(object):
+ verbose_name = 'FOSS Category'
+ verbose_name_plural = 'FOSS Categories'
+ ordering = ('name',)
+
+ def __str__(self):
+ return self.name
+
+class FossCategory(models.Model):
+ foss = models.CharField(unique=True, max_length=255)
+ description = models.TextField()
+ status = models.BooleanField(max_length=2)
+ is_learners_allowed = models.BooleanField(max_length=2,default=0 )
+ is_translation_allowed = models.BooleanField(max_length=2, default=0)
+ # user = models.ForeignKey(User, on_delete=models.PROTECT )
+ category = models.ManyToManyField(FossSuperCategory)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+ show_on_homepage = models.PositiveSmallIntegerField(default=0, help_text ='0:Series, 1:Display on home page, 2:Archived')
+ available_for_nasscom = models.BooleanField(default=True, help_text ='If unchecked, this foss will not be available for nasscom' )
+ available_for_jio = models.BooleanField(default=True, help_text ='If unchecked, this foss will not be available for jio and spoken-tutorial.in' )
+ csc_dca_programme = models.BooleanField(default=True, help_text ='If unchecked, this foss will not be available for csc-dca programme' )
+ class Meta(object):
+ verbose_name = 'FOSS'
+ verbose_name_plural = 'FOSSes'
+ ordering = ('foss', )
+
+ def __str__(self):
+ return self.foss
+
+
+class CertifiateCategories(models.Model):
+ code = models.CharField(max_length=100, unique = True)
+ title = models.CharField(max_length=255, unique=True)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ def __str__(self):
+ return f"{self.code} - {self.title}"
+
+
+class CategoryCourses(models.Model):
+ certificate_category = models.ForeignKey(CertifiateCategories,on_delete=models.CASCADE)
+ foss = models.ForeignKey(FossCategory,on_delete=models.CASCADE)
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ def __str__(self):
+ return f"{self.certificate_category.code} - {self.foss.foss}"
+
+
+
class CSC(models.Model):
CSC_PLAN = [('College Level Subscription','College Level Subscription'),
('School Level Subscription','School Level Subscription')]
csc_id = models.CharField(max_length=50) # id provided by csc
- institute = models.CharField(max_length=255)
+ institute = models.CharField(max_length=255,null=True,blank=True)
state = models.CharField(max_length=100)
- city = models.CharField(max_length=100)
+ city = models.CharField(max_length=100,null=True,blank=True)
district = models.CharField(max_length=100)
- block = models.CharField(max_length=100)
- address = models.CharField(max_length=255)
- pincode = models.CharField(max_length=6)
- plan = models.CharField(choices=CSC_PLAN,max_length=100)
+ block = models.CharField(max_length=100,null=True,blank=True)
+ address = models.CharField(max_length=255,null=True,blank=True)
+ pincode = models.CharField(max_length=6,null=True,blank=True)
+ # plan = models.CharField(choices=CSC_PLAN,max_length=100)
+ plan = models.CharField(max_length=255)
activation_status = models.BooleanField(default=True) # If the csc is inactivated for some reason ; payment not done
+ def __str__(self):
+
+ return f"{self.city},{self.district}"
class VLE(models.Model):
@@ -29,11 +108,15 @@ class VLE(models.Model):
class Meta:
unique_together = ('csc','user')
+
+ def __str__(self):
+ return f"{self.user.first_name.title()} {self.user.last_name.title()}"
+
class Transaction(models.Model):
TRANSACTION_TENURE = [('quarterly','quarterly'),('biannually','biannually'),('annually','annually')]
- vle = models.ForeignKey(VLE,on_delete=models.CASCADE)
+ vle = models.ForeignKey(VLE,on_delete=models.CASCADE,related_name='transaction_date')
csc = models.ForeignKey(CSC,on_delete=models.CASCADE)
transcdate = models.DateField(null=False)
tenure = models.CharField(choices=TRANSACTION_TENURE,max_length=10,null=True,blank=True)
@@ -43,5 +126,191 @@ class Meta:
unique_together = ('vle','csc','transcdate')
+class Vle_csc_foss(models.Model):
+
+ programme_type = models.CharField(choices=PROGRAMME_TYPE_CHOICES, max_length=100)
+# spoken_foss = models.IntegerField()
+ spoken_foss = models.ForeignKey(FossCategory,on_delete=models.CASCADE)
+ created = models.DateField(blank=True,null=True)
+ updated = models.DateField(auto_now = True, null=True)
+ vle = models.ForeignKey(VLE,on_delete=models.CASCADE)
+
+ #unique together
+ class Meta(object):
+ unique_together = (("spoken_foss","programme_type"),)
+ def __str__(self):
+ return self.spoken_foss.foss
+
+
+
+
+
+# =========== Student models start ===================================
+
+class Student(models.Model):
+ student_id = models.CharField(max_length=255)
+ user = models.ForeignKey(User,on_delete=models.CASCADE)
+ gender = models.CharField(max_length=10,blank=True,null=True)
+ dob = models.DateField(blank=True,null=True)
+ phone = models.CharField(max_length=32,blank=True)
+ edu_qualification = models.CharField(max_length=255,blank=True,null=True)
+ vle_id = models.ManyToManyField(VLE) #if student joins another csc due to location change
+ state = models.ForeignKey(State,on_delete=models.CASCADE,blank=True,null=True)
+ city = models.ForeignKey(City,on_delete=models.CASCADE,blank=True,null=True)
+ district = models.ForeignKey(District,on_delete=models.CASCADE,blank=True,null=True)
+ pincode = models.CharField(max_length=6,blank=True,null=True)
+ address = models.CharField(max_length=255,blank=True,null=True)
+ 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)
+ dca_count = models.IntegerField(default=0)
+
+
+class Student_certificate_course(models.Model):
+ student = models.ForeignKey(Student,on_delete=models.CASCADE,related_name='certificate_course')
+ cert_category = models.ForeignKey(CertifiateCategories,on_delete=models.CASCADE,related_name='category_cert')
+ programme_starting_date = models.DateField(blank=True,null=True)
+ created = models.DateField(blank=True,null=True)
+ updated = models.DateField(auto_now = True, null=True)
+
+ class Meta:
+ unique_together = ('student','cert_category')
+
+ def __str__(self):
+ return f"{self.cert_category.code}"
+
+
+class Student_Foss(models.Model):
+ student = models.ForeignKey(Student,on_delete=models.CASCADE)
+ csc_foss = models.ForeignKey(FossCategory,on_delete=models.CASCADE)
+ cert_category = models.ForeignKey(CertifiateCategories, on_delete=models.CASCADE, null=True)
+ foss_start_date = models.DateField(blank=True,null=True)
+
+ class Meta:
+ unique_together = ('student','csc_foss', 'cert_category')
+
+
+ def __str__(self):
+ return f"{self.csc_foss.foss}"
+# =========== Student models ens ===================================
+
+
+
+class Invigilator(models.Model):
+ 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(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 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(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)
+ #ToDO : Add status ; to mark if completed or cancellled
+
+ # class Meta:
+ # widgets = {
+ # 'tdate':widgets.DateInput(attrs={'type': 'date'})
+ # }
+ def get_absolute_url(self):
+ # return f"{self.foss}"
+ return reverse("detail_test",kwargs={"slug": self.slug})
+
+ def __str__(self):
+ 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)
+ test = models.ForeignKey(Test,on_delete=models.CASCADE)
+ status = models.IntegerField() #0-pending, 1-accepted, 2-rejected
+
+class StudentTest(models.Model):
+ student = models.ForeignKey(Student,on_delete=models.CASCADE)
+ test = models.ForeignKey(Test,on_delete=models.CASCADE)
+ status = models.IntegerField() #0 : Rejected, 1 : Approved
+ test_status = models.IntegerField(default=0) #0: default - test not attended ; 1: attendance marked
+ def __str__(self):
+ return f"{self.id}"
+
+ class Meta:
+ unique_together = [['student', 'test']]
+
+class TestRequest(models.Model):
+ student = models.ForeignKey(Student,on_delete=models.CASCADE)
+ foss = models.ForeignKey(FossCategory,on_delete=models.CASCADE)
+ vle = models.ForeignKey(VLE,on_delete=models.CASCADE)
+ status = models.IntegerField()
+ created = models.DateTimeField(auto_now_add=True)
+
+ def __str__(self):
+ 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 = (("mdlcourse_id", "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
\ 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_forms.py b/csc/student_forms.py
new file mode 100644
index 0000000..8b148da
--- /dev/null
+++ b/csc/student_forms.py
@@ -0,0 +1,21 @@
+from django import forms
+
+from .models import Student, Student_Foss
+GEEKS_CHOICES =(
+ ("1", "One"),
+ ("2", "Two"),
+ ("3", "Three"),
+ ("4", "Four"),
+ ("5", "Five"),
+)
+class RaiseTestRequestForm(forms.Form):
+ foss = forms.ChoiceField(choices=GEEKS_CHOICES)
+
+ def __init__(self, *args, **kwargs):
+ user = kwargs.pop('user', None)
+ student = Student.objects.get(user=user)
+ student_foss = Student_Foss.objects.filter(student=student)
+ data = [(x.csc_foss.spoken_foss.id,x.csc_foss.spoken_foss) for x in student_foss]
+ super(RaiseTestRequestForm,self).__init__(*args, **kwargs)
+ self.fields['foss'].choices = data
+
diff --git a/csc/student_urls.py b/csc/student_urls.py
index e69de29..a0b5f85 100644
--- a/csc/student_urls.py
+++ b/csc/student_urls.py
@@ -0,0 +1,25 @@
+from django import views
+from csc.student_views import *
+from .views import *
+from django.urls import path
+from django.conf.urls import url
+from .vle_views import *
+from .ajax import get_foss_from_csc,raise_test_request,apply_for_test
+app_name = 'csc'
+urlpatterns = [
+ path('', student_dashboard, name="student_dashboard"),
+ path('courses/', student_courses, name="courses"),
+ path('tests/', student_tests, name="student_tests"),
+ path('student_change_test_status/', student_change_test_status, name="student_change_test_status"),
+ path('list_foss_on_csc/', get_foss_from_csc, name="list_foss_on_csc"),
+ path('raise_test_req/', raise_test_request, name="raise_test_req"),
+ 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_utils.py b/csc/student_utils.py
new file mode 100644
index 0000000..e69de29
diff --git a/csc/student_views.py b/csc/student_views.py
index e69de29..8d34bc5 100644
--- a/csc/student_views.py
+++ b/csc/student_views.py
@@ -0,0 +1,172 @@
+
+from datetime import date
+from multiprocessing import context
+from django.urls import reverse
+from django.http import HttpResponseRedirect, JsonResponse
+from django.shortcuts import render
+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):
+ context = {}
+ student = Student.objects.get(user=request.user)
+ context['student_id'] = student.id
+
+ studentFoss = Student_Foss.objects.filter(student=student)
+ vles = student.vle_id.all()
+ tests_all = []
+ test_status = {}
+ for vle in vles:
+ 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:
+ test_status[test] = StudentTest.objects.get(test=test).status
+ except StudentTest.DoesNotExist:
+ test_status[test] = 0
+ studentTests = StudentTest.objects.filter(student=student)
+ # tests_students = [x.test for x in tests_all]
+ context['tests_all'] = tests_all
+ context['test_status'] = test_status
+
+ context['studentTests'] = studentTests
+ return render(request,'csc/student_tests.html',context)
+
+def student_change_test_status(request):
+ data = {}
+ status = request.GET.get('status')
+ id = request.GET.get('id')
+ student = Student.objects.get(user=request.user)
+ try:
+ st = StudentTest.objects.get(test_id=id)
+ st.status = status
+ st.save()
+ except StudentTest.DoesNotExist:
+ StudentTest.objects.create(test_id=id,student=student,status=status)
+
+ return HttpResponseRedirect(reverse('student:student_tests'))
+
+@login_required
+@is_student
+def student_dashboard(request):
+ context = {}
+ 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')
+ indi_fosses = [x.csc_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 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'] = indi_fosses
+ context['vle'] = student.vle_id.all()[0]
+
+ return render(request,'csc/student_dashboard.html',context)
+
+@login_required
+@is_student
+def student_courses(request):
+ context = {}
+ student = Student.objects.get(user = request.user)
+ student_foss = Student_Foss.objects.filter(student=student)
+ in_progress_foss_data = {}
+ vles = []
+ context = {}
+ # vles = VLE.objects.filter(user=request.user)
+
+ individual_foss = {}
+ fosses = [x['foss'] for x in FossCategory.objects.filter(available_for_jio=True).values('foss')]
+ context['fosses'] = fosses
+ courses = CertifiateCategories.objects.exclude(code__in=['INDI'])
+ if request.GET.get('course_search'):
+ search_term = request.GET.get('search_term')
+ q_code = Q(certificate_category__code__icontains=search_term)
+ q_title = Q(certificate_category__title__icontains=search_term)
+ q_foss = Q(foss__foss__icontains=search_term)
+ courses = [x.certificate_category for x in CategoryCourses.objects.filter(q_code|q_title|q_foss)]
+
+ else:
+ courses = CertifiateCategories.objects.exclude(code__in=['INDI'])
+ d = {}
+ for course in courses:
+ fosses = [x['foss__foss'] for x in CategoryCourses.objects.filter(certificate_category_id=course.id).values('foss__foss')]
+ # if course.code in d:
+ d[course] = fosses
+ print(f"\n\n{d}\n\n")
+ context['courses'] = d
+
+ return render(request,'csc/student_courses.html',context)
+
+def student_tests(request):
+ context = {}
+ student = Student.objects.get(user=request.user)
+ 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 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
+
+ # 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)
+
+ # 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
+ 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/_error_msg.html b/csc/templates/csc/_error_msg.html
new file mode 100644
index 0000000..5caee34
--- /dev/null
+++ b/csc/templates/csc/_error_msg.html
@@ -0,0 +1,8 @@
+
+ {% for message in messages %}
+
+
+ {{ message|safe }}
+
+ {% endfor %}
+
\ No newline at end of file
diff --git a/csc/templates/csc/_pagination.html b/csc/templates/csc/_pagination.html
new file mode 100644
index 0000000..1155396
--- /dev/null
+++ b/csc/templates/csc/_pagination.html
@@ -0,0 +1,51 @@
+
+
+
\ No newline at end of file
diff --git a/csc/templates/csc/courses.html b/csc/templates/csc/courses.html
new file mode 100644
index 0000000..8cd5ab6
--- /dev/null
+++ b/csc/templates/csc/courses.html
@@ -0,0 +1,216 @@
+{% extends 'csc_base.html'%}
+{% load crispy_forms_tags%}
+{% load helper %}
+{% block css %}
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+ Certificate Courses
+
+
+ Individual Courses
+
+ {% comment %}
+ Contact
+ {% endcomment %}
+
+
+
+
+
+
+ {% for key,value in courses.items %}
+
+
+
+
+
{{key.title}}
+
+ {% for foss in value %}
+
{{foss}}
+ {% endfor %}
+
+
+
+
+ {% comment %}
Enrolled : {{key.count}}
{% endcomment %}
+
+
+
+ {% empty %}
+
+ {% endfor%}
+
+
+
+ {% if request.user|has_group:"VLE" %}
+
+ {% endif %}
+ {% if request.user|has_group:"STUENT" %}
+
+ {% endif %}
+
+ {% comment %}
...
{% endcomment %}
+
+
+
+
+ {% comment %}
+
+
+
+ Individual FOSS
+
+
+
+
{% endcomment %}
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
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 %}
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/csc_base.html b/csc/templates/csc/csc_base.html
new file mode 100644
index 0000000..54928b9
--- /dev/null
+++ b/csc/templates/csc/csc_base.html
@@ -0,0 +1,145 @@
+{% load static %}
+{% load helper %}
+{% load crispy_forms_tags %}
+
+
+
+
+
+
+
+
+
+
+
{% block title%}CSC Academy{% endblock title%}
+
+
+ {% block css %}{% endblock css %}
+
+
+
+
+
+
+
+
+
+ {% if user.is_authenticated %}
+
+ {% include "sidebar.html" %}
+
+ {% block content %}
+
+ Problem in accessing index.
+
+ {% endblock content %}
+
+
+ {% else %}
+
+ {% block content_1 %}
+
+ Problem in accessing index.
+
+ {% endblock content_1 %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+ {% block script %}{% endblock script %}
+
+
+
+
+
+
\ No newline at end of file
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 %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+ List / Edit Invigilator
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name
+ Email
+ Contact #
+ Delete
+
+
+
+ {% for item in invigilators %}
+
+ {{item.user.first_name | title}} {{item.user.last_name | title}}
+ {{item.user.email}}
+ {{item.phone}}
+
+ {% comment %} {% endcomment %}
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+{% 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 %}
+
+
+
+
+ Update
+ Create
+ List
+
+
+
+
+
+
+
+
+
+
+
+ Name
+ Email
+ Contact #
+ Delete
+
+
+
+ {% for item in invigilators %}
+
+ {{item.user.first_name | title}} {{item.user.last_name | title}}
+ {{item.user.email}}
+ {{item.phone}}
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+{% 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 @@
+
\ No newline at end of file
diff --git a/csc/templates/csc/invigilator_dashboard.html b/csc/templates/csc/invigilator_dashboard.html
new file mode 100644
index 0000000..b1163e8
--- /dev/null
+++ b/csc/templates/csc/invigilator_dashboard.html
@@ -0,0 +1,142 @@
+{% extends 'csc_base.html' %}
+{% load csc_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
Test Invigilation Requests
+
+
+ Upcoming
+ Completed
+
+
+
+
+
+
+
+
+
+ Test
+ FOSS
+ Date
+ Time
+ Published
+ Mark attendance
+
+
+
+ {% for test in upcoming_tests %}
+
+
+ {% if test.test_name %}
+ {{test.test_name | title}}
+ {% else %}
+ {{test.foss | title}} Test
+ {% endif %}
+
+ {{test.foss | title }}
+ {{test.tdate}}
+ {{test.ttime}}
+
+ {% if test.publish %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% if test.tdate|is_today %}
+
+
+
+ attendance
+ {% else %}
+ NA
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+ Test
+ FOSS
+ Date
+ Time
+ Published
+ Mark attendance
+
+
+
+ {% for test in completed_tests %}
+
+
+ {% if test.test_name %}
+ {{test.test_name | title}}
+ {% else %}
+ {{test.foss | title}} Test
+ {% endif %}
+
+ {{test.foss | title }}
+ {{test.tdate}}
+ {{test.ttime}}
+
+ {% if test.publish %}
+
+ {% else %}
+
+ {% endif %}
+
+
+ {% if test.tdate|is_today %}
+
+
+
+ attendance
+ {% else %}
+ NA
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/invigilators.html b/csc/templates/csc/invigilators.html
new file mode 100644
index 0000000..12869fc
--- /dev/null
+++ b/csc/templates/csc/invigilators.html
@@ -0,0 +1,284 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+ {{ form.non_field_errors }}
+ {% include "_error_msg.html" %}
+
+
+
+
+
+
+ Add
+ View
+ Invigilators-Acccepted
+ Invigilators-Rejected
+
+
+
+
+
+
+
+ {{ form.non_field_errors }}
+
+
+
+
+
+
+
+ {% if invigilators %}
+
+
+
+ Name
+ Email
+ Contact #
+
+
+
+ {% for item in invigilators %}
+
+ {{item.user.first_name | title}} {{item.user.last_name | title}}
+ {{item.user.email}}
+ {{item.phone}}
+
+
+ {% endfor %}
+
+
+ {% else %}
+
+ No invigilators added.
+
+ {% endif %}
+
+
+
...
+
...
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/list_invigilators.html b/csc/templates/csc/list_invigilators.html
new file mode 100644
index 0000000..def45b9
--- /dev/null
+++ b/csc/templates/csc/list_invigilators.html
@@ -0,0 +1,86 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+ #
+ Name
+ Email
+ Phone
+ Edit
+
+
+
+
+ {% for item in invigilators %}
+
+ {{forloop.counter}}
+ {{item.user.get_full_name}}
+ {{item.user.email}}
+ {{item.phone}}
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/login.html b/csc/templates/csc/login.html
index 08e44a9..8739227 100644
--- a/csc/templates/csc/login.html
+++ b/csc/templates/csc/login.html
@@ -1,6 +1,89 @@
-
Log In
-
\ No newline at end of file
+{% extends 'csc_base.html'%}
+{% block css %}
+
+{% endblock %}
+{% block content_1 %}
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+ {% if form.non_field_errors %}
+
+ {% for message in form.non_field_errors %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/mark_attendance.html b/csc/templates/csc/mark_attendance.html
new file mode 100644
index 0000000..593a158
--- /dev/null
+++ b/csc/templates/csc/mark_attendance.html
@@ -0,0 +1,202 @@
+{% extends 'csc_base.html'%}
+{% load csc_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+ Attendance marked!
+
+
+
+
+
+
+
+
: {{test.tdate}}
+
: : {{test.ttime}}
+
Foss : {{test.foss.foss}}
+
+
+
+
+
+ Total enrolled : {{total_enrolled}}
+ Attendance marked : {{attending}}
+ Pending : {{pending}}
+
+
+
Select all
+
Deselect all
+
Mark attendance
+
+
+
+
+
+
+
+
+
+
+
+ {% if is_paginated %}
+ {% include "_pagination.html" %}
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
diff --git a/csc/templates/csc/sidebar.html b/csc/templates/csc/sidebar.html
new file mode 100644
index 0000000..fee61b3
--- /dev/null
+++ b/csc/templates/csc/sidebar.html
@@ -0,0 +1,147 @@
+{% load static %}
+{% load helper %}
+
\ No newline at end of file
diff --git a/csc/templates/csc/student_courses.html b/csc/templates/csc/student_courses.html
new file mode 100644
index 0000000..d7e0237
--- /dev/null
+++ b/csc/templates/csc/student_courses.html
@@ -0,0 +1,191 @@
+{% extends 'csc_base.html'%}
+{% load crispy_forms_tags%}
+{% load helper %}
+{% block css %}
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+ Certificate Courses
+
+
+ Individual Courses
+
+ {% comment %}
+ Contact
+ {% endcomment %}
+
+
+
+
+
+
+ {% for key,value in courses.items %}
+
+
+
+
+
{{key.title}}
+
+ {% for foss in value %}
+
{{foss}}
+ {% endfor %}
+
+
+
+
+ {% comment %}
Enrolled : {{key.count}}
{% endcomment %}
+
+
+
+ {% empty %}
+
+ {% endfor%}
+
+
+
+
+
+
+
+ #
+
+
+
+
+ {% for foss in fosses %}
+
+ {{forloop.counter}}
+ {{foss}}
+
+ {% endfor %}
+
+
+
+
+
+ {% comment %}
...
{% endcomment %}
+
+
+
+
+ {% comment %}
+
+
+
+ Individual FOSS
+
+
+
+
{% endcomment %}
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/student_dashboard.html b/csc/templates/csc/student_dashboard.html
new file mode 100644
index 0000000..c5cc810
--- /dev/null
+++ b/csc/templates/csc/student_dashboard.html
@@ -0,0 +1,411 @@
+{% extends "csc_base.html" %}
+{% load static %}
+{% load crispy_forms_tags%}
+{% load csc_tags %}
+{% block title%} CSC - VLE Dashboard {% endblock title%}
+{% block css %}
+
+
+
+
+{% endblock %}
+
+
+{% block content%}
+
+
+
+
+
+
+
+
+
+
+ CSC ID: {{ vle.csc.csc_id }}
+
+
+
+
+
+
+ {{ user.email }}
+
+
+
+
+
+
+ {{ student.id }}
+
+
+
+ {% comment %}
+
+
+ {{user.student.address | title}}, {{ user.student.city }}, {{ user.student.state }}
+
+
{% endcomment %}
+
+
+
+
+
+
+
+
+
+ {% comment %}
+
+
+
+
+
+
+
+ Total students enrolled
+
{{total_students_enrolled}}
+
+
+
+
+
+
+
+
+
+ {% endcomment %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% for key,val in courses.items %}
+
+
+
+
+ {% comment %}
Special title treatment {% endcomment %}
+
+ {% for item in val %}
+
+
+ {{item}}
+
+
+
+
+ {% with fossmdlcourse=item.id|get_csc_mdlcourseid %}
+
+
+ {% for course in fossmdlcourse %}
+
+
+
+ {% with attendance=student.id|check_attendance:course.testfoss_id %}
+
+
+ {% if attendance.status == 1%}
+
+
+
+ Attempt {{course.testfoss.foss}} Test
+
+ {% elif attendance.status >= 3%}
+
+ {% with pass_entry=student.id|check_passgrade_exists:course.testfoss_id%}
+ Request VLE for Certificate Grade: {{pass_entry.mdlgrade}}
+ {% endwith %}
+
+ {% endif %}
+ {% endwith %}
+
+
+ {% endfor %}
+ {% endwith %}
+
+
+
+ {% endfor %}
+
+
+
+
+ {% endfor %}
+
+ {% comment %} {% for key,value in upcoming_test_stats.items %}
+
+
+
+
{{key}}
+
{{value.date}} / {{value.time}}
+
Approved : {{value.approved}}
+
Rejected : {{value.rejected}}
+
+
+
+
+ {% endfor %} {% endcomment %}
+
+
+
+
+
+
+
+
+
+ {% for key in fosses %}
+
+
+
+ {{key}}
+
+ {% with fossmdlcourse=key.id|get_csc_mdlcourseid %}
+
+
+ {% for course in fossmdlcourse %}
+
+
+
+ {% with attendance=student.id|check_attendance:course.testfoss_id %}
+
+
+ {% if attendance.status == 1%}
+
+
+
+ Attempt {{course.testfoss.foss}} Test
+
+ {% elif attendance.status >= 3%}
+
+ {% with pass_entry=student.id|check_passgrade_exists:course.testfoss_id%}
+
Request VLE for Certificate Grade: {{pass_entry.mdlgrade}}
+ {% endwith %}
+
+ {% endif %}
+ {% endwith %}
+
+
+ {% endfor %}
+ {% endwith %}
+
+
+
+
+ {% endfor %}
+
+
+
+
+ {% comment %}
+
+
+
+
+ {% for key,value in upcoming_test_stats.items %}
+
+
+
+
{{key}}
+
{{value.date}} / {{value.time}}
+
Approved : {{value.approved}}
+
Rejected : {{value.rejected}}
+
+
+
+
+ {% endfor %}
+
+
+
+
{% endcomment %}
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
+
+
+
+
+
+{% block script %}
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/student_mail_template.html b/csc/templates/csc/student_mail_template.html
new file mode 100644
index 0000000..e19d71d
--- /dev/null
+++ b/csc/templates/csc/student_mail_template.html
@@ -0,0 +1,21 @@
+
+
+
+
+
Order received
+
+
+
+
Dear {{full_name}},
+
Thank you for registering under Spoken Tutorial(IIT Bombay) courses.
+
Below are the login details for Spoken Tutorial Dashboard.
+
Link to Login: https://spoken-tutorial.in/login/
+
+
username : {{username}}
+
password : {{password}}
+
+
Thanks & Regards,
+
Team,
+
Spoken Tutorial
+
+
\ No newline at end of file
diff --git a/csc/templates/csc/student_profile.html b/csc/templates/csc/student_profile.html
new file mode 100644
index 0000000..66dd44e
--- /dev/null
+++ b/csc/templates/csc/student_profile.html
@@ -0,0 +1,146 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+ {{ form.non_field_errors }}
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% for key, val in courses.items %}
+
+
+
+
+
+ {% for i in val %}
+ {{i}}
+ {% endfor %}
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/student_stats.html b/csc/templates/csc/student_stats.html
new file mode 100644
index 0000000..4736653
--- /dev/null
+++ b/csc/templates/csc/student_stats.html
@@ -0,0 +1 @@
+{{object_list}}
\ No newline at end of file
diff --git a/csc/templates/csc/student_tests.html b/csc/templates/csc/student_tests.html
new file mode 100644
index 0000000..dcfbf27
--- /dev/null
+++ b/csc/templates/csc/student_tests.html
@@ -0,0 +1,205 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+ Open Tests
+ Completed Tests
+ {% comment %} Contact {% endcomment %}
+
+
+
+
+
+
+
+ #
+ FOSS
+ Date
+ Time
+
+
+
+ {% for item in open_tests %}
+
+ {{forloop.counter}}
+ {{item.test.foss.foss}}
+ {{item.test.tdate}}
+ {{item.test.ttime}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+ #
+ FOSS
+ Date
+ Time
+ Download
+
+
+
+ {% for item in completed_tests %}
+
+ {{forloop.counter}}
+ {{item.test.foss.foss}}
+ {{item.test.tdate}}
+ {{item.test.ttime}}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/students_list.html b/csc/templates/csc/students_list.html
new file mode 100644
index 0000000..2366111
--- /dev/null
+++ b/csc/templates/csc/students_list.html
@@ -0,0 +1,223 @@
+{% extends 'csc_base.html'%}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+ {% if is_indi %}
+
+ Assign FOSS
+
+ {% endif %}
+
+
+
+
* Click on student name below to view student details.
+
+
Search results for : {{search_foss}}, {{search_course}}, {{search_name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Assign FOSS
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+{% comment %} {% endcomment %}
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/temp.html b/csc/templates/csc/temp.html
new file mode 100644
index 0000000..5557e30
--- /dev/null
+++ b/csc/templates/csc/temp.html
@@ -0,0 +1,128 @@
+{% extends 'csc_base.html'%}
+{% load csc_tags %}
+{% block css %}
+
+
+
+{% endblock css %}
+
+{% block content %}
+
+
Section coming soon !
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test 2.html b/csc/templates/csc/test 2.html
new file mode 100644
index 0000000..78a4021
--- /dev/null
+++ b/csc/templates/csc/test 2.html
@@ -0,0 +1,56 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test.html b/csc/templates/csc/test.html
new file mode 100644
index 0000000..590ec31
--- /dev/null
+++ b/csc/templates/csc/test.html
@@ -0,0 +1,54 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test_assign 2.html b/csc/templates/csc/test_assign 2.html
new file mode 100644
index 0000000..0599f86
--- /dev/null
+++ b/csc/templates/csc/test_assign 2.html
@@ -0,0 +1,124 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test_assign.html b/csc/templates/csc/test_assign.html
new file mode 100644
index 0000000..d0226bc
--- /dev/null
+++ b/csc/templates/csc/test_assign.html
@@ -0,0 +1,121 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test_confirm_delete.html b/csc/templates/csc/test_confirm_delete.html
new file mode 100644
index 0000000..07fbca2
--- /dev/null
+++ b/csc/templates/csc/test_confirm_delete.html
@@ -0,0 +1,76 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+ {{ form.non_field_errors }}
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/test_detail.html b/csc/templates/csc/test_detail.html
new file mode 100644
index 0000000..d714972
--- /dev/null
+++ b/csc/templates/csc/test_detail.html
@@ -0,0 +1,88 @@
+{% extends 'csc_base.html'%}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+ FOSS
+ {{test.foss | title }}
+
+
+ Date
+ {{test.tdate}}, {{test.ttime}}
+
+
+ Published
+ {% if test.publish %} {% else %} {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test_form.html b/csc/templates/csc/test_form.html
new file mode 100644
index 0000000..28ef6db
--- /dev/null
+++ b/csc/templates/csc/test_form.html
@@ -0,0 +1,79 @@
+{% extends 'csc_base.html'%}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/test_list 2.html b/csc/templates/csc/test_list 2.html
new file mode 100644
index 0000000..efbb78d
--- /dev/null
+++ b/csc/templates/csc/test_list 2.html
@@ -0,0 +1,96 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load csc_tags %}
+{% block css %}
+
+
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+
+
+ Test
+ FOSS
+ Date
+ Time
+ Published
+ Delete
+ Edit
+ Mark attendance
+
+
+
+ {% for test in tests %}
+
+
+ {% if test.test_name %}
+ {{test.test_name | title}}
+ {% else %}
+ {{test.foss | title}} Test
+ {% endif %}
+
+ {{test.foss | title }}
+ {{test.tdate}}
+ {{test.ttime}}
+
+ {% if test.publish %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% if test.tdate|is_today %}
+
+
+
+ attendance
+ {% else %}
+ NA
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+{% comment %} {% endcomment %}
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test_list.html b/csc/templates/csc/test_list.html
new file mode 100644
index 0000000..fa78ca1
--- /dev/null
+++ b/csc/templates/csc/test_list.html
@@ -0,0 +1,97 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load csc_tags %}
+{% block css %}
+
+
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+
+
+ #
+ Title
+ FOSS
+ Date
+ Time
+ Published
+ Delete
+ Edit
+
+
+
+
+ {% for test in tests %}
+
+ {{forloop.counter}}
+
+ {% if test.test_name %}
+ {{test.test_name | title}}
+ {% else %}
+ {{test.foss | title}} Test
+ {% endif %}
+
+ {{test.foss | title }}
+ {{test.tdate}}
+ {{test.ttime}}
+
+ {% if test.publish %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% if test.tdate|is_today %}
+
+
+
+ attendance
+
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/test_update_form.html b/csc/templates/csc/test_update_form.html
new file mode 100644
index 0000000..eb380be
--- /dev/null
+++ b/csc/templates/csc/test_update_form.html
@@ -0,0 +1,124 @@
+{% extends 'csc_base.html'%}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+ Home
+
+
+ Students
+
+
+ Contact
+
+
+
+
+
+
+
+
+
+
+ #
+ Name
+ Email
+ Contact
+
+
+
+
+ {% for student in students %}
+
+ {{forloop.counter}}
+ {{student.user.get_full_name | title}}
+ {{student.user.email}}
+ {{student.phone}}
+
+ {% endfor %}
+
+
+
+
+
+
+
...
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/update_invigilator.html b/csc/templates/csc/update_invigilator.html
new file mode 100644
index 0000000..99a3cb2
--- /dev/null
+++ b/csc/templates/csc/update_invigilator.html
@@ -0,0 +1,62 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+{% endblock %}
+
diff --git a/csc/templates/csc/update_test 2.html b/csc/templates/csc/update_test 2.html
new file mode 100644
index 0000000..aca7c60
--- /dev/null
+++ b/csc/templates/csc/update_test 2.html
@@ -0,0 +1,42 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load csc_tags %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/update_test.html b/csc/templates/csc/update_test.html
new file mode 100644
index 0000000..a7e8cb9
--- /dev/null
+++ b/csc/templates/csc/update_test.html
@@ -0,0 +1,53 @@
+{% extends 'csc_base.html'%}
+{% load static %}
+{% load csc_tags %}
+{% load crispy_forms_tags %}
+{% block css %}
+
+
+{% endblock css %}
+
+{% block content %}
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+
+
+ {{ message }}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+{% endblock %}
+
+
+
+
diff --git a/csc/templates/csc/vle.html b/csc/templates/csc/vle.html
index 1632736..edf4ed4 100644
--- a/csc/templates/csc/vle.html
+++ b/csc/templates/csc/vle.html
@@ -1 +1,330 @@
-vle dashboard
\ No newline at end of file
+{% extends "csc_base.html" %}
+{% load static %}
+{% load crispy_forms_tags%}
+{% block title%} CSC - VLE Dashboard {% endblock title%}
+{% block css %}
+
+
+
+
+{% endblock %}
+
+
+{% block content%}
+
+
+
+
+
+
+
+
+
+
+ CSC ID: {{ vle.csc.csc_id }}
+
+
+
+
+
+
+ {{ user.email }}
+
+
+
+
+
+
+ {{ vle.phone }}
+
+
+
+
+
+
+ {{vle.csc.address | title}}, {{vle.csc.block}}, {{ vle.csc.city }}, {{ vle.csc.state }}
+
+
+
+ Plan:
+ {{ vle.csc.plan }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Total students enrolled
+
{{total_students_enrolled}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Total tests - completed
+
{{total_tests_completed}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Total tests - upcoming
+
{{total_upcoming_tests}}
+
+
+
+
+
+
+
+
+
+
+ {% comment %}
+
+
+
+
+
Total certificates
+
+
+
+
{{total_certificates_issued}}
+
+
+
+
+
+
+
+
+
+
+
+
{% endcomment %}
+
+
+
+ {% if upcoming_test_stats %}
+
+
+
+
+
+ {% for key,value in upcoming_test_stats.items %}
+
+
+
+
{{key}}
+
{{value.date}} / {{value.time}}
+
Total Students : {{value.total_students}}
+
+
+
+ {% endfor %}
+
+
+
+
+ {% endif %}
+
+
+ {% comment %}
+
+
Enrollment based on Course Type
+
+
DCA({{courses_offered_stats.dca}})
+
Individual({{courses_offered_stats.individual}})
+
+
+
+
+
+
{% endcomment %}
+
+
+ {% comment %}
+
+
Enrollment for DCA Courses
+
+
+
+
+
+
Enrollment for Individual Courses
+
+
+
+
+
{% endcomment %}
+
+
+
+ {% for key,value in fosses_perc.items %}
+
+ {% endfor %}
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+{% endblock %}
diff --git a/csc/templates/csc/vle_mail_template.html b/csc/templates/csc/vle_mail_template.html
new file mode 100644
index 0000000..f19093c
--- /dev/null
+++ b/csc/templates/csc/vle_mail_template.html
@@ -0,0 +1,28 @@
+
+
+
+
+
Order received
+
+
+
+
Dear {{full_name}},
+
Welcome to IIT Bombay Spoken Tutorial Program. We are happy to be partnered with CSC Academy to
+ empower youth from all over the country via VLEs.
+
Please use the below Login details for the Spoken Tutorial Dashboard:
+
Link to Login: https://spoken-tutorial.in/login/
+
+
username : {{username}}
+
password : {{password}}
+
+
Please click the following training link to know the process of
+
Student Registration Instructions : Click Here
+
Course Allotment Instructions : Click Here
+
+
In case of any query, please feel free to contact at animation-hackathon@cscacademy.org.
+
+
Thanks & Regards,
+
Team,
+
Spoken Tutorial
+
+
\ No newline at end of file
diff --git a/csc/templates/stats/stats.html b/csc/templates/stats/stats.html
new file mode 100644
index 0000000..80db1cb
--- /dev/null
+++ b/csc/templates/stats/stats.html
@@ -0,0 +1,310 @@
+
+{% extends "stats/stats_base.html" %}
+{% load static %}
+
+{% block custom_style %}
+
+{% endblock %}
+ {% block content %}
+
+
+
+
Total VLE Registered
+
{{total_vles}}
+
+
+
+
+
Total Students Registered
+
{{total_students}}
+
+
+
+
+
+
Total FOSS
+
{{total_foss}}
+
+
+
+
+
+
Total Certificate Courses
+
{{total_certificate_course}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #
+ FOSS
+ Student Count
+
+
+
+ {% for item in foss_count_tb %}
+
+ {{forloop.counter}}
+ {{item.csc_foss__foss}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #
+ FOSS
+ Student Count
+
+
+
+ {% for item in student_indi_foss %}
+
+ {{forloop.counter}}
+ {{item.csc_foss__foss}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #
+ State
+ CSC Count
+
+
+
+ {% for item in csc_state %}
+
+ {{forloop.counter}}
+ {{item.state}}
+ {{item.count}}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endblock content %}
+
+ {% block js_script %}
+
+
+ {% endblock js_script %}
+
diff --git a/csc/templates/stats/stats_base.html b/csc/templates/stats/stats_base.html
new file mode 100644
index 0000000..357ccb4
--- /dev/null
+++ b/csc/templates/stats/stats_base.html
@@ -0,0 +1,174 @@
+{% load static %}
+
+
+
+
+
+
+
+
+
CSC Stats
+
+
+
+
+
+
+
+
+
+
+ {% block custom_style %}
+ {% endblock custom_style %}
+
+
+
+
+
+
+
+
+
+
+ {% block content %}
+
+ {% endblock content %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% block js_script %}
+ {% endblock js_script %}
+
+
diff --git a/csc/templates/stats/student_report.html b/csc/templates/stats/student_report.html
new file mode 100644
index 0000000..825d92d
--- /dev/null
+++ b/csc/templates/stats/student_report.html
@@ -0,0 +1,250 @@
+{% extends "stats/stats_base.html" %}
+{% load static %}
+{% block custom_style %}
+
+
+{% endblock custom_style %}
+{% block content %}
+
+
+
+ {% comment %}
+ Certificate Course
+ {% endcomment %}
+
+ FOSS
+
+
+ Occupation
+
+
+ Education
+
+
+ Category
+
+
+ State
+
+
+ District
+
+
+
+
+
+
+
+
+
+
+ #
+ FOSS
+ Student Count
+
+
+
+ {% for item in student_foss %}
+
+ {{forloop.counter}}
+ {{item.csc_foss__foss}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ #
+ Occupation
+ Student Count
+
+
+
+ {% for item in student_occupation %}
+
+ {{forloop.counter}}
+ {% if item.occupation %}
+ {{item.occupation}}
+ {% else %} Unknown
+ {% endif %}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ #
+ Education
+ Student Count
+
+
+
+ {% for item in edu %}
+
+ {{forloop.counter}}
+ {% if item.edu_qualification %}
+ {{item.edu_qualification}}
+ {% else %} Unknown
+ {% endif %}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ #
+ Category
+ Student Count
+
+
+
+ {% for item in student_category %}
+
+ {{forloop.counter}}
+ {% if item.category %}
+ {{item.category}}
+ {% else %} Unknown
+ {% endif %}
+
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ #
+ State
+ Student Count
+
+
+
+ {% for item in student_state %}
+
+ {{forloop.counter}}
+ {{item.state__name}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ #
+ District
+ Student Count
+
+
+
+ {% for item in student_district %}
+
+ {{forloop.counter}}
+ {{item.district__name}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+{% endblock content %}
+
+
+
+{% block js_script %}
+
+
+
+{% endblock js_script %}
+
diff --git a/csc/templates/stats/student_stats.html b/csc/templates/stats/student_stats.html
new file mode 100644
index 0000000..45145c5
--- /dev/null
+++ b/csc/templates/stats/student_stats.html
@@ -0,0 +1,167 @@
+
+{% extends "stats/stats_base.html" %}
+{% load static %}
+{% block custom_style %}
+
+{% endblock custom_style %}
+{% block content %}
+
+
+ {% if query_str.name or query_str.vle_name or query_str.csc or query_str.edu or query_str.state or query_str.district or query_str.occupation %}
+
+
+ Showing Search Results For :
+ {% for item,val in query_str.items %}
+ {% if val %}
+ {% if forloop.last %}
+ {{item}} : {{val}}
+ {% else %}
+ {{item}} : {{val}} |
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ ID
+ Name
+ Email
+ Gender
+ Educational Qualification
+ VLE
+ State
+ District
+ Date of Registration
+ Occupation
+ Category
+
+
+
+ {% for student in object_list %}
+
+ {{forloop.counter}}
+ {{student.user.get_full_name | title}}
+ {{student.user.email}}
+ {{student.gender}}
+ {{student.edu_qualification}}
+
+ {% for vle in student.vle_id.all %}
+ {{vle.user.email}}
+ {% endfor %}
+
+ {{student.state}}
+ {{student.district}}
+ {{student.date_of_registration}}
+ {{student.occupation}}
+ {{student.category}}
+
+ {% endfor %}
+
+
+
+
+
+{% endblock content %}
+
diff --git a/csc/templates/stats/vle_report.html b/csc/templates/stats/vle_report.html
new file mode 100644
index 0000000..7b6035f
--- /dev/null
+++ b/csc/templates/stats/vle_report.html
@@ -0,0 +1,126 @@
+{% extends "stats/stats_base.html" %}
+{% load static %}
+{% block custom_style %}
+
+
+{% endblock custom_style %}
+{% block content %}
+
+
+
+
+ VLE State wise distribution
+
+
+ VLE District wise distribution
+
+
+
+
+
+
+
+
+
+
+ #
+ State
+ VLE Count
+
+
+
+ {% for item in csc_state %}
+
+ {{forloop.counter}}
+ {{item.state}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ #
+ District
+ VLE Count
+
+
+
+ {% for item in csc_district %}
+
+ {{forloop.counter}}
+ {{item.district}}
+ {{item.count}}
+
+ {% endfor %}
+
+
+
+
+
+
+{% endblock content %}
+
+
+
+{% block js_script %}
+
+
+
+{% endblock js_script %}
+
diff --git a/csc/templates/stats/vle_stats.html b/csc/templates/stats/vle_stats.html
new file mode 100644
index 0000000..d5dc9e3
--- /dev/null
+++ b/csc/templates/stats/vle_stats.html
@@ -0,0 +1,316 @@
+{% extends "stats/stats_base.html" %}
+{% load static %}
+{% block custom_style %}
+
+{% endblock custom_style %}
+{% block content %}
+
+
+
+
+
+
+ {% if query_str.name or query_str.csc_id or query_str.state or query_str.district %}
+
+
+ Showing Search Results For :
+ {% for item,val in query_str.items %}
+ {% if val %}
+ {% if forloop.last %}
+ {{item}} : {{val}}
+ {% else %}
+ {{item}} : {{val}} |
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ ID
+ Name
+ CSC ID
+ State
+ District
+ Plan
+ Date of Joining
+
+
+
+
+ {% for vle in object_list %}
+
+ {{forloop.counter}}
+
+ {{vle.user.get_full_name | title}}
+
+
+ {{vle.csc.csc_id}}
+ {{vle.csc.state}}
+ {{vle.csc.district}}
+ {{vle.csc.plan}}
+ {{vle.user.date_joined}}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+ Course Title
+ {% comment %} Students {% endcomment %}
+
+
+
+
+
+
+
+
+
+
+
+
+ FOSS
+ {% comment %} Students {% endcomment %}
+
+
+
+
+
+
+
+
+
+
+
+ FOSS (Individual FOSS Only)
+ {% comment %} Students {% endcomment %}
+
+
+
+
+
+
+
+
+
+{% endblock content %}
+
+{% block js_script %}
+
+{% endblock js_script %}
+
diff --git a/csc/templatetags/__init__.py b/csc/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/csc/templatetags/csc_tags.py b/csc/templatetags/csc_tags.py
new file mode 100644
index 0000000..e1f065b
--- /dev/null
+++ b/csc/templatetags/csc_tags.py
@@ -0,0 +1,58 @@
+from django import template
+import datetime
+register = template.Library()
+from csc.models import *
+from csc.utils import TEST_COMPLETED_BY_STUDENT,PASS_GRADE, TEST_ATTENDANCE_MARKED
+from csc.utils import get_valid_animation_fosses
+
+def is_today(value):
+ return value == datetime.date.today()
+
+register.filter('is_today', is_today)
+
+
+@register.filter
+def get_csc_mdlcourseid(eventfossid):
+ try:
+ csccourse = CSCFossMdlCourses.objects.filter(foss=eventfossid)
+ except CSCFossMdlCourses.DoesNotExist:
+ return None
+
+ return csccourse
+
+
+@register.filter
+def check_attendance(studentid, testfossid):
+ try:
+ print(testfossid)
+ attendance = CSCTestAtttendance.objects.filter(test__foss_id=testfossid, student_id=studentid).first()
+ print(attendance,"######################")
+ return attendance
+ except Exception as e:
+ print(e)
+ return None
+
+
+
+# @register.filter
+# def check_passgrade_exists(studentid, testfossid):
+# # valid_fosses = get_valid_animation_fosses()
+# valid_fosses = [x.test.foss.id for x in CSCTestAtttendance.objects.filter(student_id=studentid)]
+# if testfossid in valid_fosses:
+# try:
+# pass_entry = CSCTestAtttendance.objects.filter(test__foss__id=testfossid, student_id=studentid, status__gte=TEST_COMPLETED_BY_STUDENT, mdlgrade__gte=PASS_GRADE)
+# return pass_entry
+# except:
+# return None
+# else:
+ # return None
+
+
+@register.filter
+def check_passgrade_exists(studentid, testfossid):
+ try:
+ pass_entry = CSCTestAtttendance.objects.filter(test__foss__id=testfossid, student_id=studentid, status__gte=TEST_COMPLETED_BY_STUDENT, mdlgrade__gte=PASS_GRADE).first()
+ return pass_entry
+ except:
+ return None
+
diff --git a/csc/tests.py b/csc/tests.py
index 7ce503c..ae59c25 100644
--- a/csc/tests.py
+++ b/csc/tests.py
@@ -1,3 +1,32 @@
from django.test import TestCase
+from .models import *
# Create your tests here.
+def students_approve_tests(test_id,status,count,smax=50):
+ test_id =test_id
+ test = Test.objects.get(id=test_id)
+ sf = Student_Foss.objects.filter(csc_foss__spoken_foss=test.foss)
+ print(f"sf -- {sf}")
+ temp=0
+ for item in sf:
+ print(f"For loop: {item}")
+ if temp < count:
+ try:
+ student = item.student
+ print(f"{temp} ---- {student}")
+ print(f"{student.id} , {smax}")
+ if student.id < smax:
+ print("student.id < smax")
+ st=StudentTest.objects.get(student=student,test=test)
+ st.status = status
+ st.save()
+ print('saved existinng studdent')
+ temp+=1
+ else:
+ print(f"student.id > smax:")
+ except StudentTest.DoesNotExist:
+ print('exception')
+ StudentTest.objects.create(student=student,test=test,status=status)
+ print('new obbject created ')
+
+
diff --git a/csc/utils.py b/csc/utils.py
index 447ade5..24db5e2 100644
--- a/csc/utils.py
+++ b/csc/utils.py
@@ -1,6 +1,222 @@
+from django.db.models import OuterRef,Exists, Count
from genericpath import exists
from tokenize import group
+import datetime
+
+from mdl.models import MdlUser
+from csc.models import *
+# from csc.models import Test
+# from .models import REJECTED, APPROVED
+from datetime import date,timedelta
+from django.db.models import Count
+from django.conf import settings
+from django.core.mail import send_mail
+# from reportlab.pdfgen import canvas
+# from reportlab.pdfbase.ttfonts import TTFont
+# from reportlab.pdfbase import pdfmetrics
+# from reportlab.lib.styles import ParagraphStyle
+# from reportlab.platypus import Paragraph
+# from reportlab.lib.units import cm
+
+# from PyPDF2 import PdfFileWriter, PdfFileReader
+
+# from io import StringIO, BytesIO
+
+import random, string
+
+TEST_OPEN=0
+TEST_ATTENDANCE_MARKED=1
+TEST_ONGOING=2
+TEST_COMPLETED_BY_STUDENT=3
+TEST_CLOSED_BY_VLE=4
+PASS_GRADE=40.00
+
+# from reportlab.pdfgen import canvas
+# from reportlab.pdfbase.ttfonts import TTFont
+# from reportlab.pdfbase import pdfmetrics
+# from reportlab.lib.styles import ParagraphStyle
+# from reportlab.platypus import Paragraph
+# from reportlab.lib.units import cm
+
+# from PyPDF2 import PdfFileWriter, PdfFileReader
+
+# from io import StringIO, BytesIO
+
+from django.http import HttpResponse
+
+# from reportlab.pdfgen import canvas
+# from reportlab.pdfbase.ttfonts import TTFont
+# from reportlab.pdfbase import pdfmetrics
+# from reportlab.lib.styles import ParagraphStyle
+# from reportlab.platypus import Paragraph
+# from reportlab.lib.units import cm
+
+# from PyPDF2 import PdfFileWriter, PdfFileReader
+
+# from io import StringIO, BytesIO
+
+from django.http import HttpResponse
def is_user_vle(user):
- print("\n\n user is VLE \n\n")
return user.groups.filter(name="VLE").exists()
+
+def is_user_student(user):
+ return user.groups.filter(name="STUDENT").exists()
+
+def is_csc_team_role(user):
+ return user.groups.filter(name="CSC-TEAM").exists()
+
+def is_user_invigilator(user):
+ return user.groups.filter(name="INVIGILATOR").exists()
+
+def get_upcoming_test_stats(vle):
+ tests = Test.objects.filter(tdate__gte=datetime.datetime.now().date(),vle=vle)
+ d = {}
+ for test in tests:
+ total_students = CSCTestAtttendance.objects.filter(test=test).count()
+ key = test.foss.foss
+ d[key] = {'total_students' : total_students,'date': test.tdate, 'time': test.ttime, 'id':test.id }
+ return d
+
+def get_courses_offered_stats():
+ d = {}
+ dca = Student_certificate_course.objects.filter(cert_category__code='DCA').count()
+ individual = Student_certificate_course.objects.filter(cert_category__code='INDI').count()
+ d['dca'] = dca
+ d['individual'] = individual
+ return d
+
+def get_programme_stats():
+ course_count_result = (Student_certificate_course.objects.values('cert_category__code').annotate(scount=Count('cert_category__code')).order_by())
+ print(course_count_result)
+ d = {}
+ for item in course_count_result:
+ d[item['cert_category__code']]=item['scount']
+ course_count = [x for x in course_count_result]
+ print(f"course_count\n\n\n\n{course_count}")
+ return d
+ return course_count
+
+
+def get_foss_enroll_percent(vle):
+ csc_foss = [x for x in FossCategory.objects.filter(available_for_jio=True)]
+ d = {}
+ total = Student_Foss.objects.filter(csc_foss__in = csc_foss).count()
+ for item in csc_foss:
+ d[item.foss] = ((Student_Foss.objects.filter(csc_foss = item).count())/total)*100
+ return d
+
+
+def upcoming_foss_tests(foss,vle):
+ tests = Test.objects.filter(foss=foss,vle=vle,tdate__gt=date.today())
+ return list(tests)
+
+def check_student_test_status(test,student):
+ studentTest = StudentTest.objects.filter(test__in=test,student=student)
+ if studentTest:
+ return studentTest[0].test
+ return False
+
+def getFirstName(name):
+ formatted = name.split()
+ if formatted:
+ return formatted[0]
+ return ''
+
+def getLastName(name):
+ formatted = name.split(maxsplit=1)
+ if len(formatted)>1:
+ return formatted[1]
+ return ''
+
+def get_tenure_end_date(tdate):
+ subscription_period = getattr(settings, "CSC_SUBSCRIPTION_PERIOD", 100)
+ end_date = tdate + timedelta(100)
+ return end_date
+
+
+def get_valid_students_for_test(vle,test):
+ foss = test.foss
+ other_tests = Test.objects.filter(foss=foss).exclude(id=test.id)
+ students = Student.objects.filter(vle_id=vle.id,student_foss__csc_foss_id=foss.id,student_foss__foss_start_date__lte=datetime.date.today()-timedelta(days=10)).annotate(assigned=Exists(CSCTestAtttendance.objects.filter(student_id=OuterRef('id'),test=test))).annotate(ineligible=Exists(CSCTestAtttendance.objects.filter(student_id=OuterRef('id'),test__in=other_tests)))
+ return students
+
+def get_all_foss_for_vle(vle):
+ students = Student.objects.filter(vle_id=vle.id)
+ fosses = Student_Foss.objects.filter(student_id__in=students).values('csc_foss').distinct()
+ return FossCategory.objects.filter(id__in=[x['csc_foss'] for x in fosses]).order_by('foss')
+
+def send_pwd_mail_to_invi(u):
+ pwd = ''.join(random.choices(string.ascii_letters,k=10))
+ u.set_password(pwd)
+ u.save()
+ invi = Invigilator.objects.get(user=u)
+ from_email = getattr(settings, "NO_REPLY_MAIL", "no-reply@spoken-tutorial.org")
+ subject = "Login credentials for Spoken Tutorial - CSC"
+ message = f"""
+ Dear {u.get_full_name()},
+ Below are the login details for Spoken Tutorial Dashboard.
+ Link to Login: https://spoken-tutorial.in/login/
+
+ username : {u.username}
+ password : {pwd}
+
+ Thanks & Regards,
+ Team,
+ Spoken Tutorial
+ """
+ try:
+ send_mail(subject,message,from_email,[u.email],fail_silently=False)
+
+ invi.password_mail_sent = 1
+ invi.save()
+ except Exception as e:
+ invi.password_mail_sent = 0
+ invi.save()
+ print(e)
+ print(f"Failed to send mail to user : {u.email}")
+
+def send_mdl_mail(u,pwd):
+ student = Student.objects.get(user=u)
+ mdluser = MdlUser.objects.filter(email=u.email)[0]
+ # pwd = ''.join(random.choices(string.ascii_letters,k=10))
+ u.set_password(pwd)
+ u.save()
+
+ from_email = getattr(settings, "NO_REPLY_MAIL", "no-reply@spoken-tutorial.org")
+ print(f"2 SENDING Email ************************ {u.email}")
+ MDL_URL=getattr(settings, "MDL_URL", "")
+ subject = "Login credentials for Moodle Spoken Tutorial - CSC"
+ message = f"""
+ Dear {u.get_full_name()},
+ Below are the login details for Moodle Dashboard for Spoken Tutorial Test.
+ Link to Moodle Login: {MDL_URL}
+
+ username : {mdluser.username}
+ password : {pwd}
+
+ Thanks & Regards,
+ Team,
+ Spoken Tutorial
+ """
+ print(f"3 SENDING Email ************************ {u.email}")
+ try:
+ print(f"4 SENDING Email ************************ {u.email}")
+ send_mail(subject,message,from_email,[u.email],fail_silently=False)
+ student.mdl_mail_sent = True
+ student.save()
+ print(f"5 SENDING Email ************************ {u.email}")
+ except Exception as e:
+ print(f"6 SENDING Email ************************ {u.email}")
+ student.mdl_mail_sent = False
+ student.save()
+ print(e)
+ print(f"Failed to send mail to user : {u.email}")
+ print(f"7 SENDING Email ************************ {u.email}")
+
+def get_valid_animation_fosses():
+ course = CertifiateCategories.objects.get(code = 'IT07')
+ fosses = [x.foss.id for x in CategoryCourses.objects.filter(certificate_category=course)]
+ return FossCategory.objects.filter(id__in=fosses).order_by('foss')
+
+
diff --git a/csc/vle_forms.py b/csc/vle_forms.py
new file mode 100644
index 0000000..9669cfb
--- /dev/null
+++ b/csc/vle_forms.py
@@ -0,0 +1,120 @@
+from django.forms import Form, ModelForm, Textarea, TextInput
+from django import forms
+
+from csc.models import *
+from spokenlogin.models import *
+
+from .utils import get_valid_animation_fosses
+
+class FossForm(forms.Form):
+ programme_type = forms.ChoiceField(required=True, choices=PROGRAMME_TYPE_CHOICES)
+ spoken_foss = forms.ModelMultipleChoiceField(queryset=FossCategory.objects.filter(available_for_jio=True))
+
+ # class Meta(object):
+ # model = Vle_csc_foss
+ # exclude = ['created','updated']
+
+ def __init__(self, *args, **kwargs):
+ initial = ''
+ if 'instance' in kwargs:
+ initial = kwargs["instance"]
+
+ if 'user' in kwargs:
+ user = kwargs["user"]
+ del kwargs["user"]
+
+
+ super(FossForm, self).__init__(*args, **kwargs)
+
+ self.fields['programme_type'].choices = PROGRAMME_TYPE_CHOICES
+
+ if kwargs and 'data' in kwargs:
+ if kwargs['data']['programme_type'] != '':
+ programme_type = kwargs['data']['programme_type']
+
+ if programme_type == 'dca':
+ fosses = SpokenFoss.objects.filter(csc_dca_programme=True, available_for_jio=True).order_by('foss')
+ else:
+ fosses = SpokenFoss.objects.filter(csc_dca_programme=False, available_for_jio=True).order_by('foss')
+
+ # self.fields['spoken_foss'].queryset = fosses
+ self.fields['spoken_foss'].initial = kwargs['data']['spoken_foss']
+
+
+class StudentForm(forms.ModelForm):
+ GENDER = (('f','female'),('m','male'))
+ fname = forms.CharField(max_length=120,label='First Name')
+ lname = forms.CharField(max_length=120,label='Last Name')
+ gender = forms.ChoiceField(choices = GENDER)
+
+ class Meta:
+ model = Student
+ fields = ['gender','dob','phone','edu_qualification','state','city','district','pincode','address']
+ labels = {
+ 'fname' :'First Nameing'
+ }
+
+
+# class InvigilatorForm(forms.ModelForm):
+# phone = forms.CharField(max_length=15,required=False)
+# class Meta:
+# model = User
+# fields = ['first_name','last_name','email','phone']
+ # email = forms.EmailField()
+ # fname = forms.CharField(max_length=120,label='First Name')
+ # lname = forms.CharField(max_length=120,label='Last Name')
+ # phone = forms.CharField(max_length=32,label='Contact Number')
+
+class InvigilatorForm(forms.Form):
+ email = forms.EmailField(max_length=200,required=False)
+ first_name = forms.CharField(max_length=200,required=False)
+ last_name = forms.CharField(max_length=200,required=False)
+ phone = forms.CharField(max_length=20,required=False)
+
+
+class InvigilationRequestForm(forms.Form):
+
+ test = forms.ModelChoiceField(queryset=None,widget=forms.Select(attrs={'disabled':'disabled'}))
+ invigilators = forms.ModelMultipleChoiceField(queryset=None)
+
+ def __init__(self, *args, **kwargs):
+ vle_id = kwargs.pop('vle_id')
+ test_id = kwargs.pop('test_id')
+
+
+ super(InvigilationRequestForm,self).__init__(*args, **kwargs)
+ vle = VLE.objects.get(id=vle_id)
+ self.fields['test'].queryset = Test.objects.filter(vle=vle)
+ self.fields['test'].initial = test_id
+ self.fields['invigilators'].queryset = Invigilator.objects.filter(vle=vle)
+ l = InvigilationRequest.objects.filter(test_id=test_id).values('invigilator')
+ l = [x['invigilator'] for x in l]
+ self.fields['invigilators'].initial = l
+
+
+class TestForm(forms.ModelForm):
+ class Meta:
+ model = Test
+ fields = ['foss','tdate','ttime','invigilator','publish']
+ widgets = {
+ 'tdate' : forms.DateInput(attrs={'type': 'date'}),
+ 'ttime' : forms.DateInput(attrs={'type': 'time'},format='%H:%M')
+ }
+ labels = {
+ 'tdate' : 'Test Date',
+ 'ttime' : 'Test Time',
+ 'invigilator' : 'Invigilators'
+ }
+ help_texts = {
+ 'tdate' : 'Format : DD/MM/YYYY',
+ }
+
+ def __init__(self, *args, **kwargs):
+ user = kwargs.pop('user')
+ vle = VLE.objects.get(user=user)
+ super(TestForm, self).__init__(*args, **kwargs)
+ # r = get_all_foss_for_vle(vle)
+ # print(f"r ************* {len(r)}")
+ # self.fields['foss'].queryset = get_all_foss_for_vle(vle) #IMPORTANT : For querying foss valid for the vle
+
+ self.fields['foss'].queryset = get_valid_animation_fosses()
\ No newline at end of file
diff --git a/csc/vle_models.py b/csc/vle_models.py
index e69de29..0dff170 100644
--- a/csc/vle_models.py
+++ b/csc/vle_models.py
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/csc/vle_urls.py b/csc/vle_urls.py
index 4f75517..6a344f3 100644
--- a/csc/vle_urls.py
+++ b/csc/vle_urls.py
@@ -2,11 +2,56 @@
from csc.vle_views import CSCLogin
from .views import *
from django.urls import path
-from .vle_views import vle_dashboard
+from django.conf.urls import url
+from .vle_views import *
+from .ajax import ajax_mark_attendance
app_name = 'csc'
urlpatterns = [
- path('login/', CSCLogin.as_view(), name="login"),
- path('vle/', vle_dashboard, name="vle_dashboard")
+ path('login/', CSCLogin.as_view(redirect_authenticated_user=True), name="login"),
+ path('vle/', vle_dashboard, name="vle_dashboard"),
+ path('students/', student_list, name="student_list"),
+ path('student/
/', student_profile, name="student_profile"),
+ path('courses/', courses, name="courses"),
+ path('assign_foss/', assign_foss, name="assign_foss"),
+ path('get_course_stats/', get_course_stats, name="get_course_stats"),
+ path('get_stats/', get_stats, name="get_stats"),
+
+ path('test/', test, name="test"),
+ path('test_assign/', test_assign, name="test_assign"),
+ path('test_list/', test_list, name="test_list"),
+ # path('add_test/', TestCreateView.as_view(), name="add_test"),
+ path('mark_attendance/', mark_attendance, name="mark_attendance"),
+ # path('detail_test/', TestDetailView.as_view(), name="detail_test"),
+ path('detail_test/', TestDetailView.as_view(), name="detail_test"),
+ # path('update_test/', TestUpdateView.as_view(), name="update_test"),
+ path('update_test/', update_test, name="update_test"),
+ path('delete_test/', TestDeleteView.as_view(), name="delete_test"),
+ path('list_test/', TestListView.as_view(), name="list_test"),
+ path('invigilator/', invigilator, name="invigilator"),
+ path('invigilators/', invigilators, name="invigilators"),
+ # path('invigilator_dashboard/', invigilator_dashboard, name="invigilator_dashboard"),
+ path('verify_invigilator_email/', verify_invigilator_email, name="verify_invigilator_email"),
+ path('add_invigilator/', add_invigilator, name="add_invigilator"),
+ path('review_invigilation_request/', review_invigilation_request, name="review_invigilation_request"),
+ path('add_invigilator_to_test/', add_invigilator_to_test, name="add_invigilator_to_test"),
+ # path('delete_invigilator/', delete_invigilator, name="delete_invigilator"),
+
+ path('create_invigilator/', create_invigilator, name="create_invigilator"),
+ path('view_invigilators/', view_invigilators, name="view_invigilators"),
+ path('invigilators//', update_invigilator, name="update_invigilator"),
+ path('invigilators/delete//', InvigilatorDeleteView.as_view(), name="delete_invigilator"),
+
+ path('invigilator_dashboard/', invi_dashboard, name="invi_dashboard"),
+
+ url(
+ r'^get-foss-option/',
+ GetFossOptionView.as_view()
+ ),
+
+ # ajax
+ path('ajax_mark_attendance/', ajax_mark_attendance, name="ajax_mark_attendance"),
+ path('check_vle_email/', check_vle_email, name="check_vle_email")
+
]
\ No newline at end of file
diff --git a/csc/vle_views.py b/csc/vle_views.py
index aa96821..97f097b 100644
--- a/csc/vle_views.py
+++ b/csc/vle_views.py
@@ -1,8 +1,62 @@
-from django.urls import reverse
-from re import template
+from django.conf import settings
+from django.contrib import messages
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.hashers import make_password
+from django.contrib.auth.models import Group
from django.contrib.auth.views import LoginView
-from django.shortcuts import render
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
+from django.db import IntegrityError
+from django.db.models import OuterRef,Exists,Q, Exists, OuterRef
+from django.forms.models import model_to_dict
+from django.http import HttpResponseRedirect, JsonResponse
+from django.shortcuts import render,get_object_or_404, redirect
+from django.urls import reverse,reverse_lazy
+from django.utils.decorators import method_decorator
+from django.views.decorators.csrf import csrf_exempt
+from django.views.generic import View,ListView,DeleteView,DetailView,UpdateView
+from django.views.generic.edit import CreateView
+
+from csc.models import *
+from mdl.models import *
+from spokenlogin.models import *
+
+from .cron import add_vle,add_transaction
+from .decorators import is_vle
+from .models import TEST_OPEN
from .utils import *
+from .vle_forms import *
+
+import logging
+import random
+import requests
+import string
+import hashlib
+
+STUDENT_ENROLLED_FOR_TEST = 0
+ATTENDANCE_MARKED = 1
+
+class JSONResponseMixin(object):
+ """
+ A mixin that can be used to render a JSON response.
+ """
+ def render_to_json_response(self, context, **response_kwargs):
+ """
+ Returns a JSON response, transforming 'context' to make the payload.
+ """
+ return JsonResponse(
+ self.get_data(context),
+ **response_kwargs
+ )
+
+ def get_data(self, context):
+ """
+ Returns an object that will be serialized as JSON by json.dumps().
+ """
+ # Note: This is *EXTREMELY* naive; in reality, you'll need
+ # to do much more complex handling to ensure that arbitrary
+ # objects -- such as Django model instances or querysets
+ # -- can be serialized as JSON.
+ return context
class CSCLogin(LoginView):
template_name = 'csc/login.html'
@@ -10,10 +64,819 @@ class CSCLogin(LoginView):
def get_redirect_url(self):
if is_user_vle(self.request.user): return reverse('csc:vle_dashboard')
+ if is_user_student(self.request.user): return reverse('csc:vle_dashboard')
# ToDo if student ; redirect to student dashboard
-
+@login_required
+@is_vle
def vle_dashboard(request):
context = {}
- return render(request, 'csc/vle.html',context)
+ vle = VLE.objects.filter(user=request.user).first()
+
+ context['vle'] = vle
+ context['user'] = request.user
+ context['upcoming_test_stats'] = get_upcoming_test_stats(vle)
+ context['courses_offered_stats'] = get_courses_offered_stats()
+ context['total_students_enrolled'] = Student.objects.filter(vle_id=vle).count()
+ context['total_tests_completed'] = Test.objects.filter(vle=vle,tdate__lt=datetime.datetime.today().date()).count()
+ context['total_upcoming_tests'] = Test.objects.filter(vle=vle,tdate__gte=datetime.datetime.today().date()).count()
+ context['total_certificates_issued'] = StudentTest.objects.filter(status=4).count() #ToDo check condition
+
+ return render(request, 'csc/vle.html', context)
+
+@login_required
+@is_vle
+def courses(request):
+ context = {}
+ vles = VLE.objects.filter(user=request.user)
+ for vle in vles:
+ individual_foss = {}
+ fosses = FossCategory.objects.filter(available_for_jio=True)
+ indi_students = Student_certificate_course.objects.filter(cert_category__code='INDI', student__in=Student.objects.filter(vle_id=vle.id)).values_list('student_id')
+
+ for item in fosses:
+ students = Student_Foss.objects.filter(csc_foss=item.id, student__in=indi_students).count()
+ individual_foss[item.foss] = {'total_students':students}
+
+ context['individual_foss'] = individual_foss
+ vle = VLE.objects.filter(user=request.user)[0]
+ courses = CertifiateCategories.objects.exclude(code__in=['INDI'])
+ if request.GET.get('course_search'):
+ search_term = request.GET.get('search_term')
+ q_code = Q(certificate_category__code__icontains=search_term)
+ q_title = Q(certificate_category__title__icontains=search_term)
+ q_foss = Q(foss__foss__icontains=search_term)
+ courses = [x.certificate_category for x in CategoryCourses.objects.filter(q_code|q_title|q_foss)]
+ else:
+ courses = CertifiateCategories.objects.exclude(code__in=['INDI'])
+ d = {}
+ for course in courses:
+ fosses = [x['foss__foss'] for x in CategoryCourses.objects.filter(certificate_category_id=course.id).values('foss__foss')]
+ d[course] = fosses
+ print(f"\n\n{d}\n\n")
+ context['courses'] = d
+
+ return render(request,'csc/courses.html',context)
+
+@method_decorator(csrf_exempt, name='dispatch')
+class GetFossOptionView(JSONResponseMixin, View):
+ def dispatch(self, *args, **kwargs):
+ return super(GetFossOptionView, self).dispatch(*args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ programme_type = self.request.POST.get('programme_type')
+ context = {}
+ foss_option = "--------- "
+ if programme_type == 'dca':
+ fosses = FossCategory.objects.filter(csc_dca_programme=True, available_for_jio=True).order_by('foss')
+ else:
+ fosses = FossCategory.objects.filter(csc_dca_programme=False, available_for_jio=True).order_by('foss')
+ for foss in fosses:
+ foss_option += "" + str(foss.foss) + " "
+ context = {
+ 'spoken_foss_option' : foss_option,
+ }
+ return self.render_to_json_response(context)
+
+@login_required
+@is_vle
+def student_list(request):
+ context={}
+ vle = VLE.objects.filter(user=request.user).first()
+ students = []
+ course = request.GET.get('course')
+ foss = request.GET.get('foss')
+ name = request.GET.get('name')
+ # dates = request.GET.get('dates')
+ findividual = FossCategory.objects.filter(available_for_jio=True)
+ context['foss_individual'] = [x for x in findividual]
+ indi_id = CertifiateCategories.objects.get(code="INDI").id
+ indi_course = Student_certificate_course.objects.filter(student_id=OuterRef('id'),cert_category_id=indi_id)
+ s = Student.objects.filter(vle_id=vle.id).annotate(indi=Exists(indi_course))
+ distinct_courses = set()
+ vle_courses = Student_certificate_course.objects.filter(student__in=s)
+ # vle_courses = Student_certificate_course.objects.all()
+ for item in vle_courses:
+ # print(item.cert_category)
+ distinct_courses.add(item.cert_category)
+ context['distinct_courses'] = distinct_courses
+ distinct_foss = set()
+ vle_fosses = Student_Foss.objects.filter(student__in=s)
+ for item in vle_fosses:
+ distinct_foss.add(item.csc_foss)
+ l = list(distinct_foss)
+ l.sort(key=lambda x: x.foss.title())
+ context['distinct_foss'] = l
+ print(s.query)
+ # all_students = Student.objects.filter(vle_id=vle.id)
+ if name!=None:
+ context['search_name'] = name
+ s = s.filter(Q(user__first_name__icontains=name)|Q(user__last_name__icontains=name)|Q(user__email__icontains=name))
+ if course!='0' and course!=None:
+ try:
+ context['search_course'] = CertifiateCategories.objects.get(id=course)
+ except Exception as e:
+ print(e)
+ if CertifiateCategories.objects.get(id=course).code == 'INDI':
+ context['is_indi'] = True
+ try:
+ s = s.filter(id__in=[x.student.id for x in Student_certificate_course.objects.filter(cert_category__id=course)])
+ except Exception as e:
+ print(e)
+ if foss!='0' and foss!=None:
+ try:
+ context['search_foss'] = FossCategory.objects.get(id=foss)
+ except Exception as e:
+ print(e)
+ try:
+ s = s.filter(id__in=[x.student.id for x in Student_Foss.objects.filter(csc_foss=foss)])
+ except Exception as e:
+ print(e)
+ if not (course or foss or name):
+ context['is_indi'] = True
+ for item in s:
+ students.append(item)
+ context['students'] = students
+ context['students'] = s
+
+ return render(request,'csc/students_list.html',context)
+
+
+def student_profile(request,id):
+ context={}
+ student = Student.objects.get(id=id)
+ initial={'fname': student.user.first_name , 'lname': student.user.last_name, 'state' :student.state}
+ form = StudentForm(instance=student,initial=initial)
+ context['form'] = form
+ s_categories = Student_certificate_course.objects.filter(student=student)
+ d = {}
+ for category in s_categories:
+ fosses = [x.csc_foss.foss for x in Student_Foss.objects.filter(student=student,cert_category=category.cert_category)]
+ d[category.cert_category] = fosses
+ context['courses'] = d
+ return render(request,'csc/student_profile.html',context)
+
+@csrf_exempt
+def get_course_stats(request):
+ vles = VLE.objects.filter(user=request.user)
+ stats = {}
+ fosses = []
+ enrollment = []
+ for vle in vles:
+ csc_foss = Vle_csc_foss.objects.filter(vle=vle)
+ for item in csc_foss:
+ foss = item.spoken_foss.foss
+ stats[foss] = Student_Foss.objects.filter(csc_foss=item).count()
+ fosses.append(foss)
+ enrollment.append(Student_Foss.objects.filter(csc_foss=item).count())
+
+ return JsonResponse({'stats':stats,'fosses':fosses,'enrollment':enrollment,'len':len(fosses)})
+
+class TestListView(ListView):
+ model = Test
+ template_name = 'csc/test_list.html'
+ context_object_name = 'tests'
+ paginate_by = 25
+
+ def get_context_data(self, **kwargs):
+ context = super(TestListView, self).get_context_data(**kwargs)
+ vle = VLE.objects.filter(user = self.request.user).first()
+ tests = Test.objects.filter(vle = vle)
+ # tests = Test.objects.filter(vle = vle).annotate(attendance=ExpressionWrapper(F('tdate')==date.today(),output_field=BooleanField()))
+ page = self.request.GET.get('page')
+ paginator = Paginator(tests, self.paginate_by)
+ try:
+ tests = paginator.page(page)
+ except PageNotAnInteger:
+ tests = paginator.page(1)
+ except EmptyPage:
+ tests = paginator.page(paginator.num_pages)
+ context['tests'] = tests
+
+ test_req = [x for x in TestRequest.objects.filter(vle=vle)]
+ context['test_req'] = test_req
+
+ return context
+
+
+@method_decorator(login_required, name='dispatch')
+class TestCreateView(CreateView):
+ model = Test
+ template_name = 'csc/test_form.html'
+ # fields = ['test_name','foss','tdate','ttime','note_student','note_invigilator','publish']
+ fields = ['foss','tdate','ttime','publish']
+ success_url = reverse_lazy('csc:list_test')
+
+ def get_form(self, *args, **kwargs):
+ form = super(TestCreateView, self).get_form(*args, **kwargs)
+ vle = VLE.objects.filter(user=self.request.user).first()
+ vle_foss = Vle_csc_foss.objects.filter(vle=vle)
+ fosses = FossCategory.objects.filter(id__in=[x.spoken_foss.id for x in vle_foss])
+ form.fields['foss'].queryset = fosses
+ form.fields['tdate'].widget = widgets.DateInput(attrs={'type': 'date'})
+ form.fields['ttime'].widget = widgets.DateInput(attrs={'type': 'time'})
+ return form
+
+ def get_form_kwargs(self):
+ kwargs = super(TestCreateView, self).get_form_kwargs()
+ # kwargs['invi'] = self.request.user # pass the 'user' in kwargs
+ return kwargs
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ # user = self.request.user
+ # context["ticket_list"] = user.ticket_set.all()
+ vle = VLE.objects.filter(user=self.request.user).first()
+ context['tests'] = Test.objects.filter(vle=vle)
+ context['invigilators'] = Invigilator.objects.filter(vle=vle)
+ return context
+
+ def form_valid(self, form):
+ vle = VLE.objects.filter(user=self.request.user).first()
+ form.instance.vle = vle
+ print(form)
+
+ messages.success(self.request,"Test added successfully.")
+ return super().form_valid(form)
+
+@method_decorator(login_required, name='dispatch')
+class TestDeleteView(DeleteView):
+ model = Test
+ template_name = 'csc/test_confirm_delete.html'
+ success_url = reverse_lazy('csc:list_test')
+
+ def form_valid(self, form):
+ print('form is valid')
+
+@method_decorator(login_required, name='dispatch')
+class TestDetailView(DetailView):
+ model = Test
+ template_name = 'csc/test_detail.html'
+ context_object_name = 'test'
+
+
+@method_decorator(login_required, name='dispatch')
+class TestUpdateView(UpdateView):
+ model = Test
+ template_name = 'csc/test_update_form.html'
+ context_object_name = 'test'
+ # fields = ('test_name','foss', 'tdate', 'ttime', 'note_student', 'note_invigilator', 'publish' )
+ fields = ('foss', 'tdate', 'ttime', 'publish' )
+
+
+ def get_success_url(self):
+ messages.success(self.request,"Test updated successfully.")
+ return reverse_lazy('csc:detail_test', kwargs={'pk': self.object.id})
+
+ def get_form(self, *args, **kwargs):
+ form = super(TestUpdateView, self).get_form(*args, **kwargs)
+ vle = VLE.objects.filter(user=self.request.user).first()
+ vle_foss = Vle_csc_foss.objects.filter(vle=vle)
+ fosses = FossCategory.objects.filter(id__in=[x.spoken_foss.id for x in vle_foss])
+ form.fields['foss'].queryset = fosses
+ form.fields['tdate'].widget = widgets.DateInput(attrs={'type': 'date'})
+ form.fields['ttime'].widget = widgets.DateInput(attrs={'type': 'time'})
+ t = str(self.get_object().ttime).split(' ')[0]
+ form.fields['ttime'].initial = t
+ return form
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ vle = VLE.objects.filter(user=self.request.user).first()
+ id = vle.id
+ vle = VLE.objects.filter(user=self.request.user).first()
+ invigilationRequestForm = InvigilationRequestForm(vle_id=id,test_id=self.get_object().id)
+ context['invigilationRequestForm'] = invigilationRequestForm
+ t = str(self.get_object().ttime).split(' ')[0]
+ context['t']=t
+ test = self.get_object()
+ students = [x.student for x in StudentTest.objects.filter(test = test)]
+ context['students'] = students
+ return context
+
+def invigilators(request):
+ context = {}
+ form = InvigilatorForm()
+ if request.method == 'POST':
+ form = InvigilatorForm(request.POST)
+ if form.is_valid():
+ email = form.cleaned_data['email']
+ fname = form.cleaned_data['fname']
+ lname = form.cleaned_data['lname']
+ phone = form.cleaned_data['phone']
+ try:
+ user = User.objects.create(
+ username=email,first_name=fname,last_name=lname,
+ email=email,is_staff=0,is_active=1
+ )
+ password = ''.join([ random.choice(string.ascii_letters+string.digits) for x in range(8)])
+ enc_password = make_password(password)
+ user.password = enc_password
+ user.save()
+ invigilator=Invigilator.objects.create(user=user,phone=phone,added_by=request.user)
+ group = Group.objects.get(name='INVIGILATOR')
+ group.user_set.add(user)
+ messages.success(request,"Invigilator added successfully.")
+ except Exception as e:
+ print(f"Exception while creating invigilator user: {e}")
+ messages.error(request,"Error occurred while adding invigilator.")
+
+ return render(request, 'invigilators.html', {'form': form})
+
+ context['form'] = form
+ vle = VLE.objects.filter(user=request.user).first()
+ context['invigilators'] = Invigilator.objects.filter(vle=vle)
+ return render(request, 'csc/invigilators.html',context)
+
+@csrf_exempt
+def verify_invigilator_email(request):
+ USER_AND_INVIGIlATOR = 1
+ USER_NOT_INVIGIlATOR = 2
+ NOT_USER = 0
+ MULTIPLE_USER = 3
+ USER_AND_OWN_INVIGIlATOR = 4
+ data = {}
+ data['status'] = 0
+ email = request.GET.get('email','')
+ if email:
+ try:
+ user = User.objects.get(email=email)
+ is_invigilator = Invigilator.objects.filter(user=user)
+ invigilator_role = is_user_invigilator(user)
+ if (is_invigilator or invigilator_role):
+ vle = VLE.objects.filter(user=request.user)[0]
+ invigilator = is_invigilator[0]
+ if(vle in invigilator.vle.all()):
+ data['status'] = USER_AND_OWN_INVIGIlATOR
+ data['fname'] = user.first_name
+ data['lname'] = user.last_name
+ data['phone'] = is_invigilator[0].phone
+ else:
+ data['status'] = USER_AND_INVIGIlATOR
+ data['fname'] = user.first_name
+ data['lname'] = user.last_name
+ data['phone'] = is_invigilator[0].phone
+
+ return JsonResponse(data)
+ else:
+ data['status'] = USER_NOT_INVIGIlATOR
+ data['fname'] = user.first_name
+ data['lname'] = user.last_name
+ # data['fname'] = user.first_name
+ except User.MultipleObjectsReturned as e:
+ print(f"User.MultipleObjectsReturne : {e}")
+ data['status'] = MULTIPLE_USER
+ except User.DoesNotExist:
+ data['status'] = NOT_USER
+ return JsonResponse(data)
+
+# @csrf_exempt
+def add_invigilator(request):
+ vle = VLE.objects.filter(user=request.user).first()
+ try:
+ invigilator_email = request.POST.get('invigilator_email')
+ invigilator_exist = request.POST.get('flag',False)
+
+ invigilator_user = User.objects.get(email=invigilator_email)
+ if invigilator_exist:
+ invigilator = Invigilator.objects.get(user=invigilator_user)
+ else:
+ invigilator = Invigilator.objects.create(user=invigilator_user,phone=request.POST.get('phone'),added_by=request.user)
+
+ invigilator.vle.add(vle)
+ messages.success(request,"Invigilator successfullly assigned to CSC.")
+ except Exception as e:
+ print(f"Exception : {e}")
+ messages.error(request,"An error occcured while adding invigilator.")
+
+ data = {}
+ return JsonResponse(data)
+
+
+def invigilator_dashboard(request):
+ context = {}
+ invigilator = Invigilator.objects.get(user=request.user)
+ pending = InvigilationRequest.objects.filter(invigilator=invigilator,status=0)
+ accepted = InvigilationRequest.objects.filter(invigilator=invigilator,status=1)
+ rejected = InvigilationRequest.objects.filter(invigilator=invigilator,status=2)
+ all = InvigilationRequest.objects.filter(invigilator=invigilator)
+ context['pending'] = pending
+ context['accepted'] = accepted
+ context['rejected'] = rejected
+ context['all'] = all
+ return render(request,'csc/invigilator_dashboard.html',context)
+
+def add_invigilator_to_test(request):
+ data = {}
+ context = {}
+ test = request.POST.get('test_id')
+ test_id = request.POST.get('test',test)
+ invigilators = request.POST.getlist('invigilators')
+ test = Test.objects.get(id=int(test_id))
+ for invigilator in invigilators:
+ InvigilationRequest.objects.create(invigilator_id=int(invigilator),test=test,status=0)
+
+ return HttpResponseRedirect(reverse('csc:update_test',kwargs={'pk':test_id}) )
+
+def review_invigilation_request(request):
+ status = request.GET.get("review")
+ id = request.GET.get("item")
+ obj = InvigilationRequest.objects.get(id=id)
+ obj.status = int(status)
+ obj.save()
+ print(f'status : {request.GET.get("review")}')
+ return HttpResponseRedirect(reverse('csc:invigilator_dashboard') )
+
+def get_stats(request):
+ data = {}
+ data['upcoming_tests'] = get_upcoming_test_stats()
+ data['course_type_offered'] = get_courses_offered_stats()
+ data['course_count_result'] = get_programme_stats()
+ return JsonResponse(data)
+
+def mark_attendance(request,id):
+ context = {}
+
+ test = Test.objects.get(id=id)
+ # st = [x.student for x in StudentTest.objects.filter(test=test)]
+ print("\n\n")
+ print(f"1********************************")
+ st = [(x.student,x.status) for x in CSCTestAtttendance.objects.filter(test=test)]
+ print(f"2********************************")
+ context['test'] = test
+ context['students'] = st
+ total_enrolled = len(st)
+ attending = StudentTest.objects.filter(test_status=1).count()
+ pending = total_enrolled - attending
+ context['total_enrolled'] = total_enrolled
+ context['attending'] = attending
+ context['pending'] = pending
+
+ if request.method == 'POST':
+ print(f"3********************************")
+ student_attendance = request.POST.getlist('student_attendance')
+ print(f"student_attendance\n\n ************************ {student_attendance}")
+ #present
+ CSCTestAtttendance.objects.filter(test=test,student_id__in=student_attendance).update(status=TEST_ATTENDANCE_MARKED)
+ #absent
+ CSCTestAtttendance.objects.exclude(test=test,student_id__in=student_attendance).update(status=0)
+ st = [(x.student,x.status) for x in CSCTestAtttendance.objects.filter(test=test)]
+ context['students'] = st
+
+ total_enrolled = CSCTestAtttendance.objects.filter(test=test).count()
+ pending = CSCTestAtttendance.objects.filter(test=test,status=STUDENT_ENROLLED_FOR_TEST).count()
+ attendance_marked = total_enrolled - pending
+ context['total_enrolled'] = total_enrolled
+ context['attending'] = attendance_marked
+ context['pending'] = pending
+ return render(request,'csc/mark_attendance.html',context)
+
+@csrf_exempt
+def check_vle_email(request):
+ data = {}
+ email = request.POST.get('email','')
+ print(f"email - :{email}")
+ # Code to be used later when csc adds date payload in api
+ # last_update_date = VLE.objects.order_by('user__date_joined').last().user.date_joined
+ # delta_date = last_update_date - timedelta(2)
+ # payload = {'date':delta_date}
+ url = getattr(settings, "URL_FETCH_VLE", "http://exam.cscacademy.org/shareiitbombayspokentutorial")
+ # response = requests.get(url,params=payload)
+ response = requests.get(url)
+ data['status'] = 0
+ if response.status_code == 200:
+ result = response.json()['req_data']
+ # result = response.json()
+ # result = result['req_data']
+ for item in result:
+ if email == item['email']:
+ logging.debug("Found email matching csc.")
+ try:
+ csc = CSC.objects.get(csc_id=item['csc_id'])
+ except CSC.DoesNotExist:
+ print(f"**csc - {item['csc_id']} does not exists")
+ CSC.objects.create(
+ csc_id=item.get('csc_id'),institute=item.get('institute_name',''),state=item.get('state',''),
+ city=item.get('city',''),district=item.get('district',''),block=item.get('block',''),
+ address=item.get('address',''),pincode=item.get('pincode',''),plan=item.get('plan',''),
+ activation_status=1
+ )
+ csc = CSC.objects.get(csc_id=item.get('csc_id'))
+ add_vle(item,csc)
+ vle = VLE.objects.get(user__email=email)
+ add_transaction(vle,csc,item['transcdate'])
+ # messages.add_message(request,messages.INFO,'hello')
+ data['status'] = 1
+ else:
+ data['status'] = 2
+
+ return JsonResponse(data)
+
+
+
+# Test related views start
+def test(request):
+ context = {}
+ form = TestForm(user=request.user)
+ if request.method == 'POST':
+ form = TestForm(request.POST,user=request.user)
+ if form.is_valid():
+ test_data=form.save(commit=False)
+ vle = VLE.objects.filter(user=request.user)[0]
+ test_data.vle = vle
+ test_data.save()
+ form.save_m2m()
+ # messages.success(request,"Test added successfully.")
+ messages.add_message(request,messages.SUCCESS,f'Test added successfully.')
+ context['form'] = form
+
+ return render(request,'csc/test.html',context)
+
+def test_assign(request):
+ context = {}
+ vle = VLE.objects.get(user=request.user)
+ students = [x['id'] for x in Student.objects.filter(vle_id=vle.id).values('id')]
+ valid_foss_for_tests = [x['csc_foss'] for x in Student_Foss.objects.filter(student_id__in=students).values('csc_foss').distinct()]
+ tests = Test.objects.filter(vle=vle,status=TEST_OPEN,foss_id__in=valid_foss_for_tests).order_by('-tdate','foss__foss')
+ context['tests'] = tests
+ test = request.POST.get('test')
+ if test and test!='0':
+ context['test'] = test
+ try:
+ test = tests.get(id=int(test))
+ context['test'] = test.id
+ foss = test.foss
+ context['students'] = get_valid_students_for_test(vle,test)
+ except Exception as e:
+ print(e)
+ else:
+ return render(request,'csc/test_assign.html',context)
+
+ if request.method == 'POST' and request.POST.get('action_type') == 'add_students':
+ assigned_students = request.POST.getlist('students')
+ try:
+ fossMdlCourse = CSCFossMdlCourses.objects.filter(foss=foss)[0] #ToDo Change
+ mdlcourse_id = fossMdlCourse.mdlcourse_id
+ mdlquiz_id = fossMdlCourse.mdlquiz_id
+ for email in assigned_students:
+ user = User.objects.get(Q(username=email) | Q(email=email))
+ student = Student.objects.get(user=user)
+ try:
+ mdluser=MdlUser.objects.get(email=email)
+ except MdlUser.DoesNotExist:
+ pwd = ''.join(random.choices(string.ascii_letters,k=10))
+ encryp_pwd = hashlib.md5((pwd).encode('utf-8')).hexdigest()
+ mdluser = MdlUser.objects.create(username=email,firstname=user.first_name,lastname=user.last_name,email=email,password=encryp_pwd,mnethostid=1,confirmed=1)
+ send_mdl_mail(user,pwd)
+ mdluser = MdlUser.objects.get(email=email)
+ except MdlUser.MultipleObjectsReturned as e:
+ mdluser=MdlUser.objects.filter(email=email)[0]
+ print(e)
+ try:
+ ta = CSCTestAtttendance.objects.create(test=test,student=student,mdluser_id=mdluser.id,mdlcourse_id=mdlcourse_id,status=0,mdlquiz_id=mdlquiz_id)
+ except IntegrityError as e:
+ print(e)
+ if request.POST.get('action_type') == 'add_students':
+ nta = CSCTestAtttendance.objects.filter(test=test).exclude(student__user__email__in=assigned_students)
+ for item in nta:
+ item.delete()
+ else:
+ print(f"\n\n action type is NOT add_students **************************** ")
+ messages.add_message(request,messages.SUCCESS,f'Test assigned to the students.')
+ except Exception as e:
+ print(e)
+ messages.error(request,"No test in moodle for the selected foss.Please contact support.")
+ return render(request,'csc/test_assign.html',context)
+ return render(request,'csc/test_assign.html',context)
+
+
+def test_list(request):
+ context = {}
+ vle = VLE.objects.get(user=request.user)
+ tests = Test.objects.filter(vle=vle)
+ context['tests'] = tests
+ return render(request,'csc/test_list.html',context)
+
+def update_test(request,pk):
+ context = {}
+ if request.method == 'GET':
+ test = Test.objects.filter(id=pk)[0]
+ d = model_to_dict(test)
+ td = model_to_dict(test)
+ td['foss'] = test.foss.id
+ form = TestForm(initial=td,user=request.user)
+ # form = TestForm(initial=model_to_dict(test),user=request.user)
+ if request.method == 'POST':
+ t = Test.objects.get(id=pk)
+ form = TestForm(request.POST,instance=t,user=request.user)
+ if form.is_valid():
+ form.save()
+ messages.add_message(request,messages.SUCCESS,f'Test updated.')
+
+ context['form'] = form
+
+ return render(request,'csc/update_test.html',context)
+
+def invigilator(request):
+ context = {}
+ vle = VLE.objects.get(user=request.user)
+ invigilators = Invigilator.objects.filter(vle=vle)
+ context['invigilators']=invigilators
+ form_empty = InvigilatorForm()
+ context['form_empty']=form_empty
+
+ form = InvigilatorForm()
+ add = request.POST.get('add')
+ edit = request.POST.get('edit')
+
+ if request.method == 'GET':
+ if request.GET.get('invi'):
+ invi = request.GET.get('invi')
+ invi_obj = Invigilator.objects.get(id=invi)
+ i = model_to_dict(invi_obj.user)
+ i['phone'] = invi_obj.phone
+ form = InvigilatorForm(initial=i)
+ context['invi_id']=invi_obj.id
+ if request.method == 'POST':
+ if add:
+ form = InvigilatorForm(request.POST)
+ if form.is_valid():
+ email = form.cleaned_data['email']
+ phone=form.cleaned_data['phone']
+ check_user = User.objects.filter(Q(email=email) | Q(username=email))
+ if check_user:
+ check_user_invi = Invigilator.objects.filter(user=check_user[0],vle=vle)
+ if check_user_invi:
+ messages.add_message(request,messages.ERROR,f'Invigilator with email {email} already exists in your account.No changes have been made.')
+ return render(request,'csc/invigilator.html',context)
+ else:
+ invi=Invigilator.objects.create(user=check_user[0],phone=phone,vle=vle)
+ messages.add_message(request,messages.INFO,f'User with email {email} already exists in the system & has been assigned to you as an invigilator.')
+ return render(request,'csc/invigilator.html',context)
+
+ u=form.save(commit=False)
+ u.username = email
+ u.save()
+ invi=Invigilator.objects.create(user=u,phone=phone,vle=vle)
+ messages.add_message(request,messages.SUCCESS,f'Invigilator with email {email} is added.')
+ if edit:
+ invi = request.POST.get('invi_id')
+ invi_obj = Invigilator.objects.get(id=invi)
+ context['invi_id']=invi_obj.id
+ i = model_to_dict(invi_obj.user)
+ i['phone'] = invi_obj.phone
+ form = InvigilatorForm(request.POST,instance=invi_obj.user)
+ if form.is_valid():
+ u=form.save(commit=False)
+ # u.username = form.cleaned_data['email']
+ u.save()
+ invi_obj.phone=form.cleaned_data['phone']
+ invi_obj.save()
+ messages.add_message(request,messages.SUCCESS,f'Invigilator details updated successfully.')
+ else:
+ print(f"\n\n 11 errors*********************\n{form.errors}")
+ context['form'] = form
+ context['invigilator'] = form
+
+
+
+
+ return render(request,'csc/invigilator.html',context)
+
+
+def create_invigilatordelete_invigilator(request):
+ data = {}
+ try:
+
+ invi=request.GET.get("invi")
+ i=Invigilator.objects.get(id=invi)
+ email = i.user.email
+ i.delete()
+ messages.add_message(request,messages.SUCCESS,f'Invigilator {email} deleted successfully.')
+ data['status'] = 1
+ except:
+ data['status'] = 0
+ # return redirect('/csc/invigilator')
+ return JsonResponse(data)
+
+# invigilator views
+
+def create_invigilator(request):
+ context ={}
+ send_mail = False
+ vle = VLE.objects.get(user=request.user)
+ form = InvigilatorForm(request.POST or None)
+ context['form'] = form
+ if form.is_valid():
+ email = form.cleaned_data['email']
+ first_name = form.cleaned_data['first_name']
+ last_name = form.cleaned_data['last_name']
+ phone = form.cleaned_data['phone']
+ try:
+ user = User.objects.get(email=email)
+ messages.add_message(request,messages.SUCCESS,f'User with this email {email} already exists & assigned to you as an Invigilator.')
+ except User.DoesNotExist:
+ user = User.objects.create(username=email,email=email,first_name=first_name,last_name=last_name)
+ send_mail = True
+ messages.add_message(request,messages.SUCCESS,f'User email {email} is assigned to you as an Invigilator.')
+ except User.MultipleObjectsReturned:
+ user = User.objects.filter(email=email)[0]
+ messages.add_message(request,messages.SUCCESS,f'User with this email {email} already exists & assigned to you as an Invigilator.')
+ try:
+ # Invigilator.objects.create(user=user,vle=vle,phone=phone)
+ i=Invigilator.objects.create(user=user,phone=phone)
+ print(f"invi ************************************ {i}")
+ i.vle.add(vle)
+ invi_group = Group.objects.get(name='INVIGILATOR')
+ invi_group.user_set.add(user)
+ if send_mail:
+ send_pwd_mail_to_invi(user)
+ except IntegrityError:
+ messages.add_message(request,messages.ERROR,f'User with this email {email} has already been assigned to you as an Invigilator.')
+ except Exception as e:
+ print(e)
+ return render(request,'csc/create_invigilator.html',context)
+
+def view_invigilators(request):
+ context = {}
+ vle = VLE.objects.get(user=request.user)
+ invigilators = Invigilator.objects.filter(vle=vle)
+ context['invigilators'] = invigilators
+ return render(request,'csc/list_invigilators.html',context)
+
+def update_invigilator(request,id):
+ context = {}
+ invi = get_object_or_404(Invigilator, id = id)
+ # form = InvigilatorForm(request.POST or None)
+ if request.method == 'GET':
+ data = {'phone': invi.phone, 'first_name': invi.user.first_name,'last_name': invi.user.last_name,'email': invi.user.email}
+ form = InvigilatorForm(initial=data)
+ form.fields['email'].disabled = True
+ context['form'] = form
+ else:
+
+
+ form = InvigilatorForm(request.POST)
+ if form.is_valid():
+ phone = form.cleaned_data['phone']
+ first_name = form.cleaned_data['first_name']
+ last_name = form.cleaned_data['last_name']
+ invi.phone = phone
+ invi.save()
+ user = invi.user
+ user.first_name = first_name
+ user.last_name = last_name
+ user.save()
+ else:
+ print(f"4 error ****************************** {form.non_field_errors}")
+ data = {'phone': invi.phone, 'first_name': invi.user.first_name,'last_name': invi.user.last_name,'email': invi.user.email}
+ form = InvigilatorForm(initial=data)
+ form.fields['email'].disabled = True
+ context['form'] = form
+ return render(request,'csc/update_invigilator.html',context)
+
+def delete_invigilator(request, id):
+ context ={}
+ obj = get_object_or_404(Invigilator, id = id)
+ if request.method =="POST":
+ obj.delete()
+ return redirect('csc:view_invigilators')
+ return render(request, "delete_view.html", context)
+
+class InvigilatorDeleteView(DeleteView):
+ model = Invigilator
+ success_url ="/"
+ template_name = "csc/invigilator_confirm_delete.html"
+
+def invi_dashboard(request):
+ context = {}
+ invi = Invigilator.objects.get(user=request.user)
+ upcoming_tests = Test.objects.filter(invigilator=invi,tdate__gte=datetime.datetime.today())
+ completed_tests = Test.objects.filter(invigilator=invi,tdate__lt=datetime.datetime.today())
+ context['upcoming_tests'] = upcoming_tests
+ context['completed_tests'] = completed_tests
+ return render(request,'csc/invigilator_dashboard.html',context)
+
+
+# ajax functions
+@csrf_exempt
+def assign_foss(request):
+ vle = VLE.objects.get(user=request.user)
+ students = request.POST.getlist('student[]')
+ fosses = request.POST.getlist('foss[]')
+ f = FossCategory.objects.filter(foss__in=[x for x in fosses]).values_list('foss')
+ foss_name = ', '.join([x[0] for x in f])
+ for student in students:
+ #check if student has individual
+ for foss in fosses:
+ try:
+ f = FossCategory.objects.get(foss=foss)
+ s = Student.objects.get(id=int(student))
+ c = CertifiateCategories.objects.get(code='INDI')
+ scc = Student_certificate_course.objects.get(student=s,cert_category=c)
+ Student_Foss.objects.create(student=s,csc_foss=f,cert_category=scc.cert_category,foss_start_date=datetime.date.today())
+ except Exception as e:
+ print(e)
+
+ return JsonResponse({'foss':foss_name,'student_count':len(students)})
\ No newline at end of file
diff --git a/logs/utils.py b/logs/utils.py
index 345a9f7..9457a7f 100644
--- a/logs/utils.py
+++ b/logs/utils.py
@@ -24,7 +24,7 @@ def get_payload(instance):
}
},
"object": {
- "id": "https://spoken-tutorial.in/spoken/tutorial-search/?search_foss={}&search_language={}".format(instance.foss, instance.language),
+ "id": "https://beta.spoken-tutorial.in/spoken/tutorial-search/?search_foss={}&search_language={}".format(instance.foss, instance.language),
"definition": {
"name": {
"en-US": "Spoken-Tutorial-"+instance.foss.replace(' ', '-')+"-"+instance.language
diff --git a/logs/views.py b/logs/views.py
index d6eed1b..c08399d 100644
--- a/logs/views.py
+++ b/logs/views.py
@@ -3,8 +3,9 @@
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
+from drf_yasg.utils import swagger_auto_schema
from .models import TutorialProgress
-
+@swagger_auto_schema(methods=['post'], auto_schema=None)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def save_tutorial_progress(request):
@@ -15,9 +16,11 @@ def save_tutorial_progress(request):
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+@swagger_auto_schema(methods=['get','post'], auto_schema=None)
@api_view(['GET','POST'])
@permission_classes([IsAuthenticated])
def get_set_tutorial_progress(request):
+
try:
tp=TutorialProgress.objects.get(
user=request.user,
diff --git a/mdl/__init__.py b/mdl/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mdl/admin.py b/mdl/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/mdl/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/mdl/apps.py b/mdl/apps.py
new file mode 100644
index 0000000..8cc5932
--- /dev/null
+++ b/mdl/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class MdlConfig(AppConfig):
+ name = 'mdl'
diff --git a/mdl/migrations/0001_initial.py b/mdl/migrations/0001_initial.py
new file mode 100644
index 0000000..08fe410
--- /dev/null
+++ b/mdl/migrations/0001_initial.py
@@ -0,0 +1,68 @@
+# Generated by Django 3.0.3 on 2022-10-31 07:16
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='MdlQuizAttempts',
+ fields=[
+ ('id', models.BigIntegerField(primary_key=True, serialize=False)),
+ ('quiz', models.BigIntegerField()),
+ ('userid', models.BigIntegerField()),
+ ('attempt', models.IntegerField(unique=True)),
+ ('uniqueid', models.BigIntegerField(unique=True)),
+ ('layout', models.TextField()),
+ ('currentpage', models.BigIntegerField()),
+ ('preview', models.IntegerField()),
+ ('state', models.CharField(max_length=48)),
+ ('timestart', models.BigIntegerField()),
+ ('timefinish', models.BigIntegerField()),
+ ('timemodified', models.BigIntegerField()),
+ ('timemodifiedoffline', models.BigIntegerField()),
+ ('timecheckstate', models.BigIntegerField(blank=True, null=True)),
+ ('sumgrades', models.DecimalField(blank=True, decimal_places=5, max_digits=12, null=True)),
+ ],
+ options={
+ 'db_table': 'mdl_quiz_attempts',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='MdlQuizGrades',
+ fields=[
+ ('id', models.BigIntegerField(primary_key=True, serialize=False)),
+ ('quiz', models.BigIntegerField()),
+ ('userid', models.BigIntegerField()),
+ ('grade', models.DecimalField(decimal_places=5, max_digits=12)),
+ ('timemodified', models.BigIntegerField()),
+ ],
+ options={
+ 'db_table': 'mdl_quiz_grades',
+ 'managed': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='MdlUser',
+ fields=[
+ ('id', models.BigIntegerField(primary_key=True, serialize=False)),
+ ('username', models.CharField(max_length=255, unique=True)),
+ ('password', models.CharField(max_length=96)),
+ ('idnumber', models.CharField(max_length=765)),
+ ('firstname', models.CharField(max_length=300)),
+ ('lastname', models.CharField(max_length=300)),
+ ('email', models.CharField(max_length=300)),
+ ],
+ options={
+ 'db_table': 'mdl_user',
+ 'managed': False,
+ },
+ ),
+ ]
diff --git a/mdl/migrations/__init__.py b/mdl/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mdl/models.py b/mdl/models.py
new file mode 100644
index 0000000..2f28b5e
--- /dev/null
+++ b/mdl/models.py
@@ -0,0 +1,50 @@
+from django.db import models
+
+# Create your models here.
+class MdlUser(models.Model):
+ id = models.BigIntegerField(primary_key=True)
+ username = models.CharField(unique=True, max_length=255)
+ password = models.CharField(max_length=96)
+ idnumber = models.CharField(max_length=765)
+ firstname = models.CharField(max_length=300)
+ lastname = models.CharField(max_length=300)
+ email = models.CharField(max_length=300)
+ confirmed = models.IntegerField()
+ mnethostid = models.IntegerField()
+ class Meta(object):
+ db_table = 'mdl_user'
+ managed = False
+
+class MdlQuizGrades(models.Model):
+ id = models.BigIntegerField(primary_key=True)
+ quiz = models.BigIntegerField()
+ userid = models.BigIntegerField()
+ grade = models.DecimalField(max_digits=12, decimal_places=5)
+ timemodified = models.BigIntegerField()
+ class Meta(object):
+ db_table = 'mdl_quiz_grades'
+ managed = False
+
+class MdlQuizAttempts(models.Model):
+ id = models.BigIntegerField(primary_key=True)
+ quiz = models.BigIntegerField()
+ userid = models.BigIntegerField()
+ attempt = models.IntegerField(unique=True)
+ uniqueid = models.BigIntegerField(unique=True)
+ layout = models.TextField()
+ currentpage = models.BigIntegerField()
+ preview = models.IntegerField()
+ state = models.CharField(max_length=48)
+ timestart = models.BigIntegerField()
+ timefinish = models.BigIntegerField()
+ timemodified = models.BigIntegerField()
+ timemodifiedoffline = models.BigIntegerField()
+ timecheckstate = models.BigIntegerField(null=True, blank=True)
+ sumgrades = models.DecimalField(null=True, max_digits=12, decimal_places=5, blank=True)
+ class Meta(object):
+ db_table = 'mdl_quiz_attempts'
+ managed = False
+
+
+
+
\ No newline at end of file
diff --git a/mdl/tests.py b/mdl/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/mdl/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/mdl/views.py b/mdl/views.py
new file mode 100644
index 0000000..91ea44a
--- /dev/null
+++ b/mdl/views.py
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.
diff --git a/requirements.txt b/requirements.txt
index c593f72..1f692dd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,9 +2,48 @@ Django==3.0.3
mysqlclient==1.4.6
python-dotenv==0.11.0
django-crispy-forms==1.8.1
-djangorestframework==3.11.0
-django-model-utils==4.0.0
+asgiref==3.5.2
+certifi==2022.9.14
+chardet==3.0.4
+coreapi==2.3.3
+coreschema==0.0.4
+defusedxml==0.7.1
+diff-match-patch==20200713
+django-crontab==0.7.1
django-import-export==2.0.2
+django-model-utils==4.0.0
django-mysql==3.4.0
+django-rest-swagger==2.2.0
+djangorestframework==3.11.0
+drf-yasg==1.21.3
+et-xmlfile==1.1.0
+idna==2.10
+inflection==0.5.1
+isodate==0.6.1
+itypes==1.2.0
+Jinja2==3.1.2
+lxml==4.9.1
+MarkupPy==1.14
+MarkupSafe==2.1.1
+odfpy==1.4.1
+openapi-codec==1.3.2
+openpyxl==3.0.10
+packaging==21.3
+pyparsing==3.0.9
python3-saml==1.9.0
-requests==2.24.0
\ No newline at end of file
+pytz==2022.2.1
+PyYAML==6.0
+requests==2.24.0
+ruamel.yaml==0.17.21
+ruamel.yaml.clib==0.2.6
+simplejson==3.17.6
+six==1.16.0
+sqlparse==0.4.2
+tablib==3.2.1
+uritemplate==4.1.1
+urllib3==1.25.11
+xlrd==2.0.1
+xlwt==1.3.0
+xmlsec==1.3.13
+django-widget-tweaks==1.4.3
+
diff --git a/spoken/api.py b/spoken/api.py
index b82d1ca..e70f94b 100644
--- a/spoken/api.py
+++ b/spoken/api.py
@@ -1,10 +1,12 @@
+from django.urls import reverse
from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from .utils import *
from logs.models import TutorialProgress,CourseProgress
-
+from csc.utils import is_user_vle,is_user_student
class TutorialSearchAPI(APIView):
+ swagger_schema = None
"""
Search tutorial based on get url parameters.
Parameters: (search_foss, search_language)
@@ -45,6 +47,10 @@ def get(self, request, format=None):
except:
context["time_completed"] = 0
context["video_status"] = False
+ if is_user_vle(request.user):
+ context["is_csc_vle"] = is_user_vle(request.user)
+ if is_user_student(request.user):
+ context["is_csc_student"] = is_user_student(request.user)
context["foss_lang_list"] = get_all_foss_lang()
return Response(context)
\ No newline at end of file
diff --git a/spoken/templates/spoken/base.html b/spoken/templates/spoken/base.html
index ed5d1ea..47c8b02 100644
--- a/spoken/templates/spoken/base.html
+++ b/spoken/templates/spoken/base.html
@@ -1,4 +1,5 @@
{% load static%}
+{% load helper %}
{% load crispy_forms_tags %}
@@ -6,19 +7,16 @@
-
-
+
-
-
{% block title%}Spoken Tutorial{% endblock title%}
{% block css %}{% endblock css %}
-
+
Applications of Spoken Tutorials
@@ -51,16 +49,61 @@
+
{{user.username}}
{% else %}
- Login
+ LOGIN
+ {% comment %} Other - Login {% endcomment %}
+
+
{% endif %}
@@ -73,6 +116,7 @@
+
diff --git a/spoken/templates/spoken/home.html b/spoken/templates/spoken/home.html
index 875b92e..1de840d 100644
--- a/spoken/templates/spoken/home.html
+++ b/spoken/templates/spoken/home.html
@@ -358,7 +358,7 @@
Contact
+ 91 22 25764229
contact@spoken-tutorial.org
-