summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorprathamesh2017-12-05 19:38:21 +0530
committerprathamesh2017-12-05 19:38:21 +0530
commitd33e622b8790d81c80eea2305ab7a2989c21b156 (patch)
tree9dc3d84df3f955a200f7182ed9d0067e8c138f5f
parent06cc3dc5d3671ca24fc412b6078906a582b83e98 (diff)
parentd2eb978e3ca51914b16655403bd74cc84d4eab28 (diff)
downloadonline_test-d33e622b8790d81c80eea2305ab7a2989c21b156.tar.gz
online_test-d33e622b8790d81c80eea2305ab7a2989c21b156.tar.bz2
online_test-d33e622b8790d81c80eea2305ab7a2989c21b156.zip
Merge branch 'yaksh_video_lessons' of https://github.com/FOSSEE/online_test into yaksh_video_lessons
-rw-r--r--yaksh/evaluator_tests/test_simple_question_types.py6
-rw-r--r--yaksh/live_server_tests/load_test.py4
-rw-r--r--yaksh/live_server_tests/selenium_test.py9
-rw-r--r--yaksh/models.py10
-rw-r--r--yaksh/static/yaksh/css/exam.css2
-rw-r--r--yaksh/static/yaksh/js/requesthandler.js25
-rw-r--r--yaksh/templates/base.html2
-rw-r--r--yaksh/templates/exam.html96
-rw-r--r--yaksh/templates/yaksh/complete.html2
-rw-r--r--yaksh/templates/yaksh/error_template.html91
-rw-r--r--yaksh/templates/yaksh/question.html28
-rw-r--r--yaksh/templates/yaksh/quit.html2
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html4
-rw-r--r--yaksh/templates/yaksh/show_video.html7
-rw-r--r--yaksh/test_views.py888
-rw-r--r--yaksh/urls.py7
-rw-r--r--yaksh/views.py72
17 files changed, 984 insertions, 271 deletions
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index c0b81d6..b86a9d8 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -26,8 +26,7 @@ def setUpModule():
tzinfo=pytz.utc),
duration=30, active=True, attempts_allowed=1,
time_between_attempts=0, description='demo quiz 100',
- pass_criteria=0,language='Python',
- prerequisite=None,course=course,
+ pass_criteria=0,
instructions="Demo Instructions"
)
question_paper = QuestionPaper.objects.create(quiz=quiz,
@@ -38,7 +37,8 @@ def setUpModule():
question_paper=question_paper,
end_time=timezone.now()
+timedelta(minutes=5),
- attempt_number=1
+ attempt_number=1,
+ course=course
)
def tearDownModule():
diff --git a/yaksh/live_server_tests/load_test.py b/yaksh/live_server_tests/load_test.py
index 5ab1cc2..c3eb6e6 100644
--- a/yaksh/live_server_tests/load_test.py
+++ b/yaksh/live_server_tests/load_test.py
@@ -73,5 +73,7 @@ class YakshSeleniumTests(StaticLiveServerTestCase):
def test_load(self):
url = '%s%s' % (self.live_server_url, '/exam/login/')
quiz_name = "Yaksh Demo quiz"
- selenium_test = SeleniumTest(url=url, quiz_name=quiz_name)
+ module_name = "demo module"
+ selenium_test = SeleniumTest(url=url, quiz_name=quiz_name,
+ module_name=module_name)
selenium_test.run_load_test(url=url, username='demo_student', password='demo_student')
diff --git a/yaksh/live_server_tests/selenium_test.py b/yaksh/live_server_tests/selenium_test.py
index 31efcac..160ad02 100644
--- a/yaksh/live_server_tests/selenium_test.py
+++ b/yaksh/live_server_tests/selenium_test.py
@@ -26,9 +26,10 @@ class SeleniumTestError(Exception):
pass
class SeleniumTest():
- def __init__(self, url, quiz_name):
+ def __init__(self, url, quiz_name, module_name):
self.driver = webdriver.Firefox()
self.quiz_name = quiz_name
+ self.module_name = module_name
self.url = url
def run_load_test(self, url, username, password):
@@ -119,6 +120,8 @@ class SeleniumTest():
self.submit_answer(question_label, answer, loop_count)
def open_quiz(self):
+ # open module link
+ self.driver.find_element_by_link_text(self.module_name).click()
# open quiz link
quiz_link_elem = self.driver.find_element_by_link_text(self.quiz_name).click()
@@ -171,7 +174,9 @@ if __name__ == '__main__':
opts = parser.parse_args()
quiz_name = "Demo quiz"
- selenium_test = SeleniumTest(url=opts.url, quiz_name=quiz_name)
+ module_name = "demo module"
+ selenium_test = SeleniumTest(url=opts.url, quiz_name=quiz_name,
+ module_name=module_name)
pool = multiprocessing.Pool(opts.number)
pool.map(wrap_run_load_test, user_gen(opts.url, range(opts.start, opts.start + opts.number)))
pool.close()
diff --git a/yaksh/models.py b/yaksh/models.py
index 463434b..c65e9ef 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -143,13 +143,17 @@ class CourseManager(models.Manager):
#############################################################################
class Lesson(models.Model):
+ # Lesson name
name = models.CharField(max_length=255)
+
+ # Markdown text of lesson content
description = models.TextField()
+
+ # Markdown text should be in html format
html_data = models.TextField(null=True, blank=True)
- creator = models.ForeignKey(User)
- def get_files(self):
- return LessonFile.objects.filter(lesson=self)
+ # Creator of the lesson
+ creator = models.ForeignKey(User)
def __str__(self):
return "{0}".format(self.name)
diff --git a/yaksh/static/yaksh/css/exam.css b/yaksh/static/yaksh/css/exam.css
index ec48a14..7d10629 100644
--- a/yaksh/static/yaksh/css/exam.css
+++ b/yaksh/static/yaksh/css/exam.css
@@ -4,4 +4,4 @@ table td, table th { border: black solid 1px !important;
}
#stdio, #assertion {
table-layout: fixed
-} \ No newline at end of file
+}
diff --git a/yaksh/static/yaksh/js/requesthandler.js b/yaksh/static/yaksh/js/requesthandler.js
index 9749676..0663a55 100644
--- a/yaksh/static/yaksh/js/requesthandler.js
+++ b/yaksh/static/yaksh/js/requesthandler.js
@@ -53,16 +53,35 @@ function response_handler(method_type, content_type, data, uid){
} else if(content_type.indexOf("application/json") !== -1) {
res = JSON.parse(data);
request_status = res.status;
- if(method_type === "POST") {
- uid = res.uid;
+ if (request_status){
+ if(method_type === "POST") {
+ uid = res.uid;
+ }
+ check_state(request_status, uid);
+ }
+ else{
+ unlock_screen();
+ if ($("#notification")){
+ $("#notification").toggle();
+ }
+
+ var error_output = document.getElementById("error_panel");
+ error_output.innerHTML = res.error;
+ focus_on_error(error_output);
}
- check_state(request_status, uid);
} else {
reset_values();
unlock_screen();
}
}
+function focus_on_error(ele){
+ if (ele) {
+ ele.scrollIntoView(true);
+ window.scrollBy(0, -15);
+ }
+ }
+
function ajax_check_code(url, method_type, data_type, data, uid) {
$.ajax({
method: method_type,
diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html
index 007d9b8..e7c4a99 100644
--- a/yaksh/templates/base.html
+++ b/yaksh/templates/base.html
@@ -52,6 +52,7 @@
{% block content %}
{% endblock %}
</div>
+ </div>
<footer class="footer" id="footer_div">
<div class="logged_user_info" align="center">
{% block info %}
@@ -61,7 +62,6 @@
<p align="center">Developed by FOSSEE group, IIT Bombay</p>
</div>
</footer>
- </div>
</body>
</html>
diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html
index ce3bf44..d0812a4 100644
--- a/yaksh/templates/exam.html
+++ b/yaksh/templates/exam.html
@@ -33,15 +33,6 @@
</nav><!-- /.navbar -->
{% endblock %}
{% block content %}
- <script>
- $(document).ready(function() {
- var ele = document.getElementById('error_panel')
- if (ele) {
- ele.scrollIntoView(true);
- window.scrollBy(0, -15);
- }
- });
- </script>
<div class="container-fluid">
<div class="col-sm-3 col-md-2 sidebar">
<p> Question Navigator </p>
@@ -78,96 +69,11 @@
{% endblock %}
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
- <div class="row">
<div class="row">
{% block main %}
{% endblock %}
</div>
- <br/>
- {% if question.type == 'code' or question.type == 'upload' %}
- {% if error_message %}
- <div class="row" id="error_panel">
- {% for error in error_message %}
- <div class="panel panel-danger">
- <div class="panel-heading">Error No. {{ forloop.counter }}</div>
- <div class="panel-body">
- <div class="well well-sm">
- {% if not error.type %}
- <pre><code> {{error|safe}} </code></pre>
- {% elif error.type == 'assertion' %}
- {% if error.test_case %}
- <strong> We tried your code with the following test case:</strong><br/></br>
- <pre><code><strong style="color:#d9534f">{{error.test_case}}</strong></code></pre>
- {% endif %}
- <p> <b>The following error took place: </b></p>
- <table class="table table-bordered" width="100%" id='assertion'>
- <col width="30%">
- <tr class = "active">
- <td><b>Exception Name: </b></td>
- <td><span style="color: #d9534f">{{error.exception}}</span></td>
- </tr>
- <tr>
- <td><b>Exception Message: </b></td><td>{{error.message}}</td>
- </tr>
- <tr>
- {% if error.traceback %}
- <td><b>Full Traceback: </b></td>
- <td><pre>{{error.traceback}}</pre></td>
- {% endif %}
- </tr>
- </table>
- {% elif error.type == 'stdio' %}
- {% if error.given_input %}
- <table class="table table-bordered">
- <col width="30%">
- <tr class = "active">
- <td> For given Input value(s):</td>
- <td>{{error.given_input}}</td>
- </tr>
- </table>
- {% endif %}
- <table class="table table-bordered" width="100%" id="stdio">
- <col width="10%">
- <col width="40%">
- <col width="40%">
- <col width="10%">
- <tr class="info">
- <th><center>Line No.</center></th>
- <th><center>Expected Output</center></th>
- <th><center>User output</center></th>
- <th><center>Status</center></th>
- </tr>
- {% for expected,user in error.expected_output|zip:error.user_output %}
- <td> {{forloop.counter}} </td>
- <td>{{expected|default:""}} </td>
- <td>{{user|default:""}}</td>
- {% if forloop.counter0 in error.error_line_numbers or not expected or not user %}
- <td><span class ="glyphicon glyphicon-remove text-warning"/></td>
- {% else %}
- <td><span class ="glyphicon glyphicon-ok text-success"/></td>
- {% endif %}
- </tr>
- {% endfor %}
- </table>
- <table width="100%" class='table table-bordered'>
- <col width="10">
- <tr class = "danger">
- <td><b>Error:</b></td>
- <td>{{error.error_msg}}</td>
- </tr>
- </table>
-
- {% endif %}
</div>
- </div>
- </div>
- {% endfor %}
-
- </div>
- {% endif %}
- {% endif %}
- </div>
</div>
- </div>
-</div>
+
{% endblock %}
diff --git a/yaksh/templates/yaksh/complete.html b/yaksh/templates/yaksh/complete.html
index c99b8f0..6c0d878 100644
--- a/yaksh/templates/yaksh/complete.html
+++ b/yaksh/templates/yaksh/complete.html
@@ -5,7 +5,7 @@
{% csrf_token %}
{% if paper.questions_answered.all or paper.questions_unanswered.all %}
<center><table class="table table-bordered" >
- <caption> <center><h2>Submission Status</h2> </center></caption>
+ <caption> <center><h3>Submission Status</h3> </center></caption>
<thead>
<tr>
<th> Question</th>
diff --git a/yaksh/templates/yaksh/error_template.html b/yaksh/templates/yaksh/error_template.html
new file mode 100644
index 0000000..61657ae
--- /dev/null
+++ b/yaksh/templates/yaksh/error_template.html
@@ -0,0 +1,91 @@
+{% block css%}
+ <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/dashboard.css" type="text/css" />
+{% endblock %}
+
+{% load custom_filters %}
+
+{% if error_message %}
+ {% for error in error_message %}
+
+ <div class="panel panel-danger">
+ <div class="panel-heading">Error No. {{ forloop.counter }}</div>
+ <div class="panel-body">
+ <div class="well well-sm">
+ {% if not error.type %}
+ <pre><code> {{error|safe}} </code></pre>
+
+ {% elif error.type == 'assertion' %}
+
+ {% if error.test_case %}
+ <strong> We tried your code with the following test case:</strong>
+ <br/></br>
+ <pre><code><strong style="color:#d9534f">
+ {{error.test_case}}
+ </strong></code></pre>
+ {% endif %}
+ <p> <b>The following error took place: </b></p>
+ <table class="table table-bordered" width="100%" id='assertion'>
+ <col width="30%">
+ <tr class = "active">
+ <td><b>Exception Name: </b></td>
+ <td><span style="color: #d9534f">{{error.exception}}</span></td>
+ </tr>
+ <tr>
+ <td><b>Exception Message: </b></td><td>{{error.message}}</td>
+ </tr>
+ <tr>
+ {% if error.traceback %}
+ <td><b>Full Traceback: </b></td>
+ <td><pre>{{error.traceback}}</pre></td>
+ {% endif %}
+ </tr>
+ </table>
+
+ {% elif error.type == 'stdio' %}
+
+ {% if error.given_input %}
+ <table class="table table-bordered">
+ <col width="30%">
+ <tr class = "active">
+ <td> For given Input value(s):</td>
+ <td>{{error.given_input}}</td>
+ </tr>
+ </table>
+ {% endif %}
+
+ <table class="table table-bordered" width="100%" id="stdio">
+ <col width="10%">
+ <col width="40%">
+ <col width="40%">
+ <col width="10%">
+ <tr class="info">
+ <th><center>Line No.</center></th>
+ <th><center>Expected Output</center></th>
+ <th><center>User output</center></th>
+ <th><center>Status</center></th>
+ </tr>
+ {% for expected,user in error.expected_output|zip:error.user_output %}
+ <td> {{forloop.counter}} </td>
+ <td>{{expected|default:""}} </td>
+ <td>{{user|default:""}}</td>
+ {% if forloop.counter0 in error.error_line_numbers or not expected or not user %}
+ <td><span class ="glyphicon glyphicon-remove text-warning"/></td>
+ {% else %}
+ <td><span class ="glyphicon glyphicon-ok text-success"/></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ <table width="100%" class='table table-bordered'>
+ <col width="10">
+ <tr class = "danger">
+ <td><b>Error:</b></td>
+ <td>{{error.error_msg}}</td>
+ </tr>
+ </table>
+ {% endif %}
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+{% endif %} \ No newline at end of file
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 12f5da6..674fafc 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -106,15 +106,15 @@ module_id = "{{module.id}}"
{% get_unit_status course module unit user as status %}
{% if unit.quiz.id == paper.question_paper.quiz.id %}
- <span class="glyphicon glyphicon-pencil" data-toggle="tooltip" title="Current Unit"></span>
+ <span class="glyphicon glyphicon-pencil" data-toggle="tooltip" title="Currently on"></span>
{% endif %}
{% if unit.learning_type == "quiz" %}
- <a href="{{ URL_ROOT }}/exam/start/{{unit.quiz.questionpaper_set.get.id}}/{{learning_module.id}}/{{course.id}}">
+ <a href="{{ URL_ROOT }}/exam/start/{{unit.quiz.questionpaper_set.get.id}}/{{module.id}}/{{course.id}}">
{{ unit.quiz.description }}
</a>
{% else %}
- <a href="{{ URL_ROOT }}/exam/show_video/{{unit.lesson.id}}/{{learning_module.id}}/{{course.id}}">
+ <a href="{{ URL_ROOT }}/exam/show_lesson/{{unit.lesson.id}}/{{module.id}}/{{course.id}}">
{{ unit.lesson.name }}
</a>
{% endif %}
@@ -200,19 +200,19 @@ module_id = "{{module.id}}"
{% if question.type == "integer" %}
Enter Integer:<br/>
- <input name="answer" type="number" id="integer" value={{ last_attempt|safe }} />
+ <input autofocus name="answer" type="number" id="integer" value={{ last_attempt|safe }} />
<br/><br/>
{% endif %}
{% if question.type == "string" %}
Enter Text:<br/>
- <textarea name="answer" id="string">{{ last_attempt|safe }}</textarea>
+ <textarea autofocus name="answer" id="string">{{ last_attempt|safe }}</textarea>
<br/><br/>
{% endif %}
{% if question.type == "float" %}
Enter Decimal Value :<br/>
- <input name="answer" type="number" step="any" id="float" value={{ last_attempt|safe }} />
+ <input autofocus name="answer" type="number" step="any" id="float" value={{ last_attempt|safe }} />
<br/><br/>
{% endif %}
@@ -242,7 +242,7 @@ module_id = "{{module.id}}"
<a href="#answer" class="pull-right" onclick="reset_editor()" name="reset" id="reset">Undo Changes&nbsp;<span class="glyphicon glyphicon-refresh"></span></a>
</div>
</div>
- <textarea name="answer" id="answer"></textarea>
+ <textarea autofocus name="answer" id="answer"></textarea>
<br>
{% endif %}
<div class="from-group">
@@ -262,12 +262,16 @@ module_id = "{{module.id}}"
{% if question in paper.get_questions_unanswered %}
<button class="btn btn-primary" onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ paper.attempt_number }}/{{ module.id }}/{{ paper.question_paper.id }}/{{course.id}}/')" name="skip" id="skip">Attempt Later <span class="glyphicon glyphicon-arrow-right"></span></button>
{% endif %}
- </div>
- </div>
- </div>
-
+ {% endif %}
+ </div>
</form>
- {% endif %}
+ </div>
+ </div>
+ </div>
+ <br/>
+ {% if question.type == 'code' or question.type == 'upload' %}
+ <div class="row" id="error_panel"></div>
+ {% endif %}
<!-- Modal -->
<div class="modal fade " id="upload_alert" >
diff --git a/yaksh/templates/yaksh/quit.html b/yaksh/templates/yaksh/quit.html
index b168724..b38c21e 100644
--- a/yaksh/templates/yaksh/quit.html
+++ b/yaksh/templates/yaksh/quit.html
@@ -3,7 +3,7 @@
{% block pagetitle %} Yaksh Online Test {% endblock %}
{% block content %}
<center><table class="table table-bordered" >
- <caption> Submission Status </caption>
+ <caption> <center><h3>Submission Status</h3> </center> </caption>
<thead>
<tr>
<th> Question</th>
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index 3c4b985..26e550d 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -97,8 +97,8 @@ No Courses to display
</td>
<td>
{% if unit.learning_type == "quiz" %}
- {% if quiz.view_answerpaper %}
- <a href="{{ URL_ROOT }}/exam/view_answerpaper/{{ quiz.questionpaper_set.get.id }}/"><i class="fa fa-eye" aria-hidden="true"></i> Can View </a>
+ {% if unit.quiz.view_answerpaper %}
+ <a href="{{ URL_ROOT }}/exam/view_answerpaper/{{ unit.quiz.questionpaper_set.get.id }}/{{course.id}}"><i class="fa fa-eye" aria-hidden="true"></i> Can View </a>
{% else %}
<a><i class="fa fa-eye-slash" aria-hidden="true"></i> Cannot view now </a>
{% endif %}
diff --git a/yaksh/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html
index ffb0c68..ff79808 100644
--- a/yaksh/templates/yaksh/show_video.html
+++ b/yaksh/templates/yaksh/show_video.html
@@ -23,7 +23,7 @@
{{ unit.quiz.description }}
</a>
{% else %}
- <a href="{{ URL_ROOT }}/exam/show_video/{{unit.lesson.id}}/{{learning_module.id}}/{{course.id}}">
+ <a href="{{ URL_ROOT }}/exam/show_lesson/{{unit.lesson.id}}/{{learning_module.id}}/{{course.id}}">
{{ unit.lesson.name }}
</a>
{% endif %}
@@ -42,9 +42,11 @@
{% block main %}
{% if msg %}
<center>
-<div class="alert alert-warning">{{msg}}</div>
+<div class="col-md-12 col-md-offset-1 main">
+<div class="alert alert-warning">{{msg}}</div></div>
</center>
{% endif %}
+<div class="col-md-12 col-md-offset-1 main">
{% if state == "module" %}
<div class="panel panel-default">
<div class="panel-body">
@@ -71,4 +73,5 @@
</a>
</div>
{% endif %}
+</div>
{% endblock %} \ No newline at end of file
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 2dddcef..a5bd25e 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -9,6 +9,7 @@ except ImportError:
import zipfile
import shutil
from textwrap import dedent
+from markdown import Markdown
from django.contrib.auth.models import Group
from django.contrib.auth import authenticate
@@ -23,7 +24,8 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
AssignmentUpload, FileUpload, McqTestCase, IntegerTestCase, StringTestCase,\
- FloatTestCase, FIXTURES_DIR_PATH
+ FloatTestCase, FIXTURES_DIR_PATH, LearningModule, LearningUnit, Lesson,\
+ LessonFile
from yaksh.decorators import user_has_profile
@@ -492,8 +494,15 @@ class TestMonitor(TestCase):
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', course=self.course
+ creator=self.user
)
+ self.learning_unit = LearningUnit.objects.create(
+ order=1, learning_type="quiz", quiz=self.quiz)
+ self.learning_module = LearningModule.objects.create(
+ order=1, name="test module", description="test",
+ creator=self.user, check_prerequisite=False)
+ self.learning_module.learning_unit.add(self.learning_unit.id)
+ self.course.learning_module.add(self.learning_module)
self.question = Question.objects.create(
summary="Test_question", description="Add two numbers",
@@ -514,7 +523,7 @@ class TestMonitor(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="completed", passed=True,
- percent=1, marks_obtained=1
+ percent=1, marks_obtained=1, course=self.course
)
self.answerpaper.answers.add(self.new_answer)
self.answerpaper.questions_answered.add(self.question)
@@ -529,6 +538,8 @@ class TestMonitor(TestCase):
self.question.delete()
self.question_paper.delete()
self.new_answer.delete()
+ self.learning_module.delete()
+ self.learning_unit.delete()
def test_monitor_denies_student(self):
"""
@@ -568,7 +579,8 @@ class TestMonitor(TestCase):
password=self.user_plaintext_pass
)
response = self.client.get(reverse('yaksh:monitor',
- kwargs={'quiz_id': self.quiz.id}),
+ kwargs={'quiz_id': self.quiz.id,
+ 'course_id': self.course.id}),
follow=True
)
@@ -588,7 +600,8 @@ class TestMonitor(TestCase):
)
response = self.client.get(reverse('yaksh:user_data',
kwargs={'user_id':self.student.id,
- 'questionpaper_id': self.question_paper.id}),
+ 'questionpaper_id': self.question_paper.id,
+ 'course_id': self.course.id}),
follow=True
)
self.assertEqual(response.status_code, 200)
@@ -655,8 +668,15 @@ class TestGradeUser(TestCase):
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', course=self.course
+ creator=self.user
)
+ self.learning_unit = LearningUnit.objects.create(
+ order=1, learning_type="quiz", quiz=self.quiz)
+ self.learning_module = LearningModule.objects.create(
+ order=1, name="test module", description="test",
+ creator=self.user, check_prerequisite=False)
+ self.learning_module.learning_unit.add(self.learning_unit.id)
+ self.course.learning_module.add(self.learning_module)
self.question = Question.objects.create(
summary="Test_question", description="Add two numbers",
@@ -677,7 +697,7 @@ class TestGradeUser(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="completed", passed=True,
- marks_obtained=0.5
+ marks_obtained=0.5, course=self.course
)
self.answerpaper.answers.add(self.new_answer)
self.answerpaper.questions_answered.add(self.question)
@@ -693,6 +713,8 @@ class TestGradeUser(TestCase):
self.question.delete()
self.question_paper.delete()
self.new_answer.delete()
+ self.learning_module.delete()
+ self.learning_unit.delete()
def test_grade_user_denies_student(self):
"""
@@ -731,7 +753,8 @@ class TestGradeUser(TestCase):
password=self.user_plaintext_pass
)
response = self.client.get(reverse('yaksh:grade_user',
- kwargs={"quiz_id": self.quiz.id}),
+ kwargs={"quiz_id": self.quiz.id,
+ 'course_id': self.course.id}),
follow=True
)
self.assertEqual(response.status_code, 200)
@@ -751,6 +774,7 @@ class TestGradeUser(TestCase):
)
response = self.client.get(reverse('yaksh:grade_user',
kwargs={"quiz_id": self.quiz.id,
+ "course_id": self.course.id,
"user_id": self.student.id}),
follow=True
)
@@ -771,6 +795,7 @@ class TestGradeUser(TestCase):
)
self.client.get(reverse('yaksh:grade_user',
kwargs={"quiz_id": self.quiz.id,
+ "course_id": self.course.id,
"user_id": self.student.id}),
follow=True
)
@@ -778,6 +803,7 @@ class TestGradeUser(TestCase):
response = self.client.post(reverse('yaksh:grade_user',
kwargs={"quiz_id": self.quiz.id,
"user_id": self.student.id,
+ "course_id": self.course.id,
"attempt_number": self.answerpaper.attempt_number}),
data={question_marks: 1.0}
)
@@ -848,9 +874,15 @@ class TestDownloadAssignment(TestCase):
end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
- description='demo_quiz', pass_criteria=40,
- language='Python', course=self.course
+ description='demo_quiz', pass_criteria=40
)
+ self.learning_unit = LearningUnit.objects.create(
+ order=1, learning_type="quiz", quiz=self.quiz)
+ self.learning_module = LearningModule.objects.create(
+ order=1, name="test module", description="test",
+ creator=self.user, check_prerequisite=False)
+ self.learning_module.learning_unit.add(self.learning_unit.id)
+ self.course.learning_module.add(self.learning_module)
self.question = Question.objects.create(
summary="Test_question", description="Assignment Upload",
@@ -886,6 +918,8 @@ class TestDownloadAssignment(TestCase):
self.assignment2.delete()
self.quiz.delete()
self.course.delete()
+ self.learning_module.delete()
+ self.learning_unit.delete()
dir_name = self.quiz.description.replace(" ", "_")
file_path = os.sep.join((settings.MEDIA_ROOT, dir_name))
if os.path.exists(file_path):
@@ -999,24 +1033,13 @@ class TestAddQuiz(TestCase):
self.course = Course.objects.create(name="Python Course",
enrollment="Enroll Request", creator=self.user)
- self.pre_req_quiz = Quiz.objects.create(
- start_date_time=datetime(2014, 2, 1, 5, 8, 15, 0, tzone),
- end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
- duration=30, active=True, instructions="Demo Instructions",
- attempts_allowed=-1, time_between_attempts=0,
- description='pre requisite quiz', pass_criteria=40,
- language='Python', prerequisite=None,
- course=self.course
- )
-
self.quiz = Quiz.objects.create(
start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', prerequisite=self.pre_req_quiz,
- course=self.course
+ creator=self.user
)
def tearDown(self):
@@ -1024,18 +1047,16 @@ class TestAddQuiz(TestCase):
self.user.delete()
self.student.delete()
self.quiz.delete()
- self.pre_req_quiz.delete()
self.course.delete()
def test_add_quiz_denies_anonymous(self):
"""
If not logged in redirect to login page
"""
- response = self.client.get(reverse('yaksh:add_quiz',
- kwargs={'course_id': self.course.id}),
+ response = self.client.get(reverse('yaksh:add_quiz'),
follow=True
)
- redirect_destination = '/exam/login/?next=/exam/manage/addquiz/{0}/'.format(self.course.id)
+ redirect_destination = '/exam/login/?next=/exam/manage/addquiz/'
self.assertRedirects(response, redirect_destination)
def test_add_quiz_denies_non_moderator(self):
@@ -1047,8 +1068,7 @@ class TestAddQuiz(TestCase):
password=self.student_plaintext_pass
)
course_id = self.course.id
- response = self.client.get(reverse('yaksh:add_quiz',
- kwargs={'course_id': self.course.id}),
+ response = self.client.get(reverse('yaksh:add_quiz'),
follow=True
)
self.assertEqual(response.status_code, 404)
@@ -1061,8 +1081,7 @@ class TestAddQuiz(TestCase):
username=self.user.username,
password=self.user_plaintext_pass
)
- response = self.client.get(reverse('yaksh:add_quiz',
- kwargs={'course_id': self.course.id})
+ response = self.client.get(reverse('yaksh:add_quiz')
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'yaksh/add_quiz.html')
@@ -1078,7 +1097,7 @@ class TestAddQuiz(TestCase):
)
tzone = pytz.timezone('UTC')
response = self.client.post(reverse('yaksh:edit_quiz',
- kwargs={'course_id':self.course.id, 'quiz_id': self.quiz.id}),
+ kwargs={'quiz_id': self.quiz.id}),
data={
'start_date_time': '2016-01-10 09:00:15',
'end_date_time': '2016-01-15 09:00:15',
@@ -1088,10 +1107,8 @@ class TestAddQuiz(TestCase):
'time_between_attempts': 1,
'description': 'updated demo quiz',
'pass_criteria': 40,
- 'language': 'java',
'instructions': "Demo Instructions",
- 'prerequisite': self.pre_req_quiz.id,
- 'course': self.course.id
+ 'weightage': 1.0
}
)
@@ -1108,12 +1125,9 @@ class TestAddQuiz(TestCase):
self.assertEqual(updated_quiz.time_between_attempts, 1)
self.assertEqual(updated_quiz.description, 'updated demo quiz')
self.assertEqual(updated_quiz.pass_criteria, 40)
- self.assertEqual(updated_quiz.language, 'java')
- self.assertEqual(updated_quiz.prerequisite, self.pre_req_quiz)
- self.assertEqual(updated_quiz.course, self.course)
self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, '/exam/manage/courses/')
+ self.assertRedirects(response, '/exam/manage/courses/all_quizzes/')
def test_add_quiz_post_new_quiz(self):
"""
@@ -1125,8 +1139,7 @@ class TestAddQuiz(TestCase):
)
tzone = pytz.timezone('UTC')
- response = self.client.post(reverse('yaksh:add_quiz',
- kwargs={"course_id": self.course.id}),
+ response = self.client.post(reverse('yaksh:add_quiz'),
data={
'start_date_time': '2016-01-10 09:00:15',
'end_date_time': '2016-01-15 09:00:15',
@@ -1136,10 +1149,8 @@ class TestAddQuiz(TestCase):
'time_between_attempts': 2,
'description': 'new demo quiz',
'pass_criteria': 50,
- 'language': 'python',
'instructions': "Demo Instructions",
- 'prerequisite': self.pre_req_quiz.id,
- 'course': self.course.id
+ 'weightage': 1.0
}
)
quiz_list = Quiz.objects.all().order_by('-id')
@@ -1156,12 +1167,9 @@ class TestAddQuiz(TestCase):
self.assertEqual(new_quiz.time_between_attempts, 2)
self.assertEqual(new_quiz.description, 'new demo quiz')
self.assertEqual(new_quiz.pass_criteria, 50)
- self.assertEqual(new_quiz.language, 'python')
- self.assertEqual(new_quiz.prerequisite, self.pre_req_quiz)
- self.assertEqual(new_quiz.course, self.course)
self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, '/exam/manage/courses/')
+ self.assertRedirects(response, '/exam/manage/courses/all_quizzes/')
class TestAddTeacher(TestCase):
@@ -1212,8 +1220,7 @@ class TestAddTeacher(TestCase):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='pre requisite quiz', pass_criteria=40,
- language='Python', prerequisite=None,
- course=self.course
+ creator=self.user
)
self.quiz = Quiz.objects.create(
@@ -1222,8 +1229,7 @@ class TestAddTeacher(TestCase):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', prerequisite=self.pre_req_quiz,
- course=self.course
+ creator=self.user
)
def tearDown(self):
@@ -1372,8 +1378,7 @@ class TestRemoveTeacher(TestCase):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='pre requisite quiz', pass_criteria=40,
- language='Python', prerequisite=None,
- course=self.course
+ creator=self.user
)
self.quiz = Quiz.objects.create(
@@ -1382,8 +1387,7 @@ class TestRemoveTeacher(TestCase):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', prerequisite=self.pre_req_quiz,
- course=self.course
+ creator=self.user
)
def tearDown(self):
self.client.logout()
@@ -1521,13 +1525,31 @@ class TestCourses(TestCase):
email='demo_student@test.com'
)
+ self.teacher_plaintext_pass = 'teacher'
+ self.teacher = User.objects.create_user(
+ username='teacher',
+ password=self.teacher_plaintext_pass,
+ first_name='teacher_first_name',
+ last_name='teacher_last_name',
+ email='demo_teacher@test.com'
+ )
+
# Add to moderator group
self.mod_group.user_set.add(self.user1)
self.mod_group.user_set.add(self.user2)
+ self.mod_group.user_set.add(self.teacher)
+
+ # Create a learning module to add to course
+ self.learning_module = LearningModule.objects.create(
+ order=0, name="test module", description="module",
+ check_prerequisite=False, creator=self.teacher)
self.user1_course = Course.objects.create(name="Python Course",
enrollment="Enroll Request", creator=self.user1)
+ # Add teacher to user1 course
+ self.user1_course.teachers.add(self.teacher)
+
self.user2_course = Course.objects.create(name="Java Course",
enrollment="Enroll Request", creator=self.user2)
@@ -1538,8 +1560,8 @@ class TestCourses(TestCase):
self.student.delete()
self.user1_course.delete()
self.user2_course.delete()
-
-
+ self.teacher.delete()
+ self.learning_module.delete()
def test_courses_denies_anonymous(self):
"""
@@ -1583,6 +1605,64 @@ class TestCourses(TestCase):
self.assertIn(self.user1_course, response.context['courses'])
self.assertNotIn(self.user2_course, response.context['courses'])
+ def test_teacher_can_design_course(self):
+ """ Check if teacher can design the course i.e add learning modules """
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+ response = self.client.post(
+ reverse('yaksh:design_course',
+ kwargs={"course_id": self.user1_course.id}),
+ data={"Add": "Add",
+ "module_list": [str(self.learning_module.id)]
+ })
+
+ # Test add learning module
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/design_course_session.html')
+ added_learning_module = self.user1_course.learning_module.all().first()
+ self.assertEqual(self.learning_module, added_learning_module)
+ self.assertEqual(added_learning_module.order, 1)
+
+ # Test change order of learning module
+ response = self.client.post(
+ reverse('yaksh:design_course',
+ kwargs={"course_id": self.user1_course.id}),
+ data={"Change": "Change", "ordered_list": ",".join(
+ [str(self.learning_module.id)+":"+"0"]
+ )}
+ )
+ updated_learning_module = LearningModule.objects.get(
+ id=self.learning_module.id)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/design_course_session.html')
+ self.assertEqual(updated_learning_module.order, 0)
+
+ # Test change check prerequisite
+ response = self.client.post(
+ reverse('yaksh:design_course',
+ kwargs={"course_id": self.user1_course.id}),
+ data={"Change_prerequisite": "Change_prerequisite",
+ "check_prereq": [str(self.learning_module.id)]
+ })
+ updated_learning_module = LearningModule.objects.get(
+ id=self.learning_module.id)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/design_course_session.html')
+ self.assertTrue(updated_learning_module.check_prerequisite)
+
+ # Test to remove learning module from course
+ response = self.client.post(
+ reverse('yaksh:design_course',
+ kwargs={"course_id": self.user1_course.id}),
+ data={"Remove": "Remove",
+ "delete_list": [str(self.learning_module.id)]
+ })
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/design_course_session.html')
+ self.assertFalse(self.user1_course.learning_module.all().exists())
+
class TestAddCourse(TestCase):
def setUp(self):
@@ -1632,8 +1712,7 @@ class TestAddCourse(TestCase):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='pre requisite quiz', pass_criteria=40,
- language='Python', prerequisite=None,
- course=self.course
+ creator=self.user
)
self.quiz = Quiz.objects.create(
@@ -1642,8 +1721,7 @@ class TestAddCourse(TestCase):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', prerequisite=self.pre_req_quiz,
- course=self.course
+ creator=self.user
)
def tearDown(self):
@@ -1714,7 +1792,8 @@ class TestAddCourse(TestCase):
self.assertEqual(new_course.enrollment, 'open')
self.assertEqual(new_course.active, True)
self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, '/exam/manage/')
+ self.assertRedirects(response, '/exam/manage/courses',
+ target_status_code=301)
class TestCourseDetail(TestCase):
@@ -2082,12 +2161,11 @@ class TestCourseDetail(TestCase):
response = self.client.post(reverse('yaksh:toggle_course_status',
kwargs={'course_id': self.user1_course.id})
)
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.status_code, 302)
course = Course.objects.get(name="Python Course")
self.assertFalse(course.active)
- self.assertEqual(self.user1_course, response.context['course'])
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'yaksh/course_detail.html')
+ self.assertRedirects(response, '/exam/manage/courses',
+ target_status_code=301)
def test_send_mail_to_course_students(self):
""" Check if bulk mail is sent to multiple students enrolled in a course
@@ -2281,8 +2359,8 @@ class TestViewAnswerPaper(TestCase):
self.question = Question.objects.create(summary='Dummy', points=1,
type='code', user=self.user1)
- self.quiz = Quiz.objects.create(time_between_attempts=0, course=self.course,
- description='demo quiz', language='Python')
+ self.quiz = Quiz.objects.create(time_between_attempts=0,
+ description='demo quiz')
self.user3 = User.objects.get(username="demo_user3")
self.question_paper = QuestionPaper.objects.create(quiz=self.quiz,
total_marks=1.0)
@@ -2292,7 +2370,8 @@ class TestViewAnswerPaper(TestCase):
self.ans_paper = AnswerPaper.objects.create(user=self.user3,
attempt_number=1, question_paper=self.question_paper,
start_time=timezone.now(), user_ip='101.0.0.1',
- end_time=timezone.now()+timezone.timedelta(minutes=20))
+ end_time=timezone.now()+timezone.timedelta(minutes=20),
+ course=self.course)
def tearDown(self):
User.objects.all().delete()
@@ -2305,11 +2384,13 @@ class TestViewAnswerPaper(TestCase):
def test_anonymous_user(self):
# Given, user not logged in
redirect_destination = ('/exam/login/?next=/exam'
- '/view_answerpaper/{0}/'.format(self.question_paper.id))
+ '/view_answerpaper/{0}/{1}'.format(
+ self.question_paper.id, self.course.id))
# When
response = self.client.get(reverse('yaksh:view_answerpaper',
- kwargs={'questionpaper_id': self.question_paper.id}
+ kwargs={'questionpaper_id': self.question_paper.id,
+ 'course_id': self.course.id}
),
follow=True
)
@@ -2331,7 +2412,8 @@ class TestViewAnswerPaper(TestCase):
# When
response = self.client.get(reverse('yaksh:view_answerpaper',
- kwargs={'questionpaper_id': self.question_paper.id}
+ kwargs={'questionpaper_id': self.question_paper.id,
+ 'course_id': self.course.id}
),
follow=True
)
@@ -2354,7 +2436,8 @@ class TestViewAnswerPaper(TestCase):
# When
response = self.client.get(reverse('yaksh:view_answerpaper',
- kwargs={'questionpaper_id': self.question_paper.id}
+ kwargs={'questionpaper_id': self.question_paper.id,
+ 'course_id': self.course.id}
),
follow=True
)
@@ -2368,7 +2451,8 @@ class TestViewAnswerPaper(TestCase):
# When, wrong question paper id
response = self.client.get(reverse('yaksh:view_answerpaper',
- kwargs={'questionpaper_id': 190}
+ kwargs={'questionpaper_id': 190,
+ 'course_id': self.course.id}
),
follow=True
)
@@ -2391,7 +2475,8 @@ class TestViewAnswerPaper(TestCase):
# When
response = self.client.get(reverse('yaksh:view_answerpaper',
- kwargs={'questionpaper_id': self.question_paper.id}
+ kwargs={'questionpaper_id': self.question_paper.id,
+ 'course_id': self.course.id}
),
follow=True
)
@@ -2565,8 +2650,8 @@ class TestGrader(TestCase):
self.question = Question.objects.create(summary='Dummy', points=1,
type='code', user=self.user1)
- self.quiz = Quiz.objects.create(time_between_attempts=0, course=self.course,
- description='demo quiz', language='Python')
+ self.quiz = Quiz.objects.create(time_between_attempts=0,
+ description='demo quiz')
self.question_paper = QuestionPaper.objects.create(quiz=self.quiz,
total_marks=1.0)
@@ -2576,7 +2661,8 @@ class TestGrader(TestCase):
self.answerpaper = AnswerPaper.objects.create(user=self.user2,
attempt_number=1, question_paper=self.question_paper,
start_time=timezone.now(), user_ip='101.0.0.1',
- end_time=timezone.now()+timezone.timedelta(minutes=20))
+ end_time=timezone.now()+timezone.timedelta(minutes=20),
+ course=self.course)
def tearDown(self):
User.objects.all().delete()
@@ -2832,7 +2918,7 @@ class TestModeratorDashboard(TestCase):
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', course=self.course
+ creator=self.user
)
self.question = Question.objects.create(
@@ -2856,20 +2942,22 @@ class TestModeratorDashboard(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="completed", passed=True,
- marks_obtained=0.5
+ marks_obtained=0.5, course=self.course
)
self.answerpaper.answers.add(self.new_answer)
self.answerpaper.questions_answered.add(self.question)
self.answerpaper.questions.add(self.question)
# moderator trial answerpaper
+ self.trial_course = Course.objects.create(name="Trial Course",
+ enrollment="Enroll Request", creator=self.user, is_trial=True)
self.trial_quiz = Quiz.objects.create(
start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='trial quiz', pass_criteria=40,
- language='Python', course=self.course, is_trial=True
+ is_trial=True
)
self.trial_question_paper = QuestionPaper.objects.create(
@@ -2887,7 +2975,7 @@ class TestModeratorDashboard(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="completed", passed=True,
- marks_obtained=0.5
+ marks_obtained=0.5, course=self.trial_course
)
self.trial_answerpaper.answers.add(self.new_answer1)
self.trial_answerpaper.questions_answered.add(self.question)
@@ -2913,7 +3001,6 @@ class TestModeratorDashboard(TestCase):
follow=True
)
self.assertEqual(response.status_code, 200)
- self.assertRedirects(response, '/exam/quizzes/')
def test_moderator_dashboard_get_for_user_without_profile(self):
"""
@@ -2954,12 +3041,7 @@ class TestModeratorDashboard(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "yaksh/moderator_dashboard.html")
self.assertEqual(response.context['trial_paper'][0], self.trial_answerpaper)
- paper, answer_papers, users_passed, users_failed =\
- response.context['users_per_paper'][0]
- self.assertEqual(paper, self.question_paper)
- self.assertEqual(answer_papers[0], self.answerpaper)
- self.assertEqual(users_passed, 1)
- self.assertEqual(users_failed, 0)
+ self.assertEqual(response.context['courses'][0], self.course)
def test_moderator_dashboard_delete_trial_papers(self):
"""
@@ -2982,7 +3064,7 @@ class TestModeratorDashboard(TestCase):
description=self.trial_question_paper.quiz.description
)
updated_course = Course.objects.filter(
- name=self.trial_question_paper.quiz.course.name)
+ name=self.trial_course.name)
self.assertSequenceEqual(updated_answerpaper, [])
self.assertSequenceEqual(updated_quiz, [])
self.assertSequenceEqual(updated_course, [])
@@ -3103,7 +3185,7 @@ class TestDownloadcsv(TestCase):
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', course=self.course
+ creator=self.user
)
self.question = Question.objects.create(
@@ -3127,7 +3209,7 @@ class TestDownloadcsv(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="completed", passed=True,
- marks_obtained=0.5
+ marks_obtained=0.5, course=self.course
)
self.answerpaper.answers.add(self.new_answer)
self.answerpaper.questions_answered.add(self.question)
@@ -3405,7 +3487,11 @@ class TestShowQuestions(TestCase):
trial_que_paper = QuestionPaper.objects.get(
quiz__description="trial_questions"
)
- redirection_url = "/exam/start/1/{}".format(trial_que_paper.id)
+ trial_course = Course.objects.get(name="trial_course")
+ trial_module = trial_course.learning_module.all()[0]
+ redirection_url = "/exam/start/1/{0}/{1}/{2}".format(
+ trial_module.id, trial_que_paper.id, trial_course.id
+ )
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, redirection_url, target_status_code=301)
@@ -3484,7 +3570,7 @@ class TestShowStatistics(TestCase):
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', course=self.course
+ creator=self.user
)
self.question = Question.objects.create(
@@ -3506,7 +3592,7 @@ class TestShowStatistics(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="completed", passed=True,
- percent=1, marks_obtained=1
+ percent=1, marks_obtained=1, course=self.course
)
self.answerpaper.answers.add(self.new_answer)
self.answerpaper.questions_answered.add(self.question)
@@ -3532,7 +3618,8 @@ class TestShowStatistics(TestCase):
password=self.student_plaintext_pass
)
response = self.client.get(reverse('yaksh:show_statistics',
- kwargs={"questionpaper_id": self.question_paper.id}),
+ kwargs={"questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id}),
follow=True
)
self.assertEqual(response.status_code, 404)
@@ -3546,7 +3633,8 @@ class TestShowStatistics(TestCase):
password=self.user_plaintext_pass
)
response = self.client.get(reverse('yaksh:show_statistics',
- kwargs={'questionpaper_id': self.question_paper.id}),
+ kwargs={'questionpaper_id': self.question_paper.id,
+ "course_id": self.course.id}),
follow=True
)
self.assertEqual(response.status_code, 200)
@@ -3567,7 +3655,8 @@ class TestShowStatistics(TestCase):
)
response = self.client.get(reverse('yaksh:show_statistics',
kwargs={'questionpaper_id': self.question_paper.id,
- 'attempt_number': self.answerpaper.attempt_number}),
+ 'attempt_number': self.answerpaper.attempt_number,
+ "course_id": self.course.id}),
follow=True
)
self.assertEqual(response.status_code, 200)
@@ -3616,9 +3705,17 @@ class TestQuestionPaper(TestCase):
duration=30, active=True, instructions="Demo Instructions",
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', course=self.course
+ creator=self.user
)
+ self.learning_unit = LearningUnit.objects.create(
+ order=1, learning_type="quiz", quiz=self.quiz)
+ self.learning_module = LearningModule.objects.create(
+ order=1, name="test module", description="module",
+ check_prerequisite=False, creator=self.user)
+ self.learning_module.learning_unit.add(self.learning_unit.id)
+ self.course.learning_module.add(self.learning_module)
+
# Mcq Question
self.question_mcq = Question.objects.create(
summary="Test_mcq_question", description="Test MCQ",
@@ -3706,7 +3803,7 @@ class TestQuestionPaper(TestCase):
start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
user_ip="127.0.0.1", status="inprogress", passed=False,
- percent=0, marks_obtained=0
+ percent=0, marks_obtained=0, course=self.course
)
self.answerpaper.questions.add(*questions_list)
@@ -3720,6 +3817,8 @@ class TestQuestionPaper(TestCase):
self.question_mcc.delete()
self.question_int.delete()
self.question_paper.delete()
+ self.learning_module.delete()
+ self.learning_unit.delete()
def test_mcq_attempt_right_after_wrong(self):
""" Case:- Check if answerpaper and answer marks are updated after
@@ -3738,7 +3837,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": wrong_user_answer}
)
@@ -3753,7 +3854,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": right_user_answer}
)
@@ -3778,7 +3881,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": right_user_answer}
)
@@ -3793,7 +3898,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": wrong_user_answer}
)
@@ -3818,7 +3925,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_mcc.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": right_user_answer}
)
@@ -3833,7 +3942,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_mcc.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": wrong_user_answer}
)
@@ -3858,7 +3969,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_int.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": right_user_answer}
)
@@ -3873,7 +3986,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_int.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": wrong_user_answer}
)
@@ -3898,7 +4013,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_str.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": right_user_answer}
)
@@ -3913,7 +4030,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_str.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": wrong_user_answer}
)
@@ -3938,7 +4057,9 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_float.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": right_user_answer}
)
@@ -3953,10 +4074,553 @@ class TestQuestionPaper(TestCase):
self.client.post(
reverse('yaksh:check',
kwargs={"q_id": self.question_float.id, "attempt_num": 1,
- "questionpaper_id": self.question_paper.id}),
+ "questionpaper_id": self.question_paper.id,
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"answer": wrong_user_answer}
)
# Then
wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+
+
+class TestLearningModule(TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ self.mod_group = Group.objects.create(name='moderator')
+ tzone = pytz.timezone('UTC')
+ # Create Moderator with profile
+ self.user_plaintext_pass = 'demo'
+ self.user = User.objects.create_user(
+ username='demo_user',
+ password=self.user_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.user,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC'
+ )
+
+ # Create a student
+ self.student_plaintext_pass = 'demo_student'
+ self.student = User.objects.create_user(
+ username='demo_student',
+ password=self.student_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@student.com'
+ )
+
+ # Create a teacher to add to the course
+ self.teacher_plaintext_pass = 'demo_teacher'
+ self.teacher = User.objects.create_user(
+ username='demo_teacher',
+ password=self.teacher_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@student.com'
+ )
+
+ # Add to moderator group
+ self.mod_group.user_set.add(self.user)
+ self.mod_group.user_set.add(self.teacher)
+
+ self.course = Course.objects.create(
+ name="Python Course",
+ enrollment="Open Enrollment", creator=self.user)
+
+ self.question = Question.objects.create(
+ summary="Test_question", description="Add two numbers",
+ points=1.0, language="python", type="code", user=self.user
+ )
+ self.quiz = Quiz.objects.create(
+ start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
+ end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzone),
+ duration=30, active=True, instructions="Demo Instructions",
+ attempts_allowed=-1, time_between_attempts=0,
+ description='demo quiz', pass_criteria=40,
+ creator=self.user
+ )
+
+ self.question_paper = QuestionPaper.objects.create(
+ quiz=self.quiz,
+ total_marks=5.0, fixed_question_order=str(self.question.id)
+ )
+
+ self.question_paper.fixed_questions.add(self.question)
+
+ self.lesson = Lesson.objects.create(
+ name="test lesson", description="test description",
+ creator=self.user)
+ # create quiz leanring unit
+ self.learning_unit = LearningUnit.objects.create(
+ order=0, learning_type="quiz", quiz=self.quiz)
+ # create lesson leanring unit
+ self.learning_unit1 = LearningUnit.objects.create(
+ order=1, learning_type="lesson", lesson=self.lesson)
+ # create learning module
+ self.learning_module = LearningModule.objects.create(
+ order=0, name="test module", description="module",
+ check_prerequisite=False, creator=self.user)
+ self.learning_module.learning_unit.add(self.learning_unit)
+ self.learning_module.learning_unit.add(self.learning_unit1)
+ self.learning_module1 = LearningModule.objects.create(
+ order=0, name="my module", description="my description",
+ check_prerequisite=False, creator=self.user)
+ self.course.teachers.add(self.teacher)
+ self.course.learning_module.add(self.learning_module)
+
+ self.expected_url = "/exam/manage/courses/all_learning_module/"
+
+ def tearDown(self):
+ self.user.delete()
+ self.student.delete()
+ self.teacher.delete()
+ self.quiz.delete()
+ self.course.delete()
+ self.learning_unit.delete()
+ self.learning_module.delete()
+
+ def test_add_new_module_denies_non_moderator(self):
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+
+ # Student tries to add learning module
+ response = self.client.post(reverse('yaksh:add_module'),
+ data={"name": "test module1",
+ "description": "my test1",
+ "Save": "Save"})
+ self.assertEqual(response.status_code, 404)
+
+ # Student tries to view learning modules
+ response = self.client.get(reverse('yaksh:show_all_modules'))
+ self.assertEqual(response.status_code, 404)
+
+ def test_add_new_module(self):
+ """ Check if new module is created """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Test add new module
+ response = self.client.post(reverse('yaksh:add_module'),
+ data={"name": "test module1",
+ "description": "my test1",
+ "Save": "Save"})
+
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, self.expected_url)
+ learning_module = LearningModule.objects.get(name="test module1")
+ self.assertEqual(learning_module.description, "my test1")
+ self.assertEqual(learning_module.creator, self.user)
+ self.assertTrue(learning_module.check_prerequisite)
+ self.assertEqual(learning_module.html_data,
+ Markdown().convert("my test1"))
+
+ def test_edit_module(self):
+ """ Check if existing module is editable """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Test add new module
+ response = self.client.post(
+ reverse('yaksh:edit_module',
+ kwargs={"module_id": self.learning_module.id}),
+ data={"name": "test module2",
+ "description": "my test2",
+ "Save": "Save"})
+
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, self.expected_url)
+ learning_module = LearningModule.objects.get(name="test module2")
+ self.assertEqual(learning_module.description, "my test2")
+ self.assertEqual(learning_module.creator, self.user)
+ self.assertFalse(learning_module.check_prerequisite)
+ self.assertEqual(learning_module.html_data,
+ Markdown().convert("my test2"))
+
+ def test_show_all_modules(self):
+ """Try to get all learning modules"""
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:show_all_modules'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/courses.html')
+ self.assertEqual(response.context['type'], "learning_module")
+ self.assertEqual(response.context['learning_modules'][0],
+ self.learning_module)
+
+ def test_teacher_can_edit_module(self):
+ """ Check if teacher can edit the module """
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+ response = self.client.post(
+ reverse('yaksh:edit_module',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id}),
+ data={"name": "teacher module 2",
+ "description": "teacher module 2",
+ "Save": "Save"})
+
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, "/exam/manage/courses/")
+ learning_module = LearningModule.objects.get(name="teacher module 2")
+ self.assertEqual(learning_module.description, "teacher module 2")
+ self.assertEqual(learning_module.creator, self.user)
+
+ def test_teacher_can_design_module(self):
+ """ Check if teacher can design the module i.e add learning units """
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+ response = self.client.post(
+ reverse('yaksh:design_module',
+ kwargs={"module_id": self.learning_module1.id,
+ "course_id": self.course.id}),
+ data={"Add": "Add",
+ "choosen_list": ",".join([str(self.quiz.id)+":"+"quiz"])
+ })
+
+ # Test add learning unit
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/add_module.html')
+ learning_unit = self.learning_module1.learning_unit.all().first()
+ self.assertEqual(self.quiz, learning_unit.quiz)
+ self.assertEqual(learning_unit.learning_type, "quiz")
+ self.assertEqual(learning_unit.order, 1)
+
+ # Test change order of learning unit
+ response = self.client.post(
+ reverse('yaksh:design_module',
+ kwargs={"module_id": self.learning_module1.id,
+ "course_id": self.course.id}),
+ data={"Change": "Change",
+ "ordered_list": ",".join([str(learning_unit.id)+":"+"0"])
+ })
+ updated_learning_unit = LearningUnit.objects.get(id=learning_unit.id)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/add_module.html')
+ self.assertEqual(updated_learning_unit.order, 0)
+
+ # Test change check prerequisite
+ response = self.client.post(
+ reverse('yaksh:design_module',
+ kwargs={"module_id": self.learning_module1.id,
+ "course_id": self.course.id}),
+ data={"Change_prerequisite": "Change_prerequisite",
+ "check_prereq": [str(learning_unit.id)]
+ })
+ updated_learning_unit = LearningUnit.objects.get(id=learning_unit.id)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/add_module.html')
+ self.assertFalse(updated_learning_unit.check_prerequisite)
+
+ # Test to remove learning unit from learning module
+ response = self.client.post(
+ reverse('yaksh:design_module',
+ kwargs={"module_id": self.learning_module1.id,
+ "course_id": self.course.id}),
+ data={"Remove": "Remove",
+ "delete_list": [str(learning_unit.id)]
+ })
+ updated_learning_unit = LearningUnit.objects.filter(
+ id=learning_unit.id).exists()
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/add_module.html')
+ self.assertFalse(updated_learning_unit)
+ self.assertFalse(self.learning_module1.learning_unit.all().exists())
+
+ def test_get_learning_units_for_design(self):
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+ response = self.client.get(
+ reverse('yaksh:design_module',
+ kwargs={"module_id": self.learning_module1.id,
+ "course_id": self.course.id}))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/add_module.html')
+ self.assertEqual(response.context['quiz_les_list'], set())
+ self.assertEqual(len(response.context['learning_units']), 0)
+ self.assertEqual(response.context['status'], "design")
+ self.assertEqual(response.context['module_id'],
+ str(self.learning_module1.id))
+ self.assertEqual(response.context['course_id'], str(self.course.id))
+
+ def test_view_module(self):
+ """ Student tries to view a module containing learning units """
+
+ # Student is not enrolled in the course is thrown out
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+ response = self.client.get(
+ reverse('yaksh:view_module',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id}))
+ self.assertEqual(response.status_code, 404)
+
+ # add student to the course
+ self.course.students.add(self.student.id)
+
+ # check if enrolled student can view module
+ response = self.client.get(
+ reverse('yaksh:view_module',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id}))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "yaksh/show_video.html")
+ self.assertEqual(response.context['learning_units'][0],
+ self.learning_unit)
+ self.assertEqual(response.context["state"], "module")
+ self.assertEqual(response.context["first_unit"], self.learning_unit)
+ self.assertEqual(response.context["user"], self.student)
+
+ def test_get_next_unit(self):
+ """ Check if we get correct next unit """
+
+ # Student who is not enrolled is thrown out
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+ response = self.client.get(
+ reverse('yaksh:view_module',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id}))
+ self.assertEqual(response.status_code, 404)
+
+ # Enroll student in the course
+ self.course.students.add(self.student.id)
+
+ # Get First unit as next unit
+ response = self.client.get(
+ reverse('yaksh:next_unit',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id,
+ "current_unit_id": self.learning_unit.id,
+ "first_unit": "1"}),
+ follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context['attempt_num'], 1)
+ self.assertEqual(response.context["questionpaper"],
+ self.question_paper)
+ self.assertEqual(response.context["course"], self.course)
+ self.assertEqual(response.context["module"], self.learning_module)
+ self.assertEqual(response.context["user"], self.student)
+
+ # Get next unit after first unit
+ response = self.client.get(
+ reverse('yaksh:next_unit',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id,
+ "current_unit_id": self.learning_unit.id}),
+ follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["state"], "lesson")
+ self.assertEqual(response.context["current_unit"].id,
+ self.learning_unit1.id)
+
+
+class TestLessons(TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ self.mod_group = Group.objects.create(name='moderator')
+ tzone = pytz.timezone('UTC')
+ # Create Moderator with profile
+ self.user_plaintext_pass = 'demo'
+ self.user = User.objects.create_user(
+ username='demo_user',
+ password=self.user_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.user,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC'
+ )
+
+ # Create a student
+ self.student_plaintext_pass = 'demo_student'
+ self.student = User.objects.create_user(
+ username='demo_student',
+ password=self.student_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@student.com'
+ )
+
+ # Create a teacher to add to the course
+ self.teacher_plaintext_pass = 'demo_teacher'
+ self.teacher = User.objects.create_user(
+ username='demo_teacher',
+ password=self.teacher_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@student.com'
+ )
+
+ # Add to moderator group
+ self.mod_group.user_set.add(self.user)
+ self.mod_group.user_set.add(self.teacher)
+
+ self.course = Course.objects.create(
+ name="Python Course",
+ enrollment="Open Enrollment", creator=self.user)
+
+ self.quiz = Quiz.objects.create(
+ start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
+ end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
+ duration=30, active=True, instructions="Demo Instructions",
+ attempts_allowed=-1, time_between_attempts=0,
+ description='demo quiz', pass_criteria=40,
+ creator=self.user
+ )
+
+ self.lesson = Lesson.objects.create(
+ name="test lesson", description="test description",
+ creator=self.user)
+ self.learning_unit = LearningUnit.objects.create(
+ order=0, learning_type="lesson", lesson=self.lesson)
+ self.learning_module = LearningModule.objects.create(
+ order=0, name="test module", description="module",
+ check_prerequisite=False, creator=self.user)
+ self.learning_module.learning_unit.add(self.learning_unit.id)
+ self.course.learning_module.add(self.learning_module.id)
+ self.course.teachers.add(self.teacher.id)
+
+ self.expected_url = "/exam/manage/courses/"
+
+ def tearDown(self):
+ self.user.delete()
+ self.student.delete()
+ self.teacher.delete()
+ self.quiz.delete()
+ self.course.delete()
+ self.learning_unit.delete()
+ self.learning_module.delete()
+ self.lesson.delete()
+
+ def test_edit_lesson_denies_non_moderator(self):
+ """ Student should not be allowed to edit lesson """
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+
+ # Student tries to edit lesson
+ response = self.client.get(
+ reverse('yaksh:edit_lesson',
+ kwargs={"lesson_id": self.lesson.id,
+ "course_id": self.course.id}))
+ self.assertEqual(response.status_code, 404)
+
+ def test_teacher_can_edit_lesson(self):
+ """ Teacher should be allowed to edit lesson """
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+ dummy_file = SimpleUploadedFile("test.txt", b"test")
+ response = self.client.post(
+ reverse('yaksh:edit_lesson',
+ kwargs={"lesson_id": self.lesson.id,
+ "course_id": self.course.id}),
+ data={"name": "updated lesson",
+ "description": "updated description",
+ "Lesson_files": dummy_file,
+ "Save": "Save"}
+ )
+
+ # Teacher edits existing lesson and adds file
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, self.expected_url)
+ updated_lesson = Lesson.objects.get(name="updated lesson")
+ self.assertEqual(updated_lesson.description, "updated description")
+ self.assertEqual(updated_lesson.creator, self.user)
+ self.assertEqual(updated_lesson.html_data,
+ Markdown().convert("updated description"))
+ lesson_files = LessonFile.objects.filter(
+ lesson=self.lesson).first()
+ self.assertIn("test.txt", lesson_files.file.name)
+ lesson_file_path = lesson_files.file.path
+ # Teacher removes the lesson file
+ response = self.client.post(
+ reverse('yaksh:edit_lesson',
+ kwargs={"lesson_id": self.lesson.id,
+ "course_id": self.course.id}),
+ data={"delete_files": [str(lesson_files.id)],
+ "Delete": "Delete"}
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, self.expected_url)
+ lesson_file_exists = LessonFile.objects.filter(
+ lesson=self.lesson).exists()
+ self.assertFalse(lesson_file_exists)
+ self.assertFalse(os.path.exists(lesson_file_path))
+
+ def test_show_lesson(self):
+ """ Student should be able to view lessons """
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+ response = self.client.get(
+ reverse('yaksh:show_lesson',
+ kwargs={"lesson_id": self.lesson.id,
+ "module_id": self.learning_module.id,
+ "course_id": self.course.id}))
+ # Student is unable to view lesson
+ self.assertEqual(response.status_code, 404)
+
+ # Add student to course
+ self.course.students.add(self.student.id)
+ response = self.client.get(
+ reverse('yaksh:show_lesson',
+ kwargs={"lesson_id": self.lesson.id,
+ "module_id": self.learning_module.id,
+ "course_id": self.course.id}))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["state"], "lesson")
+ self.assertEqual(response.context["current_unit"], self.learning_unit)
+
+ def test_show_all_lessons(self):
+ """ Moderator should be able to see all created lessons"""
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:show_all_lessons'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "yaksh/courses.html")
+ self.assertEqual(response.context["type"], "lesson")
+ self.assertEqual(response.context["lessons"][0], self.lesson)
+
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 8348666..dada74f 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -32,9 +32,10 @@ urlpatterns = [
views.skip),
url(r'^enroll_request/(?P<course_id>\d+)/$', views.enroll_request, name='enroll_request'),
url(r'^self_enroll/(?P<course_id>\d+)/$', views.self_enroll, name='self_enroll'),
- url(r'^view_answerpaper/(?P<questionpaper_id>\d+)/$', views.view_answerpaper, name='view_answerpaper'),
- url(r'^show_video/(?P<lesson_id>\d+)/(?P<module_id>\d+)/(?P<course_id>\d+)/$',
- views.show_video, name='show_video'),
+ url(r'^view_answerpaper/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)$',
+ views.view_answerpaper, name='view_answerpaper'),
+ url(r'^show_lesson/(?P<lesson_id>\d+)/(?P<module_id>\d+)/(?P<course_id>\d+)/$',
+ views.show_lesson, name='show_lesson'),
url(r'^quizzes/view_module/(?P<module_id>\d+)/(?P<course_id>\d+)/$',
views.view_module, name='view_module'),
url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<current_unit_id>\d+)/$',
diff --git a/yaksh/views.py b/yaksh/views.py
index 524d4d5..e0688f0 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -8,7 +8,7 @@ from django.http import HttpResponse, JsonResponse
from django.core.urlresolvers import reverse
from django.contrib.auth import login, logout, authenticate
from django.shortcuts import render_to_response, get_object_or_404, redirect
-from django.template import RequestContext
+from django.template import RequestContext, Context, Template
from django.http import Http404
from django.db.models import Sum, Max, Q, F
from django.views.decorators.csrf import csrf_exempt
@@ -296,6 +296,8 @@ def add_quiz(request, quiz_id=None, course_id=None):
quiz = get_object_or_404(Quiz, pk=quiz_id)
if quiz.creator != user:
raise Http404('This quiz does not belong to you')
+ else:
+ quiz = None
if course_id:
course = get_object_or_404(Course, pk=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
@@ -303,25 +305,15 @@ def add_quiz(request, quiz_id=None, course_id=None):
context = {}
if request.method == "POST":
- if quiz_id is None:
- form = QuizForm(request.POST)
- if form.is_valid():
- form.instance.creator = user
- form.save()
- if not course_id:
- return my_redirect("/exam/manage/courses/all_quizzes/")
- else:
- return my_redirect("/exam/manage/courses/")
- else:
- quiz = Quiz.objects.get(id=quiz_id)
- form = QuizForm(request.POST, instance=quiz)
- if form.is_valid():
+ form = QuizForm(request.POST, instance=quiz)
+ if form.is_valid():
+ if quiz is None:
form.instance.creator = user
- form.save()
- if not course_id:
- return my_redirect("/exam/manage/courses/all_quizzes/")
- else:
- return my_redirect("/exam/manage/courses/")
+ form.save()
+ if not course_id:
+ return my_redirect("/exam/manage/courses/all_quizzes/")
+ else:
+ return my_redirect("/exam/manage/courses/")
else:
quiz = Quiz.objects.get(id=quiz_id) if quiz_id else None
@@ -728,9 +720,24 @@ def get_result(request, uid, course_id, module_id):
result['status'] = result_state.get('status')
if result['status'] == 'done':
result = json.loads(result_state.get('result'))
- next_question, error_message, paper = _update_paper(request, uid, result)
- return show_question(request, next_question, paper, error_message,
- course_id=course_id, module_id=module_id)
+ template_path = os.path.join(*[os.path.dirname(__file__),
+ 'templates', 'yaksh',
+ 'error_template.html'
+ ]
+ )
+ next_question, error_message, paper = _update_paper(request, uid,
+ result
+ )
+ if result.get('success'):
+ return show_question(request, next_question, paper, error_message,
+ course_id=course_id, module_id=module_id)
+ else:
+ with open(template_path) as f:
+ template_data = f.read()
+ template = Template(template_data)
+ context = Context({"error_message": result.get('error')})
+ render_error = template.render(context)
+ result["error"] = render_error
return JsonResponse(result)
@@ -822,6 +829,8 @@ def add_course(request, course_id=None):
ci = RequestContext(request)
if course_id:
course = Course.objects.get(id=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404("You are not allowed to view this course")
else:
course = None
@@ -1732,7 +1741,8 @@ def test_quiz(request, mode, quiz_id, course_id=None):
def view_answerpaper(request, questionpaper_id, course_id):
user = request.user
quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz
- if quiz.view_answerpaper and user in quiz.course.students.all():
+ course = get_object_or_404(Course, pk=course_id)
+ if quiz.view_answerpaper and user in course.students.all():
data = AnswerPaper.objects.get_user_data(user, questionpaper_id,
course_id)
has_user_assignment = AssignmentUpload.objects.filter(
@@ -2174,7 +2184,8 @@ def edit_lesson(request, lesson_id=None, course_id=None):
lesson_file_form = LessonFileForm(request.POST, request.FILES)
lessonfiles = request.FILES.getlist('Lesson_files')
if lesson_form.is_valid():
- lesson_form.instance.creator = user
+ if lesson is None:
+ lesson_form.instance.creator = user
lesson = lesson_form.save()
lesson.html_data = get_html_text(lesson.description)
lesson.save()
@@ -2194,6 +2205,7 @@ def edit_lesson(request, lesson_id=None, course_id=None):
files = LessonFile.objects.filter(id__in=remove_files_id)
for file in files:
file.remove()
+ return my_redirect(redirect_url)
lesson_files = LessonFile.objects.filter(lesson=lesson)
lesson_files_form = LessonFileForm()
@@ -2209,7 +2221,7 @@ def edit_lesson(request, lesson_id=None, course_id=None):
@login_required
@email_verified
-def show_video(request, lesson_id, module_id, course_id):
+def show_lesson(request, lesson_id, module_id, course_id):
user = request.user
course = Course.objects.get(id=course_id)
if user not in course.students.all():
@@ -2222,7 +2234,8 @@ def show_video(request, lesson_id, module_id, course_id):
learning_units = learn_module.get_learning_units()
if learn_unit.has_prerequisite():
if not learn_unit.is_prerequisite_passed(user, learn_module, course):
- return my_redirect("/exam/quizzes/")
+ msg = "You have not passed the prerequisite"
+ return view_module(request, learn_module.id, course_id, msg=msg)
context = {'lesson': learn_unit.lesson, 'user': user,
'course': course, 'state': "lesson",
'learning_units': learning_units, "current_unit": learn_unit,
@@ -2328,7 +2341,8 @@ def add_module(request, module_id=None, course_id=None):
if "Save" in request.POST:
module_form = LearningModuleForm(request.POST, instance=module)
if module_form.is_valid():
- module_form.instance.creator = user
+ if module is None:
+ module_form.instance.creator = user
module = module_form.save()
module.html_data = get_html_text(module.description)
module.save()
@@ -2429,7 +2443,7 @@ def get_next_unit(request, course_id, module_id, current_unit_id,
return my_redirect("/exam/start/{0}/{1}/{2}".format(
next_unit.quiz.questionpaper_set.get().id, module_id, course_id))
else:
- return my_redirect("/exam/show_video/{0}/{1}/{2}".format(
+ return my_redirect("/exam/show_lesson/{0}/{1}/{2}".format(
next_unit.lesson.id, module_id, course_id))
@@ -2509,7 +2523,7 @@ def view_module(request, module_id, course_id, msg=None):
learning_module = course.learning_module.get(id=module_id)
if learning_module.has_prerequisite():
if not learning_module.is_prerequisite_passed(user, course):
- msg = "You have not completed previous learning module"
+ msg = "You have not completed the previous learning module"
return quizlist_user(request, msg=msg)
learning_units = learning_module.get_learning_units()
context['learning_units'] = learning_units