diff options
Diffstat (limited to 'yaksh')
-rwxr-xr-x | yaksh/bash_files/sample2.args | 1 | ||||
-rwxr-xr-x | yaksh/bash_files/sample2.sh | 2 | ||||
-rw-r--r-- | yaksh/file_utils.py | 22 | ||||
-rw-r--r-- | yaksh/fixtures/demo_questions.zip | bin | 0 -> 1510 bytes | |||
-rw-r--r-- | yaksh/management/commands/create_demo_course.py | 92 | ||||
-rw-r--r-- | yaksh/models.py | 63 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 56 | ||||
-rw-r--r-- | yaksh/templates/yaksh/showquestions.html | 2 | ||||
-rw-r--r-- | yaksh/test_models.py | 74 | ||||
-rw-r--r-- | yaksh/test_views.py | 4 | ||||
-rw-r--r-- | yaksh/views.py | 32 |
11 files changed, 300 insertions, 48 deletions
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 Binary files differnew file mode 100644 index 0000000..562bf8a --- /dev/null +++ b/yaksh/fixtures/demo_questions.zip diff --git a/yaksh/management/commands/create_demo_course.py b/yaksh/management/commands/create_demo_course.py new file mode 100644 index 0000000..ef3cb6b --- /dev/null +++ b/yaksh/management/commands/create_demo_course.py @@ -0,0 +1,92 @@ +import os +from django.core.management.base import BaseCommand +from yaksh.models import Course, Question, Quiz, QuestionPaper, Profile, FileUpload +from yaksh.file_utils import extract_files +from yaksh.views import add_to_group +from django.contrib.auth.models import User +from django.utils import timezone +from datetime import datetime, timedelta +import pytz +import zipfile + + +def create_demo_course(): + """ creates a demo course, quiz """ + + success = False + print("Creating Demo User...") + # create a demo user + user, u_status = User.objects.get_or_create(username='yaksh_demo_user', + email='demo@test.com') + user.set_password("demo") + user.save() + Profile.objects.get_or_create(user=user, roll_number=0, + institute='demo_institute', + department='demo_department', + position='Faculty') + demo_user = User.objects.filter(username='yaksh_demo_user') + add_to_group(demo_user) + + print("Creating Demo Course...") + # create a demo course + course, c_status = Course.objects.get_or_create(name="Yaksh_Demo_course", + enrollment="open", + creator=user) + + print("Creating Demo Quiz...") + demo_quiz = Quiz.objects.filter(course=course) + if not demo_quiz: + # create a demo quiz + demo_quiz = Quiz.objects.get_or_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) + else: + print("Demo Quiz Already Created") + + print("Creating Demo Questions...") + questions = Question.objects.filter(active=True, summary="Yaksh Demo Question") + files = FileUpload.objects.filter(question__in=questions) + if not files: + #create demo question + f_path = os.path.join(os.getcwd(), 'yaksh', 'fixtures', 'demo_questions.json') + zip_file_path = os.path.join(os.getcwd(), 'yaksh', 'fixtures', 'demo_questions.zip') + extract_files(zip_file_path) + ques = Question() + ques.read_json("questions_dump.json", user) + + if questions: + print("Creating Demo Question Paper...") + # create a demo questionpaper + que_ppr = QuestionPaper.objects.filter(quiz=demo_quiz[0]) + if not que_ppr: + question_paper, q_ppr_status = QuestionPaper.objects.get_or_create(quiz=demo_quiz[0], + total_marks=5.0, + shuffle_questions=True + ) + # add fixed set of questions to the question paper + for question in questions: + question_paper.fixed_questions.add(question) + else: + print("Question Paper Already Created") + else: + print("Questions Not Found Please Check Question Summary") + + success = True + return success + + +class Command(BaseCommand): + help = "Create a Demo Course, Demo Quiz" + + def handle(self, *args, **options): + """ Handle command to create demo course """ + status = create_demo_course() + if status: + self.stdout.write("Successfully Created") + else: + self.stdout.write("Unable to create Demo Course") diff --git a/yaksh/models.py b/yaksh/models.py index 8e8a0d3..e7a60df 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -10,9 +10,14 @@ 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 shutil +import zipfile +import tempfile + languages = ( ("python", "Python"), @@ -228,30 +233,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) @@ -278,6 +288,41 @@ 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 __unicode__(self): return self.summary diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index 109b996..fe15342 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"> @@ -150,6 +153,53 @@ <center><h4> No new Courses allotted</h4></center> <br><br> {% endif %} +<hr/> +{% if demo_course %} +{% with demo_course as course %} + <h5> This is a Demo Course + <a href="" onclick="$('#help').show(); return false;">Help + </a></h5> + <div style="display: none;" id="help"> + <ol> + <li> This is a Demo Course + <ul> + <li><i>You will not be able to edit Demo course and Demo Quiz.</li> + <li>You can only take Demo Quiz by User Mode or God Mode.</li> + <li>This is for you to get familiar with the interface.</i></li> + </ul> + </p> + <a href="" onclick="$('#help').hide(); return false"> Close </a> + </div> + <div class="row show-grid"> + <div class="span14"> + <div class="row"> + <div class="span6"> + <p> + <b><u>Course</u></b> + {% if course.active %} + <span class="label success">Active</span> + {% else %} + <span class="label important">Closed</span> + {% endif %} + </p> + <h5>{{ course.name }}<h5/> + </br></br> + </div> + <div class="span6"> + <p><b><u>Quiz(zes)</u></b></p> + {% if course.get_quizzes %} + {% for quiz in course.get_quizzes %} + <a href="{{URL_ROOT}}/exam/manage/addquiz/{{course.id}}/{{quiz.id}}/">{{ quiz.description }}</a><br> + {% endfor %} + {% else %} + <p><b>No quiz </b></p> + {% endif %} + </div> + </div> + </div> + </div> +{% endwith %} +{% 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 ca64f8c..0056638 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 + StdioBasedTestCase, FileUpload 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 7d23ce9..4f9b18d 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -650,7 +650,9 @@ class TestCourses(TestCase): self.client = Client() self.mod_group = Group.objects.create(name='moderator') - + User.objects.get_or_create(username='demo_user', + password='demo', + email='demo@test.com') # Create Moderator with profile self.user1_plaintext_pass = 'demo1' self.user1 = User.objects.create_user( diff --git a/yaksh/views.py b/yaksh/views.py index 9f7c7a9..6644705 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -22,6 +22,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 @@ -34,6 +35,7 @@ from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\ from yaksh.xmlrpc_clients import code_server from settings import URL_ROOT from yaksh.models import AssignmentUpload +from file_utils import extract_files # The directory where user data can be saved. OUTPUT_DIR = abspath(join(dirname(__file__), 'output')) @@ -73,6 +75,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") @@ -80,6 +83,7 @@ def add_to_group(users): if not is_moderator(user): user.groups.add(group) + def index(request): """The start page. """ @@ -677,7 +681,12 @@ def courses(request): ci = RequestContext(request) if not is_moderator(user): raise Http404('You are not allowed to view this page') - courses = Course.objects.filter(creator=user, is_trial=False) + try: + demo_user = User.objects.get(username="yaksh_demo_user") + except User.DoesNotExist: + demo_user = None + courses = Course.objects.filter(Q(creator=user) | Q(creator=demo_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, @@ -885,22 +894,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" |