summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.txt7
-rw-r--r--online_test/__init__.py2
-rw-r--r--yaksh/fixtures/marks_header_missing.csv3
-rw-r--r--yaksh/fixtures/marks_header_modified.csv3
-rw-r--r--yaksh/fixtures/marks_invalid_data.csv3
-rw-r--r--yaksh/fixtures/marks_invalid_question_id.csv3
-rw-r--r--yaksh/fixtures/marks_invalid_user.csv3
-rw-r--r--yaksh/fixtures/marks_not_attempted_question.csv3
-rw-r--r--yaksh/fixtures/marks_single_question.csv3
-rw-r--r--yaksh/migrations/0027_release_0_28_0.py18
-rw-r--r--yaksh/test_views.py186
-rw-r--r--yaksh/views.py72
12 files changed, 271 insertions, 35 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index f25f0a6..972af20 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,10 @@
+=== 0.28.0 (04-11-2020) ===
+
+* Add ability for added teachers to delete posts and comments
+* Add feature to update marks using CSV upload
+* Add feature to visualise in lesson questions
+* Fix katex render to allow math inline
+
=== 0.27.0 (08-10-2020) ===
* Fix template footer CSS
diff --git a/online_test/__init__.py b/online_test/__init__.py
index c9ad251..dc5cdab 100644
--- a/online_test/__init__.py
+++ b/online_test/__init__.py
@@ -4,4 +4,4 @@ from online_test.celery_settings import app as celery_app
__all__ = ('celery_app',)
-__version__ = '0.27.0'
+__version__ = '0.28.0'
diff --git a/yaksh/fixtures/marks_header_missing.csv b/yaksh/fixtures/marks_header_missing.csv
new file mode 100644
index 0000000..8c3a747
--- /dev/null
+++ b/yaksh/fixtures/marks_header_missing.csv
@@ -0,0 +1,3 @@
+username,Q-1212-Dummy1-1.0-marks
+student1,0.9
+student2,1
diff --git a/yaksh/fixtures/marks_header_modified.csv b/yaksh/fixtures/marks_header_modified.csv
new file mode 100644
index 0000000..08ba31d
--- /dev/null
+++ b/yaksh/fixtures/marks_header_modified.csv
@@ -0,0 +1,3 @@
+username,Q-1212-Dummmy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments
+student1,0.75,fine work,1,not nice
+student2,1,good work,0,not okay
diff --git a/yaksh/fixtures/marks_invalid_data.csv b/yaksh/fixtures/marks_invalid_data.csv
new file mode 100644
index 0000000..44fb2bb
--- /dev/null
+++ b/yaksh/fixtures/marks_invalid_data.csv
@@ -0,0 +1,3 @@
+username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments
+student1,NA,good work,1,nice
+student2,1,good work,0,bad
diff --git a/yaksh/fixtures/marks_invalid_question_id.csv b/yaksh/fixtures/marks_invalid_question_id.csv
new file mode 100644
index 0000000..eb1d921
--- /dev/null
+++ b/yaksh/fixtures/marks_invalid_question_id.csv
@@ -0,0 +1,3 @@
+username,Q-12112-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments
+student1,1,good work,1,nice
+student2,1,good work,0,bad
diff --git a/yaksh/fixtures/marks_invalid_user.csv b/yaksh/fixtures/marks_invalid_user.csv
new file mode 100644
index 0000000..bd31071
--- /dev/null
+++ b/yaksh/fixtures/marks_invalid_user.csv
@@ -0,0 +1,3 @@
+username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments
+student1,1,good work,1,nice
+student452,1,good work,0,bad
diff --git a/yaksh/fixtures/marks_not_attempted_question.csv b/yaksh/fixtures/marks_not_attempted_question.csv
new file mode 100644
index 0000000..3c3e2e7
--- /dev/null
+++ b/yaksh/fixtures/marks_not_attempted_question.csv
@@ -0,0 +1,3 @@
+username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments
+student1,1,good work,1,nice
+student2,0.3,very good,1,good
diff --git a/yaksh/fixtures/marks_single_question.csv b/yaksh/fixtures/marks_single_question.csv
new file mode 100644
index 0000000..9677730
--- /dev/null
+++ b/yaksh/fixtures/marks_single_question.csv
@@ -0,0 +1,3 @@
+username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments
+student1,0.5,okay work
+student2,1,good work
diff --git a/yaksh/migrations/0027_release_0_28_0.py b/yaksh/migrations/0027_release_0_28_0.py
new file mode 100644
index 0000000..a248665
--- /dev/null
+++ b/yaksh/migrations/0027_release_0_28_0.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.7 on 2020-11-04 13:40
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('yaksh', '0026_release_0_27_0'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='answer',
+ name='comment',
+ field=models.TextField(blank=True, null=True),
+ ),
+ ]
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 3443c36..4e1343e 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -2672,14 +2672,23 @@ class TestUploadMarks(TestCase):
question=self.question2, answer="answer2",
correct=False, error=json.dumps([]), marks=0
)
+ self.answer12 = Answer(
+ question=self.question1, answer="answer12",
+ correct=False, error=json.dumps([]), marks=0
+ )
self.answer1.save()
+ self.answer12.save()
self.answer2.save()
self.ans_paper1.answers.add(self.answer1)
self.ans_paper1.answers.add(self.answer2)
+ self.ans_paper2.answers.add(self.answer12)
self.ans_paper1.questions_answered.add(self.question1)
self.ans_paper1.questions_answered.add(self.question2)
+ self.ans_paper2.questions_answered.add(self.question1)
self.ans_paper1.questions.add(self.question1)
self.ans_paper1.questions.add(self.question2)
+ self.ans_paper2.questions.add(self.question1)
+ self.ans_paper2.questions.add(self.question2)
def tearDown(self):
self.client.logout()
@@ -2696,6 +2705,181 @@ class TestUploadMarks(TestCase):
self.question2.delete()
self.mod_group.delete()
+ def test_upload_users_marks_not_attempted_question(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_not_attempted_question.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+ ans_paper = AnswerPaper.objects.get(user=self.student2,
+ question_paper=self.question_paper,
+ course=self.course)
+ self.assertEqual(ans_paper.marks_obtained, 1.3)
+ answer = Answer.objects.get(answer='answer12')
+ self.assertEqual(answer.comment.strip(), 'very good')
+
+ def test_upload_users_marks_invalid_question_id(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_invalid_question_id.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+
+ def test_upload_users_marks_invalid_user(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_invalid_user.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+
+ def test_upload_users_marks_invalid_data(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_invalid_data.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+
+ def test_upload_users_marks_headers_missing(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_header_missing.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+ ans_paper = AnswerPaper.objects.get(user=self.student1,
+ question_paper=self.question_paper,
+ course=self.course)
+ self.assertEqual(ans_paper.marks_obtained, 0.9)
+
+ def test_upload_users_marks_headers_modified(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_header_modified.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+ answer = Answer.objects.get(answer='answer1')
+ self.assertEqual(answer.comment.strip(), 'fine work')
+ self.assertNotEqual(answer.marks, 0.75)
+ answer = Answer.objects.get(answer='answer2')
+ self.assertEqual(answer.comment.strip(), 'not nice')
+
+ def test_upload_users_marks_csv_single_question(self):
+ # Given
+ self.client.login(
+ username=self.teacher.username,
+ password='teacher'
+ )
+ csv_file_path = os.path.join(FIXTURES_DIR_PATH,
+ "marks_single_question.csv")
+ csv_file = open(csv_file_path, 'rb')
+ upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
+
+ # When
+ response = self.client.post(
+ reverse('yaksh:upload_marks',
+ kwargs={'course_id': self.course.id,
+ 'questionpaper_id': self.question_paper.id}),
+ data={'csv_file': upload_file})
+ csv_file.close()
+
+ # Then
+ self.assertEqual(response.status_code, 302)
+ ans_paper = AnswerPaper.objects.get(user=self.student1,
+ question_paper=self.question_paper,
+ course=self.course)
+ self.assertEqual(ans_paper.marks_obtained, 0.5)
+ answer = Answer.objects.get(answer='answer1')
+ self.assertEqual(answer.comment.strip(), 'okay work')
+
def test_upload_users_with_correct_csv(self):
# Given
self.client.login(
@@ -2705,7 +2889,6 @@ class TestUploadMarks(TestCase):
csv_file_path = os.path.join(FIXTURES_DIR_PATH, "marks_correct.csv")
csv_file = open(csv_file_path, 'rb')
upload_file = SimpleUploadedFile(csv_file_path, csv_file.read())
- previous_total = self.ans_paper1.marks_obtained
# When
response = self.client.post(
@@ -2717,7 +2900,6 @@ class TestUploadMarks(TestCase):
# Then
self.assertEqual(response.status_code, 302)
- self.assertEqual(previous_total, 0)
ans_paper = AnswerPaper.objects.get(user=self.student1,
question_paper=self.question_paper,
course=self.course)
diff --git a/yaksh/views.py b/yaksh/views.py
index c6bd560..95a7218 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -4075,12 +4075,8 @@ def upload_marks(request, course_id, questionpaper_id):
except TypeError:
messages.warning(request, "Bad CSV file")
return redirect('yaksh:monitor', quiz.id, course_id)
- user_ids, question_ids = _get_header_info(reader)
- csv_file.seek(0)
- reader = csv.DictReader(csv_file.read().decode('utf-8').splitlines(),
- dialect=dialect)
- _read_marks_csv(reader, course, question_paper, user_ids, question_ids)
- messages.warning(request, "Marks uploaded!")
+ question_ids = _get_header_info(reader)
+ _read_marks_csv(request, reader, course, question_paper, question_ids)
return redirect('yaksh:monitor', quiz.id, course_id)
@@ -4092,43 +4088,55 @@ def _get_header_info(reader):
qid = int(field.split('-')[1])
if qid not in question_ids:
question_ids.append(qid)
- for row in reader:
- username = row['username']
- user = User.objects.filter(username=username).first()
- if not user:
- pass
- user_ids.append(user.id)
- return user_ids, question_ids
+ return question_ids
-def _read_marks_csv(reader, course, question_paper, user_ids, question_ids):
- answerpapers = question_paper.answerpaper_set.filter(course=course,
- user_id__in=user_ids)
+def _read_marks_csv(request, reader, course, question_paper, question_ids):
+ messages.info(request, 'Marks Uploaded!')
for row in reader:
username = row['username']
user = User.objects.filter(username=username).first()
- if not user:
- pass
- answerpaper = answerpapers.get(user=user)
+ if user:
+ answerpapers = question_paper.answerpaper_set.filter(course=course,
+ user_id=user.id)
+ else:
+ messages.info(request, '{0} user not found!'.format(username))
+ continue
+ answerpaper = answerpapers.last()
+ if not answerpaper:
+ messages.info(request, '{0} has no answerpaper!'.format(username))
+ continue
answers = answerpaper.answers.all()
- answered = answerpaper.questions_answered.all().values_list('id',
- flat=True)
+ questions = answerpaper.questions.all().values_list('id', flat=True)
for qid in question_ids:
question = Question.objects.filter(id=qid).first()
if not question:
- pass
- if qid in answered:
+ messages.info(request,
+ '{0} is an invalid question id!'.format(qid))
+ continue
+ if qid in questions:
answer = answers.filter(question_id=qid).last()
if not answer:
- pass
- answer.set_marks(
- float(row['Q-{0}-{1}-{2}-marks'.format(
- qid, question.summary, question.points)])
- )
- answer.set_comment(
- row['Q-{0}-{1}-comments'.format(
- qid, question.summary, question.points)]
- )
+ answer = Answer(question_id=qid, marks=0, correct=False,
+ answer='Created During Marks Update!',
+ error=json.dumps([]))
+ answer.save()
+ answerpaper.answers.add(answer)
+ key1 = 'Q-{0}-{1}-{2}-marks'.format(qid, question.summary,
+ question.points)
+ key2 = 'Q-{0}-{1}-comments'.format(qid, question.summary,
+ question.points)
+ if key1 in reader.fieldnames:
+ try:
+ answer.set_marks(float(row[key1]))
+ except ValueError:
+ messages.info(request,
+ '{0} invalid marks!'.format(row[key1]))
+ if key2 in reader.fieldnames:
+ answer.set_comment(row[key2])
answer.save()
answerpaper.update_marks(state='completed')
answerpaper.save()
+ messages.info(request,
+ 'Updated successfully for user: {0}, question: {1}'.format(
+ username, question.summary))