summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorprathamesh2016-09-23 13:15:06 +0530
committerprathamesh2016-09-23 13:15:06 +0530
commitd4a0ace8042f590659b8e7ec02fcdc3a24fe0592 (patch)
treefe804651f57ba0e364a11d1719edcf3d260f5ebe
parent03e54d7f6a42c0d9207db554abba9da854d494e4 (diff)
parent8e4ead8ab4caa9366e5357840e1912cbfb8cdb85 (diff)
downloadonline_test-d4a0ace8042f590659b8e7ec02fcdc3a24fe0592.tar.gz
online_test-d4a0ace8042f590659b8e7ec02fcdc3a24fe0592.tar.bz2
online_test-d4a0ace8042f590659b8e7ec02fcdc3a24fe0592.zip
Merge branch 'master' of https://github.com/FOSSEE/online_test into regrade_papers
Resolved Conflicts: yaksh/models.py yaksh/test_models.py yaksh/urls.py yaksh/views.py
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml5
-rwxr-xr-xyaksh/bash_files/sample2.args1
-rwxr-xr-xyaksh/bash_files/sample2.sh2
-rw-r--r--yaksh/file_utils.py22
-rw-r--r--yaksh/fixtures/demo_questions.zipbin0 -> 1510 bytes
-rw-r--r--yaksh/live_server_tests/__init__.py0
-rw-r--r--yaksh/live_server_tests/load_test.py77
-rw-r--r--yaksh/live_server_tests/selenium_test.py145
-rw-r--r--yaksh/models.py110
-rw-r--r--yaksh/templates/manage.html20
-rw-r--r--yaksh/templates/yaksh/courses.html10
-rw-r--r--yaksh/templates/yaksh/showquestions.html2
-rw-r--r--yaksh/test_models.py74
-rw-r--r--yaksh/test_views.py1
-rw-r--r--yaksh/tests/test_questions.json50
-rw-r--r--yaksh/urls.py1
-rw-r--r--yaksh/views.py45
18 files changed, 518 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore
index b85f182..4f26967 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ apache/*
migrations
wsgi.log
*.sqlite3
+data/ \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 8ad6c5f..3484d2d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,11 +14,16 @@ install:
- pip install -q Django==$DJANGO
- pip install -q pytz==2016.4
- pip install -q python-social-auth==0.2.19
+ - pip install selenium
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y scilab
+ - "export DISPLAY=:99.0"
+ - "sh -e /etc/init.d/xvfb start"
+ - sleep 3 # give xvfb some time to start
# command to run tests
script:
- python manage.py test -v 2 yaksh
+ - python manage.py test -v 2 yaksh.live_server_tests
diff --git a/yaksh/bash_files/sample2.args b/yaksh/bash_files/sample2.args
new file mode 100755
index 0000000..cf4499d
--- /dev/null
+++ b/yaksh/bash_files/sample2.args
@@ -0,0 +1 @@
+file1.csv file2.csv file3.csv
diff --git a/yaksh/bash_files/sample2.sh b/yaksh/bash_files/sample2.sh
new file mode 100755
index 0000000..5dc55b8
--- /dev/null
+++ b/yaksh/bash_files/sample2.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+cat $1 | cut -d: -f2 | paste -d: $3 - $2
diff --git a/yaksh/file_utils.py b/yaksh/file_utils.py
index 8f6f6e5..afcf9e8 100644
--- a/yaksh/file_utils.py
+++ b/yaksh/file_utils.py
@@ -13,12 +13,10 @@ def copy_files(file_paths):
file_name = os.path.basename(file_path)
files.append(file_name)
shutil.copy(file_path, os.getcwd())
- if extract and zipfile.is_zipfile(file_name):
- unzip = zipfile.ZipFile(file_name)
- for zip_files in unzip.namelist():
- files.append(zip_files)
- unzip.extractall()
- unzip.close()
+ if extract:
+ z_files = extract_files(file_name)
+ for file in z_files:
+ files.append(file)
return files
@@ -31,3 +29,15 @@ def delete_files(files):
os.remove(file)
else:
shutil.rmtree(file)
+
+
+def extract_files(zip_file):
+ zfiles = []
+ if zipfile.is_zipfile(zip_file):
+ zip_file = zipfile.ZipFile(zip_file, 'r')
+ for z_file in zip_file.namelist():
+ zfiles.append(z_file)
+ zip_file.extractall()
+ zip_file.close()
+ return zfiles
+
diff --git a/yaksh/fixtures/demo_questions.zip b/yaksh/fixtures/demo_questions.zip
new file mode 100644
index 0000000..562bf8a
--- /dev/null
+++ b/yaksh/fixtures/demo_questions.zip
Binary files differ
diff --git a/yaksh/live_server_tests/__init__.py b/yaksh/live_server_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/yaksh/live_server_tests/__init__.py
diff --git a/yaksh/live_server_tests/load_test.py b/yaksh/live_server_tests/load_test.py
new file mode 100644
index 0000000..17934d4
--- /dev/null
+++ b/yaksh/live_server_tests/load_test.py
@@ -0,0 +1,77 @@
+import os
+import signal
+import subprocess
+from datetime import datetime
+import pytz
+from threading import Thread
+from selenium.webdriver.firefox.webdriver import WebDriver
+
+from django.contrib.staticfiles.testing import StaticLiveServerTestCase
+
+from yaksh.models import User, Profile, Question, Quiz, Course, QuestionPaper, TestCase
+from selenium_test import SeleniumTest
+
+from yaksh.code_server import ServerPool
+from yaksh import settings
+
+
+class YakshSeleniumTests(StaticLiveServerTestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(YakshSeleniumTests, cls).setUpClass()
+ # setup a demo code server
+ settings.code_evaluators['python']['standardtestcase'] = \
+ "yaksh.python_assertion_evaluator.PythonAssertionEvaluator"
+ settings.code_evaluators['c']['standardtestcase'] = \
+ "yaksh.cpp_code_evaluator.CppCodeEvaluator"
+ settings.code_evaluators['bash']['standardtestcase'] = \
+ "yaksh.bash_code_evaluator.BashCodeEvaluator"
+ settings.SERVER_POOL_PORT = 53578
+ code_server_pool = ServerPool(ports=settings.SERVER_PORTS, pool_port=settings.SERVER_POOL_PORT)
+ cls.code_server_pool = code_server_pool
+ cls.code_server_thread = t = Thread(target=code_server_pool.run)
+ t.start()
+
+ demo_student = User.objects.create_user(username='demo_student',
+ password='demo_student',
+ email='demo_student@test.com'
+ )
+ demo_student_profile = Profile.objects.create(user=demo_student,
+ roll_number=3, institute='IIT',
+ department='Chemical', position='Student'
+ )
+
+ demo_mod = User.objects.create_user(username='demo_mod',
+ password='demo_mod',
+ email='demo_mod@test.com'
+ )
+ demo_mod_profile = Profile.objects.create(user=demo_mod,
+ roll_number=0, institute='IIT',
+ department='Chemical', position='Moderator'
+ )
+
+ course_obj = Course()
+ course_obj.create_demo(demo_mod)
+ demo_course = Course.objects.get(id=1)
+
+ demo_course.students.add(demo_student)
+
+ @classmethod
+ def tearDownClass(cls):
+ User.objects.all().delete()
+ Question.objects.all().delete()
+ Quiz.objects.all().delete()
+ Course.objects.all().delete()
+
+ settings.SERVER_POOL_PORT = 53579
+
+ cls.code_server_pool.stop()
+ cls.code_server_thread.join()
+
+ super(YakshSeleniumTests, cls).tearDownClass()
+
+ 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)
+ 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
new file mode 100644
index 0000000..01ccc96
--- /dev/null
+++ b/yaksh/live_server_tests/selenium_test.py
@@ -0,0 +1,145 @@
+from selenium import webdriver
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.common.exceptions import WebDriverException
+
+import multiprocessing
+import argparse
+
+class SeleniumTest():
+ def __init__(self, url, quiz_name):
+ self.driver = webdriver.Firefox()
+ self.quiz_name = quiz_name
+ self.url = url
+
+ def run_load_test(self, url, username, password):
+ try:
+ self.driver.delete_all_cookies()
+ self.driver.get(self.url)
+ self.login(username, password)
+ self.open_quiz()
+ self.close_quiz()
+ self.logout()
+ self.driver.close()
+ except Exception as e:
+ with open("/tmp/yaksh_load_test_log.txt", "ab") as f:
+ f.write('Username: {0}\nError: {1}\n'.format(username, e))
+ self.driver.close()
+
+ def login(self, username, password):
+ # get the username, password and submit form elements
+ username_elem = self.driver.find_element_by_id("id_username")
+ password_elem = self.driver.find_element_by_id("id_password")
+ submit_login_elem = self.driver.find_element_by_css_selector('button.btn')
+
+ # Type in the username, password and submit form
+ username_elem.send_keys(username)
+ password_elem.send_keys(password)
+ submit_login_elem.click()
+
+ def submit_answer(self, question_label, answer, loop_count=1):
+ self.driver.implicitly_wait(2)
+ for count in range(loop_count):
+ self.driver.find_element_by_link_text(question_label).click()
+ submit_answer_elem = self.driver.find_element_by_id("check")
+ self.driver.execute_script('editor.setValue({})'.format(answer))
+ submit_answer_elem.click()
+
+ def test_c_question(self, question_label):
+ # Incorrect Answer
+ loop_count = 10
+ answer = '\"int add(int a, int b, int c)\\n{return;}\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ # Infinite Loop
+ loop_count = 3
+ answer = '\"int add(int a, int b, int c)\\n{while(1){}}\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ # Correct Answer
+ loop_count = 1
+ answer = '\"int add(int a, int b, int c)\\n{return a + b + c;}\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ def test_python_question(self, question_label):
+ # Incorrect Answer
+ loop_count = 10
+ answer = '\"def is_palindrome(s):\\n return s\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ # Infinite Loop
+ loop_count = 3
+ answer = '\"while True:\\n pass"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ # Correct Answer
+ loop_count = 1
+ answer = '\"def is_palindrome(s):\\n return s[::-1] == s\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ def test_bash_question(self, question_label):
+ # Incorrect Answer
+ loop_count = 10
+ answer = '\"#!/bin/bash\\nls\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ # Infinite Loop
+ loop_count = 3
+ answer = '\"#!/bin/bash\\nwhile [ 1 ]; do : ; done\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ # Correct Answer
+ loop_count = 1
+ answer = '\"#!/bin/bash\\ncat $1 | cut -d: -f2 | paste -d: $3 - $2\"'
+ self.submit_answer(question_label, answer, loop_count)
+
+ def open_quiz(self):
+ # open quiz link
+ quiz_link_elem = self.driver.find_element_by_link_text(self.quiz_name).click()
+
+ # Get page elements
+ start_exam_elem = WebDriverWait(self.driver, 5).until(
+ EC.presence_of_element_located((By.NAME, "start"))
+ )
+ start_exam_elem.click()
+
+ self.test_c_question(question_label=1)
+ self.test_python_question(question_label=3)
+ self.test_bash_question(question_label=2)
+
+ def close_quiz(self):
+ quit_link_elem = WebDriverWait(self.driver, 5).until(
+ EC.presence_of_element_located((By.ID, "login_again"))
+ )
+ quit_link_elem.click()
+
+ def logout(self):
+ logout_link_elem = WebDriverWait(self.driver, 5).until(
+ EC.presence_of_element_located((By.ID, "logout"))
+ )
+ logout_link_elem.click()
+
+def user_gen(url, ids):
+ return [(url, 'User%d'%x, 'User%d'%x) for x in ids]
+
+def wrap_run_load_test(args):
+ url = "http://yaksh.fossee.aero.iitb.ac.in/exam/"
+ selenium_test = SeleniumTest(url=url, quiz_name=quiz_name)
+ return selenium_test.run_load_test(*args)
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('url', type=str, help="url of the website being tested")
+ parser.add_argument('start', type=int, help="Starting user id")
+ parser.add_argument("-n", "--number", type=int, default=10, help="number of users")
+ opts = parser.parse_args()
+
+ quiz_name = "Demo quiz"
+ selenium_test = SeleniumTest(url=opts.url, quiz_name=quiz_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()
+ pool.join()
+
diff --git a/yaksh/models.py b/yaksh/models.py
index d14943a..7c4d5c4 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -10,17 +10,23 @@ from django.forms.models import model_to_dict
from django.contrib.contenttypes.models import ContentType
from taggit.managers import TaggableManager
from django.utils import timezone
+from django.core.files import File
+from StringIO import StringIO
import pytz
import os
import stat
from os.path import join, abspath, dirname, exists
import shutil
+import zipfile
+import tempfile
+from file_utils import extract_files
from yaksh.xmlrpc_clients import code_server
# The directory where user data can be saved.
OUTPUT_DIR = abspath(join(dirname(__file__), 'output'))
+
languages = (
("python", "Python"),
("bash", "Bash"),
@@ -157,6 +163,23 @@ class Course(models.Model):
def remove_teachers(self, *teachers):
self.teachers.remove(*teachers)
+ def create_demo(self, user):
+ course = Course.objects.filter(creator=user, name="Yaksh Demo course")
+ if not course:
+ course = Course.objects.create(name="Yaksh Demo course",
+ enrollment="open",
+ creator=user)
+ quiz = Quiz()
+ demo_quiz = quiz.create_demo_quiz(course)
+ demo_ques = Question()
+ demo_ques.create_demo_questions(user)
+ demo_que_ppr = QuestionPaper()
+ demo_que_ppr.create_demo_quiz_ppr(demo_quiz)
+ success = True
+ else:
+ success = False
+ return success
+
def __unicode__(self):
return self.name
@@ -247,30 +270,35 @@ class Question(models.Model):
return json.dumps(question_data)
- def dump_into_json(self, question_ids, user):
+ def dump_questions(self, question_ids, user):
questions = Question.objects.filter(id__in=question_ids, user_id=user.id)
questions_dict = []
+ zip_file_name = StringIO()
+ zip_file = zipfile.ZipFile(zip_file_name, "a")
for question in questions:
test_case = question.get_test_cases()
+ file_names = question._add_and_get_files(zip_file)
q_dict = {'summary': question.summary,
'description': question.description,
- 'points': question.points,
- 'language': question.language,
- 'type': question.type,
- 'active': question.active,
+ 'points': question.points, 'language': question.language,
+ 'type': question.type, 'active': question.active,
'test_case_type': question.test_case_type,
'snippet': question.snippet,
- 'testcase': [case.get_field_value() for case in test_case]}
+ 'testcase': [case.get_field_value() for case in test_case],
+ 'files': file_names}
questions_dict.append(q_dict)
+ question._add_json_to_zip(zip_file, questions_dict)
+ return zip_file_name
- return json.dumps(questions_dict, indent=2)
-
- def load_from_json(self, questions_list, user):
+ def load_questions(self, questions_list, user):
questions = json.loads(questions_list)
for question in questions:
question['user'] = user
+ file_names = question.pop('files')
test_cases = question.pop('testcase')
que, result = Question.objects.get_or_create(**question)
+ if file_names:
+ que._add_files_to_db(file_names)
model_class = get_model_class(que.test_case_type)
for test_case in test_cases:
model_class.objects.get_or_create(question=que, **test_case)
@@ -297,6 +325,48 @@ class Question(models.Model):
return test_case
+ def _add_and_get_files(self, zip_file):
+ files = FileUpload.objects.filter(question=self)
+ files_list = []
+ for f in files:
+ zip_file.write(f.file.path, (os.path.basename(f.file.path)))
+ files_list.append(((os.path.basename(f.file.path)), f.extract))
+ return files_list
+
+ def _add_files_to_db(self, file_names):
+ for file_name, extract in file_names:
+ que_file = open(file_name, 'r')
+ #Converting to Python file object with some Django-specific additions
+ django_file = File(que_file)
+ FileUpload.objects.get_or_create(file=django_file,
+ question=self,
+ extract=extract)
+ os.remove(file_name)
+
+ def _add_json_to_zip(self, zip_file, q_dict):
+ json_data = json.dumps(q_dict, indent=2)
+ tmp_file_path = tempfile.mkdtemp()
+ json_path = os.path.join(tmp_file_path, "questions_dump.json")
+ with open(json_path, "w") as json_file:
+ json_file.write(json_data)
+ zip_file.write(json_path, os.path.basename(json_path))
+ zip_file.close()
+ shutil.rmtree(tmp_file_path)
+
+ def read_json(self, json_file, user):
+ if os.path.exists(json_file):
+ with open(json_file, 'r') as q_file:
+ questions_list = q_file.read()
+ self.load_questions(questions_list, user)
+ os.remove(json_file)
+
+ def create_demo_questions(self, user):
+ zip_file_path = os.path.join(os.getcwd(), 'yaksh',
+ 'fixtures', 'demo_questions.zip')
+ extract_files(zip_file_path)
+ self.read_json("questions_dump.json", user)
+
+
def __unicode__(self):
return self.summary
@@ -461,6 +531,17 @@ class Quiz(models.Model):
def has_prerequisite(self):
return True if self.prerequisite else False
+
+ def create_demo_quiz(self, course):
+ demo_quiz = Quiz.objects.create(start_date_time=timezone.now(),
+ end_date_time=timezone.now() + timedelta(176590),
+ duration=30, active=True,
+ attempts_allowed=-1,
+ time_between_attempts=0,
+ description='Yaksh Demo quiz', pass_criteria=0,
+ language='Python', prerequisite=None,
+ course=course)
+ return demo_quiz
def __unicode__(self):
desc = self.description or 'Quiz'
@@ -592,6 +673,17 @@ class QuestionPaper(models.Model):
if self.quiz.has_prerequisite():
prerequisite = self._get_prequisite_paper()
return prerequisite._is_questionpaper_passed(user)
+
+ def create_demo_quiz_ppr(self, demo_quiz):
+ question_paper = QuestionPaper.objects.create(quiz=demo_quiz,
+ total_marks=5.0,
+ shuffle_questions=True
+ )
+ questions = Question.objects.filter(active=True,
+ summary="Yaksh Demo Question")
+ # add fixed set of questions to the question paper
+ for question in questions:
+ question_paper.fixed_questions.add(question)
def __unicode__(self):
return "Question Paper for " + self.quiz.description
diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html
index 9e004e6..63c0ea7 100644
--- a/yaksh/templates/manage.html
+++ b/yaksh/templates/manage.html
@@ -78,6 +78,26 @@
<h4>Moderator's Dashboard!</h4>
<h5>Click on the button given below to add a new course.</h5>
<button class="btn" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/add_course");'>Add New Course</button>
+ <h5>Click on the button to Create a Demo course.
+ <a href="" onclick="$('#help').show(); return false;">Help </a></h5>
+ <button class="btn" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/create_demo_course");'>Create Demo Course</button>
+ <div style="display: none;" id="help">
+ <ol>
+ <ul>
+ <li>A Demo Course and Demo Quiz will be created (Click Courses link on nav bar to view courses).</li>
+ <li>Some Demo Questions are also created for you (Click Questions link on nav bar to view questions).</li>
+ <li>In Courses you can view Demo Quiz.</li>
+ <li>Click on the Demo Quiz and Click on User Mode or God Mode to take the quiz.
+ </li>
+ <li>You can also edit the Demo quiz.
+ </li>
+ </ul>
+ </p>
+ <a href="" onclick="$('#help').hide(); return false"> Close </a>
+ </div>
+ {% if msg %}
+ <h4>{{ msg }}</h4>
+ {% endif %}
</center>
{% if trial_paper %}
<h5/> You have trial papers.
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
index 109b996..43f323b 100644
--- a/yaksh/templates/yaksh/courses.html
+++ b/yaksh/templates/yaksh/courses.html
@@ -15,6 +15,10 @@
{% else %}
<center><h3> Course(s) Created</h3></center>
{% for course in courses %}
+ {% if user != course.creator %}
+ <h4> {{course.creator.get_full_name}} added you to this course</h4>
+ {% endif %}
+
<div class="row show-grid">
<div class="span14">
<div class="row">
@@ -81,10 +85,9 @@
<br><br>
{% endfor %}
{% endif %}
-
+<hr/>
{% if allotted_courses %}
<center><h3> Course(s) Allotted </h3></center>
-
{% for course in allotted_courses %}
<div class="row show-grid">
<div class="span14">
@@ -141,6 +144,7 @@
{% endif %}
</div>
</div>
+ <br/>
<button class="btn primary"type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/addquiz/{{course.id}}/");'>Add New Quiz</button>
</div>
</div>
@@ -150,6 +154,6 @@
<center><h4> No new Courses allotted</h4></center>
<br><br>
{% endif %}
+<hr/>
<center><button class="btn primary" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/add_course");'>Add New Course</button></center>
-
{% endblock %}
diff --git a/yaksh/templates/yaksh/showquestions.html b/yaksh/templates/yaksh/showquestions.html
index 2f4d218..185cbfb 100644
--- a/yaksh/templates/yaksh/showquestions.html
+++ b/yaksh/templates/yaksh/showquestions.html
@@ -11,7 +11,7 @@
{% block manage %}
-<h4>Upload json file for adding questions</h4>
+<h4>Upload ZIP file for adding questions</h4>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ upload_form.as_p }}
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 31513ad..bce2b1b 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -1,13 +1,17 @@
import unittest
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
- StdioBasedTestCase, McqTestCase
+ StdioBasedTestCase, FileUpload, McqTestCase
import json
from datetime import datetime, timedelta
from django.utils import timezone
import pytz
from django.contrib.auth.models import Group
-
+from django.core.files import File
+import zipfile
+import os
+import shutil
+import tempfile
def setUpModule():
# create user profile
@@ -58,7 +62,11 @@ def tearDownModule():
User.objects.all().delete()
Question.objects.all().delete()
Quiz.objects.all().delete()
-
+
+ que_id_list = ["25", "22", "24", "27"]
+ for que_id in que_id_list:
+ dir_path = os.path.join(os.getcwd(), "yaksh", "data","question_{0}".format(que_id))
+ shutil.rmtree(dir_path)
###############################################################################
class ProfileTestCases(unittest.TestCase):
@@ -106,6 +114,20 @@ class QuestionTestCases(unittest.TestCase):
)
self.question2.save()
+ # create a temp directory and add files for loading questions test
+ file_path = os.path.join(os.getcwd(), "yaksh", "test.txt")
+ self.load_tmp_path = tempfile.mkdtemp()
+ shutil.copy(file_path, self.load_tmp_path)
+ file1 = os.path.join(self.load_tmp_path, "test.txt")
+
+ # create a temp directory and add files for dumping questions test
+ self.dump_tmp_path = tempfile.mkdtemp()
+ shutil.copy(file_path, self.dump_tmp_path)
+ file2 = os.path.join(self.dump_tmp_path, "test.txt")
+ file = open(file2, "r")
+ django_file = File(file)
+ file = FileUpload.objects.create(file=django_file, question=self.question2)
+
self.question1.tags.add('python', 'function')
self.assertion_testcase = StandardTestCase(question=self.question1,
test_case='assert myfunc(12, 13) == 15'
@@ -122,9 +144,14 @@ class QuestionTestCases(unittest.TestCase):
"language": "Python", "type": "Code",
"test_case_type": "standardtestcase",
"testcase": self.test_case_upload_data,
+ "files": [[file1, 0]],
"summary": "Json Demo"}]
self.json_questions_data = json.dumps(questions_data)
+ def tearDown(self):
+ shutil.rmtree(self.load_tmp_path)
+ shutil.rmtree(self.dump_tmp_path)
+
def test_question(self):
""" Test question """
self.assertEqual(self.question1.summary, 'Demo question')
@@ -139,28 +166,38 @@ class QuestionTestCases(unittest.TestCase):
tag_list.append(tag.name)
self.assertEqual(tag_list, ['python', 'function'])
- def test_dump_questions_into_json(self):
+ def test_dump_questions(self):
""" Test dump questions into json """
question = Question()
question_id = [self.question2.id]
- questions = json.loads(question.dump_into_json(question_id, self.user2))
+ questions_zip = question.dump_questions(question_id, self.user2)
+ que_file = FileUpload.objects.get(question=self.question2.id)
+ zip_file = zipfile.ZipFile(questions_zip, "r")
+ tmp_path = tempfile.mkdtemp()
+ zip_file.extractall(tmp_path)
test_case = self.question2.get_test_cases()
- for q in questions:
- self.assertEqual(self.question2.summary, q['summary'])
- self.assertEqual(self.question2.language, q['language'])
- self.assertEqual(self.question2.type, q['type'])
- self.assertEqual(self.question2.description, q['description'])
- self.assertEqual(self.question2.points, q['points'])
- self.assertTrue(self.question2.active)
- self.assertEqual(self.question2.snippet, q['snippet'])
- self.assertEqual(self.question2.test_case_type, q['test_case_type'])
- self.assertEqual([case.get_field_value() for case in test_case], q['testcase'])
-
- def test_load_questions_from_json(self):
+ with open("{0}/questions_dump.json".format(tmp_path), "r") as f:
+ questions = json.loads(f.read())
+ for q in questions:
+ self.assertEqual(self.question2.summary, q['summary'])
+ self.assertEqual(self.question2.language, q['language'])
+ self.assertEqual(self.question2.type, q['type'])
+ self.assertEqual(self.question2.description, q['description'])
+ self.assertEqual(self.question2.points, q['points'])
+ self.assertTrue(self.question2.active)
+ self.assertEqual(self.question2.snippet, q['snippet'])
+ self.assertEqual(self.question2.test_case_type, q['test_case_type'])
+ self.assertEqual(os.path.basename(que_file.file.path), q['files'][0][0])
+ self.assertEqual([case.get_field_value() for case in test_case], q['testcase'])
+ for file in zip_file.namelist():
+ os.remove(os.path.join(tmp_path, file))
+
+ def test_load_questions(self):
""" Test load questions into database from json """
question = Question()
- result = question.load_from_json(self.json_questions_data, self.user1)
+ result = question.load_questions(self.json_questions_data, self.user1)
question_data = Question.objects.get(pk=25)
+ file = FileUpload.objects.get(question=25)
test_case = question_data.get_test_cases()
self.assertEqual(question_data.summary, 'Json Demo')
self.assertEqual(question_data.language, 'Python')
@@ -170,6 +207,7 @@ class QuestionTestCases(unittest.TestCase):
self.assertTrue(question_data.active)
self.assertEqual(question_data.snippet, 'def fact()')
self.assertEqual(question_data.test_case_type, 'standardtestcase')
+ self.assertEqual(os.path.basename(file.file.path), "test.txt")
self.assertEqual([case.get_field_value() for case in test_case], self.test_case_upload_data)
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index b5830ec..b52dd2a 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -650,7 +650,6 @@ class TestCourses(TestCase):
self.client = Client()
self.mod_group = Group.objects.create(name='moderator')
-
# Create Moderator with profile
self.user1_plaintext_pass = 'demo1'
self.user1 = User.objects.create_user(
diff --git a/yaksh/tests/test_questions.json b/yaksh/tests/test_questions.json
new file mode 100644
index 0000000..d617f22
--- /dev/null
+++ b/yaksh/tests/test_questions.json
@@ -0,0 +1,50 @@
+[
+ {
+ "snippet": "",
+ "testcase": [
+ {
+ "test_case": "assert is_palindrome(\"hello\") == False"
+ },
+ {
+ "test_case": "assert is_palindrome(\"nitin\") == True"
+ }
+ ],
+ "points": 3.0,
+ "test_case_type": "standardtestcase",
+ "description": "Write a function <code>is_palindrome(arg)</code> which will take one string argument. Return true if the argument is palindrome & false otherwise.\r\n<br><br>\r\nFor Example:\r\n<br>\r\n<code>is_palindrome(\"Hello\")</code> should return <code>False</code>\r\n<br>\r\n<br><br>",
+ "language": "python",
+ "active": true,
+ "type": "code",
+ "summary": "Python, check palindrome (Code)"
+ },
+ {
+ "snippet": "#!/bin/bash",
+ "testcase": [
+ {
+ "test_case": "bash_files/sample.sh, bash_files/sample.args"
+ }
+ ],
+ "points": 1.0,
+ "test_case_type": "standardtestcase",
+ "description": "Write a bash script that takes exactly two arguments and returns the sum of the numbers",
+ "language": "bash",
+ "active": true,
+ "type": "code",
+ "summary": "Bash Question Concatenate Files(Code)"
+ },
+ {
+ "snippet": "",
+ "testcase": [
+ {
+ "test_case": "c_cpp_files/main2.c"
+ }
+ ],
+ "points": 1.0,
+ "test_case_type": "standardtestcase",
+ "description": "Write a program to add 3 nos",
+ "language": "c",
+ "active": true,
+ "type": "code",
+ "summary": "selenium test"
+ }
+]
diff --git a/yaksh/urls.py b/yaksh/urls.py
index bbc5bdc..c4619b6 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -111,4 +111,5 @@ urlpatterns += [
url(r'^manage/regrade/paper/(?P<course_id>\d+)/(?P<answerpaper_id>\d+)/$',
views.regrade, name='regrade'),
url(r'^manage/(?P<mode>[\w\-]+)/(?P<quiz_id>\d+)/$', views.test_quiz),
+ url(r'^manage/create_demo_course/$', views.create_demo_course),
]
diff --git a/yaksh/views.py b/yaksh/views.py
index 44caef9..4c5b9b8 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -20,6 +20,7 @@ import pytz
from taggit.models import Tag
from itertools import chain
import json
+import zipfile
# Local imports.
from yaksh.models import get_model_class, Quiz, Question, QuestionPaper, QuestionSet, Course
from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase, FileUpload,\
@@ -30,6 +31,7 @@ from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\
get_object_form, FileForm
from settings import URL_ROOT
from yaksh.models import AssignmentUpload
+from file_utils import extract_files
@@ -54,6 +56,7 @@ def is_moderator(user):
if user.groups.filter(name='moderator').exists():
return True
+
def add_to_group(users):
""" add users to moderator group """
group = Group.objects.get(name="moderator")
@@ -61,6 +64,7 @@ def add_to_group(users):
if not is_moderator(user):
user.groups.add(group)
+
def index(request):
"""The start page.
"""
@@ -234,7 +238,7 @@ def add_quiz(request, course_id, quiz_id=None):
course = get_object_or_404(Course, pk=course_id)
ci = RequestContext(request)
if not is_moderator(user) or (user != course.creator and user not in course.teachers.all()):
- raise Http404('You are not allowed to view this page!')
+ raise Http404('You are not allowed to view this course !')
context = {}
if request.method == "POST":
if quiz_id is None:
@@ -626,6 +630,7 @@ def courses(request):
raise Http404('You are not allowed to view this page')
courses = Course.objects.filter(creator=user, is_trial=False)
allotted_courses = Course.objects.filter(teachers=user, is_trial=False)
+
context = {'courses': courses, "allotted_courses": allotted_courses}
return my_render_to_response('yaksh/courses.html', context,
context_instance=ci)
@@ -832,22 +837,25 @@ def show_all_questions(request):
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
questions_file = request.FILES['file']
- if questions_file.name.split('.')[-1] == "json":
- questions_list = questions_file.read()
- question = Question()
- question.load_from_json(questions_list, user)
+ file_name = questions_file.name.split('.')
+ if file_name[-1] == "zip":
+ ques = Question()
+ extract_files(questions_file)
+ ques.read_json("questions_dump.json", user)
else:
- message = "Please Upload a JSON file"
+ message = "Please Upload a ZIP file"
context['message'] = message
if request.POST.get('download') == 'download':
question_ids = request.POST.getlist('question')
if question_ids:
question = Question()
- questions = question.dump_into_json(question_ids, user)
- response = HttpResponse(questions, content_type='text/json')
- response['Content-Disposition'] = 'attachment; filename=\
- "{0}_questions.json"'.format(user)
+ zip_file = question.dump_questions(question_ids, user)
+ response = HttpResponse(content_type='application/zip')
+ response['Content-Disposition'] = '''attachment;\
+ filename={0}_questions.zip'''.format(user)
+ zip_file.seek(0)
+ response.write(zip_file.read())
return response
else:
context['msg'] = "Please select atleast one question to download"
@@ -1235,6 +1243,23 @@ def view_answerpaper(request, questionpaper_id):
@login_required
+def create_demo_course(request):
+ """ creates a demo course for user """
+ user = request.user
+ ci = RequestContext(request)
+ if not is_moderator(user):
+ raise("You are not allowed to view this page")
+ demo_course = Course()
+ success = demo_course.create_demo(user)
+ if success:
+ msg = "Created Demo course successfully"
+ else:
+ msg = "Demo course already created"
+ context = {'msg': msg}
+ return my_render_to_response('manage.html', context, context_instance=ci)
+
+
+@login_required
def grader(request, extra_context=None):
user = request.user
if not is_moderator(user):