def test_select_components(self): """ Test submission component classes: subclasses, selection, sorting. """ _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date="2010-04-01") a1.save() a2 = NumericActivity(name="Assignment 2", short_name="A2", status="RLS", offering=course, position=1, max_grade=15, due_date="2010-03-01") a2.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c1 = URL.Component(activity=a1, title="URL Link", position=8) c1.save() c2 = Archive.Component(activity=a1, title="Archive File", position=1, max_size=100000) c2.save() c3 = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c3.save() comps = select_all_components(a1) self.assertEqual(len(comps), 3) self.assertEqual(comps[0].title, 'Archive File') # make sure position=1 is first self.assertEqual(str(comps[1].Type), "submission.models.code.Code") self.assertEqual(str(comps[2].Type), "submission.models.url.URL")
def test_add_activity_components(self): c = CourseOffering.objects.get(slug = self.c_slug) #add a numeric activity and its components a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0) a.save() co1 = ActivityComponent(numeric_activity = a, title = 'part1', max_mark = 20, position = 1) co2 = ActivityComponent(numeric_activity = a, title = 'part2', max_mark = 30, position = 2) co3 = ActivityComponent(numeric_activity = a, title = 'part3', max_mark = 50, position = 3) co1.save() co2.save() co3.save() self.client.login_user('ggbaker') response = basic_page_tests(self, self.client, reverse('offering:marking:manage_activity_components', args=(self.c_slug,a.slug))) forms = response.context['formset'].forms self.assertEqual(forms[0].instance.title, 'part1') self.assertEqual(forms[1].instance.title, 'part2') self.assertEqual(forms[2].instance.title, 'part3')
def test_out_of_zero(self): """ Test activities out of zero """ c = CourseOffering.objects.get(slug=self.course_slug) a = NumericActivity(offering=c, name="AZero", short_name="AZ", status="RLS", group=False, deleted=False, max_grade=0, position=1) a.save() stud = c.member_set.filter(role="STUD")[0] # test as instructor client = Client() client.login_user("ggbaker") url = reverse('marking.views.change_grade_status', kwargs={'course_slug': c.slug, 'activity_slug': a.slug, 'userid': stud.person.userid}) response = basic_page_tests(self, client, url) self.assertContains(response, "out of 0") response = client.post(url, {'grade-status-value': 3, 'grade-status-flag': 'GRAD', 'grade-status-comment': ''}) self.assertEquals(response.status_code, 302) g = NumericGrade.objects.get(activity=a, member=stud) self.assertEquals(g.value, 3) url = reverse('grades.views.activity_info', kwargs={'course_slug': c.slug, 'activity_slug': a.slug}) response = basic_page_tests(self, client, url) url = reverse('grades.views.student_info', kwargs={'course_slug': c.slug, 'userid': stud.person.userid}) response = basic_page_tests(self, client, url) # test as student client.login_user(stud.person.userid) url = reverse('grades.views.course_info', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) url = reverse('grades.views.activity_info', kwargs={'course_slug': c.slug, 'activity_slug': a.slug}) response = basic_page_tests(self, client, url)
def test_group_setMark(self): c = CourseOffering.objects.get(slug = self.c_slug) #add a numeric activity a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0) a.save() #take 2 students to make a group stud1 = Member.objects.get(person = Person.objects.get(userid = '0aaa0'), offering = c) stud2 = Member.objects.get(person = Person.objects.get(userid = '0aaa1'), offering = c) group = Group.objects.create(courseoffering = c, name = 'hello', manager = stud1) member1 = GroupMember.objects.create(group = group, student = stud1, confirmed = True, activity=a) member2 = GroupMember.objects.create(group = group, student = stud2, confirmed = True, activity=a) MARK = 30 group_mark = GroupActivityMark(group = group, numeric_activity = a) group_mark.setMark(MARK, entered_by='ggbaker') group_mark.save() num_grades = NumericGrade.objects.filter(activity = a).order_by('member__person__userid') self.assertEqual(len(num_grades), 2) self.assertEqual(num_grades[0].member, stud1) self.assertEqual(num_grades[0].value, MARK) self.assertEqual(num_grades[0].flag, 'GRAD') self.assertEqual(num_grades[1].member, stud2) self.assertEqual(num_grades[1].value, MARK) self.assertEqual(num_grades[1].flag, 'GRAD')
def test_group_submission_view(self): """ test if group submission can be viewed by group member and non group member """ now = datetime.datetime.now() _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date=now, group=True) a1.save() a2 = NumericActivity(name="Assignment 2", short_name="A2", status="RLS", offering=course, position=1, max_grade=15, due_date=now, group=True) a2.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c1 = URL.Component(activity=a1, title="URL Link", position=8) c1.save() c2 = Archive.Component(activity=a1, title="Archive File", position=1, max_size=100000) c2.save() c3 = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c3.save() userid1 = "0aaa0" userid2 = "0aaa1" userid3 = "0aaa2" for u in [userid1, userid2,userid3]: p = Person.objects.get(userid=u) m = Member(person=p, offering=course, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() m = Member.objects.get(person__userid=userid1, offering=course) g = Group(name="Test Group", manager=m, courseoffering=course) g.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() m = Member.objects.get(person__userid=userid2, offering=course) gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() m = Member.objects.get(person__userid=userid3, offering=course) gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() client = Client() # login as "0aaa0", member of group : test_group for assignment1 and assgnment2 client.login_user("0aaa0") #submission page for assignment 1 url = reverse('submission.views.show_components', kwargs={'course_slug': course.slug,'activity_slug':a1.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, "This is a group submission. You will submit on behalf of the group Test Group.") self.assertContains(response, "You haven't made a submission for this component.")
def test_upload(self): _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date=datetime.datetime.now() + datetime.timedelta(hours=1), group=False) a1.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c.save() userid1 = "0aaa0" userid2 = "0aaa1" userid3 = "0aaa2" for u in [userid1, userid2,userid3]: p = Person.objects.get(userid=u) m = Member(person=p, offering=course, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() # submit as student client = Client() client.login_user("0aaa0") url = reverse('submission.views.show_components', kwargs={'course_slug': course.slug,'activity_slug':a1.slug}) response = basic_page_tests(self, client, url) # submit a file tmpf = tempfile.NamedTemporaryFile(suffix=".py", delete=False) codecontents = 'print "Hello World!"\n' tmpf.write(codecontents) tmpf.close() try: fh = open(tmpf.name, "r") data = {"%i-code" % (c.id): fh} response = client.post(url, data) self.assertEquals(response.status_code, 302) finally: os.unlink(tmpf.name) # make sure it's there and correct subs = StudentSubmission.objects.all() self.assertEquals(len(subs), 1) sub = subs[0] self.assertEquals(sub.member.person.userid, '0aaa0') codes = SubmittedCode.objects.all() self.assertEquals(len(codes), 1) code = codes[0] code.code.open() self.assertEquals(code.code.read(), codecontents)
def test_add_common_problems(self): c = CourseOffering.objects.get(slug = self.c_slug) a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 1) a.save() co1 = ActivityComponent(numeric_activity = a, title = 'part1', max_mark = 50, position = 1) co2 = ActivityComponent(numeric_activity = a, title = 'part2', max_mark = 50, position = 2) co1.save() co2.save() #add some common problems cp1 = CommonProblem(activity_component = co1, title = 'cp1', penalty="0") cp2 = CommonProblem(activity_component = co1, title = 'cp2', penalty="1.12") cp3 = CommonProblem(activity_component = co2, title = 'cp3', penalty="-2.3") cp1.save() cp2.save() cp3.save() self.client.login_user('ggbaker') response = basic_page_tests(self, self.client, reverse('offering:marking:manage_common_problems', args=(self.c_slug,a.slug))) forms = response.context['formset'].forms ins0 = forms[0].instance ins1 = forms[1].instance ins2 = forms[2].instance self.assertEqual(ins0.title, 'cp1') self.assertEqual(ins0.activity_component, co1) self.assertEqual(ins1.title, 'cp2') self.assertEqual(ins1.activity_component, co1) self.assertEqual(ins2.title, 'cp3') self.assertEqual(ins2.activity_component, co2) #test the marking page as well url = reverse('offering:marking:marking_student', args=(self.c_slug, a.slug, '0aaa0')) response = basic_page_tests(self, self.client, url) mark_components = response.context['component_data'] com1 = mark_components[0] com2 = mark_components[1] self.assertEqual(com1['component'], co1) self.assertEqual(len(com1['common_problems']), 2) self.assertEqual(com2['component'], co2) self.assertEqual(len(com2['common_problems']), 1)
def test_post_activity_components(self): c = CourseOffering.objects.get(slug = self.c_slug) #add a numeric activity and its components a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0) a.save() self.client.login_user('ggbaker') url = reverse('offering:marking:manage_activity_components', args=(self.c_slug, a.slug)) # 2 forms for the first 2 components to add post_data = {'form-0-id' : ['', ''], 'form-1-id' : ['', ''], 'form-0-title': ['part1'], 'form-1-title': ['part2'], 'form-0-max_mark' : ['20'], 'form-1-max_mark': ['20'], 'form-0-description' : ['basic1'], 'form-1-description': ['basic2'], 'form-TOTAL_FORMS' : ['3'], 'form-INITIAL_FORMS':['0']} response = self.client.post(url, post_data, follow = True) self.assertEqual(response.status_code, 200) cps = ActivityComponent.objects.filter(numeric_activity = a, deleted = False) self.assertEqual(len(cps), 2) self.assertEqual(cps[0].title, 'part1') self.assertEqual(cps[1].title, 'part2') # keep the first 2 components, and add 2 more new components post_data2 = {'form-2-id' : ['', ''], 'form-3-id' : ['', ''], 'form-2-title': ['part3'], 'form-3-title': ['part4'], 'form-2-max_mark' : ['30'], 'form-3-max_mark': ['30'], 'form-2-description' : ['advanced1'], 'form-3-description': ['advanced2'], } post_data.update(post_data2) post_data['form-0-id'] = [str(cps[0].id), str(cps[0].id)] post_data['form-1-id'] = [str(cps[1].id), str(cps[1].id)] post_data['form-INITIAL_FORMS'] = ['2'] post_data['form-TOTAL_FORMS'] = ['5'] response = self.client.post(url, post_data, follow = True) self.assertEqual(response.status_code, 200) cps = ActivityComponent.objects.filter(numeric_activity = a, deleted = False) self.assertEqual(len(cps), 4) self.assertEqual(cps[2].title, 'part3') self.assertEqual(cps[3].title, 'part4')
def test_mark_history(self): c = CourseOffering.objects.get(slug = self.c_slug) #add a numeric activity a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0) a.save() #take 2 students to make a group stud1 = Member.objects.get(person = Person.objects.get(userid = '0aaa0'), offering = c) stud2 = Member.objects.get(person = Person.objects.get(userid = '0aaa1'), offering = c) group = Group.objects.create(courseoffering = c, name = 'hello', manager = stud1) member1 = GroupMember.objects.create(group = group, student = stud1, confirmed = True, activity=a) member2 = GroupMember.objects.create(group = group, student = stud2, confirmed = True, activity=a) ngrade = NumericGrade(activity = a, member = stud2) ngrade.save(entered_by='ggbaker') #assign mark to 0aaa1 individually twice and via the group twice, make some interval between saves std_mark = StudentActivityMark(numeric_grade = ngrade, created_by = 'ggbaker') std_mark.setMark(20, entered_by='ggbaker') std_mark.save() group_mark = GroupActivityMark(group = group, numeric_activity = a, created_by = 'ggbaker') group_mark.setMark(30, entered_by='ggbaker') group_mark.save() std_mark = StudentActivityMark(numeric_grade = ngrade, created_by = 'ggbaker') std_mark.setMark(40, entered_by='ggbaker') std_mark.save() group_mark = GroupActivityMark(group = group, numeric_activity = a, created_by = 'ggbaker') group_mark.setMark(50, entered_by='ggbaker') group_mark.save() self.client.login_user('ggbaker') response = self.client.get(reverse('offering:marking:mark_history_student', args=(self.c_slug, a.slug, '0aaa1'))) self.assertEqual(response.status_code, 200) latest_act_mark = response.context['current_mark'] self.assertEqual(len(response.context['marks_individual']), 2) self.assertEqual(len(response.context['marks_via_group']), 2) self.assertEqual(group_mark, latest_act_mark)
def test_calc_letter(self): """ Test calculated letter functionality """ s, c = create_offering() na = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, max_grade=15, percent=10, group=False) na.save() a = CalLetterActivity(offering=c, name="A1 Letter", short_name="A1L", status="RLS", numeric_activity=na, exam_activity=None, position=3) a.save() # test cutoff getter/setter cs = a.get_cutoffs() cs[1] = decimal.Decimal('271') / decimal.Decimal('3') a.set_cutoffs(cs) s.save() cs = a.get_cutoffs() self.assertAlmostEquals(float(cs[1]), 90.333333333333)
class TestImportViews(TestCase): fixtures = ['basedata', 'coredata', 'grades'] def setUp(self): self.c_slug = TEST_COURSE_SLUG self.client = Client() self.c = CourseOffering.objects.get(slug = self.c_slug) self.a1 = NumericActivity(offering = self.c, name = 'test_assignment_1', short_name = 'ta1', status = 'RLS', due_date = datetime.now(), max_grade = 100, position = 0) self.a1.save() def check_student_db_grade(self, grade, s, g): self.assertEqual(grade.member, s) self.assertEqual(grade.value, Decimal(g)) self.assertEqual(grade.flag, 'GRAD') def test_import_view(self): self.client.login_user('ggbaker') # Import the file, check that resulting HTML has correct entries in fields for two affected students url = reverse('offering:mark_all_students', kwargs={'course_slug':self.c_slug, 'activity_slug':self.a1.slug}) with open('marking/testfiles/newformat_noprob_userid.csv') as file: post_data = {'import-file-file':[file]} response = self.client.post(url+"?import=true", post_data, follow=True) self.assertEqual(response.status_code, 200) stud1 = Member.objects.get(person = Person.objects.get(userid = '0aaa0'), offering = self.c) stud2 = Member.objects.get(person = Person.objects.get(userid = '0aaa1'), offering = self.c) STUD1_GRADE = '88' STUD2_GRADE = '15' self.assertContains(response, b'value="%b"' % (STUD1_GRADE.encode('utf8'),)) self.assertContains(response, b'value="%b"' % (STUD2_GRADE.encode('utf8'),)) # Submit the grades, check that they were added to DB post_data={'0aaa0-value':STUD1_GRADE, '0aaa1-value':STUD2_GRADE} response = self.client.post(url, post_data, follow=True) self.assertEqual(response.status_code, 200) num_grades = NumericGrade.objects.filter(activity = self.a1).order_by('member__person__userid') self.assertEqual(len(num_grades), 2) self.check_student_db_grade(num_grades[0], stud1, STUD1_GRADE) self.check_student_db_grade(num_grades[1], stud2, STUD2_GRADE)
def test_activity_pages(self): """ Test pages around activities """ s, c = create_offering() # add some assignments and members a = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, max_grade=15, percent=10) a.save() a1=a a = NumericActivity(name="Assignment 2", short_name="A2", status="URLS", offering=c, position=6, max_grade=20, group=True) a.save() p = Person.objects.get(userid="ggbaker") m = Member(person=p, offering=c, role="INST", added_reason="UNK") m.save() p = Person.objects.get(userid="0aaa0") m = Member(person=p, offering=c, role="STUD", added_reason="UNK") m.save() # test instructor pages client = Client() client.login_user("ggbaker") response = basic_page_tests(self, client, '/' + c.slug + '/') self.assertContains(response, 'href="' + reverse('groups.views.groupmanage', kwargs={'course_slug':c.slug}) +'"') basic_page_tests(self, client, c.get_absolute_url()) basic_page_tests(self, client, reverse('grades.views.student_info', kwargs={'course_slug': c.slug, 'userid': '0aaa0'})) basic_page_tests(self, client, reverse('grades.views.add_numeric_activity', kwargs={'course_slug':c.slug})) basic_page_tests(self, client, reverse('grades.views.add_letter_activity', kwargs={'course_slug':c.slug})) # test student pages client = Client() client.login_user("0aaa0") response = basic_page_tests(self, client, '/' + c.slug + '/') self.assertContains(response, "Gregory Baker") self.assertContains(response, 'href="' + reverse('groups.views.groupmanage', kwargs={'course_slug':c.slug}) +'"') response = basic_page_tests(self, client, a.get_absolute_url()) # small class (one student) shouldn't contain summary stats self.assertNotContains(response, "Histogram") self.assertNotContains(response, "Standard Deviation")
def test_component_view_page(self): _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date="2010-04-01") a1.save() a2 = NumericActivity(name="Assignment 2", short_name="A2", status="RLS", offering=course, position=1, max_grade=15, due_date="2010-03-01") a2.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c1 = URL.Component(activity=a1, title="URL Link", position=8) c1.save() c2 = Archive.Component(activity=a1, title="Archive File", position=1, max_size=100000) c2.save() c3 = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c3.save() client = Client() client.login_user("ggbaker") # When no component, should display error message url = reverse('submission.views.show_components', kwargs={'course_slug':course.slug, 'activity_slug':a2.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, 'No components configured.') # add component and test component = URL.Component(activity=a2, title="URL2", position=1) component.save() component = Archive.Component(activity=a2, title="Archive2", position=1, max_size=100) component.save() # should all appear response = basic_page_tests(self, client, url) self.assertContains(response, "URL2") self.assertContains(response, "Archive2") # make sure type displays #self.assertContains(response, '<li class="view"><label>Type:</label>Archive</li>') # delete component self.assertRaises(NotImplementedError, component.delete)
def test_activities(self): """ Test activity classes: subclasses, selection, sorting. """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, max_grade=15) a.save() a = NumericActivity(name="Assignment 2", short_name="A2", status="RLS", offering=c, position=6, max_grade=15) a.save() a = LetterActivity(name="Project", short_name="Proj", status="URLS", offering=c, position=1) a.save() a = CalNumericActivity(name="Total", short_name="Total", status="URLS", offering=c, position=42, max_grade=30, formula="[A1]+[A2]") a.save() allact = all_activities_filter(offering=c) self.assertEqual(len(allact), 4) self.assertEqual(allact[0].slug, 'proj') # make sure position=1 is first self.assertEqual(type(allact[1]), NumericActivity) self.assertEqual(type(allact[3]), CalNumericActivity)
def test_replace_activity(self): """ Can we safely replace an activity with one of the same name/shortname? """ c = CourseOffering.objects.get(slug=self.course_slug) a = NumericActivity(offering=c, name="Assign1", short_name="A1", status="RLS", group=False, deleted=False, max_grade=10, position=1) a.save() a.safely_delete() self.assertEqual(a.deleted, True) self.assertNotEqual(a.name, 'Assign1') self.assertNotEqual(a.short_name, 'A1') # replace with same type a = CalNumericActivity(offering=c, name="Assign1", short_name="A1", status="URLS", group=True, deleted=False, max_grade=15, position=2) a.save() a.safely_delete() # replace with a different type a = LetterActivity(offering=c, name="Assign1", short_name="A1", status="RLS", group=False, deleted=False, position=3) a.save()
def create_coredata(): """ Create enough of the coredata.models stuff to run basic tests """ create_units() # restore ggbaker's real emplid so import_offerings will match p = find_person('ggbaker') p.emplid = find_emplid('ggbaker') p.first_name = 'Gregorʏ' p.pref_first_name = 'Greg' p.save() # import a few more people we definitely need later find_person('popowich') find_person('dixon') find_person('diana') find_person('dzhao') find_person('pba7') # import a limited set of course offerings offerings = import_offerings( import_semesters=import_strms, extra_where="(subject='CMPT' AND (catalog_nbr LIKE '%% 12%%')) " "OR (subject='CMPT' AND (catalog_nbr LIKE '%% 16%%')) " "OR (subject='ENSC' AND (catalog_nbr LIKE '%% 10%%')) ") offerings = list(offerings) offerings.sort() if not CourseOffering.objects.filter(slug=TEST_COURSE_SLUG): o = CourseOffering.objects.filter(subject='CMPT', semester__name=import_strms()[0]) \ .order_by('number', 'section').first() raise ValueError( "courselib.testing.TEST_COURSE_SLUG isn't an offering we have. Maybe use '%s'." % (o.slug)) # import instructors for o in offerings: import_offering_members(o, students=False) # try to guess instructors' userids for p in Person.objects.filter(userid__isnull=True): p.userid = guess_userid(p.emplid) p.save() fake_emplids() p = find_person('ggbaker') p.first_name = 'Gregorʏ' p.pref_first_name = 'Greg' p.save() # use/import no real emplids after this # create some fake undergrad/grad students for i in range(20): userid = "0aaa%i" % (i) fname = randname(8) p = random.randint(1, 2) if p == 1: pref = fname[:4] else: pref = fname p = Person(emplid=300000300 + i, userid=userid, last_name='Student', first_name=fname, middle_name=randname(6), pref_first_name=pref) p.save() userid = "0ggg%i" % (i) fname = randname(8) p = random.randint(1, 2) if p == 1: pref = fname[:4] else: pref = fname p = Person(emplid=300000500 + i, userid=userid, last_name='Grad', first_name=fname, middle_name=randname(6), pref_first_name=pref) p.config['gender'] = random.choice(('M', 'F', 'U')) p.config['gpa'] = round(random.triangular(0.0, 4.33, 2.33), 2) p.config['visa'] = random.choice([x for x, _ in VISA_STATUSES]) p.config['citizen'] = random.choice(('Canadian', 'OtherCountrian')) p.save() # some memberships/roles/etc assumed by tests o = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) ensure_member(Person.objects.get(userid='ggbaker'), o, "INST", 0, "AUTO", "NONS") ensure_member(Person.objects.get(userid='0ggg0'), o, "TA", 0, "AUTO", "NONS") ensure_member(Person.objects.get(userid='0aaa0'), o, "STUD", 3, "AUTO", "UGRD") ensure_member(Person.objects.get(userid='0aaa1'), o, "STUD", 3, "AUTO", "UGRD") d = Person.objects.get(userid='dzhao') set_privacy_signed(d) set_privacy_da_signed(d) config = UserConfig(user=d, key='photo-agreement', value={'agree': True}) config.save() u = Unit.objects.get(slug='cmpt') r1 = Role(person=d, role='ADVS', unit=u, expiry=role_expiry) r1.save() r2 = Role(person=d, role='ADMN', unit=u, expiry=role_expiry) r2.save() r3 = Role(person=Person.objects.get(userid='pba7'), role='SYSA', unit=Unit.objects.get(slug='univ'), expiry=role_expiry) r3.save() r4 = Role(person=d, role='INV', unit=u, expiry=role_expiry) r4.save() r5 = Role(person=d, role='OUTR', unit=u, expiry=role_expiry) r5.save() r6 = Role(person=d, role='SPAC', unit=u, expiry=role_expiry) r6.save() # ensures course appears in menu for students a = NumericActivity(offering=o, name='Assignmenț 1', short_name='A1', status='URLS', position=1, percent=10, max_grade=10, due_date=(o.semester.start + datetime.timedelta(days=60))) a.save() return itertools.chain( SemesterWeek.objects.filter(semester__name__gt=SEMESTER_CUTOFF), Unit.objects.all(), Course.objects.all(), CourseOffering.objects.all(), Person.objects.order_by('emplid'), Member.objects.all(), UserConfig.objects.all(), [r1, r2, r3, r4, r5, r6, a.activity_ptr, a], )
def test_activity_pages(self): """ Test pages around activities """ s, c = create_offering() # add some assignments and members a = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, max_grade=15, percent=10) a.save() a1 = a a = NumericActivity(name="Assignment 2", short_name="A2", status="URLS", offering=c, position=6, max_grade=20, group=True) a.save() p = Person.objects.get(userid="ggbaker") m = Member(person=p, offering=c, role="INST", added_reason="UNK") m.save() p = Person.objects.get(userid="0aaa0") m = Member(person=p, offering=c, role="STUD", added_reason="UNK") m.save() # test instructor pages client = Client() client.login_user("ggbaker") response = basic_page_tests(self, client, '/' + c.slug + '/') self.assertContains( response, 'href="' + reverse('offering:groups:groupmanage', kwargs={'course_slug': c.slug}) + '"') basic_page_tests(self, client, c.get_absolute_url()) basic_page_tests( self, client, reverse('offering:student_info', kwargs={ 'course_slug': c.slug, 'userid': '0aaa0' })) basic_page_tests( self, client, reverse('offering:add_numeric_activity', kwargs={'course_slug': c.slug})) basic_page_tests( self, client, reverse('offering:add_letter_activity', kwargs={'course_slug': c.slug})) # test student pages client = Client() client.login_user("0aaa0") response = basic_page_tests(self, client, '/' + c.slug + '/') self.assertContains(response, "Gregorʏ (Greg) Baker") self.assertContains( response, 'href="' + reverse('offering:groups:groupmanage', kwargs={'course_slug': c.slug}) + '"') response = basic_page_tests(self, client, a.get_absolute_url()) # small class (one student) shouldn't contain summary stats self.assertNotContains(response, "Histogram") self.assertNotContains(response, "Standard Deviation")
def create_test_offering(): """ main test course: interesting data for grades, marking, submission, groups """ from grades.models import Activity, LetterActivity, CalNumericActivity, CalLetterActivity from submission.models import SubmissionComponent from submission.models.code import CodeComponent from submission.models.pdf import PDFComponent from groups.models import Group, GroupMember from marking.models import ActivityComponent crs = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) crs.set_labtut(True) crs.set_discussion(True) crs.set_url("http://www.cs.sfu.ca/CC/165/common/") crs.set_taemail("*****@*****.**") crs.save() # create example activities a1 = NumericActivity.objects.get(offering=crs, slug='a1') a2 = NumericActivity(offering=crs, name="Assignment 2", short_name="A2", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=70), percent=10, group=True, max_grade=20, position=2) a2.set_url("http://www.cs.sfu.ca/CC/165/common/a2") a2.save() pr = LetterActivity(offering=crs, name="Project", short_name="Proj", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=80), percent=40, group=True, position=3) pr.save() re = LetterActivity(offering=crs, name="Report", short_name="Rep", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=81), percent=10, group=False, position=4) re.save() ex = NumericActivity(offering=crs, name="Final Exam", short_name="Exam", status="URLS", due_date=None, percent=30, group=False, max_grade=90, position=5) ex.save() to = CalNumericActivity(offering=crs, name="Final Percent", short_name="Perc", status="INVI", due_date=None, percent=0, group=False, max_grade=100, formula="[[activitytotal]]", position=6) to.save() to = CalLetterActivity(offering=crs, name="Letter Grade", short_name="Letter", status="INVI", due_date=None, percent=0, group=False, numeric_activity=to, position=6) to.save() # make A1 submittable and markable s = CodeComponent(activity=a1, title="Cöde File", description="The code you're submitting.", allowed=".py,.java") s.save() s = PDFComponent(activity=a1, title="Report", description="Report on what you did.", specified_filename="report.pdf") s.save() m = ActivityComponent( numeric_activity=a1, max_mark=5, title="Part ➀", description="Part ➀ was done well and seems to work.", position=1, slug='part-1') m.save() m = ActivityComponent( numeric_activity=a1, max_mark=5, title="Part 2", description="Part 2 was done well and seems to work.", position=2) m.save() # create some groups members = list(Member.objects.filter(offering=crs, role='STUD')) random.shuffle(members) m = members.pop() g = Group(name="SomeGroup", courseoffering=crs, manager=m) g.save() for m in [m, members.pop()]: gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() m = members.pop() g = Group(name="AnotherGroup", courseoffering=crs, manager=m) g.save() for m in [m, members.pop(), members.pop()]: gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=pr) gm.save() return itertools.chain( Activity.objects.all(), NumericActivity.objects.all(), LetterActivity.objects.all(), CalNumericActivity.objects.all(), CalLetterActivity.objects.all(), SubmissionComponent.objects.all(), CodeComponent.objects.all(), PDFComponent.objects.all(), Group.objects.all(), GroupMember.objects.all(), ActivityComponent.objects.all(), )
def create_coredata(): """ Create enough of the coredata.models stuff to run basic tests """ create_units() # restore ggbaker's real emplid so import_offerings will match p = find_person('ggbaker') p.emplid = find_emplid('ggbaker') # import a few more people we definitely need later find_person('popowich') find_person('dixon') find_person('diana') find_person('dzhao') find_person('pba7') # import a limited set of course offerings offerings = import_offerings(import_semesters=import_strms, extra_where= "(subject='CMPT' AND (catalog_nbr LIKE '%% 12%%')) " "OR (subject='ENSC' AND (catalog_nbr LIKE '%% 10%%')) " ) offerings = list(offerings) offerings.sort() if not CourseOffering.objects.filter(slug=TEST_COURSE_SLUG): o = CourseOffering.objects.filter(subject='CMPT', semester__name=import_strms()[0]) \ .order_by('number', 'section').first() raise ValueError, "courselib.testing.TEST_COURSE_SLUG isn't an offering we have. Maybe use '%s'." % (o.slug) # import instructors for o in offerings: import_offering_members(o, students=False) # try to guess instructors' userids for p in Person.objects.filter(userid__isnull=True): p.userid = guess_userid(p.emplid) p.save() fake_emplids() # use/import no real emplids after this # create some fake undergrad/grad students for i in range(20): userid = "0aaa%i" % (i) fname = randname(8) p = random.randint(1,2) if p == 1: pref = fname[:4] else: pref = fname p = Person(emplid=300000300+i, userid=userid, last_name='Student', first_name=fname, middle_name=randname(6), pref_first_name=pref) p.save() userid = "0ggg%i" % (i) fname = randname(8) p = random.randint(1,2) if p == 1: pref = fname[:4] else: pref = fname p = Person(emplid=300000500+i, userid=userid, last_name='Grad', first_name=fname, middle_name=randname(6), pref_first_name=pref) p.config['gender'] = random.choice(('M','F','U')) p.config['gpa'] = round(random.triangular(0.0, 4.33, 2.33), 2) p.config['visa'] = random.choice([x for x,_ in VISA_STATUSES]) p.config['citizen'] = random.choice(('Canadian', 'OtherCountrian')) p.save() # some memberships/roles/etc assumed by tests o = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) ensure_member(Person.objects.get(userid='ggbaker'), o, "INST", 0, "AUTO", "NONS") ensure_member(Person.objects.get(userid='0ggg0'), o, "TA", 0, "AUTO", "NONS") ensure_member(Person.objects.get(userid='0aaa0'), o, "STUD", 3, "AUTO", "UGRD") ensure_member(Person.objects.get(userid='0aaa1'), o, "STUD", 3, "AUTO", "UGRD") d = Person.objects.get(userid='dzhao') set_privacy_signed(d) r1 = Role(person=d, role='ADVS', unit=Unit.objects.get(slug='cmpt')) r1.save() r2 = Role(person=d, role='ADMN', unit=Unit.objects.get(slug='cmpt')) r2.save() r3 = Role(person=Person.objects.get(userid='pba7'), role='SYSA', unit=Unit.objects.get(slug='univ')) r3.save() # ensures course appears in menu for students a = NumericActivity(offering=o, name='Assignment 1', short_name='A1', status='URLS', position=1, percent=10, max_grade=10, due_date=(o.semester.start + datetime.timedelta(days=60))) a.save() return itertools.chain( SemesterWeek.objects.filter(semester__name__gt=SEMESTER_CUTOFF), Unit.objects.all(), Course.objects.all(), CourseOffering.objects.all(), Person.objects.order_by('emplid'), Member.objects.all(), [r1, r2, r3, a.activity_ptr, a], )
def test_formulas(self): """ Test the formula parsing & evaluation. """ # set up course and related data s, c = create_offering() p = Person.objects.get(userid="0aaa0") m = Member(person=p, offering=c, role="STUD", credits=3, added_reason="UNK") m.save() a = NumericActivity(name="Paragraph", short_name="\u00b6", status="RLS", offering=c, position=3, max_grade=40, percent=5) a.save() g = NumericGrade(activity=a, member=m, value="4.5", flag="CALC") g.save(entered_by='ggbaker') a1 = NumericActivity(name="Assignment #1", short_name="A1", status="RLS", offering=c, position=1, max_grade=15, percent=10) a1.save() g = NumericGrade(activity=a1, member=m, value=10, flag="GRAD") g.save(entered_by='ggbaker') a2 = NumericActivity(name="Assignment #2", short_name="A2", status="URLS", offering=c, position=2, max_grade=40, percent=20) a2.save(entered_by='ggbaker') g = NumericGrade(activity=a2, member=m, value=30, flag="GRAD") g.save(entered_by='ggbaker') ca = CalNumericActivity(name="Final Grade", short_name="FG", status="RLS", offering=c, position=4, max_grade=1) ca.save() activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) # make sure a formula can be pickled and unpickled safely (i.e. can be cached) tree = parse("sum([Assignment #1], [A1], [A2])/20*-3", c, ca) p = pickle.dumps(tree) tree2 = pickle.loads(p) self.assertEqual(tree, tree2) # check that it found the right list of columns used self.assertEqual(cols_used(tree), set(['A1', 'A2', 'Assignment #1'])) # test parsing and evaluation to make sure we get the right values out for expr, correct in test_formulas: tree = parse(expr, c, ca) res = eval_parse(tree, ca, act_dict, m, False) self.assertAlmostEqual(correct, res, msg="Incorrect result for %s" % (expr, )) # test some badly-formed stuff for appropriate exceptions tree = parse("1 + BEST(3, [A1], [A2])", c, ca) self.assertRaises(EvalException, eval_parse, tree, ca, act_dict, m, True) tree = parse("1 + BEST(0, [A1], [A2])", c, ca) self.assertRaises(EvalException, eval_parse, tree, ca, act_dict, m, True) tree = parse("[Foo] /2", c, ca) self.assertRaises(KeyError, eval_parse, tree, ca, act_dict, m, True) tree = parse("[a1] /2", c, ca) self.assertRaises(KeyError, eval_parse, tree, ca, act_dict, m, True) self.assertRaises(ParseException, parse, "AVG()", c, ca) self.assertRaises(ParseException, parse, "(2+3*84", c, ca) self.assertRaises(ParseException, parse, "2+3**84", c, ca) self.assertRaises(ParseException, parse, "AVG(2,3,4", c, ca) self.assertRaises(ParseException, parse, "{something}", c, ca) # test visible/invisible switching tree = parse("[Assignment #2]", c, ca) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) res = eval_parse(tree, ca, act_dict, m, False) self.assertAlmostEqual(res, 30.0) # test unreleased/missing grade conditions expr = "[Assignment #2]" tree = parse(expr, c, ca) # unreleased assignment (with grade) should not be included in the calculation a2.status = 'URLS' a2.save() activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) # ... unless the instructor said to do so. ca.set_calculation_leak(True) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 30.0) # explicit no grade (released assignment) g.flag = "NOGR" g.save(entered_by='ggbaker') a2.status = 'RLS' a2.save(entered_by='ggbaker') activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) # no grade in database (released assignment) g.delete() activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) # test [[activitytotal]] expr = "[[activitytotal]]" tree = parse(expr, c, ca) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 7.229166666)
def test_add_common_problems(self): c = CourseOffering.objects.get(slug=self.c_slug) a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 1) a.save() co1 = ActivityComponent(numeric_activity=a, title='part1', max_mark=50, position=1) co2 = ActivityComponent(numeric_activity=a, title='part2', max_mark=50, position=2) co1.save() co2.save() #add some common problems cp1 = CommonProblem(activity_component=co1, title='cp1', penalty="0") cp2 = CommonProblem(activity_component=co1, title='cp2', penalty="1.12") cp3 = CommonProblem(activity_component=co2, title='cp3', penalty="-2.3") cp1.save() cp2.save() cp3.save() self.client.login_user('ggbaker') response = basic_page_tests( self, self.client, reverse('offering:marking:manage_common_problems', args=(self.c_slug, a.slug))) forms = response.context['formset'].forms ins0 = forms[0].instance ins1 = forms[1].instance ins2 = forms[2].instance self.assertEqual(ins0.title, 'cp1') self.assertEqual(ins0.activity_component, co1) self.assertEqual(ins1.title, 'cp2') self.assertEqual(ins1.activity_component, co1) self.assertEqual(ins2.title, 'cp3') self.assertEqual(ins2.activity_component, co2) #test the marking page as well url = reverse('offering:marking:marking_student', args=(self.c_slug, a.slug, '0aaa0')) response = basic_page_tests(self, self.client, url) mark_components = response.context['component_data'] com1 = mark_components[0] com2 = mark_components[1] self.assertEqual(com1['component'], co1) self.assertEqual(len(com1['common_problems']), 2) self.assertEqual(com2['component'], co2) self.assertEqual(len(com2['common_problems']), 1)
def test_upload(self): _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date=datetime.datetime.now() + datetime.timedelta(hours=1), group=False) a1.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c.save() userid1 = "0aaa0" userid2 = "0aaa1" userid3 = "0aaa2" for u in [userid1, userid2, userid3]: p = Person.objects.get(userid=u) m = Member(person=p, offering=course, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() # submit as student client = Client() client.login_user("0aaa0") url = reverse('offering:submission:show_components', kwargs={ 'course_slug': course.slug, 'activity_slug': a1.slug }) response = basic_page_tests(self, client, url) # submit a file tmpf = tempfile.NamedTemporaryFile(suffix=".py", delete=False) codecontents = b'print "Hello World!"\n' tmpf.write(codecontents) tmpf.close() try: fh = open(tmpf.name, "r") data = {"%i-code" % (c.id): fh} response = client.post(url, data) self.assertEqual(response.status_code, 302) finally: os.unlink(tmpf.name) # make sure it's there and correct subs = StudentSubmission.objects.all() self.assertEqual(len(subs), 1) sub = subs[0] self.assertEqual(sub.member.person.userid, '0aaa0') codes = SubmittedCode.objects.all() self.assertEqual(len(codes), 1) code = codes[0] code.code.open() self.assertEqual(code.code.read(), codecontents)
def test_post_activity_components(self): c = CourseOffering.objects.get(slug=self.c_slug) #add a numeric activity and its components a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0) a.save() self.client.login_user('ggbaker') url = reverse(manage_activity_components, args=(self.c_slug, a.slug)) # 2 forms for the first 2 components to add post_data = { 'form-0-id': ['', ''], 'form-1-id': ['', ''], 'form-0-title': ['part1'], 'form-1-title': ['part2'], 'form-0-max_mark': ['20'], 'form-1-max_mark': ['20'], 'form-0-description': ['basic1'], 'form-1-description': ['basic2'], 'form-TOTAL_FORMS': ['3'], 'form-INITIAL_FORMS': ['0'] } response = self.client.post(url, post_data, follow=True) self.assertEquals(response.status_code, 200) cps = ActivityComponent.objects.filter(numeric_activity=a, deleted=False) self.assertEquals(len(cps), 2) self.assertEquals(cps[0].title, 'part1') self.assertEquals(cps[1].title, 'part2') # keep the first 2 components, and add 2 more new components post_data2 = { 'form-2-id': ['', ''], 'form-3-id': ['', ''], 'form-2-title': ['part3'], 'form-3-title': ['part4'], 'form-2-max_mark': ['30'], 'form-3-max_mark': ['30'], 'form-2-description': ['advanced1'], 'form-3-description': ['advanced2'], } post_data.update(post_data2) post_data['form-0-id'] = [str(cps[0].id), str(cps[0].id)] post_data['form-1-id'] = [str(cps[1].id), str(cps[1].id)] post_data['form-INITIAL_FORMS'] = ['2'] post_data['form-TOTAL_FORMS'] = ['5'] response = self.client.post(url, post_data, follow=True) self.assertEquals(response.status_code, 200) cps = ActivityComponent.objects.filter(numeric_activity=a, deleted=False) self.assertEquals(len(cps), 4) self.assertEquals(cps[2].title, 'part3') self.assertEquals(cps[3].title, 'part4')
class TestImportFunctionsNumeric(TestCase): fixtures = ['basedata', 'coredata', 'grades'] def setUp(self): self.c_slug = TEST_COURSE_SLUG self.client = Client() self.c = CourseOffering.objects.get(slug = self.c_slug) self.a1 = NumericActivity(offering = self.c, name = 'test_assignment_1', short_name = 'ta1', status = 'RLS', due_date = datetime.now(), max_grade = 100, position = 0) self.a1.save() self.students = self.c.members.filter(person__role='STUD') self.userids = [p.userid for p in self.students] self.emplids = [p.emplid for p in self.students] def get_test_file(self, name): with open(name, 'r') as inp: r = csv.reader(inp) self.values = [] for line in r: self.values.append(line) def compare_grade_lists(self, data_returned): for sname, grade in self.values: sname = _strip_email_userid(sname) self.assertIn(sname, list(data_returned.keys())) self.assertEqual(data_returned[sname], grade) def test_import_grades_old_format(self): inName = 'marking/testfiles/oldformat_noprob.csv' self.get_test_file(inName) data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, []) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_old_format_unknown_userid(self): inName = 'marking/testfiles/oldformat_unk_uid.csv' self.get_test_file(inName) bad_id = [n for n,_ in self.values if n not in self.userids] [0] data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, ['Error found in the file (row 2): Unmatched student number ' 'or user-id ({0}).'. format(bad_id)]) self.assertEqual(len(data_to_return), 2) def test_import_grades_old_format_unknown_emplid(self): inName = 'marking/testfiles/oldformat_unk_emplid.csv' self.get_test_file(inName) bad_emplid = [e for e,_ in self.values if int(e) not in self.emplids] [0] data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertIn('Error found in the file (row 1): Unmatched student number ' 'or user-id ({0}).'. format(bad_emplid), err) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format(self): inName = 'marking/testfiles/newformat_noprob_userid.csv' self.get_test_file(inName) del self.values[0] # Delete header row data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, []) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_short_row(self): inName = 'marking/testfiles/newformat_shortrow_userid.csv' self.get_test_file(inName) del self.values[0] # Delete header row data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, []) self.assertEqual(len(data_to_return), len(self.values)-1) def test_import_grades_new_format_junk_cols(self): inName = 'marking/testfiles/newformat_noprob_junk_cols.csv' self.get_test_file(inName) del self.values[0] # Delete header row for i, row in enumerate(self.values): self.values[i] = [self.values[i][3], self.values[i][1]] data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, []) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_new_format_missing_uid_col(self): ''' Judgement call on the design: If the import file lacks a field named 'Userid', we treat it as an old-format file and get an error on the first line. This unfortunate outcome is required if we are to avoid misinterpreting a short assignment name that matches a student id and thereby misinterpreting an old-style file as though it were a defective (i.e., no 'Userid' column) new-style file. ''' inName = 'marking/testfiles/newformat_missing_uid_col.csv' data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertIn('Error found in the file (row 1): Unmatched student number or user-id (Junk1).', err) self.assertIn('Error found in the file (row 2): Unmatched student number or user-id (w1).', err) self.assertIn('Error found in the file (row 3): Unmatched student number or user-id (w2).', err) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_missing_act_col(self): inName = 'marking/testfiles/newformat_missing_act_col.csv' data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertIn('Error in file header line: No ' 'column labelled for activity {0}.'.format(self.a1.short_name), err) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_dup_act_col(self): inName = 'marking/testfiles/newformat_dup_act_col.csv' data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertIn('Error in file header line: Two columns ' 'labelled {0}.'.format(self.a1.short_name), err) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_missing_values(self): ''' OK for students to have no grade assigned. ''' inName = 'marking/testfiles/newformat_missing_student_grade.csv' self.get_test_file(inName) del self.values[0] # Delete header row for i, row in enumerate(self.values): self.values[i] = [self.values[i][6], self.values[i][1]] data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, []) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_new_format_bad_utf8(self): inName = 'marking/testfiles/newformat_bad_utf8.csv' data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertIn('File cannot be decoded as UTF-8 data: make sure it has been saved as UTF-8 text.', err) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_utf8_bom(self): inName = 'marking/testfiles/newformat_utf8_bom.csv' data_to_return = {} with open(inName, 'rb') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, [])
def test_mark_history(self): c = CourseOffering.objects.get(slug=self.c_slug) #add a numeric activity a = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0) a.save() #take 2 students to make a group stud1 = Member.objects.get(person=Person.objects.get(userid='0aaa0'), offering=c) stud2 = Member.objects.get(person=Person.objects.get(userid='0aaa1'), offering=c) group = Group.objects.create(courseoffering=c, name='hello', manager=stud1) member1 = GroupMember.objects.create(group=group, student=stud1, confirmed=True, activity=a) member2 = GroupMember.objects.create(group=group, student=stud2, confirmed=True, activity=a) ngrade = NumericGrade(activity=a, member=stud2) ngrade.save(entered_by='ggbaker') #assign mark to 0aaa1 individually twice and via the group twice, make some interval between saves std_mark = StudentActivityMark(numeric_grade=ngrade, created_by='ggbaker') std_mark.setMark(20, entered_by='ggbaker') std_mark.save() group_mark = GroupActivityMark(group=group, numeric_activity=a, created_by='ggbaker') group_mark.setMark(30, entered_by='ggbaker') group_mark.save() std_mark = StudentActivityMark(numeric_grade=ngrade, created_by='ggbaker') std_mark.setMark(40, entered_by='ggbaker') std_mark.save() group_mark = GroupActivityMark(group=group, numeric_activity=a, created_by='ggbaker') group_mark.setMark(50, entered_by='ggbaker') group_mark.save() self.client.login_user('ggbaker') response = self.client.get( reverse('marking.views.mark_history_student', args=(self.c_slug, a.slug, '0aaa1'))) self.assertEquals(response.status_code, 200) latest_act_mark = response.context['current_mark'] self.assertEquals(len(response.context['marks_individual']), 2) self.assertEquals(len(response.context['marks_via_group']), 2) self.assertEquals(group_mark, latest_act_mark)
def test_frontend(self): client = Client() client.login_user('ggbaker') # set up a course c = CourseOffering.objects.get(slug = self.c_slug) a1 = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0, group=True) a1.save() a2 = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0, group=False) a2.save() stud1 = Member.objects.get(person = Person.objects.get(userid = '0aaa0'), offering = c) stud2 = Member.objects.get(person = Person.objects.get(userid = '0aaa1'), offering = c) instr = Member.objects.get(person = Person.objects.get(userid = 'ggbaker'), offering = c) group = Group.objects.create(courseoffering = c, name = 'hello', manager = stud1) member1 = GroupMember.objects.create(group = group, student = stud1, confirmed = True, activity=a1) member2 = GroupMember.objects.create(group = group, student = stud2, confirmed = True, activity=a1) # marking form (student) url = reverse('offering:marking:marking_student', kwargs={'course_slug':c.slug, 'activity_slug':a2.slug, 'userid':stud1.person.userid}) response = basic_page_tests(self, client, url) ac = ActivityComponent(numeric_activity=a2, max_mark=5, title="AC Title", description="AC Description", position=1, deleted=False) ac.save() ac = ActivityComponent(numeric_activity=a2, max_mark=5, title="AC Title2", description="AC Description2", position=2, deleted=False) ac.save() cp = CommonProblem(activity_component=ac, title="CP title", penalty=2, description="Cp description", deleted=False) cp.save() response = basic_page_tests(self, client, url) # submit the form and check that objects were created PENALTY = '12.5' # Percentage CMP_1_VALUE = '5.5' CMP_2_VALUE = '3' ADJ = '1.25'# Adjustments are subtracted TOTAL_MARK = ((Decimal(CMP_1_VALUE) + Decimal(CMP_2_VALUE) - Decimal(ADJ)) * (1 - (Decimal(PENALTY) / 100))).quantize(Decimal('.01'), rounding=ROUND_HALF_EVEN) response = client.post(url, {'cmp-1-value': float(CMP_1_VALUE), 'cmp-1-comment': 'perfect part 1', 'cmp-2-value': float(CMP_2_VALUE), 'cmp-2-comment': 'ok', 'mark_adjustment': float(ADJ), 'mark_adjustment_reason': 'reason', 'late_penalty': float(PENALTY), 'overall_comment': 'overall'}) self.assertEqual(response.status_code, 302) sam = StudentActivityMark.objects.filter(activity=a2, numeric_grade__member=stud1) self.assertEqual(len(sam), 1) sam = sam[0] self.assertEqual(sam.mark_adjustment, Decimal(ADJ)) self.assertEqual(sam.late_penalty, Decimal(PENALTY)) self.assertEqual(sam.overall_comment, 'overall') self.assertEqual(sam.mark, TOTAL_MARK) acms = sam.activitycomponentmark_set.all() self.assertEqual(len(acms), 2) self.assertEqual(acms[0].value, Decimal(CMP_1_VALUE)) self.assertEqual(acms[0].comment, 'perfect part 1') g = NumericGrade.objects.get(activity=a2, member=stud1) self.assertEqual(g.value, TOTAL_MARK) # make sure we get old data for "mark based on" response = basic_page_tests(self, client, url + "?base_activity_mark="+str(sam.id)) #self.assertContains(response, 'name="cmp-1-value" type="text" value="{0}'.format(CMP_1_VALUE)) #self.assertContains(response, 'name="late_penalty" type="text" value="{0}'.format(PENALTY)) # look at the "view details" page url = reverse('offering:marking:mark_summary_student', kwargs={'course_slug':c.slug, 'activity_slug':a2.slug, 'userid':stud1.person.userid}) response = basic_page_tests(self, client, url) self.assertContains(response, 'perfect part 1') # marking form (group) url = reverse('offering:marking:marking_student', kwargs={'course_slug':c.slug, 'activity_slug':a1.slug, 'userid':stud1.person.userid}) response = basic_page_tests(self, client, url) ac = ActivityComponent(numeric_activity=a1, max_mark=5, title="AC Title", description="AC Description", position=1, deleted=False) ac.save() ac = ActivityComponent(numeric_activity=a1, max_mark=5, title="AC Title2", description="AC Description2", position=2, deleted=False) ac.save() cp = CommonProblem(activity_component=ac, title="CP title", penalty=2, description="Cp description", deleted=False) cp.save() response = basic_page_tests(self, client, url) # common problem form url = reverse('offering:marking:manage_common_problems', kwargs={'course_slug':c.slug, 'activity_slug':a2.slug}) response = basic_page_tests(self, client, url) # mark all (student and group) url = reverse('offering:mark_all_students', kwargs={'course_slug':c.slug, 'activity_slug':a2.slug}) response = basic_page_tests(self, client, url) # mark all (student and group) url = reverse('offering:mark_all_groups', kwargs={'course_slug':c.slug, 'activity_slug':a1.slug}) response = basic_page_tests(self, client, url)
def test_frontend(self): client = Client() client.login_user('ggbaker') # set up a course c = CourseOffering.objects.get(slug=self.c_slug) a1 = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0, group=True) a1.save() a2 = NumericActivity(offering = c, name = 'test_assignment_1', \ short_name = 'ta1', status = 'RLS', \ due_date = datetime.now(), max_grade = 100, position = 0, group=False) a2.save() stud1 = Member.objects.get(person=Person.objects.get(userid='0aaa0'), offering=c) stud2 = Member.objects.get(person=Person.objects.get(userid='0aaa1'), offering=c) instr = Member.objects.get(person=Person.objects.get(userid='ggbaker'), offering=c) group = Group.objects.create(courseoffering=c, name='hello', manager=stud1) member1 = GroupMember.objects.create(group=group, student=stud1, confirmed=True, activity=a1) member2 = GroupMember.objects.create(group=group, student=stud2, confirmed=True, activity=a1) # marking form (student) url = reverse('marking.views.marking_student', kwargs={ 'course_slug': c.slug, 'activity_slug': a2.slug, 'userid': stud1.person.userid }) response = basic_page_tests(self, client, url) ac = ActivityComponent(numeric_activity=a2, max_mark=5, title="AC Title", description="AC Description", position=1, deleted=False) ac.save() ac = ActivityComponent(numeric_activity=a2, max_mark=5, title="AC Title2", description="AC Description2", position=2, deleted=False) ac.save() cp = CommonProblem(activity_component=ac, title="CP title", penalty=2, description="Cp description", deleted=False) cp.save() response = basic_page_tests(self, client, url) # submit the form and check that objects were created PENALTY = '12.5' # Percentage CMP_1_VALUE = '5.5' CMP_2_VALUE = '3' ADJ = '1.25' # Adjustments are subtracted TOTAL_MARK = ( (Decimal(CMP_1_VALUE) + Decimal(CMP_2_VALUE) - Decimal(ADJ)) * (1 - (Decimal(PENALTY) / 100))).quantize(Decimal('.01'), rounding=ROUND_HALF_EVEN) response = client.post( url, { 'cmp-1-value': float(CMP_1_VALUE), 'cmp-1-comment': 'perfect part 1', 'cmp-2-value': float(CMP_2_VALUE), 'cmp-2-comment': 'ok', 'mark_adjustment': float(ADJ), 'mark_adjustment_reason': 'reason', 'late_penalty': float(PENALTY), u'overall_comment': 'overall' }) self.assertEquals(response.status_code, 302) sam = StudentActivityMark.objects.filter(activity=a2, numeric_grade__member=stud1) self.assertEquals(len(sam), 1) sam = sam[0] self.assertEquals(sam.mark_adjustment, Decimal(ADJ)) self.assertEquals(sam.late_penalty, Decimal(PENALTY)) self.assertEquals(sam.overall_comment, 'overall') self.assertEquals(sam.mark, TOTAL_MARK) acms = sam.activitycomponentmark_set.all() self.assertEquals(len(acms), 2) self.assertEquals(acms[0].value, Decimal(CMP_1_VALUE)) self.assertEquals(acms[0].comment, 'perfect part 1') g = NumericGrade.objects.get(activity=a2, member=stud1) self.assertEquals(g.value, TOTAL_MARK) # make sure we get old data for "mark based on" response = basic_page_tests(self, client, url + "?base_activity_mark=" + str(sam.id)) #self.assertContains(response, 'name="cmp-1-value" type="text" value="{0}'.format(CMP_1_VALUE)) #self.assertContains(response, 'name="late_penalty" type="text" value="{0}'.format(PENALTY)) # look at the "view details" page url = reverse('marking.views.mark_summary_student', kwargs={ 'course_slug': c.slug, 'activity_slug': a2.slug, 'userid': stud1.person.userid }) response = basic_page_tests(self, client, url) self.assertContains(response, 'perfect part 1') # marking form (group) url = reverse('marking.views.marking_student', kwargs={ 'course_slug': c.slug, 'activity_slug': a1.slug, 'userid': stud1.person.userid }) response = basic_page_tests(self, client, url) ac = ActivityComponent(numeric_activity=a1, max_mark=5, title="AC Title", description="AC Description", position=1, deleted=False) ac.save() ac = ActivityComponent(numeric_activity=a1, max_mark=5, title="AC Title2", description="AC Description2", position=2, deleted=False) ac.save() cp = CommonProblem(activity_component=ac, title="CP title", penalty=2, description="Cp description", deleted=False) cp.save() response = basic_page_tests(self, client, url) # common problem form url = reverse('marking.views.manage_common_problems', kwargs={ 'course_slug': c.slug, 'activity_slug': a2.slug }) response = basic_page_tests(self, client, url) # mark all (student and group) url = reverse('marking.views.mark_all_students', kwargs={ 'course_slug': c.slug, 'activity_slug': a2.slug }) response = basic_page_tests(self, client, url) # mark all (student and group) url = reverse('marking.views.mark_all_groups', kwargs={ 'course_slug': c.slug, 'activity_slug': a1.slug }) response = basic_page_tests(self, client, url)
def test_group_submission_view(self): """ test if group submission can be viewed by group member and non group member """ now = datetime.datetime.now() _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date=now, group=True) a1.save() a2 = NumericActivity(name="Assignment 2", short_name="A2", status="RLS", offering=course, position=1, max_grade=15, due_date=now, group=True) a2.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c1 = URL.Component(activity=a1, title="URL Link", position=8) c1.save() c2 = Archive.Component(activity=a1, title="Archive File", position=1, max_size=100000) c2.save() c3 = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c3.save() userid1 = "0aaa0" userid2 = "0aaa1" userid3 = "0aaa2" for u in [userid1, userid2, userid3]: p = Person.objects.get(userid=u) m = Member(person=p, offering=course, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() m = Member.objects.get(person__userid=userid1, offering=course) g = Group(name="Test Group", manager=m, courseoffering=course) g.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() m = Member.objects.get(person__userid=userid2, offering=course) gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() m = Member.objects.get(person__userid=userid3, offering=course) gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() client = Client() # login as "0aaa0", member of group : test_group for assignment1 and assgnment2 client.login_user("0aaa0") #submission page for assignment 1 url = reverse('offering:submission:show_components', kwargs={ 'course_slug': course.slug, 'activity_slug': a1.slug }) response = basic_page_tests(self, client, url) self.assertContains( response, "This is a group activity. You will submit on behalf of the group “Test Group”." ) self.assertContains( response, "You haven't made a submission for this component.")
def create_test_offering(): """ main test course: interesting data for grades, marking, submission, groups """ from grades.models import Activity, LetterActivity, CalNumericActivity, CalLetterActivity from submission.models import SubmissionComponent from submission.models.code import CodeComponent from submission.models.pdf import PDFComponent from groups.models import Group, GroupMember from marking.models import ActivityComponent crs = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) crs.set_labtut(True) crs.set_url("http://www.cs.sfu.ca/CC/165/common/") crs.set_taemail("*****@*****.**") crs.save() # create example activities a1 = NumericActivity.objects.get(offering=crs, slug='a1') a2 = NumericActivity(offering=crs, name="Assignment 2", short_name="A2", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=70), percent=10, group=True, max_grade=20, position=2) a2.set_url("http://www.cs.sfu.ca/CC/165/common/a2") a2.save() pr = LetterActivity(offering=crs, name="Project", short_name="Proj", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=80), percent=40, group=True, position=3) pr.save() re = LetterActivity(offering=crs, name="Report", short_name="Rep", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=81), percent=10, group=False, position=4) re.save() ex = NumericActivity(offering=crs, name="Final Exam", short_name="Exam", status="URLS", due_date=None, percent=30, group=False, max_grade=90, position=5) ex.save() to = CalNumericActivity(offering=crs, name="Final Percent", short_name="Perc", status="INVI", due_date=None, percent=0, group=False, max_grade=100, formula="[[activitytotal]]", position=6) to.save() to = CalLetterActivity(offering=crs, name="Letter Grade", short_name="Letter", status="INVI", due_date=None, percent=0, group=False, numeric_activity=to, position=6) to.save() # make A1 submittable and markable s = CodeComponent(activity=a1, title="Code File", description="The code you're submitting.", allowed=".py,.java") s.save() s = PDFComponent(activity=a1, title="Report", description="Report on what you did.", specified_filename="report.pdf") s.save() m = ActivityComponent(numeric_activity=a1, max_mark=5, title="Part 1", description="Part 1 was done well and seems to work.", position=1) m.save() m = ActivityComponent(numeric_activity=a1, max_mark=5, title="Part 2", description="Part 2 was done well and seems to work.", position=2) m.save() # create some groups members = list(Member.objects.filter(offering=crs, role='STUD')) random.shuffle(members) m = members.pop() g = Group(name="SomeGroup", courseoffering=crs, manager=m) g.save() for m in [m, members.pop()]: gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() m = members.pop() g = Group(name="AnotherGroup", courseoffering=crs, manager=m) g.save() for m in [m, members.pop(), members.pop()]: gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=pr) gm.save() return itertools.chain( Activity.objects.all(), NumericActivity.objects.all(), LetterActivity.objects.all(), CalNumericActivity.objects.all(), CalLetterActivity.objects.all(), SubmissionComponent.objects.all(), CodeComponent.objects.all(), PDFComponent.objects.all(), Group.objects.all(), GroupMember.objects.all(), ActivityComponent.objects.all(), )
def test_class_1(slug): """ main test course: 20 students, TA, some assignments """ crs = CourseOffering.objects.get(slug=slug) crs.set_labtut(True) crs.set_url("http://www.cs.sfu.ca/CC/165/common/") crs.set_taemail("*****@*****.**") crs.save() for i in range(10): lab = "D1%02i" % (random.randint(1, 4)) p = Person.objects.get(userid="0aaa%i" % (i)) if Member.objects.filter(person=p, offering=crs, role="STUD"): # randomly added by other student-adder: skip continue m = Member(person=p, offering=crs, role="STUD", credits=3, career="UGRD", added_reason="AUTO", labtut_section=lab) m.save() if not Member.objects.filter( person__userid='ggbaker', offering=crs, role='INST'): Member(person=Person.objects.get(userid='ggbaker'), offering=crs, role='INST').save() # create a TA p = Person(emplid=fake_emplid(), userid="0grad1", last_name="Gradstudent", first_name="Douglas", middle_name="", pref_first_name="Doug") p.save() m = Member(person=p, offering=crs, role="TA", credits=0, career="NONS", added_reason="AUTO", labtut_section=None) m.save() # create example activities crs.activity_set.all().update(deleted=True) a1 = NumericActivity(offering=crs, name="Assignment 1", short_name="A1", status="RLS", due_date=crs.semester.start + datetime.timedelta(days=60), percent=10, group=False, max_grade=10, position=1) a1.set_url("http://www.cs.sfu.ca/CC/165/common/a1") a1.save() a2 = NumericActivity(offering=crs, name="Assignment 2", short_name="A2", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=70), percent=10, group=True, max_grade=20, position=2) a2.save() pr = LetterActivity(offering=crs, name="Project", short_name="Proj", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=80), percent=40, group=True, position=3) pr.save() re = LetterActivity(offering=crs, name="Report", short_name="Rep", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=81), percent=10, group=False, position=4) re.save() ex = NumericActivity(offering=crs, name="Final Exam", short_name="Exam", status="URLS", due_date=None, percent=30, group=False, max_grade=90, position=5) ex.save() to = CalNumericActivity(offering=crs, name="Final Percent", short_name="Perc", status="INVI", due_date=None, percent=0, group=False, max_grade=100, formula="[[activitytotal]]", position=6) to.save() to = CalLetterActivity(offering=crs, name="Letter Grade", short_name="Letter", status="INVI", due_date=None, percent=0, group=False, numeric_activity=to, position=6) to.save() # make A1 submittable and markable s = CodeComponent(activity=a1, title="Code File", description="The code you're submitting.", allowed=".py,.java") s.save() s = PDFComponent(activity=a1, title="Report", description="Report on what you did.", specified_filename="report.pdf") s.save() m = ActivityComponent( numeric_activity=a1, max_mark=5, title="Part 1", description="Part 1 was done well and seems to work.", position=1) m.save() m = ActivityComponent( numeric_activity=a1, max_mark=5, title="Part 2", description="Part 2 was done well and seems to work.", position=2) m.save() # create some groups g = Group(name="SomeGroup", courseoffering=crs, manager=Member.objects.get(offering=crs, person__userid="0aaa0", role='STUD')) g.save() for userid in ['0aaa0', '0aaa1', '0aaa5', '0aaa8']: gm = GroupMember(group=g, student=Member.objects.get(offering=crs, person__userid=userid), confirmed=True, activity=a2) gm.save() g = Group(name="AnotherGroup", courseoffering=crs, manager=Member.objects.get(offering=crs, person__userid="0aaa4")) g.save() for userid in ['0aaa4', '0aaa6', '0aaa7', '0aaa2']: gm = GroupMember(group=g, student=Member.objects.get(offering=crs, person__userid=userid), confirmed=True, activity=a2) gm.save() gm = GroupMember(group=g, student=Member.objects.get(offering=crs, person__userid=userid), confirmed=True, activity=pr) gm.save()
def test_class_1(slug): """ main test course: 20 students, TA, some assignments """ crs = CourseOffering.objects.get(slug=slug) crs.set_labtut(True) crs.set_url("http://www.cs.sfu.ca/CC/165/common/") crs.set_taemail("*****@*****.**") crs.save() for i in range(10): lab = "D1%02i" % (random.randint(1,4)) p = Person.objects.get(userid="0aaa%i"%(i)) if Member.objects.filter(person=p, offering=crs, role="STUD"): # randomly added by other student-adder: skip continue m = Member(person=p, offering=crs, role="STUD", credits=3, career="UGRD", added_reason="AUTO", labtut_section=lab) m.save() if not Member.objects.filter(person__userid='ggbaker', offering=crs, role='INST'): Member(person=Person.objects.get(userid='ggbaker'), offering=crs, role='INST').save() # create a TA p = Person(emplid=fake_emplid(), userid="0grad1", last_name="Gradstudent", first_name="Douglas", middle_name="", pref_first_name="Doug") p.save() m = Member(person=p, offering=crs, role="TA", credits=0, career="NONS", added_reason="AUTO", labtut_section=None) m.save() # create example activities crs.activity_set.all().update(deleted=True) a1 = NumericActivity(offering=crs, name="Assignment 1", short_name="A1", status="RLS", due_date=crs.semester.start + datetime.timedelta(days=60), percent=10, group=False, max_grade=10, position=1) a1.set_url("http://www.cs.sfu.ca/CC/165/common/a1") a1.save() a2 = NumericActivity(offering=crs, name="Assignment 2", short_name="A2", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=70), percent=10, group=True, max_grade=20, position=2) a2.save() pr = LetterActivity(offering=crs, name="Project", short_name="Proj", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=80), percent=40, group=True, position=3) pr.save() re = LetterActivity(offering=crs, name="Report", short_name="Rep", status="URLS", due_date=crs.semester.start + datetime.timedelta(days=81), percent=10, group=False, position=4) re.save() ex = NumericActivity(offering=crs, name="Final Exam", short_name="Exam", status="URLS", due_date=None, percent=30, group=False, max_grade=90, position=5) ex.save() to = CalNumericActivity(offering=crs, name="Final Percent", short_name="Perc", status="INVI", due_date=None, percent=0, group=False, max_grade=100, formula="[[activitytotal]]", position=6) to.save() to = CalLetterActivity(offering=crs, name="Letter Grade", short_name="Letter", status="INVI", due_date=None, percent=0, group=False, numeric_activity=to, position=6) to.save() # make A1 submittable and markable s = CodeComponent(activity=a1, title="Code File", description="The code you're submitting.", allowed=".py,.java") s.save() s = PDFComponent(activity=a1, title="Report", description="Report on what you did.", specified_filename="report.pdf") s.save() m = ActivityComponent(numeric_activity=a1, max_mark=5, title="Part 1", description="Part 1 was done well and seems to work.", position=1) m.save() m = ActivityComponent(numeric_activity=a1, max_mark=5, title="Part 2", description="Part 2 was done well and seems to work.", position=2) m.save() # create some groups g = Group(name="SomeGroup", courseoffering=crs, manager=Member.objects.get(offering=crs, person__userid="0aaa0", role='STUD')) g.save() for userid in ['0aaa0', '0aaa1', '0aaa5', '0aaa8']: gm = GroupMember(group=g, student=Member.objects.get(offering=crs, person__userid=userid), confirmed=True, activity=a2) gm.save() g = Group(name="AnotherGroup", courseoffering=crs, manager=Member.objects.get(offering=crs, person__userid="0aaa4")) g.save() for userid in ['0aaa4', '0aaa6', '0aaa7', '0aaa2']: gm = GroupMember(group=g, student=Member.objects.get(offering=crs, person__userid=userid), confirmed=True, activity=a2) gm.save() gm = GroupMember(group=g, student=Member.objects.get(offering=crs, person__userid=userid), confirmed=True, activity=pr) gm.save()
def test_group_change(self): """ Test changing group <-> individual on an activity. Should only be possible in some conditions. """ s, c = create_offering() # add some assignments and members due = datetime.datetime.now() + datetime.timedelta(days=1) due_date = str(due.date()) due_time = due.time().strftime("%H:%M:%S") a = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, max_grade=15, percent=10, due_date=due, group=False) a.save() p = Person.objects.get(userid="ggbaker") m = Member(person=p, offering=c, role="INST", added_reason="UNK") m.save() p = Person.objects.get(userid="0aaa0") m = Member(person=p, offering=c, role="STUD", added_reason="UNK") m.save() client = Client() client.login_user("ggbaker") url = reverse('grades.views.edit_activity', kwargs={'course_slug': c.slug, 'activity_slug': a.slug}) # for whatever reason, '0' is group and '1' is individual for the group value submit_dict = {'name': a.name, 'short_name': a.short_name, 'status': a.status, 'due_date_0': due_date, 'due_date_1': due_time, 'percent': a.percent, 'max_grade': a.max_grade, 'group': '1', 'extend_group': 'None'} # no change response = client.post(url, submit_dict) self.assertEquals(response.status_code, 302) # successful submit -> redirect self.assertEquals(NumericActivity.objects.get(id=a.id).group, False) # change indiv -> group submit_dict['group'] = '0' response = client.post(url, submit_dict) self.assertEquals(response.status_code, 302) self.assertEquals(NumericActivity.objects.get(id=a.id).group, True) # try with activity past due a.due_date = datetime.datetime.now() - datetime.timedelta(days=1) a.save() submit_dict['due_date_0'] = str(a.due_date.date()) submit_dict['group'] = '0' response = client.post(url, submit_dict) self.assertEquals(response.status_code, 200) # error on form -> 200 and back to form with error self.assertContains(response, "due date has passed") # try with a mark in the system a.due_date = datetime.datetime.now() + datetime.timedelta(days=1) a.save() submit_dict['due_date_0'] = str(a.due_date.date()) submit_dict['group'] = '0' g = NumericGrade(activity=a, member=m, value=2, flag="GRAD") g.save(entered_by='ggbaker') response = client.post(url, submit_dict) self.assertEquals(response.status_code, 200) self.assertContains(response, "grades have already been given") # try with a submission in the system g.flag = "NOGR" g.save(entered_by='ggbaker') s = StudentSubmission(activity=a, member=m) s.save() response = client.post(url, submit_dict) self.assertEquals(response.status_code, 200) self.assertContains(response, "submissions have already been made")
def test_out_of_zero(self): """ Test activities out of zero """ c = CourseOffering.objects.get(slug=self.course_slug) a = NumericActivity(offering=c, name="AZero", short_name="AZ", status="RLS", group=False, deleted=False, max_grade=0, position=1) a.save() stud = c.member_set.filter(role="STUD")[0] # test as instructor client = Client() client.login_user("ggbaker") url = reverse('offering:change_grade_status', kwargs={ 'course_slug': c.slug, 'activity_slug': a.slug, 'userid': stud.person.userid }) response = basic_page_tests(self, client, url) self.assertContains(response, "out of 0") response = client.post( url, { 'grade-status-value': 3, 'grade-status-flag': 'GRAD', 'grade-status-comment': '' }) self.assertEqual(response.status_code, 302) g = NumericGrade.objects.get(activity=a, member=stud) self.assertEqual(g.value, 3) url = reverse('offering:activity_info', kwargs={ 'course_slug': c.slug, 'activity_slug': a.slug }) response = basic_page_tests(self, client, url) url = reverse('offering:student_info', kwargs={ 'course_slug': c.slug, 'userid': stud.person.userid }) response = basic_page_tests(self, client, url) # test as student client.login_user(stud.person.userid) url = reverse('offering:course_info', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) url = reverse('offering:activity_info', kwargs={ 'course_slug': c.slug, 'activity_slug': a.slug }) response = basic_page_tests(self, client, url)
def test_formulas(self): """ Test the formula parsing & evaluation. """ # set up course and related data s, c = create_offering() p = Person.objects.get(userid="0aaa0") m = Member(person=p, offering=c, role="STUD", credits=3, added_reason="UNK") m.save() a = NumericActivity(name="Paragraph", short_name=u"\u00b6", status="RLS", offering=c, position=3, max_grade=40, percent=5) a.save() g = NumericGrade(activity=a, member=m, value="4.5", flag="CALC") g.save(entered_by='ggbaker') a1 = NumericActivity(name="Assignment #1", short_name="A1", status="RLS", offering=c, position=1, max_grade=15, percent=10) a1.save() g = NumericGrade(activity=a1, member=m, value=10, flag="GRAD") g.save(entered_by='ggbaker') a2 = NumericActivity(name="Assignment #2", short_name="A2", status="URLS", offering=c, position=2, max_grade=40, percent=20) a2.save(entered_by='ggbaker') g = NumericGrade(activity=a2, member=m, value=30, flag="GRAD") g.save(entered_by='ggbaker') ca = CalNumericActivity(name="Final Grade", short_name=u"FG", status="RLS", offering=c, position=4, max_grade=1) ca.save() activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) # make sure a formula can be pickled and unpickled safely (i.e. can be cached) tree = parse("sum([Assignment #1], [A1], [A2])/20*-3", c, ca) p = pickle.dumps(tree) tree2 = pickle.loads(p) self.assertEqual(tree, tree2) # check that it found the right list of columns used self.assertEqual(cols_used(tree), set(['A1', 'A2', 'Assignment #1'])) # test parsing and evaluation to make sure we get the right values out for expr, correct in test_formulas: tree = parse(expr, c, ca) res = eval_parse(tree, ca, act_dict, m, False) self.assertAlmostEqual(correct, res, msg=u"Incorrect result for %s"%(expr,)) # test some badly-formed stuff for appropriate exceptions tree = parse("1 + BEST(3, [A1], [A2])", c, ca) self.assertRaises(EvalException, eval_parse, tree, ca, act_dict, m, True) tree = parse("1 + BEST(0, [A1], [A2])", c, ca) self.assertRaises(EvalException, eval_parse, tree, ca, act_dict, m, True) tree = parse("[Foo] /2", c, ca) self.assertRaises(KeyError, eval_parse, tree, ca, act_dict, m, True) tree = parse("[a1] /2", c, ca) self.assertRaises(KeyError, eval_parse, tree, ca, act_dict, m, True) self.assertRaises(ParseException, parse, "AVG()", c, ca) self.assertRaises(ParseException, parse, "(2+3*84", c, ca) self.assertRaises(ParseException, parse, "2+3**84", c, ca) self.assertRaises(ParseException, parse, "AVG(2,3,4", c, ca) self.assertRaises(ParseException, parse, "{something}", c, ca) # test visible/invisible switching tree = parse("[Assignment #2]", c, ca) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) res = eval_parse(tree, ca, act_dict, m, False) self.assertAlmostEqual(res, 30.0) # test unreleased/missing grade conditions expr = "[Assignment #2]" tree = parse(expr, c, ca) # unreleased assignment (with grade) a2.status='URLS' a2.save() activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) # explicit no grade (released assignment) g.flag="NOGR" g.save(entered_by='ggbaker') a2.status='RLS' a2.save(entered_by='ggbaker') activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) # no grade in database (released assignment) g.delete() activities = NumericActivity.objects.filter(offering=c) act_dict = activities_dictionary(activities) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 0.0) # test [[activitytotal]] expr = "[[activitytotal]]" tree = parse(expr, c, ca) res = eval_parse(tree, ca, act_dict, m, True) self.assertAlmostEqual(res, 7.229166666)
def test_group_change(self): """ Test changing group <-> individual on an activity. Should only be possible in some conditions. """ s, c = create_offering() # add some assignments and members due = datetime.datetime.now() + datetime.timedelta(days=1) due_date = str(due.date()) due_time = due.time().strftime("%H:%M:%S") a = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, max_grade=15, percent=10, due_date=due, group=False) a.save() p = Person.objects.get(userid="ggbaker") m = Member(person=p, offering=c, role="INST", added_reason="UNK") m.save() p = Person.objects.get(userid="0aaa0") m = Member(person=p, offering=c, role="STUD", added_reason="UNK") m.save() client = Client() client.login_user("ggbaker") url = reverse('offering:edit_activity', kwargs={ 'course_slug': c.slug, 'activity_slug': a.slug }) # for whatever reason, '0' is group and '1' is individual for the group value submit_dict = { 'name': a.name, 'short_name': a.short_name, 'status': a.status, 'due_date_0': due_date, 'due_date_1': due_time, 'percent': a.percent, 'max_grade': a.max_grade, 'group': '1', 'extend_group': 'None' } # no change response = client.post(url, submit_dict) self.assertEqual(response.status_code, 302) # successful submit -> redirect self.assertEqual(NumericActivity.objects.get(id=a.id).group, False) # change indiv -> group submit_dict['group'] = '0' response = client.post(url, submit_dict) self.assertEqual(response.status_code, 302) self.assertEqual(NumericActivity.objects.get(id=a.id).group, True) # try with activity past due a.due_date = datetime.datetime.now() - datetime.timedelta(days=1) a.save() submit_dict['due_date_0'] = str(a.due_date.date()) submit_dict['group'] = '0' response = client.post(url, submit_dict) self.assertEqual( response.status_code, 200) # error on form -> 200 and back to form with error self.assertContains(response, "due date has passed") # try with a mark in the system a.due_date = datetime.datetime.now() + datetime.timedelta(days=1) a.save() submit_dict['due_date_0'] = str(a.due_date.date()) submit_dict['group'] = '0' g = NumericGrade(activity=a, member=m, value=2, flag="GRAD") g.save(entered_by='ggbaker') response = client.post(url, submit_dict) self.assertEqual(response.status_code, 200) self.assertContains(response, "grades have already been given") # try with a submission in the system g.flag = "NOGR" g.save(entered_by='ggbaker') s = StudentSubmission(activity=a, member=m) s.save() response = client.post(url, submit_dict) self.assertEqual(response.status_code, 200) self.assertContains(response, "submissions have already been made")
def test_component_view_page(self): _, course = create_offering() a1 = NumericActivity(name="Assignment 1", short_name="A1", status="RLS", offering=course, position=2, max_grade=15, due_date="2010-04-01") a1.save() a2 = NumericActivity(name="Assignment 2", short_name="A2", status="RLS", offering=course, position=1, max_grade=15, due_date="2010-03-01") a2.save() p = Person.objects.get(userid="ggbaker") member = Member(person=p, offering=course, role="INST", career="NONS", added_reason="UNK") member.save() c1 = URL.Component(activity=a1, title="URL Link", position=8) c1.save() c2 = Archive.Component(activity=a1, title="Archive File", position=1, max_size=100000) c2.save() c3 = Code.Component(activity=a1, title="Code File", position=3, max_size=2000, allowed=".py") c3.save() client = Client() client.login_user("ggbaker") # When no component, should display error message url = reverse('offering:submission:show_components', kwargs={ 'course_slug': course.slug, 'activity_slug': a2.slug }) response = basic_page_tests(self, client, url) self.assertContains(response, 'No components configured.') # add component and test component = URL.Component(activity=a2, title="URL2", position=1) component.save() component = Archive.Component(activity=a2, title="Archive2", position=1, max_size=100) component.save() # should all appear response = basic_page_tests(self, client, url) self.assertContains(response, "URL2") self.assertContains(response, "Archive2") # make sure type displays #self.assertContains(response, '<li class="view"><label>Type:</label>Archive</li>') # delete component self.assertRaises(NotImplementedError, component.delete)
class TestImportFunctionsNumeric(TestCase): fixtures = ['test_data'] def setUp(self): self.c_slug = TEST_COURSE_SLUG self.client = Client() self.c = CourseOffering.objects.get(slug=self.c_slug) self.a1 = NumericActivity(offering=self.c, name='test_assignment_1', short_name='ta1', status='RLS', due_date=datetime.now(), max_grade=100, position=0) self.a1.save() self.students = self.c.members.filter(person__role='STUD') self.userids = [p.userid for p in self.students] self.emplids = [p.emplid for p in self.students] def get_test_file(self, name): with open(name, 'r') as inp: r = csv.reader(inp) self.values = [] for line in r: self.values.append(line) def compare_grade_lists(self, data_returned): for sname, grade in self.values: sname = _strip_email_userid(sname) self.assertIn(sname, data_returned.keys()) self.assertEqual(data_returned[sname], grade) def test_import_grades_old_format(self): inName = 'marking/testfiles/oldformat_noprob.csv' self.get_test_file(inName) data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, None) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_old_format_unknown_userid(self): inName = 'marking/testfiles/oldformat_unk_uid.csv' self.get_test_file(inName) bad_id = [n for n, _ in self.values if n not in self.userids][0] data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'Error found in the file (row 2): Unmatched student number ' 'or user-id ({0}).'.format(bad_id)) self.assertEqual(len(data_to_return), 0) def test_import_grades_old_format_unknown_emplid(self): inName = 'marking/testfiles/oldformat_unk_emplid.csv' self.get_test_file(inName) bad_emplid = [e for e, _ in self.values if int(e) not in self.emplids][0] data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'Error found in the file (row 1): Unmatched student number ' 'or user-id ({0}).'.format(bad_emplid)) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format(self): inName = 'marking/testfiles/newformat_noprob_userid.csv' self.get_test_file(inName) del self.values[0] # Delete header row data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, None) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_short_row(self): inName = 'marking/testfiles/newformat_shortrow_userid.csv' self.get_test_file(inName) del self.values[0] # Delete header row data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, None) self.assertEqual(len(data_to_return), len(self.values) - 1) def test_import_grades_new_format_junk_cols(self): inName = 'marking/testfiles/newformat_noprob_junk_cols.csv' self.get_test_file(inName) del self.values[0] # Delete header row for i, row in enumerate(self.values): self.values[i] = [self.values[i][3], self.values[i][1]] data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, None) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_new_format_missing_uid_col(self): ''' Judgement call on the design: If the import file lacks a field named 'Userid', we treat it as an old-format file and get an error on the first line. This unfortunate outcome is required if we are to avoid misinterpreting a short assignment name that matches a student id and thereby misinterpreting an old-style file as though it were a defective (i.e., no 'Userid' column) new-style file. ''' inName = 'marking/testfiles/newformat_missing_uid_col.csv' data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'Error found in the file (row 1): Unmatched student number or user-id (Junk1).' ) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_missing_act_col(self): inName = 'marking/testfiles/newformat_missing_act_col.csv' data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'Error in file header line: No ' 'column labelled for activity {0}.'.format(self.a1.short_name)) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_dup_act_col(self): inName = 'marking/testfiles/newformat_dup_act_col.csv' data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'Error in file header line: Two columns ' 'labelled {0}.'.format(self.a1.short_name)) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_missing_values(self): ''' OK for students to have no grade assigned. ''' inName = 'marking/testfiles/newformat_missing_student_grade.csv' self.get_test_file(inName) del self.values[0] # Delete header row for i, row in enumerate(self.values): self.values[i] = [self.values[i][6], self.values[i][1]] data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual(err, None) self.assertEqual(len(data_to_return), len(self.values)) self.compare_grade_lists(data_to_return) def test_import_grades_new_format_bad_utf8(self): inName = 'marking/testfiles/newformat_bad_utf8.csv' data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'File cannot be decoded as UTF-8 data: make sure it has been saved as UTF-8 text.' ) self.assertEqual(len(data_to_return), 0) def test_import_grades_new_format_utf8_bom(self): inName = 'marking/testfiles/newformat_utf8_bom.csv' data_to_return = {} with open(inName, 'r') as inp: err = _compose_imported_grades(inp, self.students, data_to_return, self.a1) self.assertEqual( err, 'File contains bad UTF-8 data: make sure it has been saved as UTF-8 text.' ) self.assertEqual(len(data_to_return), 0)