def create_grades(): """ Test data for grades, marking, submission, groups """ undergrads = list(Person.objects.filter(last_name='Student')) grads = list(Person.objects.filter(last_name='Grad').exclude(userid='0ggg0')) for o in CourseOffering.objects.all(): # TA m = Member(person=random.choice(grads), role='TA', offering=o, credits=0, career='NONS', added_reason='TAC') m.config['bu'] = 5 m.save() # students if o.slug == TEST_COURSE_SLUG: # there are some specific students we need in this offering for the marking/testfiles/* imports students = set(random.sample(undergrads, 6) + list(Person.objects.filter(userid__in=['0aaa0', '0aaa1', '0aaa2']))) else: students = random.sample(undergrads, 4) for p in students: Member.objects.get_or_create(person=p, role='STUD', offering=o, credits=3, career='UGRD', added_reason='AUTO') return itertools.chain( Holiday.objects.filter(semester__name__gt=SEMESTER_CUTOFF), MeetingTime.objects.all(), Member.objects.filter(role__in=['TA', 'STUD']), create_test_offering(), )
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 create_test_offering(): """ Create a CourseOffering (and related stuff) that can be used in tests with no fixtures """ s = create_fake_semester('1144') u = Unit(label='BABL', name="Department of Babbling") u.save() o = CourseOffering(subject='BABL', number='123', section='F104', semester=s, component='LEC', owner=u, title='Babbling for Baferad Ferzizzles', enrl_cap=100, enrl_tot=5, wait_tot=0) o.save() i = Person(first_name='Insley', last_name='Instructorberg', emplid=20000009, userid='instr') i.save() s = Person(first_name='Stanley', last_name='Studentson', emplid=20000010, userid='student') s.save() Member(offering=o, person=i, role='INST').save() Member(offering=o, person=s, role='STUD').save() return o
def test_course_offering(self): """ Create and test a course offering """ s, c = create_offering() # should have a get_absolute_url url = c.get_absolute_url() self.assertEquals(url, str(url)) self.assertEquals(url[0], '/') self.assertEquals(str(c), "CMPT 120 D100 (Fall 2007)") self.assertEquals(c.name(), "CMPT 120 D1") # check uniqueness criteria c2 = CourseOffering(subject="CMPT", number="120", section="D100", semester=s, component="LAB", graded=True, crse_id=11112, class_nbr=22223, campus='SURRY', enrl_cap=101, enrl_tot=100, wait_tot=3) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, c2.save) c2 = CourseOffering(subject="CMPT", number="121", section="D100", semester=s, component="LAB", graded=True, crse_id=11111, class_nbr=22223, campus='SURRY', enrl_cap=101, enrl_tot=100, wait_tot=3) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, c2.save) c2 = CourseOffering(subject="MACM", number="121", section="D102", semester=s, component="LAB", graded=True, crse_id=11112, class_nbr=22222, campus='SURRY', enrl_cap=101, enrl_tot=100, wait_tot=3) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, c2.save) # test some course memberships p1 = Person(emplid=210012345, userid="test1", last_name="Lname", first_name="Fname", pref_first_name="Fn", middle_name="M") p1.save() m = Member(person=p1, offering=c, role="INST", credits=0, career="NONS", added_reason="AUTO") m.save() self.assertEqual( str(list(c.instructors())), "[<Person: Lname, Fname>]") self.assertEqual( str(list(c.tas())), "[]") self.assertEqual( c.student_count(), 0) m.role = "TA" m.save() self.assertEqual( str(list(c.instructors())), "[]") self.assertEqual( str(list(c.tas())), "[<Person: Lname, Fname>]") self.assertEqual( c.student_count(), 0) m.role = "STUD" m.save() self.assertEqual( str(list(c.instructors())), "[]") self.assertEqual( str(list(c.tas())), "[]") self.assertEqual( c.student_count(), 1) self.assertEqual( str(m), "test1 (210012345) in CMPT 120 D100 (Fall 2007)")
def daily_cleanup(): logger.info('Cleaning up database') # cleanup sessions table call_command('clearsessions') # cleanup old news items NewsItem.objects.filter(updated__lt=datetime.datetime.now()-datetime.timedelta(days=365)).delete() # cleanup old log entries LogEntry.objects.filter(datetime__lt=datetime.datetime.now()-datetime.timedelta(days=365)).delete() # cleanup old official grades Member.clear_old_official_grades()
def daily_cleanup(): logger.info('Cleaning up database') # cleanup sessions table call_command('clearsessions') # cleanup old news items NewsItem.objects.filter(updated__lt=datetime.datetime.now() - datetime.timedelta(days=365)).delete() # cleanup old log entries LogEntry.objects.filter(datetime__lt=datetime.datetime.now() - datetime.timedelta(days=365)).delete() # cleanup old official grades Member.clear_old_official_grades()
def test_sort_letter(self): """ Test sorting letter grades """ s, c = create_offering() a = LetterActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, due_date=None, group=False) a.save() ms = [] for i in range(10): p = Person.objects.get(userid="0aaa%i"%i) m = Member(person=p, offering=c, role="STUD", added_reason="UNK") m.save() ms.append(m) g = LetterGrade(activity=a, member=ms[0], letter_grade="B+", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[1], letter_grade="A", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[2], letter_grade="D", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[3], letter_grade="B-", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[4], letter_grade="P", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[5], letter_grade="GN", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[6], letter_grade="F", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[7], letter_grade="DE", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[8], letter_grade="C-", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[9], letter_grade="N", flag="GRAD") g.save(entered_by='ggbaker') g_objs = LetterGrade.objects.filter(activity=a) gs = [g.letter_grade for g in g_objs] gs_sort = sorted_letters(gs) self.assertEqual(gs_sort, ['A', 'B+', 'B-', 'C-', 'D', 'P', 'F', 'DE', 'N', 'GN']) # pre-sort by userid for median testing (so we know which subsets we're grabbing) gs = [(int(g.member.person.userid[4:]), g.letter_grade) for g in g_objs] gs.sort() gs = [g for u,g in gs] # odd-length case self.assertEqual(median_letters(sorted_letters(gs[0:5])), "B-") # even length with median at boundary self.assertEqual(median_letters(sorted_letters(gs[0:6])), "B-/D") # empty list self.assertEqual(median_letters([]), "\u2014")
def test_sort_letter(self): """ Test sorting letter grades """ s, c = create_offering() a = LetterActivity(name="Assignment 1", short_name="A1", status="RLS", offering=c, position=2, due_date=None, group=False) a.save() ms = [] for i in range(10): p = Person.objects.get(userid="0aaa%i"%i) m = Member(person=p, offering=c, role="STUD", added_reason="UNK") m.save() ms.append(m) g = LetterGrade(activity=a, member=ms[0], letter_grade="B+", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[1], letter_grade="A", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[2], letter_grade="D", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[3], letter_grade="B-", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[4], letter_grade="P", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[5], letter_grade="GN", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[6], letter_grade="F", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[7], letter_grade="DE", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[8], letter_grade="C-", flag="GRAD") g.save(entered_by='ggbaker') g = LetterGrade(activity=a, member=ms[9], letter_grade="N", flag="GRAD") g.save(entered_by='ggbaker') g_objs = LetterGrade.objects.filter(activity=a) gs = [g.letter_grade for g in g_objs] gs_sort = sorted_letters(gs) self.assertEquals(gs_sort, ['A', 'B+', 'B-', 'C-', 'D', 'P', 'F', 'DE', 'N', 'GN']) # pre-sort by userid for median testing (so we know which subsets we're grabbing) gs = [(int(g.member.person.userid[4:]), g.letter_grade) for g in g_objs] gs.sort() gs = [g for u,g in gs] # odd-length case self.assertEquals(median_letters(sorted_letters(gs[0:5])), "B-") # even length with median at boundary self.assertEquals(median_letters(sorted_letters(gs[0:6])), "B-/D") # empty list self.assertEquals(median_letters([]), u"\u2014")
def daily_cleanup(): logger.info('Cleaning up database') # cleanup sessions table call_command('clearsessions') call_command('django_cas_ng_clean_sessions') # cleanup old news items NewsItem.objects.filter(updated__lt=datetime.datetime.now()-datetime.timedelta(days=365)).delete() # cleanup old log entries LogEntry.objects.filter(datetime__lt=datetime.datetime.now()-datetime.timedelta(days=365)).delete() # cleanup old official grades Member.clear_old_official_grades() # cleanup old similarity reports from submission.models.base import SimilarityResult SimilarityResult.cleanup_old()
def fill_courses(): """ Put 10 students and a TA in each course. """ global all_students for crs in CourseOffering.objects.exclude(component="CAN"): lett = random.choice(string.ascii_lowercase) for i in range(10): userid = "0%s%i" % (lett * 3, i) m = Member(person=all_students[userid], offering=crs, role="STUD", credits=3, career="UGRD", added_reason="AUTO") m.save() # and the TA userid = "0%sgrad" % (lett * 3) m = Member(person=all_students[userid], offering=crs, role="TA", credits=0, career="NONS", added_reason="AUTO") m.save()
def index(request): userid = request.user.username memberships, excluded = Member.get_memberships(userid) staff_memberships = [m for m in memberships if m.role in ['INST', 'TA', 'APPR']] # for docs link is_instructor = len([m for m in memberships if m.role == 'INST']) > 0 # For TUGs link news_list = _get_news_list(userid, 5) roles = Role.all_roles(userid) is_grad = GradStudent.objects.filter(person__userid=userid, current_status__in=STATUS_ACTIVE).exists() has_grads = Supervisor.objects.filter(supervisor__userid=userid, supervisor_type='SEN', removed=False).exists() form_groups = FormGroup.objects.filter(members__userid=request.user.username).exists() has_ras = RAAppointment.objects.filter(hiring_faculty__userid=request.user.username, deleted=False).exists() # Only CMPT admins should see the one different TA module. Only non-CMPT TA Admins should see the other. # re-factored to take into account the very few people who should see both (mainly FAS Departmental Admins) cmpt_taadmn = Role.objects_fresh.filter(person__userid=userid, role='TAAD', unit__label='CMPT').exists() other_taadmn = Role.objects_fresh.filter(person__userid=userid, role='TAAD').exclude(unit__label='CMPT').exists() context = {'memberships': memberships, 'staff_memberships': staff_memberships, 'news_list': news_list, 'roles': roles, 'is_grad':is_grad, 'has_grads': has_grads, 'has_ras': has_ras, 'excluded': excluded, 'form_groups': form_groups, 'cmpt_taadmn': cmpt_taadmn, 'other_taadmn': other_taadmn, 'is_instructor': is_instructor} return render(request, "dashboard/index.html", context)
def handle(self, *args, **options): assert settings.DEPLOY_MODE != 'production' raise NotImplementedError() n_fake = 200 fake_students = [Person( emplid=str(500000000 + i), userid='fake%03i' % (i,), last_name='Fake', first_name=random_name(8), middle_name=random_name(5), title=random.choice(['Mr', 'M', 'Ms', 'Dr', 'Count']) ) for i in range(n_fake)] for p in fake_students: p.pref_first_name = random.choice([None, p.first_name[:4]]) students_per_class = 10 for o in CourseOffering.objects.all(): student_people = random.choices(fake_students, k=students_per_class) student_members = [ Member(person=p, offering=o, role='STUD', added_reason='AUTO', credits=3, career='UGRD') for p in student_people ] data.append(student_members) print(serializers.serialize('json', itertools.chain(*data), indent=2))
def index(request): userid = request.user.username memberships, excluded = Member.get_memberships(userid) staff_memberships = [ m for m in memberships if m.role in ['INST', 'TA', 'APPR'] ] # for docs link news_list = _get_news_list(userid, 5) roles = Role.all_roles(userid) is_grad = GradStudent.objects.filter( person__userid=userid, current_status__in=STATUS_ACTIVE).exists() has_grads = Supervisor.objects.filter(supervisor__userid=userid, supervisor_type='SEN', removed=False).exists() form_groups = FormGroup.objects.filter( members__userid=request.user.username).exists() has_ras = RAAppointment.objects.filter( hiring_faculty__userid=request.user.username, deleted=False).exists() #messages.add_message(request, messages.SUCCESS, 'Success message.') #messages.add_message(request, messages.WARNING, 'Warning message.') #messages.add_message(request, messages.INFO, 'Info message.') #messages.add_message(request, messages.ERROR, 'Error message.') context = { 'memberships': memberships, 'staff_memberships': staff_memberships, 'news_list': news_list, 'roles': roles, 'is_grad': is_grad, 'has_grads': has_grads, 'has_ras': has_ras, 'excluded': excluded, 'form_groups': form_groups } return render(request, "dashboard/index.html", context)
def index(request): userid = request.user.username memberships, excluded = Member.get_memberships(userid) staff_memberships = [m for m in memberships if m.role in ['INST', 'TA', 'APPR']] # for docs link is_instructor = len([m for m in memberships if m.role == 'INST']) > 0 # For TUGs link news_list = _get_news_list(userid, 5) roles = Role.all_roles(userid) is_grad = GradStudent.objects.filter(person__userid=userid, current_status__in=STATUS_ACTIVE).exists() has_grads = Supervisor.objects.filter(supervisor__userid=userid, supervisor_type='SEN', removed=False).exists() form_groups = FormGroup.objects.filter(members__userid=request.user.username).exists() has_ras = RAAppointment.objects.filter(hiring_faculty__userid=request.user.username, deleted=False).exists() # Only CMPT admins should see the one different TA module. Only non-CMPT TA Admins should see the other. # re-factored to take into account the very few people who should see both (mainly FAS Departmental Admins) cmpt_taadmn = Role.objects_fresh.filter(person__userid=userid, role='TAAD', unit__label__in=['CMPT', 'SEE']).exists() other_taadmn = Role.objects_fresh.filter(person__userid=userid, role='TAAD').exclude(unit__label__in=['CMPT', 'SEE']).exists() context = {'memberships': memberships, 'staff_memberships': staff_memberships, 'news_list': news_list, 'roles': roles, 'is_grad':is_grad, 'has_grads': has_grads, 'has_ras': has_ras, 'excluded': excluded, 'form_groups': form_groups, 'cmpt_taadmn': cmpt_taadmn, 'other_taadmn': other_taadmn, 'is_instructor': is_instructor} return render(request, "dashboard/index.html", context)
def test_course_page(self): """ Check out a course front-page """ _, c = create_offering() client = Client() # not logged in: should be redirected to login page response = client.get(c.get_absolute_url()) self.assertEqual(response.status_code, 302) # log in as student "0aaa0" client.login_user("0aaa0") p = Person.objects.get(userid="0aaa0") # not in the course: should get 403 Forbidden response = client.get(c.get_absolute_url()) self.assertEqual(response.status_code, 403) # add to course and try again m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() response = client.get(c.get_absolute_url()) self.assertEqual(response.status_code, 200) validate_content(self, response.content, c.get_absolute_url()) # dropped students should be forbidden m.role="DROP" m.save() response = client.get(c.get_absolute_url()) self.assertEqual(response.status_code, 403)
def ensure_member(person, offering, role, cred, added_reason, career, labtut_section=None, grade=None): """ Make sure this member exists with the right properties. """ if person.emplid in [200133427, 200133425, 200133426]: # these are: ["Faculty", "Tba", "Sessional"]. Ignore them: they're ugly. return m_old = Member.objects.filter(person=person, offering=offering) if len(m_old)>1: # may be other manually-created dropped entries: that's okay. m_old = Member.objects.filter(person=person, offering=offering).exclude(role="DROP") if len(m_old)>1: raise KeyError, "Already duplicate entries: %r" % (m_old) elif len(m_old)==0: m_old = Member.objects.filter(person=person, offering=offering) if len(m_old)>=1: m = m_old[0] else: m = Member(person=person, offering=offering) m.role = role m.labtut_section = labtut_section m.credits = cred m.added_reason = added_reason m.career = career # record official grade if we have it (and might need it) if has_letter_activities(m.offering): m.official_grade = grade or None else: m.official_grade = None # if offering is being given lab/tutorial sections, flag it as having them # there must be some way to detect this in ps_class_tbl, but I can't see it. if labtut_section and not offering.labtut(): offering.set_labtut(True) offering.save_if_dirty() m.save_if_dirty() return m
def get_queryset(self): all = self.request.query_params.get('all', None) if all == 'yes': memberships = Member.objects.exclude(role="DROP").exclude(offering__component="CAN") \ .filter(offering__graded=True, person__userid=self.request.user.username) else: memberships, _ = Member.get_memberships(self.request.user.username) offerings = [m.offering for m in memberships] return offerings
def index(request): userid = request.user.username memberships, excluded = Member.get_memberships(userid) staff_memberships = [ m for m in memberships if m.role in ['INST', 'TA', 'APPR'] ] # for docs link is_instructor = len([m for m in memberships if m.role == 'INST' ]) > 0 # For TUGs link news_list = _get_news_list(userid, 5) roles = Role.all_roles(userid) is_grad = GradStudent.objects.filter( person__userid=userid, current_status__in=STATUS_ACTIVE).exists() has_grads = Supervisor.objects.filter( supervisor__userid=userid, supervisor_type__in=['SEN', 'COM', 'COS'], removed=False).exists() form_groups = FormGroup.objects.filter( members__userid=request.user.username).exists() has_ras = RAAppointment.objects.filter( hiring_faculty__userid=request.user.username, deleted=False).exists() has_ra_requests = RARequest.objects.filter( Q(supervisor__userid=request.user.username) | Q(author__userid=request.user.username), deleted=False, draft=False).exists() has_reports = AccessRule.objects.filter( person__userid=request.user.username).exists() # Only CMPT admins should see the one different TA module. They can now also see the other module as we hope to # transition them over. cmpt_taadmn = Role.objects_fresh.filter(person__userid=userid, role='TAAD', unit__label='CMPT').exists() context = { 'memberships': memberships, 'staff_memberships': staff_memberships, 'news_list': news_list, 'roles': roles, 'is_grad': is_grad, 'has_grads': has_grads, 'has_ras': has_ras, 'has_ra_requests': has_ra_requests, 'excluded': excluded, 'form_groups': form_groups, 'cmpt_taadmn': cmpt_taadmn, 'is_instructor': is_instructor, 'has_reports': has_reports } return render(request, "dashboard/index.html", context)
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_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_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 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_search_updates(self): """ Make sure indexing in Haystack is working as we expect. """ fname = 'TestStudentUnusualName' s, c = create_offering() # clear the search index and query: we shouldn't find anything. haystack_clear_index() results = SearchQuerySet().models(Member).filter(text__fuzzy=fname) self.assertEqual(results.count(), 0) # add something searchable and query: we don't expect it to appear in real-time p = Person(last_name='Test', first_name=fname, userid='0aaa99999', emplid=123456) p.save() m = Member(person=p, offering=c, role='STUD') m.save() results = SearchQuerySet().models(Member).filter(text__fuzzy=fname) self.assertEqual(results.count(), 0) # ... but after update_index.we do. haystack_update_index() results = SearchQuerySet().models(Member).filter(text__fuzzy=fname) self.assertEqual(results.count(), 1) # same for removing from the index. m.role = 'DROP' m.save() results = SearchQuerySet().models(Member).filter(text__fuzzy=fname) self.assertEqual(results.count(), 1) # update_index doesn't detect a data change that excludes the object from the index_queryset haystack_update_index() results = SearchQuerySet().models(Member).filter(text__fuzzy=fname) self.assertEqual(results.count(), 1) # but rebuild_index will fix that up. haystack_rebuild_index() results = SearchQuerySet().models(Member).filter(text__fuzzy=fname) self.assertEqual(results.count(), 0)
def add_membership_for(tacrs, reason, memberships): if not tacrs.contract.should_be_added_to_the_course or tacrs.total_bu <= 0: return # Find existing membership for this person+offering if it exists # (Behaviour here implies you can't be both TA and other role in one offering: I'm okay with that.) for m in memberships: if m.person == person and m.offering == tacrs.course: break else: # there was no membership: create m = Member(person=person, offering=tacrs.course) memberships.append(m) m.role = 'TA' m.credits = 0 m.career = 'NONS' m.added_reason = reason m.config['bu'] = str(tacrs.total_bu)
def fill_courses(): """ Put 10 students and a TA in each course. """ global all_students for crs in CourseOffering.objects.exclude(component="CAN"): lett = random.choice(string.ascii_lowercase) for i in range(10): userid = "0%s%i" % (lett*3, i) m = Member(person=all_students[userid], offering=crs, role="STUD", credits=3, career="UGRD", added_reason="AUTO") m.save() # and the TA userid = "0%sgrad" % (lett*3) m = Member(person=all_students[userid], offering=crs, role="TA", credits=0, career="NONS", added_reason="AUTO") m.save()
def index(request): userid = request.user.username memberships, excluded = Member.get_memberships(userid) staff_memberships = [m for m in memberships if m.role in ['INST', 'TA', 'APPR']] # for docs link news_list = _get_news_list(userid, 5) roles = Role.all_roles(userid) is_grad = GradStudent.objects.filter(person__userid=userid, current_status__in=STATUS_ACTIVE).count() > 0 has_grads = Supervisor.objects.filter(supervisor__userid=userid, supervisor_type='SEN', removed=False).count() > 0 form_groups = FormGroup.objects.filter(members__userid=request.user.username).count() > 0 #messages.add_message(request, messages.SUCCESS, 'Success message.') #messages.add_message(request, messages.WARNING, 'Warning message.') #messages.add_message(request, messages.INFO, 'Info message.') #messages.add_message(request, messages.ERROR, 'Error message.') context = {'memberships': memberships, 'staff_memberships': staff_memberships, 'news_list': news_list, 'roles': roles, 'is_grad':is_grad, 'has_grads': has_grads, 'excluded': excluded, 'form_groups': form_groups} return render(request, "dashboard/index.html", context)
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 sync_course_member(self): """ Once a contract is Signed, we should create a Member object for them. If a contract is Cancelled, we should DROP the Member object. This operation should be idempotent - run it as many times as you want, the result should always be the same. """ # if signed, create the Member objects so they have access to the courses. courses = self.course.all() for crs in courses: members = Member.objects.filter(person=self.person, role='TA', offering=crs.course) # the student should either be in the course (1) or not (0) # any other number of responses is unacceptable. assert( len(members) == 1 or len(members) == 0 ) dropped_members = Member.objects.filter(person=self.person, offering=crs.course, role='DROP') assert( len(dropped_members) == 1 or len(dropped_members) == 0) # this shouldn't be. if members and dropped_members: d = dropped_members[0] d.delete() dropped_members = [] # the student must be in one of these three states exists_and_is_in_the_course = len(members) > 0 exists_and_is_dropped = len(dropped_members) > 0 does_not_exist = len(members) == 0 and len(dropped_members) == 0 assert(exists_and_is_in_the_course or exists_and_is_dropped or does_not_exist) assert(not(exists_and_is_in_the_course and exists_and_is_dropped)) assert(not(exists_and_is_dropped and does_not_exist)) assert(not(exists_and_is_in_the_course and does_not_exist)) assert(len(dropped_members) < 2) assert(len(members) < 2) if self.should_be_added_to_the_course: if exists_and_is_dropped: m = dropped_members[0] elif exists_and_is_in_the_course: m = members[0] elif does_not_exist: m = Member(person=self.person, offering=crs.course, role='TA', added_reason='TAC', credits=0, career='NONS') else: assert(False) m.added_reason='TAC' m.role = 'TA' m.config['bu'] = crs.total_bu m.save() crs.member = m crs.save(always_allow=True) else: if exists_and_is_dropped: pass elif exists_and_is_in_the_course: m = members[0] if m.added_reason == 'TAC': m.role = 'DROP' m.save() crs.member = None crs.save(always_allow=True) elif does_not_exist: pass # If they are TAC-added members of any other course this semester, # they probably shouldn't be. members = Member.objects.filter(person=self.person, role='TA', added_reason='TAC', offering__semester=self.category.hiring_semester.semester) courseofferings = [crs.course for crs in courses] for member in members: if member.offering not in courseofferings: member.role = 'DROP' member.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('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_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_student(self): """ Check out group pages for student: go through the whole group-creation process from the student side. """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="URLS", offering=c, position=3, max_grade=20, group=True) 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() userid1 = "0aaa6" userid2 = "0aaa0" userid3 = "0aaa1" userid4 = "0aaa2" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() client = Client() client.login_user(userid1) # group management screen url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, "You don't belong to any group") url = reverse('groups.views.create', kwargs={'course_slug': c.slug}) self.assertContains(response, 'href="%s"'%(url)) # group creation form response = basic_page_tests(self, client, url) # submit group create url = reverse('groups.views.submit', kwargs={'course_slug': c.slug}) response = client.post(url, {"GroupName": "Test Group", "a1-selected": True, "a2-selected": False}) self.assertEquals(response.status_code, 302) gs = Group.objects.filter(courseoffering=c) self.assertEquals(len(gs), 1) self.assertEquals(gs[0].name, "Test Group") self.assertEquals(gs[0].manager.person.userid, userid1) gms = GroupMember.objects.filter(group__courseoffering=c) self.assertEquals(len(gms), 1) self.assertEquals(gms[0].student.person.userid, userid1) self.assertEquals(gms[0].confirmed, True) # member invite form url = reverse('groups.views.invite', kwargs={'course_slug': c.slug, 'group_slug':'g-test-group'}) response = basic_page_tests(self, client, url) # submit invite form: invite userid2 and userid3 response = client.post(url, {"name": userid2}) response = client.post(url, {"name": userid3}) self.assertEquals(response.status_code, 302) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid__in=[userid2,userid3]) self.assertEquals(len(gms), 2) self.assertEquals(gms[0].confirmed, False) self.assertEquals(gms[1].confirmed, False) # log in as userid2 and confirm client.login_user(userid2) url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, ", "+userid1) self.assertContains(response, ", "+userid2+" (unconfirmed)") url = reverse('groups.views.join', kwargs={'course_slug': c.slug, 'group_slug':'g-test-group'}) response = client.post(url) self.assertEquals(response.status_code, 302) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid=userid2) self.assertEquals(len(gms), 1) self.assertEquals(gms[0].confirmed, True) # log in as userid3 and reject client.login_user(userid3) url = reverse('groups.views.reject', kwargs={'course_slug': c.slug, 'group_slug':'g-test-group'}) response = client.post(url) self.assertEquals(response.status_code, 302) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid=userid3) self.assertEquals(len(gms), 0) # inviting userid4 shouldn't work if already in a group m = Member.objects.get(person__userid=userid4, offering=c) g = Group(name="Other Group", manager=m, courseoffering=c) g.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() client.login_user(userid1) url = reverse('groups.views.invite', kwargs={'course_slug': c.slug, 'group_slug':'g-test-group'}) response = client.post(url, {"name": userid4}) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid=userid4) self.assertEqual(len(gms), 1) self.assertEqual(gms[0].group.slug, 'g-other-group')
def save(self, *args, **kwargs): super(TAContract, self).save(*args, **kwargs) # set SIN field on any GradStudent objects for this person from grad.models import GradStudent for gs in GradStudent.objects.filter(person=self.application.person): dummy_sins = ['999999999', '000000000', '123456789'] if (('sin' not in gs.config or ('sin' in gs.config and gs.config['sin'] in dummy_sins)) and not self.sin in dummy_sins ): gs.person.set_sin(self.sin) gs.person.save() # if signed, create the Member objects so they have access to the courses. courses = TACourse.objects.filter(contract=self) for crs in courses: members = Member.objects.filter(person=self.application.person, offering=crs.course).exclude(role='DROP') # assert( len(members) <= 1 ) dropped_members = Member.objects.filter(person=self.application.person, offering=crs.course, role='DROP') # Should Member just have an optional FK to TACourse rather than getting a copy of the BU? if (self.status in ['SGN', 'ACC'] and crs.bu > 0) and not members: if dropped_members: m = dropped_members[0] # if this student was added/dropped by the prof, then added_reason might not be CTA m.added_reason='CTA' m.role = "TA" else: # signed, but not a member: create m = Member(person=self.application.person, offering=crs.course, role='TA', added_reason='CTA', credits=0, career='NONS') m.config['bu'] = crs.total_bu m.save() elif (self.status in ['SGN', 'ACC'] and crs.bu > 0 ) and members: # change in BU -> change in BU for Member m = members[0] if not 'bu' in m.config or m.config['bu'] != crs.total_bu: # if this student was added by the prof, then added_reason might not be CTA m.config['bu'] = crs.total_bu m.added_reason='CTA' m.save() elif ( (not self.status in ['SGN', 'ACC']) or crs.bu == 0) and members: # already in course, but status isn't signed: remove m = members[0] if m.role == 'TA' and m.added_reason == 'CTA': m.role = 'DROP' m.save() else: # (self.status not in ['SGN', 'ACC'] or crs.bu == 0) and not members # there is no contract and this student doesn't exist as a Member in the course pass if self.status in ('CAN', 'REJ'): # These students should be removed from their courses. crs.bu = 0 crs.save() # If this course has 0 BUs and a course Member record, clear that record. if crs.bu == 0 and members: m = members[0] if m.role == 'TA' and m.added_reason == 'CTA': m.role = 'DROP' m.save() # If they are CTA-added members of any other course this semester, they probably shouldn't be members = Member.objects.filter(person=self.application.person, role='TA', added_reason='CTA', offering__semester=self.posting.semester ) courseofferings = [crs.course for crs in courses if crs.bu > 0] for member in members: if member.offering not in courseofferings: member.role = 'DROP' member.save()
def test_instructor_workflow(self): """ Work through the site as an instructor """ s, c = create_offering() userid1 = "0aaa0" userid2 = "0aaa1" userid3 = "0aaa2" userid4 = "ggbaker" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() m.role="INST" m.save() client = Client() client.login_user("ggbaker") # course main screen url = reverse('grades.views.course_info', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) url = reverse('grades.views.activity_choice', kwargs={'course_slug': c.slug}) self.assertContains(response, 'href="' + url +'"') url = reverse('grades.views.add_numeric_activity', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) # add activity import datetime now = datetime.datetime.now() due = now + datetime.timedelta(days=7) response = client.post(url, {'name':'Assignment 1', 'short_name':'A1', 'status':'URLS', 'due_date_0':due.strftime('%Y-%m-%d'), 'due_date_1':due.strftime('%H:%M:%S'), 'percent': '10', 'group': '1', 'max_grade': 25, 'extend_group': 'None'}) self.assertEquals(response.status_code, 302) acts = NumericActivity.objects.filter(offering=c) self.assertEquals(len(acts), 1) a = acts[0] self.assertEquals(a.name, "Assignment 1") self.assertEquals(a.slug, "a1") self.assertEquals(a.max_grade, 25) self.assertEquals(a.group, False) self.assertEquals(a.deleted, False) # add calculated numeric activity url = reverse('grades.views.add_cal_numeric_activity', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) response = client.post(url, {'name':'Total', 'short_name':'Total', 'status':'URLS', 'group': '1', 'max_grade': 30, 'formula': '[A1]+5'}) self.assertEquals(response.status_code, 302) acts = CalNumericActivity.objects.filter(offering=c) self.assertEquals(len(acts), 1) a = acts[0] self.assertEquals(a.slug, "total") self.assertEquals(a.max_grade, 30) self.assertEquals(a.group, False) self.assertEquals(a.deleted, False) self.assertEquals(a.formula, '[A1]+5') # formula tester url = reverse('grades.views.formula_tester', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) response = client.get(url, {'formula': '[A1]+5', 'a1-status': 'RLS', 'a1-value': '6', 'total-status': 'URLS'}) self.assertContains(response, '<div id="formula_result">11.0</div>') validate_content(self, response.content, url)
def test_group_staff(self): """ Check out group pages for an instructor: go through the group-creation process from the instructor side. """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="URLS", offering=c, position=3, max_grade=20, group=True) a.save() a = NumericActivity(name="Assignment 2", short_name="A2", status="URLS", offering=c, position=6, max_grade=20, group=True) a.save() userid1 = "0aaa6" userid2 = "0aaa0" userid3 = "0aaa1" userid4 = "ggbaker" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() m.role="INST" m.save() client = Client() client.login_user("ggbaker") # group management screen url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, "There are currently no groups in this course") url = reverse('groups.views.create', kwargs={'course_slug': c.slug}) self.assertContains(response, 'href="%s"'%(url)) # group creation form response = basic_page_tests(self, client, url) # submit group create url = reverse('groups.views.submit', kwargs={'course_slug': c.slug}) response = client.post(url, {"GroupName": "Test Group", "a1-selected": True, "a2-selected": False, '0aaa6-selected': False, '0aaa0-selected': True, '0aaa1-selected': True}) self.assertEquals(response.status_code, 302) gs = Group.objects.filter(courseoffering=c) self.assertEquals(len(gs), 1) self.assertEquals(gs[0].name, "Test Group") self.assertEquals(gs[0].slug, "g-test-group") gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0]) self.assertEquals(len(gms), 2) self.assertEquals(gms[0].confirmed, True) self.assertEquals(gms[1].confirmed, True) self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid2,userid3])) # check group management screen again url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) # add membership form url = reverse('groups.views.assign_student', kwargs={'course_slug': c.slug, 'group_slug': "g-test-group"}) response = basic_page_tests(self, client, url) # submit add membership response = client.post(url, {"a1-selected": True, "a2-selected": True, '0aaa6-selected': False, '0aaa0-selected': False, '0aaa1-selected': True}) self.assertEquals(response.status_code, 302) # both still in for A1 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a1") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid2,userid3])) # 0aaa1 added for A2 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a2") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid3])) # remove member form url = reverse('groups.views.remove_student', kwargs={'course_slug': c.slug, 'group_slug': "g-test-group"}) response = basic_page_tests(self, client, url) # submit remove member response = client.post(url, {'0aaa6_a1-selected': True, '0aaa0_a1-selected': False, '0aaa1_a1-selected': True}) self.assertEquals(response.status_code, 302) # 0aaa1 gone for A1 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a1") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid2])) # 0aaa1 still there for A2 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a2") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid3])) # rename group form url = reverse('groups.views.change_name', kwargs={'course_slug': c.slug, 'group_slug': "g-test-group"}) response = basic_page_tests(self, client, url) # submit change name response = client.post(url, {'name': 'otherName'}) self.assertEquals(response.status_code, 302) g = Group.objects.get(courseoffering=c) self.assertEquals(g.name, 'otherName') self.assertEquals(g.slug, 'g-test-group') # recheck basic view with more data url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) url = reverse('groups.views.view_group', kwargs={'course_slug': c.slug, 'group_slug': "g-test-group"}) 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 test_group_models(self): """ Test the backend for groups """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="URLS", offering=c, position=3, max_grade=20, group=True) 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() a2 = a userid1 = "0aaa4" userid2 = "0aaa0" userid3 = "0aaa1" userid4 = "0aaa2" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() # basics m = Member.objects.get(person__userid=userid1, offering=c) g = Group(name="Test Group", manager=m, courseoffering=c) g.save() self.assertEqual(g.slug, 'g-test-group') gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() m = Member.objects.get(person__userid=userid2, offering=c) gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() gs = Group.objects.filter(courseoffering=c) self.assertEqual(len(gs), 1) g = gs[0] self.assertEqual(set([gm.student.person.userid for gm in g.groupmember_set.all()]), set([userid1,userid2])) # check uniqueness of activity + member m = Member.objects.get(person__userid=userid3, offering=c) g2 = Group(name="Other Group", manager=m, courseoffering=c) g2.save() gm = GroupMember(group=g2, student=m, confirmed=True, activity=a1) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, gm.save) # uniqueness of group name g3 = Group(name="Other Group", manager=m, courseoffering=c) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, g3.save) # finding all activities this group covers members = GroupMember.objects.filter(group=g) all_act = all_activities(members) self.assertEqual(set(a.slug for a in all_act), set([a1.slug])) # add a member for assignment 2 and check again gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() members = GroupMember.objects.filter(group=g) all_act = all_activities(members) self.assertEqual(set(a.slug for a in all_act), set([a1.slug, a2.slug])) # check student-editable tests # test dates m = Member.objects.get(offering=c, person__userid="0aaa0") gm = GroupMember.objects.get(group=g, student=m, activity=a1) a1.due_date = datetime.datetime.now() - datetime.timedelta(days=1) # yesterday a1.save() self.assertTrue("passed" in gm.student_editable("0aaa0")) a1.due_date = datetime.datetime.now() + datetime.timedelta(days=1) # tomorrow a1.save() gm = GroupMember.objects.get(group=g, student=m, activity=a1) self.assertEqual(gm.student_editable("0aaa0"), '') # not member for this activity self.assertTrue("not a member" in gm.student_editable("0aaa1")) # already graded gr = NumericGrade(activity=a1, member=m, value=1, flag="GRAD") gr.save(entered_by='ggbaker') self.assertTrue("grade" in gm.student_editable("0aaa0")) gr.flag="NOGR" gr.save(entered_by='ggbaker') self.assertEqual(gm.student_editable("0aaa0"), '') # submission made s = GroupSubmission(group=g, creator=m, activity=a1) s.save() self.assertTrue("submission" in gm.student_editable("0aaa0"))
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)
def test_group_student(self): """ Check out group pages for student: go through the whole group-creation process from the student side. """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="URLS", offering=c, position=3, max_grade=20, group=True) 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() userid1 = "0aaa6" userid2 = "0aaa0" userid3 = "0aaa1" userid4 = "0aaa2" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() client = Client() client.login_user(userid1) # group management screen url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, "You don't belong to any group") url = reverse('groups.views.create', kwargs={'course_slug': c.slug}) self.assertContains(response, 'href="%s"' % (url)) # group creation form response = basic_page_tests(self, client, url) # submit group create url = reverse('groups.views.submit', kwargs={'course_slug': c.slug}) response = client.post(url, { "GroupName": "Test Group", "a1-selected": True, "a2-selected": False }) self.assertEquals(response.status_code, 302) gs = Group.objects.filter(courseoffering=c) self.assertEquals(len(gs), 1) self.assertEquals(gs[0].name, "Test Group") self.assertEquals(gs[0].manager.person.userid, userid1) gms = GroupMember.objects.filter(group__courseoffering=c) self.assertEquals(len(gms), 1) self.assertEquals(gms[0].student.person.userid, userid1) self.assertEquals(gms[0].confirmed, True) # member invite form url = reverse('groups.views.invite', kwargs={ 'course_slug': c.slug, 'group_slug': 'g-test-group' }) response = basic_page_tests(self, client, url) # submit invite form: invite userid2 and userid3 response = client.post(url, {"name": userid2}) response = client.post(url, {"name": userid3}) self.assertEquals(response.status_code, 302) gms = GroupMember.objects.filter( group__courseoffering=c, student__person__userid__in=[userid2, userid3]) self.assertEquals(len(gms), 2) self.assertEquals(gms[0].confirmed, False) self.assertEquals(gms[1].confirmed, False) # log in as userid2 and confirm client.login_user(userid2) url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, ", " + userid1) self.assertContains(response, ", " + userid2 + " (unconfirmed)") url = reverse('groups.views.join', kwargs={ 'course_slug': c.slug, 'group_slug': 'g-test-group' }) response = client.post(url) self.assertEquals(response.status_code, 302) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid=userid2) self.assertEquals(len(gms), 1) self.assertEquals(gms[0].confirmed, True) # log in as userid3 and reject client.login_user(userid3) url = reverse('groups.views.reject', kwargs={ 'course_slug': c.slug, 'group_slug': 'g-test-group' }) response = client.post(url) self.assertEquals(response.status_code, 302) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid=userid3) self.assertEquals(len(gms), 0) # inviting userid4 shouldn't work if already in a group m = Member.objects.get(person__userid=userid4, offering=c) g = Group(name="Other Group", manager=m, courseoffering=c) g.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() client.login_user(userid1) url = reverse('groups.views.invite', kwargs={ 'course_slug': c.slug, 'group_slug': 'g-test-group' }) response = client.post(url, {"name": userid4}) gms = GroupMember.objects.filter(group__courseoffering=c, student__person__userid=userid4) self.assertEqual(len(gms), 1) self.assertEqual(gms[0].group.slug, 'g-other-group')
def test_group_staff(self): """ Check out group pages for an instructor: go through the group-creation process from the instructor side. """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="URLS", offering=c, position=3, max_grade=20, group=True) a.save() a = NumericActivity(name="Assignment 2", short_name="A2", status="URLS", offering=c, position=6, max_grade=20, group=True) a.save() userid1 = "0aaa6" userid2 = "0aaa0" userid3 = "0aaa1" userid4 = "ggbaker" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() m.role = "INST" m.save() client = Client() client.login_user("ggbaker") # group management screen url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) self.assertContains(response, "There are currently no groups in this course") url = reverse('groups.views.create', kwargs={'course_slug': c.slug}) self.assertContains(response, 'href="%s"' % (url)) # group creation form response = basic_page_tests(self, client, url) # submit group create url = reverse('groups.views.submit', kwargs={'course_slug': c.slug}) response = client.post( url, { "GroupName": "Test Group", "a1-selected": True, "a2-selected": False, '0aaa6-selected': False, '0aaa0-selected': True, '0aaa1-selected': True }) self.assertEquals(response.status_code, 302) gs = Group.objects.filter(courseoffering=c) self.assertEquals(len(gs), 1) self.assertEquals(gs[0].name, "Test Group") self.assertEquals(gs[0].slug, "g-test-group") gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0]) self.assertEquals(len(gms), 2) self.assertEquals(gms[0].confirmed, True) self.assertEquals(gms[1].confirmed, True) self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid2, userid3])) # check group management screen again url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) # add membership form url = reverse('groups.views.assign_student', kwargs={ 'course_slug': c.slug, 'group_slug': "g-test-group" }) response = basic_page_tests(self, client, url) # submit add membership response = client.post( url, { "a1-selected": True, "a2-selected": True, '0aaa6-selected': False, '0aaa0-selected': False, '0aaa1-selected': True }) self.assertEquals(response.status_code, 302) # both still in for A1 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a1") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid2, userid3])) # 0aaa1 added for A2 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a2") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid3])) # remove member form url = reverse('groups.views.remove_student', kwargs={ 'course_slug': c.slug, 'group_slug': "g-test-group" }) response = basic_page_tests(self, client, url) # submit remove member response = client.post( url, { '0aaa6_a1-selected': True, '0aaa0_a1-selected': False, '0aaa1_a1-selected': True }) self.assertEquals(response.status_code, 302) # 0aaa1 gone for A1 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a1") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid2])) # 0aaa1 still there for A2 gms = GroupMember.objects.filter(group__courseoffering=c, group=gs[0], activity__slug="a2") self.assertEquals(set(gm.student.person.userid for gm in gms), set([userid3])) # rename group form url = reverse('groups.views.change_name', kwargs={ 'course_slug': c.slug, 'group_slug': "g-test-group" }) response = basic_page_tests(self, client, url) # submit change name response = client.post(url, {'name': 'otherName'}) self.assertEquals(response.status_code, 302) g = Group.objects.get(courseoffering=c) self.assertEquals(g.name, 'otherName') self.assertEquals(g.slug, 'g-test-group') # recheck basic view with more data url = reverse('groups.views.groupmanage', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) url = reverse('groups.views.view_group', kwargs={ 'course_slug': c.slug, 'group_slug': "g-test-group" }) response = basic_page_tests(self, client, url)
def test_group_models(self): """ Test the backend for groups """ s, c = create_offering() a = NumericActivity(name="Assignment 1", short_name="A1", status="URLS", offering=c, position=3, max_grade=20, group=True) 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() a2 = a userid1 = "0aaa4" userid2 = "0aaa0" userid3 = "0aaa1" userid4 = "0aaa2" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() # basics m = Member.objects.get(person__userid=userid1, offering=c) g = Group(name="Test Group", manager=m, courseoffering=c) g.save() self.assertEqual(g.slug, 'g-test-group') gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() m = Member.objects.get(person__userid=userid2, offering=c) gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) gm.save() gs = Group.objects.filter(courseoffering=c) self.assertEqual(len(gs), 1) g = gs[0] self.assertEqual( set([gm.student.person.userid for gm in g.groupmember_set.all()]), set([userid1, userid2])) # check uniqueness of activity + member m = Member.objects.get(person__userid=userid3, offering=c) g2 = Group(name="Other Group", manager=m, courseoffering=c) g2.save() gm = GroupMember(group=g2, student=m, confirmed=True, activity=a1) gm.save() gm = GroupMember(group=g, student=m, confirmed=True, activity=a1) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, gm.save) # uniqueness of group name g3 = Group(name="Other Group", manager=m, courseoffering=c) # south doesn't seem to create the constraints in SQLite for testing #self.assertRaises(IntegrityError, g3.save) # finding all activities this group covers members = GroupMember.objects.filter(group=g) all_act = all_activities(members) self.assertEqual(set(a.slug for a in all_act), set([a1.slug])) # add a member for assignment 2 and check again gm = GroupMember(group=g, student=m, confirmed=True, activity=a2) gm.save() members = GroupMember.objects.filter(group=g) all_act = all_activities(members) self.assertEqual(set(a.slug for a in all_act), set([a1.slug, a2.slug])) # check student-editable tests # test dates m = Member.objects.get(offering=c, person__userid="0aaa0") gm = GroupMember.objects.get(group=g, student=m, activity=a1) a1.due_date = datetime.datetime.now() - datetime.timedelta( days=1) # yesterday a1.save() self.assertTrue("passed" in gm.student_editable("0aaa0")) a1.due_date = datetime.datetime.now() + datetime.timedelta( days=1) # tomorrow a1.save() gm = GroupMember.objects.get(group=g, student=m, activity=a1) self.assertEqual(gm.student_editable("0aaa0"), '') # not member for this activity self.assertTrue("not a member" in gm.student_editable("0aaa1")) # already graded gr = NumericGrade(activity=a1, member=m, value=1, flag="GRAD") gr.save(entered_by='ggbaker') self.assertTrue("grade" in gm.student_editable("0aaa0")) gr.flag = "NOGR" gr.save(entered_by='ggbaker') self.assertEqual(gm.student_editable("0aaa0"), '') # submission made s = GroupSubmission(group=g, creator=m, activity=a1) s.save() self.assertTrue("submission" in gm.student_editable("0aaa0"))
def sync_course_member(self): """ Once a contract is Signed, we should create a Member object for them. If a contract is Cancelled, we should DROP the Member object. This operation should be idempotent - run it as many times as you want, the result should always be the same. """ # if signed, create the Member objects so they have access to the courses. courses = self.course.all() for crs in courses: members = Member.objects.filter(person=self.person, role='TA', offering=crs.course) # the student should either be in the course (1) or not (0) # any other number of responses is unacceptable. assert (len(members) == 1 or len(members) == 0) dropped_members = Member.objects.filter(person=self.person, offering=crs.course, role='DROP') assert (len(dropped_members) == 1 or len(dropped_members) == 0) # this shouldn't be. if members and dropped_members: d = dropped_members[0] d.delete() dropped_members = [] # the student must be in one of these three states exists_and_is_in_the_course = len(members) > 0 exists_and_is_dropped = len(dropped_members) > 0 does_not_exist = len(members) == 0 and len(dropped_members) == 0 assert (exists_and_is_in_the_course or exists_and_is_dropped or does_not_exist) assert (not (exists_and_is_in_the_course and exists_and_is_dropped)) assert (not (exists_and_is_dropped and does_not_exist)) assert (not (exists_and_is_in_the_course and does_not_exist)) assert (len(dropped_members) < 2) assert (len(members) < 2) if self.should_be_added_to_the_course: if exists_and_is_dropped: m = dropped_members[0] elif exists_and_is_in_the_course: m = members[0] elif does_not_exist: m = Member(person=self.person, offering=crs.course, role='TA', added_reason='TAC', credits=0, career='NONS') else: assert (False) m.added_reason = 'TAC' m.role = 'TA' m.config['bu'] = crs.total_bu m.save() crs.member = m crs.save(always_allow=True) else: if exists_and_is_dropped: pass elif exists_and_is_in_the_course: m = members[0] if m.added_reason == 'TAC': m.role = 'DROP' m.save() crs.member = None crs.save(always_allow=True) elif does_not_exist: pass # If they are TAC-added members of any other course this semester, # they probably shouldn't be. members = Member.objects.filter( person=self.person, role='TA', added_reason='TAC', offering__semester=self.category.hiring_semester.semester) courseofferings = [crs.course for crs in courses] for member in members: if member.offering not in courseofferings: member.role = 'DROP' member.save()
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 handle(self, *args, **options): assert settings.DEPLOY_MODE != 'production' # import public data dumped from production with transaction.atomic(): for obj in serializers.deserialize( 'json', open(options['data_file'], 'rt', encoding='utf8').read()): obj.save() # create fake students and a TA in every offering with transaction.atomic(): fake_students = [ Person(emplid=str(500000000 + i), userid='fake%03i' % (i, ), last_name='Fake', first_name=random_name(8), middle_name=random_name(5), title=random.choice(['Mr', 'M', 'Ms', 'Dr'])) for i in range(n_fake_students) ] for p in fake_students: p.pref_first_name = random.choice([None, p.first_name[:4]]) p.save() fake_grads = [ Person(emplid=str(510000000 + i), userid='grad%03i' % (i, ), last_name='Grad', first_name=random_name(8), middle_name=random_name(5), title=random.choice(['Mr', 'M', 'Ms', 'Dr'])) for i in range(n_fake_grads) ] for p in fake_grads: p.pref_first_name = random.choice([None, p.first_name[:4]]) p.save() for o in CourseOffering.objects.all(): ta_person = random.choice(fake_grads) m = Member(person=ta_person, offering=o, role='TA', added_reason='AUTO', credits=0, career='NONS') m.save() student_people = set( random.choices(fake_students, k=students_per_class)) for p in student_people: m = Member(person=p, offering=o, role='STUD', added_reason='AUTO', credits=3, career='UGRD') m.save() r = Role(person=Person.objects.get(userid='ggbaker'), role='SYSA', unit=Unit.objects.get(label='UNIV'), expiry=datetime.date.today() + datetime.timedelta(days=730)) r.save()
def manage_tas(request, course_slug): course = get_object_or_404(CourseOffering, slug=course_slug) longform = False if not Member.objects.filter(offering=course, person__userid=request.user.username, role="INST"): # only instructors can manage TAs return ForbiddenResponse(request, "Only instructors can manage TAs") if request.method == 'POST' and 'action' in request.POST and request.POST['action']=='add': form = TAForm(offering=course, data=request.POST) if form.non_field_errors(): # have an unknown userid longform = True elif form.is_valid(): userid = form.cleaned_data['userid'] if not Person.objects.filter(userid=userid) \ and form.cleaned_data['fname'] and form.cleaned_data['lname']: # adding a new person: handle that. eid = 1 # search for an unused temp emplid while True: emplid = "%09i" % (eid) if not Person.objects.filter(emplid=emplid): break eid += 1 p = Person(first_name=form.cleaned_data['fname'], pref_first_name=form.cleaned_data['fname'], last_name=form.cleaned_data['lname'], middle_name='', userid=userid, emplid=emplid) p.save() else: p = Person.objects.get(userid=userid) m = Member(person=p, offering=course, role="TA", credits=0, career="NONS", added_reason="TAIN") m.save() #LOG EVENT# l = LogEntry(userid=request.user.username, description=("TA added by instructor: %s for %s") % (userid, course), related_object=m) l.save() messages.success(request, 'Added %s as a TA.' % (p.name())) return HttpResponseRedirect(reverse(manage_tas, kwargs={'course_slug': course.slug})) elif request.method == 'POST' and 'action' in request.POST and request.POST['action']=='del': userid = request.POST['userid'] ms = Member.objects.filter(person__userid=userid, offering=course, role="TA", added_reason="TAIN") if ms: m = ms[0] m.role = "DROP" m.save() #LOG EVENT# l = LogEntry(userid=request.user.username, description=("TA removed by instructor: %s for %s") % (userid, course), related_object=m) l.save() messages.success(request, 'Removed %s as a TA.' % (m.person.name())) return HttpResponseRedirect(reverse(manage_tas, kwargs={'course_slug': course.slug})) else: form = TAForm(offering=course) tas = Member.objects.filter(role="TA", offering=course) context = {'course': course, 'form': form, 'tas': tas, 'longform': longform} return render(request, 'coredata/manage_tas.html', context)
def manage_tas(request, course_slug): course = get_object_or_404(CourseOffering, slug=course_slug) longform = False if not Member.objects.filter(offering=course, person__userid=request.user.username, role="INST"): # only instructors can manage TAs return ForbiddenResponse(request, "Only instructors can manage TAs") if request.method == 'POST' and 'action' in request.POST and request.POST[ 'action'] == 'add': form = TAForm(offering=course, data=request.POST) if form.non_field_errors(): # have an unknown userid longform = True elif form.is_valid(): userid = form.cleaned_data['userid'] if not Person.objects.filter(userid=userid) \ and form.cleaned_data['fname'] and form.cleaned_data['lname']: # adding a new person: handle that. eid = 1 # search for an unused temp emplid while True: emplid = "%09i" % (eid) if not Person.objects.filter(emplid=emplid): break eid += 1 p = Person(first_name=form.cleaned_data['fname'], pref_first_name=form.cleaned_data['fname'], last_name=form.cleaned_data['lname'], middle_name='', userid=userid, emplid=emplid) p.save() else: p = Person.objects.get(userid=userid) m = Member(person=p, offering=course, role="TA", credits=0, career="NONS", added_reason="TAIN") m.save() #LOG EVENT# l = LogEntry(userid=request.user.username, description=("TA added by instructor: %s for %s") % (userid, course), related_object=m) l.save() messages.success(request, 'Added %s as a TA.' % (p.name())) return HttpResponseRedirect( reverse(manage_tas, kwargs={'course_slug': course.slug})) elif request.method == 'POST' and 'action' in request.POST and request.POST[ 'action'] == 'del': userid = request.POST['userid'] ms = Member.objects.filter(person__userid=userid, offering=course, role="TA", added_reason="TAIN") if ms: m = ms[0] m.role = "DROP" m.save() #LOG EVENT# l = LogEntry(userid=request.user.username, description=("TA removed by instructor: %s for %s") % (userid, course), related_object=m) l.save() messages.success(request, 'Removed %s as a TA.' % (m.person.name())) return HttpResponseRedirect( reverse(manage_tas, kwargs={'course_slug': course.slug})) else: form = TAForm(offering=course) tas = Member.objects.filter(role="TA", offering=course) context = { 'course': course, 'form': form, 'tas': tas, 'longform': longform } return render(request, 'coredata/manage_tas.html', context)
def test_instructor_workflow(self): """ Work through the site as an instructor """ s, c = create_offering() userid1 = "0aaa0" userid2 = "0aaa1" userid3 = "0aaa2" userid4 = "ggbaker" for u in [userid1, userid2, userid3, userid4]: p = Person.objects.get(userid=u) m = Member(person=p, offering=c, role="STUD", credits=3, career="UGRD", added_reason="UNK") m.save() m.role = "INST" m.save() client = Client() client.login_user("ggbaker") # course main screen url = reverse('offering:course_info', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) url = reverse('offering:activity_choice', kwargs={'course_slug': c.slug}) self.assertContains(response, 'href="' + url + '"') url = reverse('offering:add_numeric_activity', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) # add activity import datetime now = datetime.datetime.now() due = now + datetime.timedelta(days=7) response = client.post( url, { 'name': 'Assignment 1', 'short_name': 'A1', 'status': 'URLS', 'due_date_0': due.strftime('%Y-%m-%d'), 'due_date_1': due.strftime('%H:%M:%S'), 'percent': '10', 'group': '1', 'max_grade': 25, 'extend_group': 'None' }) self.assertEqual(response.status_code, 302) acts = NumericActivity.objects.filter(offering=c) self.assertEqual(len(acts), 1) a = acts[0] self.assertEqual(a.name, "Assignment 1") self.assertEqual(a.slug, "a1") self.assertEqual(a.max_grade, 25) self.assertEqual(a.group, False) self.assertEqual(a.deleted, False) # add calculated numeric activity url = reverse('offering:add_cal_numeric_activity', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) response = client.post( url, { 'name': 'Total', 'short_name': 'Total', 'status': 'URLS', 'group': '1', 'max_grade': 30, 'formula': '[A1]+5' }) self.assertEqual(response.status_code, 302) acts = CalNumericActivity.objects.filter(offering=c) self.assertEqual(len(acts), 1) a = acts[0] self.assertEqual(a.slug, "total") self.assertEqual(a.max_grade, 30) self.assertEqual(a.group, False) self.assertEqual(a.deleted, False) self.assertEqual(a.formula, '[A1]+5') # formula tester url = reverse('offering:formula_tester', kwargs={'course_slug': c.slug}) response = basic_page_tests(self, client, url) response = client.get( url, { 'formula': '[A1]+5', 'a1-status': 'RLS', 'a1-value': '6', 'total-status': 'URLS' }) self.assertContains(response, '<div id="formula_result">11.0</div>') validate_content(self, response.content, url)
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 context_memberships(userid): if userid: return Member.get_memberships(userid)[0] else: return []
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_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 = '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)