diff options
Diffstat (limited to 'yaksh/models.py')
-rw-r--r-- | yaksh/models.py | 197 |
1 files changed, 185 insertions, 12 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index 1b76eed..0c5a6f5 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,14 +9,6 @@ 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 try: @@ -31,14 +24,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 = ( @@ -281,11 +289,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 +1350,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 +2765,164 @@ 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 + + +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) |