summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/evaluator_tests/test_r_evaluation.py196
-rw-r--r--yaksh/middleware/one_session_per_user.py15
-rw-r--r--yaksh/middleware/user_time_zone.py11
-rw-r--r--yaksh/models.py9
-rw-r--r--yaksh/r_code_evaluator.py5
-rw-r--r--yaksh/static/yaksh/js/codemirror/mode/r/index.html85
-rw-r--r--yaksh/static/yaksh/js/codemirror/mode/r/r.js164
-rw-r--r--yaksh/static/yaksh/js/design_course.js10
-rw-r--r--yaksh/static/yaksh/js/requesthandler.js3
-rw-r--r--yaksh/templates/yaksh/add_course.html18
-rw-r--r--yaksh/templates/yaksh/add_exercise.html14
-rw-r--r--yaksh/templates/yaksh/add_lesson.html10
-rw-r--r--yaksh/templates/yaksh/add_module.html5
-rw-r--r--yaksh/templates/yaksh/add_quiz.html14
-rw-r--r--yaksh/templates/yaksh/course_added_modules.html141
-rw-r--r--yaksh/templates/yaksh/course_detail_options.html18
-rw-r--r--yaksh/templates/yaksh/course_modules.html8
-rw-r--r--yaksh/templates/yaksh/courses.html24
-rw-r--r--yaksh/templates/yaksh/design_course_session.html8
-rw-r--r--yaksh/templates/yaksh/design_questionpaper.html9
-rw-r--r--yaksh/templates/yaksh/question.html1
-rw-r--r--yaksh/test_views.py219
-rw-r--r--yaksh/urls.py39
-rw-r--r--yaksh/views.py193
24 files changed, 800 insertions, 419 deletions
diff --git a/yaksh/evaluator_tests/test_r_evaluation.py b/yaksh/evaluator_tests/test_r_evaluation.py
new file mode 100644
index 0000000..b161dc9
--- /dev/null
+++ b/yaksh/evaluator_tests/test_r_evaluation.py
@@ -0,0 +1,196 @@
+from __future__ import unicode_literals
+import unittest
+from textwrap import dedent
+import os
+import tempfile
+import shutil
+from psutil import Process
+
+from yaksh.grader import Grader
+from yaksh.settings import SERVER_TIMEOUT
+from yaksh.evaluator_tests.test_python_evaluation import EvaluatorBaseTest
+
+
+class RAssertionEvaluationTestCase(EvaluatorBaseTest):
+ def setUp(self):
+ self.tmp_file = os.path.join(tempfile.gettempdir(), 'test.txt')
+ with open(self.tmp_file, 'wb') as f:
+ f.write('2'.encode('ascii'))
+ tmp_in_dir_path = tempfile.mkdtemp()
+ self.in_dir = tmp_in_dir_path
+ self.test_case = dedent(
+ '''
+ source("function.r")
+ check_empty = function(obj){
+ stopifnot(is.null(obj) == FALSE)
+ }
+ check = function(input, output){
+ stopifnot(input == output)
+ }
+ is_correct = function(){
+ if (count == 3){
+ quit("no", 31)
+ }
+ }
+ check_empty(odd_or_even(3))
+ check(odd_or_even(6), "EVEN")
+ check(odd_or_even(1), "ODD")
+ check(odd_or_even(10), "EVEN")
+ check(odd_or_even(777), "ODD")
+ check(odd_or_even(778), "EVEN")
+ count = 3
+ is_correct()
+ '''
+ )
+ self.test_case_data = [{"test_case": self.test_case,
+ "test_case_type": "standardtestcase",
+ "weight": 0.0
+ }]
+ self.timeout_msg = ("Code took more than {0} seconds to run. "
+ "You probably have an infinite loop in"
+ " your code.").format(SERVER_TIMEOUT)
+ self.file_paths = None
+
+ def tearDown(self):
+ os.remove(self.tmp_file)
+ shutil.rmtree(self.in_dir)
+
+ def test_correct_answer(self):
+ # Given
+ user_answer = dedent(
+ '''
+ odd_or_even <- function(n){
+ if(n %% 2 == 0){
+ return("EVEN")
+ }
+ return("ODD")
+ }
+ '''
+ )
+ kwargs = {'metadata': {
+ 'user_answer': user_answer,
+ 'file_paths': self.file_paths,
+ 'partial_grading': False,
+ 'language': 'r'},
+ 'test_case_data': self.test_case_data,
+ }
+
+ # When
+ grader = Grader(self.in_dir)
+ result = grader.evaluate(kwargs)
+
+ # Then
+ self.assertTrue(result.get('success'))
+
+ def test_incorrect_answer(self):
+ # Given
+ user_answer = dedent(
+ '''
+ odd_or_even <- function(n){
+ if(n %% 2 == 0){
+ return("ODD")
+ }
+ return("EVEN")
+ }
+ '''
+ )
+ err = ['Error: input == output is not TRUE\nExecution halted\n']
+ kwargs = {'metadata': {
+ 'user_answer': user_answer,
+ 'file_paths': self.file_paths,
+ 'partial_grading': False,
+ 'language': 'r'},
+ 'test_case_data': self.test_case_data,
+ }
+
+ # When
+ grader = Grader(self.in_dir)
+ result = grader.evaluate(kwargs)
+ errors = result.get('error')
+ # Then
+ self.assertFalse(result.get('success'))
+ self.assertEqual(errors, err)
+
+ def test_error_code(self):
+ # Given
+ user_answer = dedent(
+ '''
+ odd_or_even <- function(n){
+ a
+ }
+ '''
+ )
+ kwargs = {'metadata': {
+ 'user_answer': user_answer,
+ 'file_paths': self.file_paths,
+ 'partial_grading': False,
+ 'language': 'r'},
+ 'test_case_data': self.test_case_data,
+ }
+
+ # When
+ grader = Grader(self.in_dir)
+ result = grader.evaluate(kwargs)
+ errors = result.get('error')
+
+ # Then
+ self.assertFalse(result.get("success"))
+ self.assertIn("object 'a' not found", errors[0])
+
+ def test_empty_function(self):
+ # Given
+ user_answer = dedent(
+ '''
+ odd_or_even <- function(n){
+ }
+ '''
+ )
+ kwargs = {'metadata': {
+ 'user_answer': user_answer,
+ 'file_paths': self.file_paths,
+ 'partial_grading': False,
+ 'language': 'r'},
+ 'test_case_data': self.test_case_data,
+ }
+
+ # When
+ grader = Grader(self.in_dir)
+ result = grader.evaluate(kwargs)
+ errors = result.get('error')
+
+ # Then
+ self.assertFalse(result.get("success"))
+ self.assertIn("Error: is.null(obj) == FALSE is not TRUE", errors[0])
+
+ def test_infinite_loop(self):
+ # Given
+ user_answer = dedent(
+ '''
+ odd_or_even <- function(n){
+ while(0 == 0){
+ a <- 1
+ }
+ }
+ '''
+ )
+ kwargs = {'metadata': {
+ 'user_answer': user_answer,
+ 'file_paths': self.file_paths,
+ 'partial_grading': False,
+ 'language': 'r'},
+ 'test_case_data': self.test_case_data,
+ }
+
+ # When
+ grader = Grader(self.in_dir)
+ result = grader.evaluate(kwargs)
+
+ # Then
+ self.assertFalse(result.get("success"))
+ self.assert_correct_output(self.timeout_msg,
+ result.get("error")[0]["message"]
+ )
+ parent_proc = Process(os.getpid()).children()
+ if parent_proc:
+ children_procs = Process(parent_proc[0].pid)
+ self.assertFalse(any(children_procs.children(recursive=True)))
diff --git a/yaksh/middleware/one_session_per_user.py b/yaksh/middleware/one_session_per_user.py
index 3b8d302..114c92b 100644
--- a/yaksh/middleware/one_session_per_user.py
+++ b/yaksh/middleware/one_session_per_user.py
@@ -25,14 +25,8 @@ class OneSessionPerUserMiddleware(object):
self.get_response = get_response
def __call__(self, request):
- return self.get_response(request)
-
- def process_request(self, request):
- """
- # Documentation:
- # https://docs.djangoproject.com/en/1.5/topics/auth/customizing/
- #extending-the-existing-user-model
- """
+ # Code to be executed for each request before
+ # the view (and later middleware) are called.
if isinstance(request.user, User):
current_key = request.session.session_key
if hasattr(request.user, 'concurrentuser'):
@@ -46,3 +40,8 @@ class OneSessionPerUserMiddleware(object):
concurrent_user=request.user,
session_key=current_key,
)
+
+ response = self.get_response(request)
+ # Code to be executed for each request/response after
+ # the view is called.
+ return response
diff --git a/yaksh/middleware/user_time_zone.py b/yaksh/middleware/user_time_zone.py
index 92035e8..8140851 100644
--- a/yaksh/middleware/user_time_zone.py
+++ b/yaksh/middleware/user_time_zone.py
@@ -12,12 +12,17 @@ class TimezoneMiddleware(object):
self.get_response = get_response
def __call__(self, request):
- return self.get_response(request)
-
- def process_request(self, request):
+ # Code to be executed for each request before
+ # the view (and later middleware) are called.
user = request.user
user_tz = 'Asia/Kolkata'
if hasattr(user, 'profile'):
if user.profile.timezone:
user_tz = user.profile.timezone
timezone.activate(pytz.timezone(user_tz))
+
+ response = self.get_response(request)
+
+ # Code to be executed for each request/response after
+ # the view is called.
+ return response
diff --git a/yaksh/models.py b/yaksh/models.py
index 12c902b..52a0414 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -841,6 +841,15 @@ class LearningModule(models.Model):
write_templates_to_zip(zip_file, module_file_path, module_data,
module_name, folder_name)
+ def get_unit_order(self, type, unit):
+ if type == "lesson":
+ order = self.get_learning_units().get(
+ type=type, lesson=unit).order
+ else:
+ order = self.get_learning_units().get(
+ type=type, quiz=unit).order
+ return order
+
def __str__(self):
return self.name
diff --git a/yaksh/r_code_evaluator.py b/yaksh/r_code_evaluator.py
index ca4c94a..11bc970 100644
--- a/yaksh/r_code_evaluator.py
+++ b/yaksh/r_code_evaluator.py
@@ -49,7 +49,7 @@ class RCodeEvaluator(BaseEvaluator):
# Throw message if there are commmands that terminates scilab
add_err = ""
if terminate_commands:
- add_err = "Please do not use quit() in your\
+ add_err = "Please do not use quit() q() in your\
code.\n Otherwise your code will not be evaluated\
correctly.\n"
@@ -79,7 +79,8 @@ class RCodeEvaluator(BaseEvaluator):
new_string = ""
terminate_commands = False
for line in string.splitlines():
- new_line = re.sub(r"quit.*$", "", line)
+ new_line = re.sub(r'quit(.*$)', "", line)
+ new_line = re.sub(r'q(.*$)', "", new_line)
if line != new_line:
terminate_commands = True
new_string = new_string + '\n' + new_line
diff --git a/yaksh/static/yaksh/js/codemirror/mode/r/index.html b/yaksh/static/yaksh/js/codemirror/mode/r/index.html
new file mode 100644
index 0000000..6dd9634
--- /dev/null
+++ b/yaksh/static/yaksh/js/codemirror/mode/r/index.html
@@ -0,0 +1,85 @@
+<!doctype html>
+
+<title>CodeMirror: R mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="r.js"></script>
+<style>
+ .CodeMirror { border-top: 1px solid silver; border-bottom: 1px solid silver; }
+ .cm-s-default span.cm-semi { color: blue; font-weight: bold; }
+ .cm-s-default span.cm-dollar { color: orange; font-weight: bold; }
+ .cm-s-default span.cm-arrow { color: brown; }
+ .cm-s-default span.cm-arg-is { color: brown; }
+ </style>
+<div id=nav>
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+
+ <ul>
+ <li><a href="../../index.html">Home</a>
+ <li><a href="../../doc/manual.html">Manual</a>
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
+ </ul>
+ <ul>
+ <li><a href="../index.html">Language modes</a>
+ <li><a class=active href="#">R</a>
+ </ul>
+</div>
+
+<article>
+<h2>R mode</h2>
+<form><textarea id="code" name="code">
+# Code from http://www.mayin.org/ajayshah/KB/R/
+
+# FIRST LEARN ABOUT LISTS --
+X = list(height=5.4, weight=54)
+print("Use default printing --")
+print(X)
+print("Accessing individual elements --")
+cat("Your height is ", X$height, " and your weight is ", X$weight, "\n")
+
+# FUNCTIONS --
+square <- function(x) {
+ return(x*x)
+}
+cat("The square of 3 is ", square(3), "\n")
+
+ # default value of the arg is set to 5.
+cube <- function(x=5) {
+ return(x*x*x);
+}
+cat("Calling cube with 2 : ", cube(2), "\n") # will give 2^3
+cat("Calling cube : ", cube(), "\n") # will default to 5^3.
+
+# LEARN ABOUT FUNCTIONS THAT RETURN MULTIPLE OBJECTS --
+powers <- function(x) {
+ parcel = list(x2=x*x, x3=x*x*x, x4=x*x*x*x);
+ return(parcel);
+}
+
+X = powers(3);
+print("Showing powers of 3 --"); print(X);
+
+# WRITING THIS COMPACTLY (4 lines instead of 7)
+
+powerful <- function(x) {
+ return(list(x2=x*x, x3=x*x*x, x4=x*x*x*x));
+}
+print("Showing powers of 3 --"); print(powerful(3));
+
+# In R, the last expression in a function is, by default, what is
+# returned. So you could equally just say:
+powerful <- function(x) {list(x2=x*x, x3=x*x*x, x4=x*x*x*x)}
+</textarea></form>
+ <script>
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
+ </script>
+
+ <p><strong>MIME types defined:</strong> <code>text/x-rsrc</code>.</p>
+
+ <p>Development of the CodeMirror R mode was kindly sponsored
+ by <a href="https://twitter.com/ubalo">Ubalo</a>.</p>
+
+ </article>
diff --git a/yaksh/static/yaksh/js/codemirror/mode/r/r.js b/yaksh/static/yaksh/js/codemirror/mode/r/r.js
new file mode 100644
index 0000000..d41d1c5
--- /dev/null
+++ b/yaksh/static/yaksh/js/codemirror/mode/r/r.js
@@ -0,0 +1,164 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("wordChars", "r", /[\w.]/);
+
+CodeMirror.defineMode("r", function(config) {
+ function wordObj(str) {
+ var words = str.split(" "), res = {};
+ for (var i = 0; i < words.length; ++i) res[words[i]] = true;
+ return res;
+ }
+ var atoms = wordObj("NULL NA Inf NaN NA_integer_ NA_real_ NA_complex_ NA_character_");
+ var builtins = wordObj("list quote bquote eval return call parse deparse");
+ var keywords = wordObj("if else repeat while function for in next break");
+ var blockkeywords = wordObj("if else repeat while function for");
+ var opChars = /[+\-*\/^<>=!&|~$:]/;
+ var curPunc;
+
+ function tokenBase(stream, state) {
+ curPunc = null;
+ var ch = stream.next();
+ if (ch == "#") {
+ stream.skipToEnd();
+ return "comment";
+ } else if (ch == "0" && stream.eat("x")) {
+ stream.eatWhile(/[\da-f]/i);
+ return "number";
+ } else if (ch == "." && stream.eat(/\d/)) {
+ stream.match(/\d*(?:e[+\-]?\d+)?/);
+ return "number";
+ } else if (/\d/.test(ch)) {
+ stream.match(/\d*(?:\.\d+)?(?:e[+\-]\d+)?L?/);
+ return "number";
+ } else if (ch == "'" || ch == '"') {
+ state.tokenize = tokenString(ch);
+ return "string";
+ } else if (ch == "." && stream.match(/.[.\d]+/)) {
+ return "keyword";
+ } else if (/[\w\.]/.test(ch) && ch != "_") {
+ stream.eatWhile(/[\w\.]/);
+ var word = stream.current();
+ if (atoms.propertyIsEnumerable(word)) return "atom";
+ if (keywords.propertyIsEnumerable(word)) {
+ // Block keywords start new blocks, except 'else if', which only starts
+ // one new block for the 'if', no block for the 'else'.
+ if (blockkeywords.propertyIsEnumerable(word) &&
+ !stream.match(/\s*if(\s+|$)/, false))
+ curPunc = "block";
+ return "keyword";
+ }
+ if (builtins.propertyIsEnumerable(word)) return "builtin";
+ return "variable";
+ } else if (ch == "%") {
+ if (stream.skipTo("%")) stream.next();
+ return "variable-2";
+ } else if (ch == "<" && stream.eat("-")) {
+ return "arrow";
+ } else if (ch == "=" && state.ctx.argList) {
+ return "arg-is";
+ } else if (opChars.test(ch)) {
+ if (ch == "$") return "dollar";
+ stream.eatWhile(opChars);
+ return "operator";
+ } else if (/[\(\){}\[\];]/.test(ch)) {
+ curPunc = ch;
+ if (ch == ";") return "semi";
+ return null;
+ } else {
+ return null;
+ }
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ if (stream.eat("\\")) {
+ var ch = stream.next();
+ if (ch == "x") stream.match(/^[a-f0-9]{2}/i);
+ else if ((ch == "u" || ch == "U") && stream.eat("{") && stream.skipTo("}")) stream.next();
+ else if (ch == "u") stream.match(/^[a-f0-9]{4}/i);
+ else if (ch == "U") stream.match(/^[a-f0-9]{8}/i);
+ else if (/[0-7]/.test(ch)) stream.match(/^[0-7]{1,2}/);
+ return "string-2";
+ } else {
+ var next;
+ while ((next = stream.next()) != null) {
+ if (next == quote) { state.tokenize = tokenBase; break; }
+ if (next == "\\") { stream.backUp(1); break; }
+ }
+ return "string";
+ }
+ };
+ }
+
+ function push(state, type, stream) {
+ state.ctx = {type: type,
+ indent: state.indent,
+ align: null,
+ column: stream.column(),
+ prev: state.ctx};
+ }
+ function pop(state) {
+ state.indent = state.ctx.indent;
+ state.ctx = state.ctx.prev;
+ }
+
+ return {
+ startState: function() {
+ return {tokenize: tokenBase,
+ ctx: {type: "top",
+ indent: -config.indentUnit,
+ align: false},
+ indent: 0,
+ afterIdent: false};
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (state.ctx.align == null) state.ctx.align = false;
+ state.indent = stream.indentation();
+ }
+ if (stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+ if (style != "comment" && state.ctx.align == null) state.ctx.align = true;
+
+ var ctype = state.ctx.type;
+ if ((curPunc == ";" || curPunc == "{" || curPunc == "}") && ctype == "block") pop(state);
+ if (curPunc == "{") push(state, "}", stream);
+ else if (curPunc == "(") {
+ push(state, ")", stream);
+ if (state.afterIdent) state.ctx.argList = true;
+ }
+ else if (curPunc == "[") push(state, "]", stream);
+ else if (curPunc == "block") push(state, "block", stream);
+ else if (curPunc == ctype) pop(state);
+ state.afterIdent = style == "variable" || style == "keyword";
+ return style;
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != tokenBase) return 0;
+ var firstChar = textAfter && textAfter.charAt(0), ctx = state.ctx,
+ closing = firstChar == ctx.type;
+ if (ctx.type == "block") return ctx.indent + (firstChar == "{" ? 0 : config.indentUnit);
+ else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+ else return ctx.indent + (closing ? 0 : config.indentUnit);
+ },
+
+ lineComment: "#"
+ };
+});
+
+CodeMirror.defineMIME("text/x-rsrc", "r");
+
+});
diff --git a/yaksh/static/yaksh/js/design_course.js b/yaksh/static/yaksh/js/design_course.js
index dbff9fd..4e2dc9d 100644
--- a/yaksh/static/yaksh/js/design_course.js
+++ b/yaksh/static/yaksh/js/design_course.js
@@ -20,7 +20,11 @@ $(document).ready(function(){
$(this).append('<input type="hidden" name="ordered_list" value='+order_list+'>');
return true;
});
- var msg = "Check Prerequisite is set to Yes by default \n" +
- "To change, select the Change checkbox and Click Change Prerequisite button \n";
- $("#prereq_msg").attr("title", msg);
+ var completion_msg = "This will check if the previous module is completed " +
+ "before viewing the next module."
+ $("#prereq_msg").attr("title", completion_msg);
+ $("#prereq_msg").tooltip();
+ var completion_msg = "This will check if the previous module is completed " +
+ "before viewing the next module based on quiz passing status."
+ $("#prereq_passing_msg").attr("title", completion_msg);
}); \ No newline at end of file
diff --git a/yaksh/static/yaksh/js/requesthandler.js b/yaksh/static/yaksh/js/requesthandler.js
index 7ccdef0..80b67fb 100644
--- a/yaksh/static/yaksh/js/requesthandler.js
+++ b/yaksh/static/yaksh/js/requesthandler.js
@@ -160,7 +160,8 @@ $(document).ready(function(){
'cpp': 'text/x-c++src',
'java': 'text/x-java',
'bash': 'text/x-sh',
- 'scilab': 'text/x-csrc'
+ 'scilab': 'text/x-csrc',
+ 'r':'text/x-rsrc',
}
// Code mirror Options
diff --git a/yaksh/templates/yaksh/add_course.html b/yaksh/templates/yaksh/add_course.html
index 97c6f56..0072a95 100644
--- a/yaksh/templates/yaksh/add_course.html
+++ b/yaksh/templates/yaksh/add_course.html
@@ -26,22 +26,10 @@
Add/Edit Course
</a>
</li>
- <li class="nav-item dropdown hide">
- <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="true">More</a>
- <div class="dropdown-menu hide" x-placement="bottom-start" style="position: absolute; transform: translate3d(0px, 37px, 0px); top: 0px; left: 0px; will-change: transform;">
- <a class="dropdown-item" href="{% url 'yaksh:show_all_quizzes' %}">
- View Quizzes
- </a>
- <a class="dropdown-item" href="{% url 'yaksh:show_all_lessons' %}">
- View Lessons
- </a>
- <a class="dropdown-item" href="{% url 'yaksh:show_all_modules' %}">
- View Modules
- </a>
- <a href="{% url 'grades:grading_systems'%}" class="dropdown-item" >
- View Grading Systems
+ <li class="nav-item">
+ <a href="{% url 'grades:grading_systems'%}" class="nav-link" >
+ Add/View Grading Systems
</a>
- </div>
</li>
</ul>
</div>
diff --git a/yaksh/templates/yaksh/add_exercise.html b/yaksh/templates/yaksh/add_exercise.html
index d3d9068..542d1c4 100644
--- a/yaksh/templates/yaksh/add_exercise.html
+++ b/yaksh/templates/yaksh/add_exercise.html
@@ -23,17 +23,9 @@
</div>
{% endfor %}
{% endif %}
- {% if course_id %}
- <a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}">
- <i class="fa fa-arrow-left"></i>
- Back
- </a>
- {% else %}
- <a class="btn btn-primary" href="{% url 'yaksh:show_all_quizzes' %}">
- <i class="fa fa-arrow-left"></i>
- Back
- </a>
- {% endif %}
+ <a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}">
+ <i class="fa fa-arrow-left"></i>&nbsp;Back
+ </a>
<br><br>
<form name=frm id=frm action="" method="post" >
{% csrf_token %}
diff --git a/yaksh/templates/yaksh/add_lesson.html b/yaksh/templates/yaksh/add_lesson.html
index 99fc31a..b984db0 100644
--- a/yaksh/templates/yaksh/add_lesson.html
+++ b/yaksh/templates/yaksh/add_lesson.html
@@ -26,17 +26,9 @@
<div class="container">
<div class="row justify-content-center form-group">
<div class="col-md-9 col-md-offset-4">
- {% if course_id %}
<a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}">
- <i class="fa fa-arrow-left"></i>
- Back
+ <i class="fa fa-arrow-left"></i>&nbsp;Back
</a>
- {% else %}
- <a class="btn btn-primary" href="{% url 'yaksh:show_all_lessons' %}">
- <i class="fa fa-arrow-left"></i>
- Back
- </a>
- {% endif %}
<br>
{% if messages %}
<br>
diff --git a/yaksh/templates/yaksh/add_module.html b/yaksh/templates/yaksh/add_module.html
index edbfaa2..262c009 100644
--- a/yaksh/templates/yaksh/add_module.html
+++ b/yaksh/templates/yaksh/add_module.html
@@ -110,7 +110,7 @@
<!-- Add learning Units -->
{% if status == "design" %}
<div class="container">
-<center><h3><u>Add/Edit Learning Units</h3></u></center>
+<center><h2><u>{{module.name}}</u></h2></center>
{% if course_id %}
<form action="{% url 'yaksh:design_module' module_id course_id %}" method="POST" id="design_course_form">
{% else %}
@@ -161,8 +161,7 @@
<th width="25%" colspan="2">Check Prerequisite
<br>
<a href="#" data-toggle="tooltip" id="prereq_msg">
- <span class="glyphicon glyphicon-question-sign">
- </span> What's This
+ What's This&nbsp;<i class="fa fa-question-circle"></i>
</a>
</th>
</tr>
diff --git a/yaksh/templates/yaksh/add_quiz.html b/yaksh/templates/yaksh/add_quiz.html
index 5497eeb..55e3bd6 100644
--- a/yaksh/templates/yaksh/add_quiz.html
+++ b/yaksh/templates/yaksh/add_quiz.html
@@ -35,17 +35,9 @@
</div>
{% endfor %}
{% endif %}
- {% if course_id %}
- <a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}">
- <i class="fa fa-arrow-left"></i>
- Back
- </a>
- {% else %}
- <a class="btn btn-primary" href="{% url 'yaksh:show_all_quizzes' %}">
- <i class="fa fa-arrow-left"></i>
- Back
- </a>
- {% endif %}
+ <a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}">
+ <i class="fa fa-arrow-left"></i>&nbsp;Back
+ </a>
<br><br>
<form name=frm id=frm action="" method="post" >
{% csrf_token %}
diff --git a/yaksh/templates/yaksh/course_added_modules.html b/yaksh/templates/yaksh/course_added_modules.html
index c70eb7a..2d194b9 100644
--- a/yaksh/templates/yaksh/course_added_modules.html
+++ b/yaksh/templates/yaksh/course_added_modules.html
@@ -1,46 +1,113 @@
{% if is_modules %}
{% block pagetitle %} <center> <h3>Course Modules</h3> </center> {% endblock %}
+ <a href="{% url 'yaksh:add_module' course.id %}" class="btn btn-primary btn-lg">
+ <i class="fa fa-plus-circle"></i>&nbsp;Add Module
+ </a>
+ <br><br>
{% if modules %}
- <table class="table table-responsive">
- <tr>
- <th>Module</th>
- <th>Module Design</th>
- <th>Lessons/Quizzes</th>
- </tr>
+ <center>
+ <div class="alert alert-dismissible alert-info">
+ <strong>
+ For additional module settings, Click on Design Module
+ </strong>
+ </div>
+ </center>
{% for module in modules %}
- <tr>
- <td>
- <a href="{% url 'yaksh:edit_module' module.id course.id %}">
- {{module.name}}</a>
- </td>
- <td>
- <a href="{% url 'yaksh:design_module' module.id course.id %}">
- Add Quizzes/Lessons for {{module.name}}
- </a>
- </td>
- <td>
- {% for unit in module.get_learning_units %}
- <ul class="inputs-list">
- <li>
- {% if unit.type == "quiz" %}
- {% if unit.quiz.is_exercise %}
- <a href="{% url 'yaksh:edit_exercise' unit.quiz.id course.id %}">
- {{unit.quiz.description}}</a>
+ <div class="card">
+ <div class="card-header">
+ <a href="{% url 'yaksh:edit_module' course.id module.id %}">
+ <i class="fa fa-edit"></i>&nbsp;{{module.name}}
+ </a>
+ </div>
+ <div class="card-body">
+ <div class="row">
+ <div class="col">
+ <a href="{% url 'yaksh:edit_lesson' course.id module.id %}" class="btn btn-info">
+ <i class="fa fa-plus-circle"></i>&nbsp;Add Lesson
+ </a>
+ </div>
+ <div class="col">
+ <a href="{% url 'yaksh:add_quiz' course.id module.id %}" class="btn btn-success">
+ <i class="fa fa-plus-circle"></i>&nbsp;Add Quiz
+ </a>
+ </div>
+ <div class="col">
+ <a href="{% url 'yaksh:add_exercise' course.id module.id %}" class="btn btn-dark">
+ <i class="fa fa-plus-circle"></i>&nbsp;Add Exercise
+ </a>
+ </div>
+ <div class="col">
+ <a href="{% url 'yaksh:design_module' module.id course.id %}" class="btn btn-secondary">
+ Design Module
+ </a>
+ </div>
+ </div>
+ <br>
+ {% with module.get_learning_units as units %}
+ {% if units %}
+ <p><b><u>Lessons/Quizzes/Exercise</u></b><p>
+ <table class="table table-responsive-sm">
+ {% for unit in units %}
+ <tr>
+ <td>
+ {% if unit.type == "quiz" %}
+ {% if unit.quiz.is_exercise %}
+ <a href="{% url 'yaksh:edit_exercise' course.id module.id unit.quiz.id %}">
+ {{unit.quiz.description}}</a>
+ {% else %}
+ <a href="{% url 'yaksh:edit_quiz' course.id module.id unit.quiz.id %}">
+ {{unit.quiz.description}}</a>
+ {% endif %}
+ {% else %}
+ <a href="{% url 'yaksh:edit_lesson' course.id module.id unit.lesson.id %}">
+ {{unit.lesson.name}}</a>
+ {% endif %}
+ </td>
+ <td>
+ {% if unit.type == "quiz" %}
+ {% with unit.quiz as quiz %}
+ {% if quiz.questionpaper_set.get.id %}
+ <a href="{% url 'yaksh:designquestionpaper' course.id quiz.id quiz.questionpaper_set.get.id %}" class="btn btn-primary">
+ <i class="fa fa-edit"></i>
+ Edit Question Paper
+ </a>
+ {% else %}
+ <a href="{% url 'yaksh:designquestionpaper' course.id quiz.id %}" class="btn btn-success">
+ <i class="fa fa-plus-circle"></i>
+ Add Question Paper
+ </a>
+ {% endif %}
+ {% endwith %}
+ {% else %}
+ -------
+ {% endif %}
+ </td>
+ <td>
+ {% if unit.type == "quiz" %}
+ {% if unit.quiz.is_exercise %}
+ Exercise
+ {% else %}
+ Quiz
+ {% endif %}
+ {% else %}
+ Lesson
+ {% endif %}
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
{% else %}
- <a href="{% url 'yaksh:edit_quiz' unit.quiz.id course.id %}">
- {{unit.quiz.description}}</a>
+ <center>
+ <span class="badge badge-warning">
+ <big>No lesson/quiz added</big>
+ </span>
+ </center>
{% endif %}
- {% else %}
- <a href="{% url 'yaksh:edit_lesson' unit.lesson.id course.id %}">
- {{unit.lesson.name}}</a>
- {% endif %}
- </li>
- </ul>
- {% endfor %}
- </td>
- </tr>
- {% endfor %} <!-- end for modules -->
- </table>
+ {% endwith %}
+ </div>
+ </div>
+ <br>
+ {% endfor %}
{% else %}
<center>
<span class="badge badge-warning"><big>No learning modules</big></span>
diff --git a/yaksh/templates/yaksh/course_detail_options.html b/yaksh/templates/yaksh/course_detail_options.html
index 6f9a711..90662d6 100644
--- a/yaksh/templates/yaksh/course_detail_options.html
+++ b/yaksh/templates/yaksh/course_detail_options.html
@@ -5,28 +5,28 @@
</a>
</li>
<li class="nav-item">
- <a href="{% url 'yaksh:course_students' course.id %}" id="enroll-students" class="nav-link list-group-item {% if is_students %} active {% endif %}" title="View the course requested, rejected and added students" data-placement="top" data-toggle="tooltip">
+ <a href="{% url 'yaksh:course_students' course.id %}" id="enroll-students" class="nav-link list-group-item {% if is_students %} active {% endif %}" title="View the course requested, rejected and enrolled students" data-placement="top" data-toggle="tooltip">
Enroll Students
</a>
</li>
<li class="nav-item">
- <a href="{% url 'yaksh:send_mail' course.id %}" class="nav-link list-group-item {% if is_mail %} active {% endif %}" title="Send mail to course students" data-placement="top" data-toggle="tooltip">
- Send Mail
+ <a class="nav-link list-group-item {% if is_modules %} active {% endif %}" href="{% url 'yaksh:get_course_modules' course.id %}" title="View modules added to the course" data-placement="top" data-toggle="tooltip">
+ Course Modules
</a>
</li>
<li class="nav-item">
- <a href="{% url 'yaksh:course_status' course.id %}" class="nav-link list-group-item {% if is_progress %} active {% endif %}" title="View Students course progress" data-placement="top" data-toggle="tooltip">
- Course Progress
+ <a class="nav-link list-group-item {% if is_design_course %} active {% endif %}" href="{% url 'yaksh:design_course' course.id %}" title="Additional course settings" data-placement="top" data-toggle="tooltip">
+ Design Course
</a>
</li>
<li class="nav-item">
- <a class="nav-link list-group-item {% if is_design_course %} active {% endif %}" href="{% url 'yaksh:design_course' course.id %}" title="Add modules to this course" data-placement="top" data-toggle="tooltip">
- Design Course
+ <a href="{% url 'yaksh:course_status' course.id %}" class="nav-link list-group-item {% if is_progress %} active {% endif %}" title="View Students course progress" data-placement="top" data-toggle="tooltip">
+ Course Progress
</a>
</li>
<li class="nav-item">
- <a class="nav-link list-group-item {% if is_modules %} active {% endif %}" href="{% url 'yaksh:get_course_modules' course.id %}" title="View modules added to the course" data-placement="top" data-toggle="tooltip">
- Course Modules
+ <a href="{% url 'yaksh:send_mail' course.id %}" class="nav-link list-group-item {% if is_mail %} active {% endif %}" title="Send mail to course students" data-placement="top" data-toggle="tooltip">
+ Send Mail
</a>
</li>
<li class="nav-item">
diff --git a/yaksh/templates/yaksh/course_modules.html b/yaksh/templates/yaksh/course_modules.html
index 214f8c7..dd7b68d 100644
--- a/yaksh/templates/yaksh/course_modules.html
+++ b/yaksh/templates/yaksh/course_modules.html
@@ -128,9 +128,11 @@
View
</a>
{% else %}
- <a href="{% url 'yaksh:start_quiz' unit.quiz.questionpaper_set.get.id module.id course.id %}" class="btn btn-outline-info">
- View
- </a>
+ {% if unit.quiz.questionpaper_set.get %}
+ <a href="{% url 'yaksh:start_quiz' unit.quiz.questionpaper_set.get.id module.id course.id %}" class="btn btn-outline-info">
+ View
+ </a>
+ {% endif %}
{% endif %}
</td>
<td>
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
index 084d0f6..a590f8e 100644
--- a/yaksh/templates/yaksh/courses.html
+++ b/yaksh/templates/yaksh/courses.html
@@ -8,14 +8,6 @@
</script>
{% endblock %}
-{% block css %}
-<style>
- .test + .tooltip.top > .tooltip-inner {
- padding: 15px;
- font-size: 12px;
- }
-</style>
-{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="container">
@@ -32,22 +24,10 @@
Add/Edit Course
</a>
</li>
- <li class="nav-item dropdown hide">
- <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="true">More</a>
- <div class="dropdown-menu hide" x-placement="bottom-start" style="position: absolute; transform: translate3d(0px, 37px, 0px); top: 0px; left: 0px; will-change: transform;">
- <a class="dropdown-item" href="{% url 'yaksh:show_all_quizzes' %}">
- Add/View Quizzes
- </a>
- <a class="dropdown-item" href="{% url 'yaksh:show_all_lessons' %}">
- Add/View Lessons
- </a>
- <a class="dropdown-item" href="{% url 'yaksh:show_all_modules' %}">
- Add/View Modules
- </a>
- <a href="{% url 'grades:grading_systems'%}" class="dropdown-item" >
+ <li class="nav-item">
+ <a href="{% url 'grades:grading_systems'%}" class="nav-link" >
Add/View Grading Systems
</a>
- </div>
</li>
</ul>
</div>
diff --git a/yaksh/templates/yaksh/design_course_session.html b/yaksh/templates/yaksh/design_course_session.html
index a15f4b1..88ecc16 100644
--- a/yaksh/templates/yaksh/design_course_session.html
+++ b/yaksh/templates/yaksh/design_course_session.html
@@ -68,16 +68,14 @@
<th width="25%" colspan="2">Check Prerequisite Completion
<br>
<a href="#" data-toggle="tooltip" id="prereq_msg">
- <span class="glyphicon glyphicon-question-sign">
- </span> What's This
+ What's This&nbsp;<i class="fa fa-question-circle"></i>
</a>
</th>
<th width="25%" colspan="2">Check Prerequisite Passing
<br>
<a href="#" data-toggle="tooltip" id="prereq_passing_msg">
- <span class="glyphicon glyphicon-question-sign">
- </span> What's This
- </a>
+ What's This&nbsp;<i class="fa fa-question-circle"></i>
+ </a>
</th>
</tr>
<tr>
diff --git a/yaksh/templates/yaksh/design_questionpaper.html b/yaksh/templates/yaksh/design_questionpaper.html
index 6e916a3..ffbdf5f 100644
--- a/yaksh/templates/yaksh/design_questionpaper.html
+++ b/yaksh/templates/yaksh/design_questionpaper.html
@@ -17,17 +17,10 @@
{% block content %}
<div class="container">
<input type=hidden id="url_root" value={{ URL_ROOT }}>
-{% if course_id %}
- <form action="{% url 'yaksh:designquestionpaper' qpaper.quiz.id qpaper.id course_id %}" method="POST" id="design_q">
+ <form action="{% url 'yaksh:designquestionpaper' course_id qpaper.quiz.id qpaper.id %}" method="POST" id="design_q">
<a href="{% url 'yaksh:get_course_modules' course_id %}" class="btn btn-primary">
<i class="fa fa-arrow-left"></i>&nbsp;Back
</a>
-{% else %}
- <form action="{% url 'yaksh:designquestionpaper' qpaper.quiz.id qpaper.id %}" method="POST" id="design_q">
- <a href="{% url 'yaksh:show_all_quizzes' %}" class="btn btn-primary">
- <i class="fa fa-arrow-left"></i>&nbsp;Back
- </a>
-{% endif %}
{% csrf_token %}
<input type=hidden name="is_active" id="is_active" value="{{ state }}">
<center><b>Manual mode to design the {{lang}} Question Paper</center><br>
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 74343f8..92d591f 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -26,6 +26,7 @@
<script src="{% static 'yaksh/js/codemirror/mode/python/python.js' %}"></script>
<script src="{% static 'yaksh/js/codemirror/mode/clike/clike.js' %}"></script>
<script src="{% static 'yaksh/js/codemirror/mode/shell/shell.js' %}"></script>
+<script src="{% static 'yaksh/js/codemirror/mode/r/r.js' %}"></script>
<script type="text/javascript" src="{% static 'yaksh/js/mathjax/MathJax.js' %}?config=TeX-MML-AM_CHTML"></script>
<script src="{% static 'yaksh/js/jquery-sortable.js' %}"></script>
<script>
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 569d4d7..8f811c5 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -1083,6 +1083,10 @@ class TestAddQuiz(TestCase):
enrollment="Enroll Request", creator=self.user
)
+ self.module = LearningModule.objects.create(
+ name="My test module", creator=self.user, description="Test"
+ )
+
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),
@@ -1099,6 +1103,12 @@ class TestAddQuiz(TestCase):
is_exercise=True, description='demo exercise', creator=self.user
)
+ unit1 = LearningUnit.objects.create(
+ type="quiz", quiz=self.quiz, order=1)
+ unit2 = LearningUnit.objects.create(
+ type="quiz", quiz=self.exercise, order=2)
+ self.module.learning_unit.add(*[unit1.id, unit2.id])
+
def tearDown(self):
self.client.logout()
self.user.delete()
@@ -1112,10 +1122,17 @@ class TestAddQuiz(TestCase):
"""
If not logged in redirect to login page
"""
- response = self.client.get(reverse('yaksh:add_quiz'),
- follow=True
- )
- redirect_destination = '/exam/login/?next=/exam/manage/addquiz/'
+ response = self.client.get(
+ reverse('yaksh:add_quiz',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}),
+ follow=True
+ )
+ redirect_destination = (
+ '/exam/login/?next=/exam/manage/addquiz/{0}/{1}/'.format(
+ self.course.id, self.module.id
+ )
+ )
self.assertRedirects(response, redirect_destination)
def test_add_quiz_denies_non_moderator(self):
@@ -1126,9 +1143,12 @@ class TestAddQuiz(TestCase):
username=self.student.username,
password=self.student_plaintext_pass
)
- response = self.client.get(reverse('yaksh:add_quiz'),
- follow=True
- )
+ response = self.client.get(
+ reverse('yaksh:add_quiz',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}),
+ follow=True
+ )
self.assertEqual(response.status_code, 404)
def test_add_quiz_get(self):
@@ -1139,8 +1159,12 @@ class TestAddQuiz(TestCase):
username=self.user.username,
password=self.user_plaintext_pass
)
- response = self.client.get(reverse('yaksh:add_quiz')
- )
+ response = self.client.get(
+ reverse('yaksh:add_quiz',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}
+ )
+ )
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'yaksh/add_quiz.html')
self.assertIsNotNone(response.context['form'])
@@ -1156,7 +1180,9 @@ class TestAddQuiz(TestCase):
tzone = pytz.timezone('UTC')
response = self.client.post(
reverse('yaksh:edit_quiz',
- kwargs={'quiz_id': self.quiz.id}),
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id,
+ 'quiz_id': self.quiz.id}),
data={
'start_date_time': '2016-01-10 09:00:15',
'end_date_time': '2016-01-15 09:00:15',
@@ -1198,7 +1224,9 @@ class TestAddQuiz(TestCase):
tzone = pytz.timezone('UTC')
response = self.client.post(
- reverse('yaksh:add_quiz'),
+ reverse('yaksh:add_quiz',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}),
data={
'start_date_time': '2016-01-10 09:00:15',
'end_date_time': '2016-01-15 09:00:15',
@@ -1233,11 +1261,18 @@ class TestAddQuiz(TestCase):
"""
If not logged in redirect to login page
"""
- response = self.client.get(reverse('yaksh:add_exercise'),
- follow=True
- )
- redirect_destination = '/exam/login/?next=/exam/manage/add_exercise/'
- self.assertRedirects(response, redirect_destination)
+ response = self.client.get(
+ reverse('yaksh:add_exercise',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}),
+ follow=True
+ )
+ redirect = (
+ '/exam/login/?next=/exam/manage/add_exercise/{0}/{1}/'.format(
+ self.course.id, self.module.id
+ )
+ )
+ self.assertRedirects(response, redirect)
def test_add_exercise_denies_non_moderator(self):
"""
@@ -1247,9 +1282,12 @@ class TestAddQuiz(TestCase):
username=self.student.username,
password=self.student_plaintext_pass
)
- response = self.client.get(reverse('yaksh:add_exercise'),
- follow=True
- )
+ response = self.client.get(
+ reverse('yaksh:add_exercise',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}),
+ follow=True
+ )
self.assertEqual(response.status_code, 404)
def test_add_exercise_get(self):
@@ -1260,8 +1298,11 @@ class TestAddQuiz(TestCase):
username=self.user.username,
password=self.user_plaintext_pass
)
- response = self.client.get(reverse('yaksh:add_exercise')
- )
+ response = self.client.get(
+ reverse('yaksh:add_exercise',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id})
+ )
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'yaksh/add_exercise.html')
self.assertIsNotNone(response.context['form'])
@@ -1276,7 +1317,9 @@ class TestAddQuiz(TestCase):
)
response = self.client.post(
reverse('yaksh:edit_exercise',
- kwargs={'quiz_id': self.exercise.id}),
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id,
+ 'quiz_id': self.exercise.id}),
data={
'description': 'updated demo exercise',
'active': True
@@ -1301,7 +1344,9 @@ class TestAddQuiz(TestCase):
password=self.user_plaintext_pass
)
response = self.client.post(
- reverse('yaksh:add_exercise'),
+ reverse('yaksh:add_exercise',
+ kwargs={'course_id': self.course.id,
+ 'module_id': self.module.id}),
data={
'description': "Demo Exercise",
'active': True
@@ -1317,19 +1362,6 @@ class TestAddQuiz(TestCase):
self.assertEqual(new_exercise.pass_criteria, 0)
self.assertTrue(new_exercise.is_exercise)
- def test_show_all_quizzes(self):
- self.client.login(
- username=self.user.username,
- password=self.user_plaintext_pass
- )
- response = self.client.get(
- reverse('yaksh:show_all_quizzes'),
- follow=True
- )
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context['quizzes'][0], self.quiz)
- self.assertTemplateUsed(response, "yaksh/quizzes.html")
-
class TestAddAsModerator(TestCase):
def setUp(self):
@@ -2344,51 +2376,6 @@ class TestSearchFilters(TestCase):
self.assertIsNotNone(response.context['form'])
self.assertIn(self.user1_course1, response.context['courses'])
- def test_quizzes_search_filter(self):
- """ Test to check if quizzes are obtained with tags and status """
- self.client.login(
- username=self.user1.username,
- password=self.user1_plaintext_pass
- )
- response = self.client.post(
- reverse('yaksh:show_all_quizzes'),
- data={'quiz_tags': 'demo', 'quiz_status': 'active'}
- )
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'yaksh/quizzes.html')
- self.assertIsNotNone(response.context['form'])
- self.assertIn(self.quiz1, response.context['quizzes'])
-
- def test_lessons_search_filter(self):
- """ Test to check if lessons are obtained with tags and status """
- self.client.login(
- username=self.user1.username,
- password=self.user1_plaintext_pass
- )
- response = self.client.post(
- reverse('yaksh:show_all_lessons'),
- data={'lesson_tags': 'demo', 'lesson_status': 'active'}
- )
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'yaksh/lessons.html')
- self.assertIsNotNone(response.context['form'])
- self.assertIn(self.lesson1, response.context['lessons'])
-
- def test_learning_modules_search_filter(self):
- """ Test to check if learning modules are obtained with tags and status """
- self.client.login(
- username=self.user1.username,
- password=self.user1_plaintext_pass
- )
- response = self.client.post(
- reverse('yaksh:show_all_modules'),
- data={'module_tags': 'demo', 'module_status': 'active'}
- )
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'yaksh/modules.html')
- self.assertIsNotNone(response.context['form'])
- self.assertIn(self.learning_module1, response.context['modules'])
-
class TestAddCourse(TestCase):
def setUp(self):
@@ -5491,14 +5478,17 @@ class TestQuestionPaper(TestCase):
response = self.client.get(
reverse('yaksh:designquestionpaper',
- kwargs={"quiz_id": self.demo_quiz.id,
- "questionpaper_id": self.question_paper.id}))
+ kwargs={
+ "course_id": self.course.id,
+ "quiz_id": self.demo_quiz.id,
+ "questionpaper_id": self.question_paper.id}))
self.assertEqual(response.status_code, 404)
# Design question paper for a quiz
response = self.client.post(
reverse('yaksh:designquestionpaper',
- kwargs={"quiz_id": self.quiz_without_qp.id}),
+ kwargs={"course_id": self.course.id,
+ "quiz_id": self.quiz_without_qp.id}),
data={"marks": "1.0", "question_type": "code"})
self.assertEqual(response.status_code, 200)
self.assertIsNotNone(response.context['questions'])
@@ -5511,7 +5501,8 @@ class TestQuestionPaper(TestCase):
response = self.client.get(
reverse('yaksh:designquestionpaper',
- kwargs={"quiz_id": self.demo_quiz.id,
+ kwargs={"course_id": self.course.id,
+ "quiz_id": self.demo_quiz.id,
"questionpaper_id": self.question_paper.id}))
self.assertEqual(response.status_code, 404)
@@ -5523,7 +5514,8 @@ class TestQuestionPaper(TestCase):
# Should not allow teacher to view question paper
response = self.client.get(
reverse('yaksh:designquestionpaper',
- kwargs={"quiz_id": self.quiz.id,
+ kwargs={"course_id": self.course.id,
+ "quiz_id": self.quiz.id,
"questionpaper_id": self.question_paper.id}))
self.assertEqual(response.status_code, 404)
@@ -5792,14 +5784,14 @@ class TestLearningModule(TestCase):
)
# Student tries to add learning module
- response = self.client.post(reverse('yaksh:add_module'),
- data={"name": "test module1",
- "description": "my test1",
- "Save": "Save"})
+ response = self.client.post(
+ reverse('yaksh:add_module', kwargs={"course_id": self.course.id}),
+ 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):
@@ -5810,10 +5802,11 @@ class TestLearningModule(TestCase):
)
# Test add new module
- response = self.client.post(reverse('yaksh:add_module'),
- data={"name": "test module1",
- "description": "my test1",
- "Save": "Save"})
+ response = self.client.post(
+ reverse('yaksh:add_module', kwargs={"course_id": self.course.id}),
+ data={"name": "test module1",
+ "description": "my test1",
+ "Save": "Save"})
self.assertEqual(response.status_code, 200)
learning_module = LearningModule.objects.get(name="test module1")
@@ -5833,7 +5826,8 @@ class TestLearningModule(TestCase):
# Test add new module
response = self.client.post(
reverse('yaksh:edit_module',
- kwargs={"module_id": self.learning_module.id}),
+ kwargs={"course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"name": "test module2",
"description": "my test2",
"Save": "Save"})
@@ -5846,17 +5840,6 @@ class TestLearningModule(TestCase):
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.assertEqual(response.context['modules'][0],
- self.learning_module)
-
def test_teacher_can_edit_module(self):
""" Check if teacher can edit the module """
self.client.login(
@@ -6178,7 +6161,8 @@ class TestLessons(TestCase):
response = self.client.get(
reverse('yaksh:edit_lesson',
kwargs={"lesson_id": self.lesson.id,
- "course_id": self.course.id}))
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}))
self.assertEqual(response.status_code, 404)
def test_teacher_can_edit_lesson(self):
@@ -6192,7 +6176,8 @@ class TestLessons(TestCase):
response = self.client.post(
reverse('yaksh:edit_lesson',
kwargs={"lesson_id": self.lesson.id,
- "course_id": self.course.id}),
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"name": "updated lesson",
"description": "updated description",
"Lesson_files": dummy_file,
@@ -6201,7 +6186,7 @@ class TestLessons(TestCase):
)
# Teacher edits existing lesson and adds file
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.status_code, 302)
updated_lesson = Lesson.objects.get(name="updated lesson")
self.assertEqual(updated_lesson.description, "updated description")
self.assertEqual(updated_lesson.creator, self.user)
@@ -6216,7 +6201,8 @@ class TestLessons(TestCase):
response = self.client.post(
reverse('yaksh:edit_lesson',
kwargs={"lesson_id": self.lesson.id,
- "course_id": self.course.id}),
+ "course_id": self.course.id,
+ "module_id": self.learning_module.id}),
data={"delete_files": [str(lesson_files.id)],
"Delete": "Delete"}
)
@@ -6287,17 +6273,6 @@ class TestLessons(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["msg"], err_msg)
- 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/lessons.html")
- self.assertEqual(response.context["lessons"][0], self.lesson)
-
def test_preview_lesson_description(self):
""" Test preview lesson description converted from md to html"""
self.client.login(
diff --git a/yaksh/urls.py b/yaksh/urls.py
index b53d335..bdc3330 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -63,15 +63,13 @@ urlpatterns = [
url(r'^manage/addquestion/$', views.add_question, name="add_question"),
url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.add_question,
name="add_question"),
- url(r'^manage/addquiz/$', views.add_quiz, name='add_quiz'),
- url(r'^manage/add_exercise/$', views.add_exercise, name='add_exercise'),
- url(r'^manage/add_exercise/(?P<quiz_id>\d+)/$', views.add_exercise,
- name='edit_exercise'),
- url(r'^manage/add_exercise/(?P<quiz_id>\d+)/(?P<course_id>\d+)/$',
+ url(r'^manage/add_exercise/(?P<course_id>\d+)/(?P<module_id>\d+)/$',
+ views.add_exercise, name='add_exercise'),
+ url(r'^manage/add_exercise/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<quiz_id>\d+)$',
views.add_exercise, name='edit_exercise'),
- url(r'^manage/addquiz/(?P<quiz_id>\d+)/$',
- views.add_quiz, name='edit_quiz'),
- url(r'^manage/addquiz/(?P<quiz_id>\d+)/(?P<course_id>\d+)$',
+ url(r'^manage/addquiz/(?P<course_id>\d+)/(?P<module_id>\d+)/$',
+ views.add_quiz, name='add_quiz'),
+ url(r'^manage/addquiz/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<quiz_id>\d+)$',
views.add_quiz, name='edit_quiz'),
url(r'^manage/gradeuser/$', views.grade_user, name="grade_user"),
url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<course_id>\d+)/$',
@@ -91,13 +89,10 @@ urlpatterns = [
'(?P<course_id>\d+)/$',
views.user_data, name="user_data"),
url(r'^manage/user_data/(?P<user_id>\d+)/$', views.user_data),
- url(r'^manage/quiz/designquestionpaper/(?P<quiz_id>\d+)/$',
- views.design_questionpaper, name='designquestionpaper'),
- url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/'
+ url(r'^manage/designquestionpaper/(?P<course_id>\d+)/(?P<quiz_id>\d+)/'
'(?P<questionpaper_id>\d+)/$',
views.design_questionpaper, name='designquestionpaper'),
- url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/'
- '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$',
+ url(r'^manage/designquestionpaper/(?P<course_id>\d+)/(?P<quiz_id>\d+)/$',
views.design_questionpaper, name='designquestionpaper'),
url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/'
'(?P<course_id>\d+)/$',
@@ -176,29 +171,19 @@ urlpatterns = [
views.download_yaml_template, name="download_yaml_template"),
url(r'^manage/download_sample_csv/',
views.download_sample_csv, name="download_sample_csv"),
- url(r'^manage/courses/edit_lesson/$',
+ url(r'^manage/courses/edit_lesson/(?P<course_id>\d+)/(?P<module_id>\d+)/$',
views.edit_lesson, name="edit_lesson"),
- url(r'^manage/courses/edit_lesson/(?P<lesson_id>\d+)/$',
- views.edit_lesson, name="edit_lesson"),
- url(r'^manage/courses/edit_lesson/(?P<lesson_id>\d+)/(?P<course_id>\d+)/$',
+ url(r'^manage/courses/edit_lesson/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<lesson_id>\d+)/$',
views.edit_lesson, name="edit_lesson"),
url(r'^manage/courses/designmodule/(?P<module_id>\d+)/$',
views.design_module, name="design_module"),
url(r'^manage/courses/designmodule/(?P<module_id>\d+)/'
'(?P<course_id>\d+)/$', views.design_module, name="design_module"),
- url(r'^manage/courses/all_quizzes/$',
- views.show_all_quizzes, name="show_all_quizzes"),
- url(r'^manage/courses/all_lessons/$',
- views.show_all_lessons, name="show_all_lessons"),
url(r'^manage/courses/lesson/preview/$',
views.preview_html_text, name="preview_html_text"),
- url(r'^manage/courses/all_learning_module/$',
- views.show_all_modules, name="show_all_modules"),
- url(r'^manage/courses/add_module/$',
+ url(r'^manage/courses/add_module/(?P<course_id>\d+)/$',
views.add_module, name="add_module"),
- url(r'^manage/courses/add_module/(?P<module_id>\d+)/$',
- views.add_module, name="edit_module"),
- url(r'^manage/courses/add_module/(?P<module_id>\d+)/(?P<course_id>\d+)/$',
+ url(r'^manage/courses/add_module/(?P<course_id>\d+)/(?P<module_id>\d+)/$',
views.add_module, name="edit_module"),
url(r'^manage/courses/designcourse/(?P<course_id>\d+)/$',
views.design_course, name="design_course"),
diff --git a/yaksh/views.py b/yaksh/views.py
index 873c227..9efcbe9 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -177,15 +177,14 @@ def quizlist_user(request, enrolled=None, msg=None):
courses = hidden_courses
title = 'Search Results'
else:
- courses = list(Course.objects.filter(
- active=True, is_trial=False,
+ enrolled_courses = user.students.filter(is_trial=False).order_by('-id')
+ remaining_courses = list(Course.objects.filter(
+ active=True, is_trial=False, hidden=False
).exclude(
- ~Q(requests=user), ~Q(rejected=user), hidden=True
- ).order_by('-id'))
- enrolled_course = list(
- user.students.filter(is_trial=False).order_by('-id')
- )
- courses.extend(enrolled_course)
+ id__in=enrolled_courses.values_list("id", flat=True)
+ ).order_by('-id'))
+ courses = list(enrolled_courses)
+ courses.extend(remaining_courses)
title = 'All Courses'
for course in courses:
@@ -320,7 +319,7 @@ def add_question(request, question_id=None):
@login_required
@email_verified
-def add_quiz(request, quiz_id=None, course_id=None):
+def add_quiz(request, course_id=None, module_id=None, quiz_id=None):
"""To add a new quiz in the database.
Create a new quiz and store it."""
user = request.user
@@ -332,6 +331,8 @@ def add_quiz(request, quiz_id=None, course_id=None):
raise Http404('This quiz does not belong to you')
else:
quiz = None
+ if module_id:
+ module = get_object_or_404(LearningModule, id=module_id)
if course_id:
course = get_object_or_404(Course, pk=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
@@ -342,9 +343,22 @@ def add_quiz(request, quiz_id=None, course_id=None):
form = QuizForm(request.POST, instance=quiz)
if form.is_valid():
if quiz is None:
+ last_unit = module.get_learning_units().last()
+ order = last_unit.order + 1 if last_unit else 1
form.instance.creator = user
- form.save()
+ else:
+ order = module.get_unit_order("quiz", quiz)
+ added_quiz = form.save()
+ unit, created = LearningUnit.objects.get_or_create(
+ type="quiz", quiz=added_quiz, order=order
+ )
+ if created:
+ module.learning_unit.add(unit.id)
messages.success(request, "Quiz saved successfully")
+ return redirect(
+ reverse("yaksh:edit_quiz",
+ args=[course_id, module_id, added_quiz.id])
+ )
else:
form = QuizForm(instance=quiz)
context["course_id"] = course_id
@@ -355,7 +369,7 @@ def add_quiz(request, quiz_id=None, course_id=None):
@login_required
@email_verified
-def add_exercise(request, quiz_id=None, course_id=None):
+def add_exercise(request, course_id=None, module_id=None, quiz_id=None):
user = request.user
if not is_moderator(user):
raise Http404('You are not allowed to view this course !')
@@ -365,6 +379,8 @@ def add_exercise(request, quiz_id=None, course_id=None):
raise Http404('This quiz does not belong to you')
else:
quiz = None
+ if module_id:
+ module = get_object_or_404(LearningModule, id=module_id)
if course_id:
course = get_object_or_404(Course, pk=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
@@ -375,7 +391,11 @@ def add_exercise(request, quiz_id=None, course_id=None):
form = ExerciseForm(request.POST, instance=quiz)
if form.is_valid():
if quiz is None:
+ last_unit = module.get_learning_units().last()
+ order = last_unit.order + 1 if last_unit else 1
form.instance.creator = user
+ else:
+ order = module.get_unit_order("quiz", quiz)
quiz = form.save(commit=False)
quiz.is_exercise = True
quiz.time_between_attempts = 0
@@ -385,9 +405,18 @@ def add_exercise(request, quiz_id=None, course_id=None):
quiz.duration = 1000
quiz.pass_criteria = 0
quiz.save()
+ unit, created = LearningUnit.objects.get_or_create(
+ type="quiz", quiz=quiz, order=order
+ )
+ if created:
+ module.learning_unit.add(unit.id)
messages.success(
request, "{0} saved successfully".format(quiz.description)
)
+ return redirect(
+ reverse("yaksh:edit_exercise",
+ args=[course_id, module_id, quiz.id])
+ )
else:
form = ExerciseForm(instance=quiz)
context["exercise"] = quiz
@@ -1366,8 +1395,7 @@ def _get_questions_from_tags(question_tags, user, active=True):
@login_required
@email_verified
-def design_questionpaper(request, quiz_id, questionpaper_id=None,
- course_id=None):
+def design_questionpaper(request, course_id, quiz_id, questionpaper_id=None):
user = request.user
que_tags = Question.objects.filter(
active=True, user=user).values_list('tags', flat=True).distinct()
@@ -2445,7 +2473,7 @@ def download_yaml_template(request):
@login_required
@email_verified
-def edit_lesson(request, lesson_id=None, course_id=None):
+def edit_lesson(request, course_id=None, module_id=None, lesson_id=None):
user = request.user
if not is_moderator(user):
raise Http404('You are not allowed to view this page!')
@@ -2455,13 +2483,13 @@ def edit_lesson(request, lesson_id=None, course_id=None):
raise Http404('This Lesson does not belong to you')
else:
lesson = None
+ if module_id:
+ module = get_object_or_404(LearningModule, id=module_id)
if course_id:
course = get_object_or_404(Course, id=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
raise Http404('This Lesson does not belong to you')
- redirect_url = reverse("yaksh:get_course_modules", args=[course_id])
- else:
- redirect_url = reverse("yaksh:show_all_lessons")
+
context = {}
if request.method == "POST":
if "Save" in request.POST:
@@ -2477,7 +2505,11 @@ def edit_lesson(request, lesson_id=None, course_id=None):
lesson.remove_file()
if lesson_form.is_valid():
if lesson is None:
+ last_unit = module.get_learning_units().last()
+ order = last_unit.order + 1 if last_unit else 1
lesson_form.instance.creator = user
+ else:
+ order = module.get_unit_order("lesson", lesson)
lesson = lesson_form.save()
lesson.html_data = get_html_text(lesson.description)
lesson.save()
@@ -2486,9 +2518,18 @@ def edit_lesson(request, lesson_id=None, course_id=None):
LessonFile.objects.get_or_create(
lesson=lesson, file=les_file
)
+ unit, created = LearningUnit.objects.get_or_create(
+ type="lesson", lesson=lesson, order=order
+ )
+ if created:
+ module.learning_unit.add(unit.id)
messages.success(
request, "Saved {0} successfully".format(lesson.name)
)
+ return redirect(
+ reverse("yaksh:edit_lesson",
+ args=[course_id, module_id, lesson.id])
+ )
else:
context['lesson_form'] = lesson_form
context['error'] = lesson_form["video_file"].errors
@@ -2592,14 +2633,14 @@ def design_module(request, module_id, course_id=None):
for order, value in enumerate(add_values, start_val):
learning_id, type = value.split(":")
if type == "quiz":
- learning_unit = LearningUnit.objects.create(
+ unit, status = LearningUnit.objects.get_or_create(
order=order, quiz_id=learning_id,
type=type)
else:
- learning_unit = LearningUnit.objects.create(
+ unit, status = LearningUnit.objects.get_or_create(
order=order, lesson_id=learning_id,
type=type)
- to_add_list.append(learning_unit)
+ to_add_list.append(unit)
learning_module.learning_unit.add(*to_add_list)
messages.success(request, "Lesson/Quiz added successfully")
else:
@@ -2662,21 +2703,20 @@ def design_module(request, module_id, course_id=None):
context['status'] = 'design'
context['module_id'] = module_id
context['course_id'] = course_id
+ context['module'] = learning_module
return my_render_to_response(request, 'yaksh/add_module.html', context)
@login_required
@email_verified
-def add_module(request, module_id=None, course_id=None):
+def add_module(request, course_id=None, module_id=None):
user = request.user
if not is_moderator(user):
raise Http404('You are not allowed to view this page!')
- redirect_url = reverse("yaksh:show_all_modules")
if course_id:
course = Course.objects.get(id=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
raise Http404('This course does not belong to you')
- redirect_url = reverse("yaksh:get_course_modules", args=[course_id])
if module_id:
module = LearningModule.objects.get(id=module_id)
if not module.creator == user and not course_id:
@@ -2689,10 +2729,14 @@ def add_module(request, module_id=None, course_id=None):
module_form = LearningModuleForm(request.POST, instance=module)
if module_form.is_valid():
if module is None:
+ last_module = course.get_learning_modules().last()
module_form.instance.creator = user
+ if last_module:
+ module_form.instance.order = last_module.order + 1
module = module_form.save()
module.html_data = get_html_text(module.description)
module.save()
+ course.learning_module.add(module.id)
messages.success(
request,
"Saved {0} successfully".format(module.name)
@@ -2709,99 +2753,6 @@ def add_module(request, module_id=None, course_id=None):
@login_required
@email_verified
-def show_all_quizzes(request):
- user = request.user
- if not is_moderator(user):
- raise Http404('You are not allowed to view this page!')
- quizzes = Quiz.objects.filter(creator=user, is_trial=False)
-
- form = SearchFilterForm()
-
- if request.method == 'POST':
- quiz_tags = request.POST.get('search_tags')
- quiz_status = request.POST.get('search_status')
-
- if quiz_status == 'select' :
- quizzes = quizzes.filter(
- description__contains=quiz_tags)
- elif quiz_status == 'active' :
- quizzes = quizzes.filter(
- description__contains=quiz_tags, active=True)
- elif quiz_status == 'closed':
- quizzes = quizzes.filter(
- description__contains=quiz_tags, active=False)
- quizzes_found = quizzes.count()
-
- context = {"quizzes": quizzes, "form": form,
- "quizzes_found": quizzes_found}
- return my_render_to_response(request, 'yaksh/quizzes.html', context)
-
-
-@login_required
-@email_verified
-def show_all_lessons(request):
- user = request.user
- if not is_moderator(user):
- raise Http404('You are not allowed to view this page!')
- lessons = Lesson.objects.filter(creator=user)
-
- form = SearchFilterForm()
-
- if request.method == 'POST':
- lesson_tags = request.POST.get('search_tags')
- lesson_status = request.POST.get('search_status')
-
- if lesson_status == 'select' :
- lessons = lessons.filter(
- description__contains=lesson_tags)
- elif lesson_status == 'active' :
- lessons = lessons.filter(
- description__contains=lesson_tags, active=True)
- elif lesson_status == 'closed':
- lessons = lessons.filter(
- description__contains=lesson_tags, active=False)
- lessons_found = lessons.count()
-
- context = {"lessons": lessons, "form": form,
- "lessons_found": lessons_found}
- return my_render_to_response(request, 'yaksh/lessons.html', context)
-
-
-@login_required
-@email_verified
-def show_all_modules(request):
- user = request.user
- if not is_moderator(user):
- raise Http404('You are not allowed to view this page!')
- learning_modules = LearningModule.objects.filter(
- creator=user, is_trial=False)
-
- form = SearchFilterForm()
-
- if request.method == 'POST':
- module_tags = request.POST.get('search_tags')
- module_status = request.POST.get('search_status')
-
- if module_status == 'select' :
- learning_modules = learning_modules.filter(
- name__contains=module_tags)
- elif module_status == 'active' :
- learning_modules = learning_modules.filter(
- name__contains=module_tags, active=True)
- elif module_status == 'closed':
- learning_modules = learning_modules.filter(
- name__contains=module_tags, active=False)
- learning_modules_found = learning_modules.count()
-
- context = {"modules": learning_modules, "form": form,
- "modules_found": learning_modules_found}
- return my_render_to_response(
- request, 'yaksh/modules.html', context
- )
-
-
-@login_required
-@email_verified
def preview_html_text(request):
user = request.user
if not is_moderator(user):
@@ -2886,10 +2837,12 @@ def design_course(request, course_id):
else:
start_val = 1
for order, value in enumerate(add_values, start_val):
- learning_module = LearningModule.objects.get(id=int(value))
- learning_module.order = order
- learning_module.save()
- to_add_list.append(learning_module)
+ module, created = LearningModule.objects.get_or_create(
+ id=int(value)
+ )
+ module.order = order
+ module.save()
+ to_add_list.append(module)
course.learning_module.add(*to_add_list)
messages.success(request, "Modules added successfully")
else: