summaryrefslogtreecommitdiff
path: root/yaksh/models.py
diff options
context:
space:
mode:
authorankitjavalkar2020-10-07 12:04:12 +0530
committerGitHub2020-10-07 12:04:12 +0530
commit0e04d98e66b53763926af8027cbd275d890bfab4 (patch)
treee225826a16daf9a9485607457d5d67b568753553 /yaksh/models.py
parented81054c05ff297ea241ce08664e0d2323da115c (diff)
parentc6c57869fe653d2ea0502017a9fb15f2f745491b (diff)
downloadonline_test-0e04d98e66b53763926af8027cbd275d890bfab4.tar.gz
online_test-0e04d98e66b53763926af8027cbd275d890bfab4.tar.bz2
online_test-0e04d98e66b53763926af8027cbd275d890bfab4.zip
Merge pull request #774 from adityacp/upload_toc
Add table of contents for the lessons
Diffstat (limited to 'yaksh/models.py')
-rw-r--r--yaksh/models.py269
1 files changed, 256 insertions, 13 deletions
diff --git a/yaksh/models.py b/yaksh/models.py
index 1b76eed..3fa4a04 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1,3 +1,4 @@
+# Python Imports
from __future__ import unicode_literals, division
from datetime import datetime, timedelta
import uuid
@@ -8,16 +9,9 @@ from ruamel.yaml.scalarstring import PreservedScalarString
from ruamel.yaml.comments import CommentedMap
from random import sample
from collections import Counter, defaultdict
-
-from django.db import models
-from django.contrib.auth.models import User, Group, Permission
-from django.core.exceptions import ValidationError
-from django.contrib.contenttypes.models import ContentType
-from taggit.managers import TaggableManager
-from django.utils import timezone
-from django.core.files import File
import glob
-
+import sys
+import traceback
try:
from StringIO import StringIO as string_io
except ImportError:
@@ -31,14 +25,29 @@ import zipfile
import tempfile
from textwrap import dedent
from ast import literal_eval
-from .file_utils import extract_files, delete_files
+
+# Django Imports
+from django.db import models
+from django.contrib.auth.models import User, Group, Permission
+from django.core.exceptions import ValidationError
+from django.contrib.contenttypes.models import ContentType
+from taggit.managers import TaggableManager
+from django.utils import timezone
+from django.core.files import File
+from django.contrib.contenttypes.fields import (
+ GenericForeignKey, GenericRelation
+)
+from django.contrib.contenttypes.models import ContentType
from django.template import Context, Template
+from django.conf import settings
+from django.forms.models import model_to_dict
+from django.db.models import Count
+# Local Imports
from yaksh.code_server import (
submit, get_result as get_result_from_code_server
)
from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME
-from django.conf import settings
-from django.forms.models import model_to_dict
+from .file_utils import extract_files, delete_files
from grades.models import GradingSystem
languages = (
@@ -248,6 +257,15 @@ def get_image_dir(instance, filename):
))
+def is_valid_time_format(time):
+ try:
+ hh, mm, ss = time.split(":")
+ status = True
+ except ValueError:
+ status = False
+ return status
+
+
###############################################################################
class CourseManager(models.Manager):
@@ -281,11 +299,16 @@ class Lesson(models.Model):
# A video file
video_file = models.FileField(
- upload_to=get_file_dir, default=None,
+ upload_to=get_file_dir, max_length=255, default=None,
null=True, blank=True,
help_text="Please upload video files in mp4, ogv, webm format"
)
+ video_path = models.CharField(
+ max_length=255, default=None, null=True, blank=True,
+ help_text="Youtube id, vimeo id, others"
+ )
+
def __str__(self):
return "{0}".format(self.name)
@@ -1337,6 +1360,8 @@ class Question(models.Model):
# Solution for the question.
solution = models.TextField(blank=True)
+ content = GenericRelation("TableOfContents")
+
tc_code_types = {
"python": [
("standardtestcase", "Standard TestCase"),
@@ -2750,6 +2775,224 @@ class Comment(ForumBase):
self.post_field.title)
+class TOCManager(models.Manager):
+
+ def get_data(self, course_id, lesson_id):
+ contents = TableOfContents.objects.filter(
+ course_id=course_id, lesson_id=lesson_id, content__in=[2, 3, 4]
+ )
+ data = {}
+ for toc in contents:
+ data[toc] = LessonQuizAnswer.objects.filter(
+ toc_id=toc.id).values_list("toc_id").distinct().count()
+ return data
+
+ def get_question_stats(self, toc_id):
+ answers = LessonQuizAnswer.objects.get_queryset().filter(
+ toc_id=toc_id).order_by('id')
+ question = answers.first().toc.content_object
+ answers = answers.values(
+ "student__first_name", "student__last_name", "student__email",
+ "student_id", "toc_id"
+ ).distinct()
+ return question, answers
+
+ def get_answer(self, toc_id, user_id):
+ submission = LessonQuizAnswer.objects.filter(
+ toc_id=toc_id, student_id=user_id).last()
+ question = submission.toc.content_object
+ attempted_answer = submission.answer
+ if question.type == "mcq":
+ submitted_answer = literal_eval(attempted_answer.answer)
+ answers = [
+ tc.options
+ for tc in question.get_test_cases(id=submitted_answer)
+ ]
+ answer = ",".join(answers)
+ elif question.type == "mcc":
+ submitted_answer = literal_eval(attempted_answer.answer)
+ answers = [
+ tc.options
+ for tc in question.get_test_cases(id__in=submitted_answer)
+ ]
+ answer = ",".join(answers)
+ else:
+ answer = attempted_answer.answer
+ return answer, attempted_answer.correct
+
+ def add_contents(self, course_id, lesson_id, user, contents):
+ toc = []
+ messages = []
+ for content in contents:
+ name = content.get('name') or content.get('summary')
+ if "content_type" not in content or "time" not in content:
+ messages.append(
+ (False,
+ f"content_type or time key is missing in {name}")
+ )
+ else:
+ content_type = content.pop('content_type')
+ time = content.pop('time')
+ if not is_valid_time_format(time):
+ messages.append(
+ (False,
+ f"Invalid time format in {name}. "
+ "Format should be 00:00:00")
+ )
+ else:
+ if content_type == 1:
+ topic = Topic.objects.create(**content)
+ toc.append(TableOfContents(
+ course_id=course_id, lesson_id=lesson_id, time=time,
+ content_object=topic, content=content_type
+ ))
+ messages.append((True, f"{topic.name} added successfully"))
+ else:
+ content['user'] = user
+ test_cases = content.pop("testcase")
+ que_type = content.get('type')
+ if "files" in content:
+ content.pop("files")
+ if "tags" in content:
+ content.pop("tags")
+ if (que_type in ['code', 'upload']):
+ messages.append(
+ (False, f"{que_type} question is not allowed. "
+ f"{content.get('summary')} is not added")
+ )
+ else:
+ que = Question.objects.create(**content)
+ for test_case in test_cases:
+ test_case_type = test_case.pop('test_case_type')
+ model_class = get_model_class(test_case_type)
+ model_class.objects.get_or_create(
+ question=que, **test_case, type=test_case_type
+ )
+ toc.append(TableOfContents(
+ course_id=course_id, lesson_id=lesson_id,
+ time=time, content_object=que,
+ content=content_type
+ ))
+ messages.append(
+ (True, f"{que.summary} added successfully")
+ )
+ if toc:
+ TableOfContents.objects.bulk_create(toc)
+ return messages
+
+
+class TableOfContents(models.Model):
+ toc_types = ((1, "Topic"), (2, "Graded Quiz"), (3, "Exercise"), (4, "Poll"))
+ course = models.ForeignKey(Course, on_delete=models.CASCADE,
+ related_name='course')
+ lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE,
+ related_name='contents')
+ time = models.CharField(max_length=100, default=0)
+ content = models.IntegerField(choices=toc_types)
+ content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
+ object_id = models.PositiveIntegerField()
+ content_object = GenericForeignKey()
+
+ objects = TOCManager()
+
+ class Meta:
+ verbose_name_plural = "Table Of Contents"
+
+ def get_toc_text(self):
+ if self.content == 1:
+ content_name = self.content_object.name
+ else:
+ content_name = self.content_object.summary
+ return content_name
+
+ def __str__(self):
+ return f"TOC for {self.lesson.name} with {self.get_content_display()}"
+
+
+class Topic(models.Model):
+ name = models.CharField(max_length=255)
+ description = models.TextField(null=True, blank=True)
+ content = GenericRelation(TableOfContents)
+
+ def __str__(self):
+ return f"{self.name}"
+
+
+class LessonQuizAnswer(models.Model):
+ toc = models.ForeignKey(TableOfContents, on_delete=models.CASCADE)
+ student = models.ForeignKey(User, on_delete=models.CASCADE)
+ answer = models.ForeignKey(Answer, on_delete=models.CASCADE)
+
+ def check_answer(self, user_answer):
+ result = {'success': False, 'error': ['Incorrect answer'],
+ 'weight': 0.0}
+ question = self.toc.content_object
+ if question.type == 'mcq':
+ expected_answer = question.get_test_case(correct=True).id
+ if user_answer.strip() == str(expected_answer).strip():
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'mcc':
+ expected_answers = [
+ str(opt.id) for opt in question.get_test_cases(correct=True)
+ ]
+ if set(user_answer) == set(expected_answers):
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'integer':
+ expected_answers = [
+ int(tc.correct) for tc in question.get_test_cases()
+ ]
+ if int(user_answer) in expected_answers:
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'string':
+ tc_status = []
+ for tc in question.get_test_cases():
+ if tc.string_check == "lower":
+ if tc.correct.lower().splitlines()\
+ == user_answer.lower().splitlines():
+ tc_status.append(True)
+ else:
+ if tc.correct.splitlines()\
+ == user_answer.splitlines():
+ tc_status.append(True)
+ if any(tc_status):
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'float':
+ user_answer = float(user_answer)
+ tc_status = []
+ for tc in question.get_test_cases():
+ if abs(tc.correct - user_answer) <= tc.error_margin:
+ tc_status.append(True)
+ if any(tc_status):
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'arrange':
+ testcase_ids = sorted(
+ [tc.id for tc in question.get_test_cases()]
+ )
+ if user_answer == testcase_ids:
+ result['success'] = True
+ result['error'] = ['Correct answer']
+ self.answer.error = result
+ ans_status = result.get("success")
+ self.answer.correct = ans_status
+ if ans_status:
+ self.answer.marks = self.answer.question.points
+ self.answer.save()
+ return result
+
+ def __str__(self):
+ return f"Lesson answer of {self.toc} by {self.student.get_full_name()}"
+
+
class MicroManager(models.Model):
manager = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='micromanaging', null=True)