From 66e8f39bb390b3ec2547d82512349fabf67e3d7c Mon Sep 17 00:00:00 2001
From: mahesh
Date: Mon, 31 Jul 2017 17:22:54 +0530
Subject: Adds yaml serialization to download and upload questions

---
 yaksh/fixtures/demo_questions.zip | Bin 4430 -> 3893 bytes
 yaksh/models.py                   |  78 ++++++++++++++++++--------------------
 yaksh/test_models.py              |  21 +++++-----
 yaksh/test_views.py               |   2 +-
 yaksh/views.py                    |   2 +-
 5 files changed, 50 insertions(+), 53 deletions(-)

(limited to 'yaksh')

diff --git a/yaksh/fixtures/demo_questions.zip b/yaksh/fixtures/demo_questions.zip
index c68e7ef..e2ec37e 100644
Binary files a/yaksh/fixtures/demo_questions.zip and b/yaksh/fixtures/demo_questions.zip differ
diff --git a/yaksh/models.py b/yaksh/models.py
index 2fa34e4..f6504d7 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 from datetime import datetime, timedelta
 import json
+import yaml
 from random import sample
 from collections import Counter
 from django.db import models
@@ -26,6 +27,7 @@ from textwrap import dedent
 from .file_utils import extract_files, delete_files
 from yaksh.xmlrpc_clients import code_server
 from django.conf import settings
+from django.forms.models import model_to_dict
 
 
 languages = (
@@ -386,43 +388,38 @@ class Question(models.Model):
         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,
-                'snippet': question.snippet,
-                'testcase': [case.get_field_value() for case in test_case],
-                'files': file_names
-            }
+            q_dict = model_to_dict(question, exclude=['id', 'user','tags'])
+            q_dict['testcase']= [case.get_field_value() for case in test_case]
+            q_dict['files'] = file_names
+
             questions_dict.append(q_dict)
-        question._add_json_to_zip(zip_file, questions_dict)
+        question._add_yaml_to_zip(zip_file, questions_dict)
         return zip_file_name
 
     def load_questions(self, questions_list, user, file_path=None,
                        files_list=None):
         try:
-            questions = json.loads(questions_list)
-        except ValueError as exc_msg:
-            msg = "Error Parsing Json: {0}".format(exc_msg)
-            return msg
-        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, file_path)
-            for test_case in test_cases:
-                test_case_type = test_case.pop('test_case_type')
-                model_class = get_model_class(test_case_type)
-                new_test_case, obj_create_status = \
-                    model_class.objects.get_or_create(
-                        question=que, **test_case
-                    )
+            questions = yaml.safe_load_all(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, file_path)
+                for test_case in test_cases:
+                    test_case_type = test_case.pop('test_case_type')
+                    model_class = get_model_class(test_case_type)
+                    new_test_case, obj_create_status = \
+                        model_class.objects.get_or_create(
+                            question=que, **test_case
+                        )
                 new_test_case.type = test_case_type
                 new_test_case.save()
-        return "Questions Uploaded Successfully"
+                msg = "Questions Uploaded Successfully"
+        except yaml.scanner.ScannerError as exc_msg:
+            msg = "Error Parsing Yaml: {0}".format(exc_msg)
+        return msg
 
     def get_test_cases(self, **kwargs):
         tc_list = []
@@ -478,25 +475,24 @@ class Question(models.Model):
                 file_upload.extract = extract
                 file_upload.file.save(file_name, django_file, save=True)
 
-    def _add_json_to_zip(self, zip_file, q_dict):
-        json_data = json.dumps(q_dict, indent=2)
+    def _add_yaml_to_zip(self, zip_file, q_dict):
         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))
+        yaml_path = os.path.join(tmp_file_path, "questions_dump.yaml")
+        with open(yaml_path, "w") as yaml_file:
+            yaml.safe_dump_all(q_dict, yaml_file, default_flow_style=False)
+        zip_file.write(yaml_path, os.path.basename(yaml_path))
         zip_file.close()
         shutil.rmtree(tmp_file_path)
 
-    def read_json(self, file_path, user, files=None):
-        json_file = os.path.join(file_path, "questions_dump.json")
+    def read_yaml(self, file_path, user, files=None):
+        yaml_file = os.path.join(file_path, "questions_dump.yaml")
         msg = ""
-        if os.path.exists(json_file):
-            with open(json_file, 'r') as q_file:
+        if os.path.exists(yaml_file):
+            with open(yaml_file, 'r') as q_file:
                 questions_list = q_file.read()
                 msg = self.load_questions(questions_list, user, file_path, files)
         else:
