summaryrefslogtreecommitdiff
path: root/testapp/yaksh_app/management
diff options
context:
space:
mode:
authorPrabhu Ramachandran2015-09-04 18:57:57 +0530
committerPrabhu Ramachandran2015-09-04 18:57:57 +0530
commit31f5e743031d105b0406e9587dc33bb065cd6e4d (patch)
tree653fbd703850e1e12f875bc5422ec3cb809c6e07 /testapp/yaksh_app/management
parentc9abbadbb0e6a4a60edb7ef2a14d6c74648b0677 (diff)
parent3ee050fe509b83f36188566ee57b3dd16d94a8a7 (diff)
downloadonline_test-31f5e743031d105b0406e9587dc33bb065cd6e4d.tar.gz
online_test-31f5e743031d105b0406e9587dc33bb065cd6e4d.tar.bz2
online_test-31f5e743031d105b0406e9587dc33bb065cd6e4d.zip
Merge pull request #55 from ankitjavalkar/yaksh
Change app name to yaksh
Diffstat (limited to 'testapp/yaksh_app/management')
-rw-r--r--testapp/yaksh_app/management/__init__.py0
-rw-r--r--testapp/yaksh_app/management/commands/__init__.py0
-rw-r--r--testapp/yaksh_app/management/commands/add_group.py33
-rw-r--r--testapp/yaksh_app/management/commands/dump_user_data.py98
-rw-r--r--testapp/yaksh_app/management/commands/load_exam.py57
-rw-r--r--testapp/yaksh_app/management/commands/load_questions_xml.py73
-rw-r--r--testapp/yaksh_app/management/commands/results2csv.py69
7 files changed, 330 insertions, 0 deletions
diff --git a/testapp/yaksh_app/management/__init__.py b/testapp/yaksh_app/management/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testapp/yaksh_app/management/__init__.py
diff --git a/testapp/yaksh_app/management/commands/__init__.py b/testapp/yaksh_app/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testapp/yaksh_app/management/commands/__init__.py
diff --git a/testapp/yaksh_app/management/commands/add_group.py b/testapp/yaksh_app/management/commands/add_group.py
new file mode 100644
index 0000000..db0ebb6
--- /dev/null
+++ b/testapp/yaksh_app/management/commands/add_group.py
@@ -0,0 +1,33 @@
+'''
+ This command adds moderator group with permissions to add, change and delete
+ the objects in the exam app.
+ We can modify this command to add more groups by providing arguments.
+ Arguments like group-name, app-name can be passed.
+'''
+
+# django imports
+from django.core.management.base import BaseCommand, CommandError
+from django.contrib.auth.models import Group, Permission
+from django.contrib.contenttypes.models import ContentType
+from django.db.utils import IntegrityError
+
+class Command(BaseCommand):
+ help = 'Adds the moderator group'
+
+ def handle(self, *args, **options):
+ app = 'yaksh_app'
+ group = Group(name='moderator')
+ try:
+ group.save()
+ except IntegrityError:
+ raise CommandError("The group already exits")
+ else:
+ # Get the models for the given app
+ content_types = ContentType.objects.filter(app_label=app)
+ # Get list of permissions for the models
+ permission_list = Permission.objects.filter(content_type=content_types)
+ for permission in permission_list:
+ group.permissions.add(permission)
+ group.save()
+
+ self.stdout.write('Moderator group added successfully')
diff --git a/testapp/yaksh_app/management/commands/dump_user_data.py b/testapp/yaksh_app/management/commands/dump_user_data.py
new file mode 100644
index 0000000..2c1a108
--- /dev/null
+++ b/testapp/yaksh_app/management/commands/dump_user_data.py
@@ -0,0 +1,98 @@
+import sys
+
+# Django imports.
+from django.core.management.base import BaseCommand
+from django.template import Template, Context
+
+# Local imports.
+from testapp.yaksh_app.views import get_user_data
+from testapp.yaksh_app.models import User
+
+data_template = Template('''\
+===============================================================================
+Data for {{ data.user.get_full_name.title }} ({{ data.user.username }})
+
+Name: {{ data.user.get_full_name.title }}
+Username: {{ data.user.username }}
+{% if data.profile %}\
+Roll number: {{ data.profile.roll_number }}
+Position: {{ data.profile.position }}
+Department: {{ data.profile.department }}
+Institute: {{ data.profile.institute }}
+{% endif %}\
+Email: {{ data.user.email }}
+Date joined: {{ data.user.date_joined }}
+Last login: {{ data.user.last_login }}
+{% for paper in data.papers %}
+Paper: {{ paper.quiz.description }}
+---------------------------------------
+Marks obtained: {{ paper.get_total_marks }}
+Questions correctly answered: {{ paper.get_answered_str }}
+Total attempts at questions: {{ paper.answers.count }}
+Start time: {{ paper.start_time }}
+User IP address: {{ paper.user_ip }}
+{% if paper.answers.count %}
+Answers
+-------
+{% for question, answers in paper.get_question_answers.items %}
+Question: {{ question.id }}. {{ question.summary }} (Points: {{ question.points }})
+{% if question.type == "mcq" %}\
+###############################################################################
+Choices: {% for option in question.options.strip.splitlines %} {{option}}, {% endfor %}
+Student answer: {{ answers.0|safe }}
+{% else %}{# non-mcq questions #}\
+{% for answer in answers %}\
+###############################################################################
+{{ answer.answer.strip|safe }}
+# Autocheck: {{ answer.error|safe }}
+{% endfor %}{# for answer in answers #}\
+{% endif %}\
+{% with answers|last as answer %}\
+Marks: {{answer.marks}}
+{% endwith %}\
+{% endfor %}{# for question, answers ... #}\
+
+Teacher comments
+-----------------
+{{ paper.comments|default:"None" }}
+{% endif %}{# if paper.answers.count #}\
+{% endfor %}{# for paper in data.papers #}
+''')
+
+
+def dump_user_data(unames, stdout):
+ '''Dump user data given usernames (a sequence) if none is given dump all
+ their data. The data is dumped to stdout.
+ '''
+ if not unames:
+ try:
+ users = User.objects.all()
+ except User.DoesNotExist:
+ pass
+ else:
+ users = []
+ for uname in unames:
+ try:
+ user = User.objects.get(username__exact = uname)
+ except User.DoesNotExist:
+ stdout.write('User %s does not exist'%uname)
+ else:
+ users.append(user)
+
+ for user in users:
+ data = get_user_data(user.username)
+ context = Context({'data': data})
+ result = data_template.render(context)
+ stdout.write(result.encode('ascii', 'xmlcharrefreplace'))
+
+class Command(BaseCommand):
+ args = '<username1> ... <usernamen>'
+ help = '''Dumps all user data to stdout, optional usernames can be
+ specified. If none is specified all user data is dumped.
+ '''
+
+ def handle(self, *args, **options):
+ """Handle the command."""
+ # Dump data.
+ dump_user_data(args, self.stdout)
+
diff --git a/testapp/yaksh_app/management/commands/load_exam.py b/testapp/yaksh_app/management/commands/load_exam.py
new file mode 100644
index 0000000..9163fdc
--- /dev/null
+++ b/testapp/yaksh_app/management/commands/load_exam.py
@@ -0,0 +1,57 @@
+# System library imports.
+from os.path import basename
+
+# Django imports.
+from django.core.management.base import BaseCommand
+
+# Local imports.
+from testapp.yaksh_app.models import Question, Quiz
+
+def clear_exam():
+ """Deactivate all questions from the database."""
+ for question in Question.objects.all():
+ question.active = False
+ question.save()
+
+ # Deactivate old quizzes.
+ for quiz in Quiz.objects.all():
+ quiz.active = False
+ quiz.save()
+
+def load_exam(filename):
+ """Load questions and quiz from the given Python file. The Python file
+ should declare a list of name "questions" which define all the questions
+ in pure Python. It can optionally load a Quiz from an optional 'quiz'
+ object.
+ """
+ # Simply exec the given file and we are done.
+ exec(open(filename).read())
+
+ if 'questions' not in locals():
+ msg = 'No variable named "questions" with the Questions in file.'
+ raise NameError(msg)
+
+ for question in questions:
+ question[0].save()
+ for tag in question[1]:
+ question[0].tags.add(tag)
+
+ if 'quiz' in locals():
+ quiz.save()
+
+class Command(BaseCommand):
+ args = '<q_file1.py q_file2.py>'
+ help = '''loads the questions from given Python files which declare the
+ questions in a list called "questions".'''
+
+ def handle(self, *args, **options):
+ """Handle the command."""
+ # Delete existing stuff.
+ clear_exam()
+
+ # Load from files.
+ for fname in args:
+ self.stdout.write('Importing from {0} ... '.format(basename(fname)))
+ load_exam(fname)
+ self.stdout.write('Done\n')
+
diff --git a/testapp/yaksh_app/management/commands/load_questions_xml.py b/testapp/yaksh_app/management/commands/load_questions_xml.py
new file mode 100644
index 0000000..7bde9e9
--- /dev/null
+++ b/testapp/yaksh_app/management/commands/load_questions_xml.py
@@ -0,0 +1,73 @@
+# System library imports.
+from os.path import basename
+from xml.dom.minidom import parse
+from htmlentitydefs import name2codepoint
+import re
+
+# Django imports.
+from django.core.management.base import BaseCommand
+
+# Local imports.
+from testapp.yaksh_app.models import Question
+
+def decode_html(html_str):
+ """Un-escape or decode HTML strings to more usable Python strings.
+ From here: http://wiki.python.org/moin/EscapingHtml
+ """
+ return re.sub('&(%s);' % '|'.join(name2codepoint),
+ lambda m: unichr(name2codepoint[m.group(1)]), html_str)
+
+def clear_questions():
+ """Deactivate all questions from the database."""
+ for question in Question.objects.all():
+ question.active = False
+ question.save()
+
+def load_questions_xml(filename):
+ """Load questions from the given XML file."""
+ q_bank = parse(filename).getElementsByTagName("question")
+
+ for question in q_bank:
+
+ summary_node = question.getElementsByTagName("summary")[0]
+ summary = (summary_node.childNodes[0].data).strip()
+
+ desc_node = question.getElementsByTagName("description")[0]
+ description = (desc_node.childNodes[0].data).strip()
+
+ type_node = question.getElementsByTagName("type")[0]
+ type = (type_node.childNodes[0].data).strip()
+
+ points_node = question.getElementsByTagName("points")[0]
+ points = float((points_node.childNodes[0].data).strip()) \
+ if points_node else 1.0
+
+ test_node = question.getElementsByTagName("test")[0]
+ test = decode_html((test_node.childNodes[0].data).strip())
+
+ opt_node = question.getElementsByTagName("options")[0]
+ opt = decode_html((opt_node.childNodes[0].data).strip())
+
+ new_question = Question(summary=summary,
+ description=description,
+ points=points,
+ options=opt,
+ type=type,
+ test=test)
+ new_question.save()
+
+class Command(BaseCommand):
+ args = '<q_file1.xml q_file2.xml>'
+ help = 'loads the questions from given XML files'
+
+ def handle(self, *args, **options):
+ """Handle the command."""
+ # Delete existing stuff.
+ clear_questions()
+
+ # Load from files.
+ for fname in args:
+ self.stdout.write('Importing from {0} ... '.format(basename(fname)))
+ load_questions_xml(fname)
+ self.stdout.write('Done\n')
+
diff --git a/testapp/yaksh_app/management/commands/results2csv.py b/testapp/yaksh_app/management/commands/results2csv.py
new file mode 100644
index 0000000..c9b05ce
--- /dev/null
+++ b/testapp/yaksh_app/management/commands/results2csv.py
@@ -0,0 +1,69 @@
+# System library imports.
+import sys
+from os.path import basename
+
+# Django imports.
+from django.core.management.base import BaseCommand
+from django.template import Template, Context
+
+# Local imports.
+from testapp.yaksh_app.models import Quiz, QuestionPaper
+
+result_template = Template('''\
+"name","username","rollno","email","answered","total","attempts","position",\
+"department","institute"
+{% for paper in papers %}\
+"{{ paper.user.get_full_name.title }}",\
+"{{ paper.user.username }}",\
+"{{ paper.profile.roll_number }}",\
+"{{ paper.user.email }}",\
+"{{ paper.get_answered_str }}",\
+{{ paper.get_total_marks }},\
+{{ paper.answers.count }},\
+"{{ paper.profile.position }}",\
+"{{ paper.profile.department }}",\
+"{{ paper.profile.institute }}"
+{% endfor %}\
+''')
+
+def results2csv(filename, stdout):
+ """Write exam data to a CSV file. It prompts the user to choose the
+ appropriate quiz.
+ """
+ qs = Quiz.objects.all()
+
+ if len(qs) > 1:
+ print "Select quiz to save:"
+ for q in qs:
+ stdout.write('%d. %s\n'%(q.id, q.description))
+ quiz_id = int(raw_input("Please select quiz: "))
+ try:
+ quiz = Quiz.objects.get(id=quiz_id)
+ except Quiz.DoesNotExist:
+ stdout.write("Sorry, quiz %d does not exist!\n"%quiz_id)
+ sys.exit(1)
+ else:
+ quiz = qs[0]
+
+ papers = QuestionPaper.objects.filter(quiz=quiz,
+ user__profile__isnull=False)
+ stdout.write("Saving results of %s to %s ... "%(quiz.description,
+ basename(filename)))
+ # Render the data and write it out.
+ f = open(filename, 'w')
+ context = Context({'papers': papers})
+ f.write(result_template.render(context))
+ f.close()
+
+ stdout.write('Done\n')
+
+class Command(BaseCommand):
+ args = '<results.csv>'
+ help = '''Writes out the results of a quiz to a CSV file. Prompt user
+ to select appropriate quiz if there are multiple.
+ '''
+
+ def handle(self, *args, **options):
+ """Handle the command."""
+ # Save to file.
+ results2csv(args[0], self.stdout)