def test_save_and_destroy_lock_reviewsubmission(self): os = OriginalSubmission(course=self.course, exercise=self.se, submitter_user=self.s1, text="jadajada") os.save() rl = ReviewLock(review_exercise=self.re, user=self.s2, original_submission=os) rl.save() rs = ReviewSubmission(course=self.course, exercise=self.re, submitter_user=self.s2, reviewed_submission=os) self.assertEqual(ReviewLock.objects.count(), 1) rs.save_and_destroy_lock() self.assertEqual(ReviewLock.objects.count(), 0) rs2 = ReviewSubmission(course=self.course, exercise=self.re, submitter_user=self.s2, reviewed_submission=os) self.assertRaises(OperationalError, rs2.save_and_destroy_lock) rs2 = ReviewSubmission(course=self.course, exercise=self.re, submitter_user=self.s3, reviewed_submission=os) self.assertRaises(OperationalError, rs2.save_and_destroy_lock)
def test_teacherCanSubmitMultipleTimes(self): exercise = SubmissionExercise.objects.get(pk=1) OriginalSubmission(course=self.course, submitter_user=self.t1, exercise=exercise, text="jadajada").save() OriginalSubmission(course=self.course, submitter_user=self.t1, exercise=exercise, text="jadajada").save() OriginalSubmission(course=self.course, submitter_user=self.t1, exercise=exercise, text="jadajada").save() request = self.factory.get('/courses/prog1/F2018/exercises/s/1/') request.user = self.t1 self.kwargs['pk'] = 1 response = SubmissionExerciseDetailView.as_view()(request, **self.kwargs) self.assertNotContains( response, "You have reached the maximum number of submissions") self.assertContains(response, "Submit")
def test_student_can_download_submission(self): exercise = SubmissionExercise.objects.get(pk=1) group = StudentGroup(name="g-1", student_usernames=[self.s1.email, self.s2.email], course=self.course) group.save() exercise.type = 'FILE_UPLOAD' exercise.accepted_filetypes = '.txt' exercise.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) sub = OriginalSubmission(course=self.course, file=temp_file, submitter_user=self.s1, submitter_group=group, exercise=exercise) sub.save() for user, code in [(self.s1, 200), (self.s2, 200), (self.s3, 403)]: request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{sub.pk}/') request.user = user self.kwargs['pk'] = sub.pk if code == 403: self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) else: response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, code)
def create_submission_for(submission_exercise, aplus_submission, user): """ 1. check if there's an user with the aplus submitter's email 2. if not, crate one and set temporary = True 3. get the submissions file from aplus API 4. create a new original submission with the file and submitter """ file_url = aplus_submission["files"][0]["url"] filename = aplus_submission["files"][0]["filename"] file_blob = requests.get(file_url, headers={ 'Authorization': f'Token {submission_exercise.course.aplus_apikey}' }) temp_file = NamedTemporaryFile(delete=True) temp_file.name = filename temp_file.write(file_blob.content) temp_file.flush() new_orig_sub = OriginalSubmission( course=submission_exercise.course, submitter_user=user, exercise=submission_exercise, file=File(temp_file) ) new_orig_sub.save() logger.info(new_orig_sub)
def create_submission_for(self, exercise, users): submissions = [] for user in users: sub = OriginalSubmission(course=self.course, submitter_user=user, exercise=exercise, text=f"text by {user}") sub.save() submissions.append(sub) return submissions
def create_originalsubmission_for(self, exercise, users): if type(users) is not list: users = [users] submissions = [] for user in users: sub = OriginalSubmission(course=self.course, submitter_user=user, exercise=exercise, text=f"text by {user}") sub.save() submissions.append(sub) return submissions if len(submissions) > 1 else submissions[0]
def test_student_can_download_via_reviewlock(self): exercise = SubmissionExercise.objects.get(pk=1) exercise.type = 'FILE_UPLOAD' exercise.accepted_filetypes = '.txt' exercise.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) sub = OriginalSubmission(course=self.course, file=temp_file, submitter_user=self.s1, exercise=exercise) sub.save() re = ReviewExercise.objects.get(pk=1) re.type = ReviewExercise.RANDOM re.save() rlock = ReviewLock(user=self.s2, original_submission=sub, review_exercise=re, review_submission=None) rlock.save() # user with reviewlock can download request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{sub.pk}/') request.user = self.s2 self.kwargs['pk'] = sub.pk response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, 200) re.use_groups = True re.save() group = StudentGroup(name="g-1", student_usernames=[self.s2.email, self.s3.email], course=self.course) group.save() rlock.group = group rlock.save() # user in a group that has a reviewlock can download request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{sub.pk}/') request.user = self.s3 self.kwargs['pk'] = sub.pk response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, 200)
def test_student_cannot_download_answer_file_not_owned(self): s_exercise = SubmissionExercise.objects.get(name="T1 TEXT") r_exercise = ReviewExercise.objects.get(name="T1 TEXT REVIEW") sub = OriginalSubmission(course=self.course, text="juups", submitter_user=self.s1, exercise=s_exercise) sub.save() r_exercise.question_order = ['3'] r_exercise.save() rev_sub = ReviewSubmission(course=self.course, reviewed_submission=sub, submitter_user=self.s2, exercise=r_exercise) rev_sub.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) answer_with_file = Answer( question=Question.objects.get(pk=3), submission=rev_sub, uploaded_file=temp_file, ) answer_with_file.save() request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{answer_with_file.pk}/?type=answer' ) request.user = self.s3 self.kwargs['pk'] = answer_with_file.pk self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) g = StudentGroup(name="g1", course=self.course, student_usernames=[self.s1.email, self.s3.email]) g.save() sub.submitter_group = g sub.save() for user in self.students + [self.t1]: request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{answer_with_file.pk}/?type=answer' ) request.user = user self.kwargs['pk'] = answer_with_file.pk if user == self.s4: self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) else: response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, 200)
def test_student_cannot_download_hide_from_receiver_answer_file(self): s_exercise = SubmissionExercise.objects.get(name="T1 TEXT") r_exercise = ReviewExercise.objects.get(name="T1 TEXT REVIEW") sub = OriginalSubmission(course=self.course, text="juups", submitter_user=self.s1, exercise=s_exercise) sub.save() r_exercise.question_order = ['3'] r_exercise.save() rev_sub = ReviewSubmission(course=self.course, reviewed_submission=sub, submitter_user=self.s2, exercise=r_exercise) rev_sub.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) question = Question.objects.get(pk=3) answer_with_file = Answer( question=question, submission=rev_sub, uploaded_file=temp_file, ) answer_with_file.save() request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{answer_with_file.pk}/?type=answer' ) request.user = self.s1 self.kwargs['pk'] = answer_with_file.pk self.assertEqual( DownloadSubmissionView.as_view()(request, **self.kwargs).status_code, 200) question.hide_from_receiver = True question.save() self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs)
def test_student_cannot_download_submission_not_owned(self): exercise = SubmissionExercise.objects.get(pk=1) exercise.type = 'FILE_UPLOAD' exercise.accepted_filetypes = '.txt' exercise.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) sub = OriginalSubmission(course=self.course, file=temp_file, submitter_user=self.s1, exercise=exercise) sub.save() request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{sub.pk}/') request.user = self.s2 self.kwargs['pk'] = sub.pk self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs)
def test_teacherCanViewAllSubmissions(self): exercise = SubmissionExercise.objects.get(pk=1) for username in ["student1", "student2", "student3", "teacher1"]: user = User.objects.get(username=username) OriginalSubmission(course=self.course, submitter_user=user, exercise=exercise, text="jadajada").save() request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") self.kwargs['pk'] = 1 response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(OriginalSubmission.objects.all().count(), 4) self.assertEqual(response.context_data['object_list'].count(), 4) self.assertContains(response, "student1") # hacky self.assertContains(response, "student2") self.assertContains(response, "student3") self.assertContains(response, "teacher1")
def prepare_group_review_exercise_for(exercise, user, LTI_MODE): """ This is called just before showing the student a group peer-review page where the student can peer-review other students in his/her group. Because of the design of the system, there has to exist an OriginalSubmission for each ReviewSubmission. This function will do two things: 1. if the receiver of the feedback (student in the same group) has never logged into the system (and thus his/her account doesn't exist yet), a new temporary user account with his/her email will be created. for that new temp user account, temporary=True will be set. if the current user is using the system through LTI integration, lti=True will also be set since all users of a particular course either use the system through LTI or don't. this distinction makes it possible for a user to be attending two courses of which one uses LTI and one doesn't and the user accounts don't mix mistakenly. 2. create one OriginalSubmission for each of the group members so that the peer-reviews (ReviewSubmissions) can point to them 3. when the real user logins in the future, this temporary account will be removed and all submissions transerred to the actual, real user account. this logic is implemented in prplatform.users.receivers """ course = exercise.course group = course.find_studentgroup_by_user(user) if not group: return for student_email in group.student_usernames: logger.info(f"FINDING: {student_email}, LTI_MODE: {LTI_MODE}") student = User.objects.filter(email=student_email, lti=LTI_MODE).first() if not student: logger.info( f'ReviewExericse (pk={exercise.pk}): User not found for {student_email} ' f'(lti={LTI_MODE}) -> creating temp user in perpare_group_review_exercise_for' ) # temp_str distinguishes temp users created through LTI and outside of it # for the username is a unique field temp_str = 'lti-' if LTI_MODE else '' student = User(username=f"temp-{temp_str}{student_email}", email=student_email, lti=LTI_MODE, temporary=True) student.save() if exercise.reviewable_exercise.submissions \ .filter(submitter_user=student).exists(): logger.info(f'Prev sub found for {student} --> not creating new') else: logger.info(f'Prev sub NOT FOUND for {student} --> creating new') osub = OriginalSubmission( course=course, exercise=exercise.reviewable_exercise, submitter_user=student, text= ("This is *automatically* created submission for group peer-review. " "This is needed because every peer-review has to point to some original " "submission.")) osub.save()
def test_reviewsubmissionlist_list_contents(self): sub_exercise = SubmissionExercise.objects.get(pk=1) rev_exercise = ReviewExercise.objects.get(pk=1) for user in self.students[:2]: OriginalSubmission(course=self.course, submitter_user=user, exercise=sub_exercise, text="jadajada").save() for user in self.students[:2]: revsub = OriginalSubmission.objects.filter(exercise=sub_exercise) \ .exclude(submitter_user=user).first() ReviewSubmission(course=self.course, submitter_user=user, exercise=rev_exercise, reviewed_submission=revsub).save() # teacher sees all reviews request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/') request.user = User.objects.get(username="******") self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(ReviewSubmission.objects.all().count(), 2) self.assertEqual(response.context_data['object_list'].count(), 2) self.assertContains(response, "student1") # hacky self.assertContains(response, "student2") # student1 sees reviews of him by student2 request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/?mode=my') request.user = self.s1 self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(ReviewSubmission.objects.all().count(), 2) self.assertEqual(response.context_data['object_list'].count(), 1) self.assertEqual( response.context_data['object_list'][0].submitter_user, self.s2) # student2 sees reviews of him by student1 request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/?mode=my') request.user = self.s2 self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(ReviewSubmission.objects.all().count(), 2) self.assertEqual(response.context_data['object_list'].count(), 1) self.assertEqual( response.context_data['object_list'][0].submitter_user, self.s1) # if RE.show_reviews_only_to_teacher, nothing in the list rev_exercise.show_reviews_only_to_teacher = True rev_exercise.save() request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/?mode=my') request.user = self.s2 self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 0) self.assertContains(response, 'will only be visible') rev_exercise.show_reviews_only_to_teacher = False rev_exercise.save() # reviews by student missing rev_exercise.min_submission_count = 100 rev_exercise.save() request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/?mode=my') request.user = self.s2 self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 0) self.assertContains(response, 'available after completing') # available in the future -> students should not see anything rev_exercise.min_submission_count = 0 rev_exercise.show_reviews_after_date = timezone.now( ) + datetime.timedelta(days=1) rev_exercise.save() request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/?mode=my') request.user = self.s2 self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 0) self.assertContains(response, 'will be available after')
def test_reviewsubmissionlist_parties(self): sub_exercise = SubmissionExercise.objects.get(pk=1) rev_exercise = ReviewExercise.objects.get(pk=1) for user in self.students[:2]: OriginalSubmission(course=self.course, submitter_user=user, exercise=sub_exercise, text="jadajada").save() for user in self.students[:2]: revsub = OriginalSubmission.objects.filter(exercise=sub_exercise) \ .exclude(submitter_user=user).first() ReviewSubmission(course=self.course, submitter_user=user, exercise=rev_exercise, reviewed_submission=revsub).save() # teacher sees all reviews request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/') request.user = User.objects.get(username="******") self.kwargs['pk'] = 1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(ReviewSubmission.objects.all().count(), 2) self.assertEqual(response.context_data['object_list'].count(), 2) self.assertContains(response, "student1") # hacky self.assertContains(response, "student2") rev_exercise.type = ReviewExercise.RANDOM rev_exercise.save() # list doesn't show who reviewed who for url in [ '/courses/prog1/F2018/submissions/r/1/list/', '/courses/prog1/F2018/submissions/r/1/list/?mode=my' ]: request = self.factory.get(url) request.user = self.s1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertNotContains(response, 'Reviewer') self.assertNotContains(response, 'Reviewed') self.assertNotContains(response, '<td>student2', html=True) self.assertNotContains(response, '<td>student1', html=True) for typ in [ReviewExercise.CHOOSE, ReviewExercise.GROUP]: rev_exercise.type = typ rev_exercise.save() # student can see who he reviewed request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/') request.user = self.s1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertContains(response, 'Reviewer') self.assertContains(response, 'Reviewed') self.assertContains(response, '<td>student2', html=True) self.assertContains(response, '<td>student1', html=True) # student CANNOT see who reviewed him request = self.factory.get( '/courses/prog1/F2018/submissions/r/1/list/?mode=my') request.user = self.s1 response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertNotContains(response, 'Reviewer') self.assertNotContains(response, 'Reviewed') self.assertNotContains(response, '<td>student2', html=True) self.assertNotContains(response, '<td>student1', html=True)
def test_expiry_hours(self): # create original submissions for all 4 students se = SubmissionExercise.objects.get(pk=1) for s in self.students: OriginalSubmission(course=self.course, submitter_user=s, exercise=se, text=f"text by {s}").save() re = ReviewExercise.objects.get(reviewable_exercise=se) # create two locks, for s2 and s3, by loading the page for s in [self.s2, self.s3]: request = self.factory.get('/courses/prog1/F2018/exercises/r/1/') request.user = s self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains( response, f"text by {self.s1 if s == self.s2 else self.s2}") self.assertEqual(ReviewLock.objects.all().count(), 2) # make exercise expire locks in 10 hours re.reviewlock_expiry_hours = 10 re.save() # move locks 5 hours back in time # these locks should NOT expire just yet ReviewLock.objects.all().update(created=timezone.now() - timezone.timedelta(hours=5)) # load the page -> nothing should just yet expire, third lock should appear request = self.factory.get('/courses/prog1/F2018/exercises/r/1/') request.user = self.s4 self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertEqual(ReviewLock.objects.all().count(), 3) # test that created time of a ReviewLock is refreshed on access rl_s4 = ReviewLock.objects.get(user=self.s4) created_before_refresh = rl_s4.created ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertEqual(ReviewLock.objects.all().count(), 3) rl_s4.refresh_from_db() self.assertNotEqual(created_before_refresh, rl_s4.created) # change expiry to 1 hour -> all locks should be expirying now re.reviewlock_expiry_hours = 1 re.save() request.user = self.s1 # this should create a lock for s1 and expire locks of s2 and s3 response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertEqual(ReviewLock.objects.all().count(), 2) self.assertEqual( ReviewLock.objects.filter(user=self.s1).exists(), True) self.assertEqual( ReviewLock.objects.filter(user=self.s4).exists(), True) # make s1 rl expire -> still post the form # this simulates how someone may have the browser open for a long time rl_s1 = ReviewLock.objects.get(user=self.s1) rl_s1.created = rl_s1.created.replace(hour=rl_s1.created.hour - 5) rl_s1.save() rl_s1_reviewable_pk = rl_s1.original_submission.pk # this will expire rl for s1 request.user = self.s3 ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertEqual( ReviewLock.objects.filter(user=self.s1).exists(), False) # there should be locks for s3 and s4 self.assertEqual(ReviewLock.objects.all().count(), 2) self.assertEqual( re.submissions.filter(submitter_user=self.s1).exists(), False) answers = { 'choice': [rl_s1_reviewable_pk], 'Q-PREFIX-2--question': ['2'], 'Q-PREFIX-2--value_choice': ['1'], 'Q-PREFIX-1--question': ['1'], 'Q-PREFIX-1--value_text': ['some text'] } self.post(re, self.s1, answers) self.assertEqual( re.submissions.filter(submitter_user=self.s1).exists(), True)
def test_reviewlocks_created_correctly(self): se = SubmissionExercise.objects.get(pk=1) for s in self.students: OriginalSubmission(course=self.course, submitter_user=s, exercise=se, text=f"text by {s}").save() # this will create two reviewlocks since both loaded the page re = ReviewExercise.objects.get(reviewable_exercise=se) for s in [self.s1, self.s2]: request = self.factory.get('/courses/prog1/F2018/exercises/r/1/') request.user = s self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains( response, f"text by {self.s1 if s == self.s2 else self.s2}") self.assertEqual(ReviewLock.objects.all().count(), 2) # the same reviewlocks are still used since no reviews have been made for s in [self.s1, self.s2]: request = self.factory.get('/courses/prog1/F2018/exercises/r/1/') request.user = s self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains( response, f"text by {self.s1 if s == self.s2 else self.s2}") self.assertEqual(ReviewLock.objects.all().count(), 2) # after submissions are created, no more reviews can be made for s in [self.s1, self.s2]: rlock = ReviewLock.objects.get(user=s) rsub = ReviewSubmission( course=self.course, exercise=re, submitter_user=s, reviewed_submission=rlock.original_submission) rsub.save_and_destroy_lock() request = self.factory.get('/courses/prog1/F2018/exercises/r/1/') request.user = s self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains( response, 'You have already submitted your peer-reviews to this exercise.' ) self.assertEqual(ReviewLock.objects.all().count(), 0) # now in the system: 4 OS, 0 RL, 2 RS # s4 loads the page -> RL should be for s3 request = self.factory.get('/courses/prog1/F2018/exercises/r/1/') request.user = self.s4 self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains(response, f"text by {self.s3}") self.assertEqual(ReviewLock.objects.all().count(), 1) re.max_submission_count = 2 re.save() # s1 loads the page -> RL should be for s4 since no RLs for s4 exist yet request.user = self.s1 response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains(response, f"text by {self.s4}") self.assertEqual(ReviewLock.objects.all().count(), 2) # s2 loads the page -> nothing to review since all can recieve only one request.user = self.s2 response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains(response, f"no-submissions-for-peer-review") self.assertEqual(ReviewLock.objects.all().count(), 2) # increase the number of reviews an OS can get re.max_reviews_per_submission = 2 re.save() # s2 should now get s3 since cannot get himself and s1 already done request.user = self.s2 self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains(response, f"text by {self.s3}") self.assertEqual(ReviewLock.objects.all().count(), 3) # s3 has not yet loaded the page -> should get s1 request.user = self.s3 self.kwargs['pk'] = re.pk response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertContains(response, f"text by {self.s1}") self.assertEqual(ReviewLock.objects.all().count(), 4)
def test_reviewsubmission_detail_answers(self): sub_exercise = SubmissionExercise.objects.get(name='T1 TEXT') rev_exercise = ReviewExercise.objects.get(name='T1 TEXT REVIEW') rev_exercise.question_order += [3] rev_exercise.save() for user in self.students[:2]: OriginalSubmission(course=self.course, submitter_user=user, exercise=sub_exercise, text="jadajada").save() rs_objects = [] temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) for user in self.students[:2]: reviewed_sub = OriginalSubmission.objects.filter(exercise=sub_exercise) \ .exclude(submitter_user=user).first() rs = ReviewSubmission.objects.create( course=self.course, submitter_user=user, exercise=rev_exercise, reviewed_submission=reviewed_sub) rs_objects.append(rs) Answer(submission=rs, value_text="juupase juu", question=Question.objects.get(pk=1)).save() Answer(submission=rs, value_choice="1", question=Question.objects.get(pk=2)).save() Answer(submission=rs, uploaded_file=temp_file, question=Question.objects.get(pk=3)).save() # teacher sees all reviews request = self.factory.get('/courses/prog1/F2018/submissions/r/1/1/') request.user = User.objects.get(username="******") self.kwargs['pk'] = rev_exercise.pk self.kwargs['sub_pk'] = rs_objects[0].pk response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "juupase juu") self.assertContains(response, "Score the work") # student sees hidden answer *given* by him request = self.factory.get('/courses/prog1/F2018/submissions/r/1/1/') request.user = self.s1 self.kwargs['pk'] = rev_exercise.pk self.kwargs['sub_pk'] = rs_objects[0].pk response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "juupase juu") self.assertContains(response, "Score the work") # student cannot see answer to a hidden question request = self.factory.get('/courses/prog1/F2018/submissions/r/1/1/') request.user = self.s2 self.kwargs['pk'] = rev_exercise.pk self.kwargs['sub_pk'] = rs_objects[0].pk response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "juupase juu") self.assertNotContains(response, "Score the work")
def test_reviewsubmission_detail_permissions(self): sub_exercise = SubmissionExercise.objects.get(name='T1 TEXT') rev_exercise = ReviewExercise.objects.get(name='T1 TEXT REVIEW') rev_exercise.question_order += [3] rev_exercise.save() for user in self.students[:2]: OriginalSubmission(course=self.course, submitter_user=user, exercise=sub_exercise, text="jadajada").save() rs_objects = [] temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) for user in self.students[:2]: reviewed_sub = OriginalSubmission.objects.filter(exercise=sub_exercise) \ .exclude(submitter_user=user).first() rs = ReviewSubmission.objects.create( course=self.course, submitter_user=user, exercise=rev_exercise, reviewed_submission=reviewed_sub) rs_objects.append(rs) Answer(submission=rs, value_text="juupase juu", question=Question.objects.get(pk=1)).save() Answer(submission=rs, value_choice="1", question=Question.objects.get(pk=2)).save() Answer(submission=rs, uploaded_file=temp_file, question=Question.objects.get(pk=3)).save() request = self.factory.get('/courses/prog1/F2018/submissions/r/1/1/') request.user = self.s2 self.kwargs['pk'] = rev_exercise.pk self.kwargs['sub_pk'] = rs_objects[0].pk # student cannot view feedback if available date in the future rev_exercise.show_reviews_after_date = timezone.now( ) + timezone.timedelta(hours=1) rev_exercise.save() self.assertRaises(PermissionDenied, ReviewSubmissionDetailView.as_view(), request, **self.kwargs) rev_exercise.show_reviews_after_date = None rev_exercise.save() self.assertEqual( ReviewSubmissionDetailView.as_view()(request, **self.kwargs).status_code, 200) # student cannot view feedback if requirements not met rev_exercise.min_submission_count = 999 rev_exercise.save() self.assertRaises(PermissionDenied, ReviewSubmissionDetailView.as_view(), request, **self.kwargs) rev_exercise.min_submission_count = 0 rev_exercise.save() self.assertEqual( ReviewSubmissionDetailView.as_view()(request, **self.kwargs).status_code, 200) # receiver *and* owner (feedback to self) can access rev_exercise.show_reviews_after_date = timezone.now( ) + timezone.timedelta(hours=1) rev_exercise.save() rs_objects[0].submitter_user = self.s2 rs_objects[0].save() rs_objects[0].reviewed_submission.submitter_user = self.s2 rs_objects[0].reviewed_submission.save() self.assertEqual( ReviewSubmissionDetailView.as_view()(request, **self.kwargs).status_code, 200) rev_exercise.show_reviews_after_date = None rev_exercise.save() # third party cannot see anything request.user = self.s3 self.assertRaises(PermissionDenied, ReviewSubmissionDetailView.as_view(), request, **self.kwargs)
def test_original_submission_delete_confirmation_page_shows_cascades(self): os = OriginalSubmission( course=self.course, exercise=SubmissionExercise.objects.get(name="T1 TEXT"), submitter_user=self.s1, text="jada jada jada jada", ) os.save() request = self.factory.get(os.get_delete_url()) self.kwargs['pk'] = os.exercise.pk self.kwargs['sub_pk'] = os.pk request.user = self.s1 # students cannot access self.assertRaises(PermissionDenied, OriginalSubmissionDeleteView.as_view(), request, **self.kwargs) request.user = self.t1 response = OriginalSubmissionDeleteView.as_view()(request, **self.kwargs) self.assertContains(response, "No peer-reviews exist yet") res = [] for s in [self.s2, self.s3]: re = ReviewSubmission( course=self.course, exercise=ReviewExercise.objects.get(name="T1 TEXT REVIEW"), reviewed_submission=os, submitter_user=s, ) re.save() res.append(re) response = OriginalSubmissionDeleteView.as_view()(request, **self.kwargs) self.assertNotContains(response, "No peer-reviews exist yet") # -> is turned into -> in the response -> just replace that when looking for self.assertContains(response, str(res[0]).replace(">", ">")) self.assertContains(response, str(res[1]).replace(">", ">")) self.assertEqual(OriginalSubmission.objects.count(), 1) self.assertEqual(ReviewSubmission.objects.count(), 2) request = self.factory.get(res[0].get_delete_url()) self.kwargs['pk'] = res[0].exercise.pk self.kwargs['sub_pk'] = res[0].pk request.user = self.s1 # students cannot access self.assertRaises(PermissionDenied, ReviewSubmissionDeleteView.as_view(), request, **self.kwargs) # delete page doesn't list any useful information, no need to check os.delete() self.assertEqual(OriginalSubmission.objects.count(), 0) self.assertEqual(ReviewSubmission.objects.count(), 0)
def test_download_tokens_allow_downloading(self): exercise = SubmissionExercise.objects.get(pk=1) exercise.type = 'FILE_UPLOAD' exercise.accepted_filetypes = '.txt' exercise.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) sub = OriginalSubmission(course=self.course, file=temp_file, submitter_user=self.s1, exercise=exercise) sub.save() rev = ReviewSubmission(course=self.course, submitter_user=self.s1, exercise=exercise.review_exercise, reviewed_submission=sub) rev.save() answer_with_file = Answer( question=Question.objects.get(pk=3), submission=rev, uploaded_file=temp_file, ) answer_with_file.save() view_request = self.factory.get(exercise.get_absolute_url() + "?some=queryparams") osub_token = sub.get_download_token_for(self.s1, view_request) view_request = self.factory.get( exercise.review_exercise.get_absolute_url() + "?some=queryparams") rsub_token = rev.get_download_token_for(self.s1, view_request) cases = [ (AnonymousUser, 403, sub, ""), (AnonymousUser, 200, sub, f"?dl_token={osub_token}"), (AnonymousUser, 403, sub, f"?dl_token={osub_token}_THIS_SHOULD_CAUSE_PROBLEMS"), (AnonymousUser, 403, answer_with_file, ""), (AnonymousUser, 200, answer_with_file, f"&dl_token={rsub_token}"), (AnonymousUser, 403, answer_with_file, f"&dl_token={rsub_token}_THIS_SHOULD_CAUSE_PROBLEMS"), ] for user, code, sub, query in cases: url = f"{sub.get_file_download_url()}{query}" request = self.factory.get(url) request.user = user self.kwargs['pk'] = sub.pk if code == 403: self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) else: response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, code) DownloadToken.objects.all().delete() for user, _, sub, query in cases: url = f"{sub.get_file_download_url()}{query}" request = self.factory.get(url) request.user = user self.kwargs['pk'] = sub.pk self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs)
def test_studentCanOnlyViewPersonalSubmissions(self): exercise = SubmissionExercise.objects.get(pk=1) for username in ["student1", "student2", "student3"]: user = User.objects.get(username=username) OriginalSubmission(course=self.course, submitter_user=user, exercise=exercise, text="jadajada").save() request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") self.kwargs['pk'] = 1 response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(OriginalSubmission.objects.all().count(), 3) self.assertEqual(response.context_data['object_list'].count(), 1) self.assertNotContains(response, "student2") # hacky self.assertNotContains(response, "student3") ###################### # # same thing for group submissions # OriginalSubmission.objects.all().delete() g1 = StudentGroup.objects.create( course=self.course, name='group1', student_usernames=['*****@*****.**', '*****@*****.**']) g2 = StudentGroup.objects.create(course=self.course, name='group2', student_usernames=['*****@*****.**']) exercise.use_groups = True exercise.save() OriginalSubmission( course=self.course, exercise=exercise, text='jada jada', submitter_user=User.objects.get(username='******'), submitter_group=g1).save() # both group members should see 1 request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 1) self.assertEqual( response.context_data['object_list'].first().submitter_group, g1) self.assertContains(response, 'group1') request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 1) self.assertEqual( response.context_data['object_list'].first().submitter_group, g1) self.assertContains(response, 'group1') # outsider should see nothing request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 0) self.assertNotContains(response, 'group1') OriginalSubmission( course=self.course, exercise=exercise, text='jada jada', submitter_user=User.objects.get(username='******'), submitter_group=g1).save() OriginalSubmission( course=self.course, exercise=exercise, text='jada jada', submitter_user=User.objects.get(username='******'), submitter_group=g2).save() # g1 nenbers should see two submissions request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 2) self.assertEqual( response.context_data['object_list'].first().submitter_group, g1) self.assertNotContains(response, 'group2') self.assertContains(response, 'group1') request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 2) self.assertEqual( response.context_data['object_list'].first().submitter_group, g1) self.assertNotContains(response, 'group2') self.assertContains(response, 'group1') # g2 member should see one submission request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 1) self.assertEqual( response.context_data['object_list'].first().submitter_group, g2) self.assertNotContains(response, 'group1') self.assertContains(response, 'group2') # submissions not belonging to any group # this could happen if the teacher changed use_groups later on OriginalSubmission( course=self.course, exercise=exercise, text='jada jada', submitter_user=User.objects.get(username='******'), ).save() OriginalSubmission( course=self.course, exercise=exercise, text='jada jada', submitter_user=User.objects.get(username='******'), ).save() # student4 should see nothing since he's not in a group and has submitted # nothing request = self.factory.get( '/courses/prog1/F2018/submissions/s/1/list/') request.user = User.objects.get(username="******") response = OriginalSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual(response.context_data['object_list'].count(), 0) self.assertNotContains(response, 'group1') self.assertNotContains(response, 'group2')
def test_ReviewSubmissionDetail_shows_correct_info(self): sub_exercise = SubmissionExercise.objects.get(name='T1 TEXT') rev_exercise = ReviewExercise.objects.get(name='T1 TEXT REVIEW') for user in self.students: OriginalSubmission(course=self.course, submitter_user=user, exercise=sub_exercise, text="jadajada").save() reviewed = self.s2 reviewer = self.s1 reviewed_sub = OriginalSubmission.objects.get(exercise=sub_exercise, submitter_user=reviewed) rev_sub = ReviewSubmission.objects.create( course=self.course, submitter_user=reviewer, exercise=rev_exercise, reviewed_submission=reviewed_sub) request = self.factory.get(rev_sub.get_absolute_url()) self.kwargs['pk'] = rev_exercise.pk self.kwargs['sub_pk'] = rev_sub.pk rev_exercise.min_submission_count = 0 # so that receiver can view rev_exercise.type = ReviewExercise.RANDOM rev_exercise.save() request.user = self.t1 response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "<strong>Submitter") self.assertContains(response, "<strong>Reviewed submission") request.user = reviewer response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "<strong>Submitter") self.assertNotContains(response, "<strong>Reviewed") request.user = reviewed response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertNotContains(response, "<strong>Submitter") self.assertContains(response, "<strong>Reviewed submission") rev_exercise.type = ReviewExercise.CHOOSE rev_exercise.save() request.user = self.t1 response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "<strong>Submitter") self.assertContains(response, "<strong>Reviewed submission") request.user = reviewer response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "<strong>Submitter") self.assertNotContains(response, "<strong>Reviewed submission") self.assertContains(response, "<strong>Reviewed student") request.user = reviewed response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertNotContains(response, "<strong>Submitter") self.assertContains(response, "<strong>Reviewed submission") self.assertNotContains(response, "<strong>Reviewed student") rev_exercise.type = ReviewExercise.GROUP rev_exercise.save() request.user = self.t1 response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "<strong>Submitter") self.assertContains(response, "<strong>Reviewed student") request.user = reviewer response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertContains(response, "<strong>Submitter") self.assertNotContains(response, "<strong>Reviewed submission") self.assertContains(response, "<strong>Reviewed student") request.user = reviewed response = ReviewSubmissionDetailView.as_view()(request, **self.kwargs) self.assertNotContains(response, "<strong>Submitter") self.assertNotContains(response, "<strong>Reviewed submission") self.assertNotContains(response, "<strong>Reviewed student")