-            msg = "Please upload zip file with questions_dump.json in it."
+            msg = "Please upload zip file with questions_dump.yaml in it."
 
         if files:
             delete_files(files, file_path)
@@ -507,7 +503,7 @@ class Question(models.Model):
             settings.FIXTURE_DIRS, 'demo_questions.zip'
         )
         files, extract_path = extract_files(zip_file_path)
-        self.read_json(extract_path, user, files)
+        self.read_yaml(extract_path, user, files)
 
     def __str__(self):
         return self.summary
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index c86d9a3..8dc3244 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -3,6 +3,7 @@ from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
     QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
     StdIOBasedTestCase, FileUpload, McqTestCase, AssignmentUpload
 import json
+import yaml
 from datetime import datetime, timedelta
 from django.utils import timezone
 import pytz
@@ -111,7 +112,7 @@ class QuestionTestCases(unittest.TestCase):
             user=self.user1
         )
 
-        self.question2 = Question.objects.create(summary='Demo Json',
+        self.question2 = Question.objects.create(summary='Yaml Json',
             language='python',
             type='code',
             active=True,
@@ -159,8 +160,8 @@ class QuestionTestCases(unittest.TestCase):
                            "language": "Python", "type": "Code",
                            "testcase": self.test_case_upload_data,
                            "files": [[file1, 0]],
-                           "summary": "Json Demo"}]
-        self.json_questions_data = json.dumps(questions_data)
+                           "summary": "Yaml Demo"}]
+        self.yaml_questions_data = yaml.safe_dump_all(questions_data)
 
     def tearDown(self):
         shutil.rmtree(self.load_tmp_path)
@@ -191,7 +192,7 @@ class QuestionTestCases(unittest.TestCase):
             self.assertIn(tag, ['python', 'function'])
 
     def test_dump_questions(self):
-        """ Test dump questions into json """
+        """ Test dump questions into Yaml """
         question = Question()
         question_id = [self.question2.id]
         questions_zip = question.dump_questions(question_id, self.user2)
@@ -200,8 +201,8 @@ class QuestionTestCases(unittest.TestCase):
         tmp_path = tempfile.mkdtemp()
         zip_file.extractall(tmp_path)
         test_case = self.question2.get_test_cases()
-        with open("{0}/questions_dump.json".format(tmp_path), "r") as f:
-            questions = json.loads(f.read())
+        with open("{0}/questions_dump.yaml".format(tmp_path), "r") as f:
+            questions = yaml.safe_load_all(f.read())
             for q in questions:
                 self.assertEqual(self.question2.summary, q['summary'])
                 self.assertEqual(self.question2.language, q['language'])
@@ -216,13 +217,13 @@ class QuestionTestCases(unittest.TestCase):
             os.remove(os.path.join(tmp_path, file))
 
     def test_load_questions(self):
-        """ Test load questions into database from json """
+        """ Test load questions into database from Yaml """
         question = Question()
-        result = question.load_questions(self.json_questions_data, self.user1)
-        question_data = Question.objects.get(summary="Json Demo")
+        result = question.load_questions(self.yaml_questions_data, self.user1)
+        question_data = Question.objects.get(summary="Yaml Demo")
         file = FileUpload.objects.get(question=question_data)
         test_case = question_data.get_test_cases()
-        self.assertEqual(question_data.summary, 'Json Demo')
+        self.assertEqual(question_data.summary, 'Yaml Demo')
         self.assertEqual(question_data.language, 'Python')
         self.assertEqual(question_data.type, 'Code')
         self.assertEqual(question_data.description, 'factorial of a no')
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index ef222e2..e5308fc 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -2983,7 +2983,7 @@ class TestShowQuestions(TestCase):
         zip_file = string_io(response.content)
         zipped_file = zipfile.ZipFile(zip_file, 'r')
         self.assertIsNone(zipped_file.testzip())
-        self.assertIn('questions_dump.json', zipped_file.namelist())
+        self.assertIn('questions_dump.yaml', zipped_file.namelist())
         zip_file.close()
         zipped_file.close()
 
diff --git a/yaksh/views.py b/yaksh/views.py
index c10ba6a..7e73a28 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -1015,7 +1015,7 @@ def show_all_questions(request):
                 if file_name[-1] == "zip":
                     ques = Question()
                     files, extract_path = extract_files(questions_file)
-                    context['message'] = ques.read_json(extract_path, user,
+                    context['message'] = ques.read_yaml(extract_path, user,
                                                         files)
                 else:
                     message = "Please Upload a ZIP file"
-- 
cgit