def _show_components_staff(request, course_slug, activity_slug): """ Show all the components of this activity Responsible for updating position """ course = get_object_or_404(CourseOffering, slug=course_slug) activity = get_object_or_404(course.activity_set, slug=activity_slug, deleted=False) #if POST, update the positions if request.method == 'POST': component_list = select_all_components(activity, include_deleted=True) counter = 0 for component in component_list: counter = counter + 1 t = request.POST.get('' + str(counter) + '_position'); #in case t is not a number try: component.position = int(t) component.save() except: pass messages.add_message(request, messages.SUCCESS, 'Component positions updated.') return HttpResponseRedirect(reverse(show_components, args=[course_slug, activity_slug])) component_list = select_all_components(activity, include_deleted=True) return render(request, "submission/component_view_staff.html", {"course":course, "activity":activity, "component_list":component_list})
def edit_single(request, course_slug, activity_slug): course = get_object_or_404(CourseOffering, slug=course_slug) activity = get_object_or_404(course.activity_set, slug=activity_slug, deleted=False) component_list = select_all_components(activity) #get component edit_id = request.GET.get('id') component = get_component(activity=activity, id=edit_id) if component is None: return NotFoundResponse(request) form = component.Type.ComponentForm(instance=component) #if form submitted if request.method == 'POST': new_form = component.Type.ComponentForm(request.POST) if new_form.is_valid(): new_component = new_form.save(commit=False) new_component.activity = activity new_component.id = component.id if new_component.position == None: count = len(select_all_components(activity)) new_component.position = count + 1 new_component.save() #LOG EVENT# l = LogEntry(userid=request.user.username, description=("edited component %s of %s") % (component.title, activity), related_object=new_component) l.save() messages.add_message( request, messages.SUCCESS, 'Component "' + new_component.title + '" successfully updated.') return HttpResponseRedirect( reverse('offering:submission:show_components', args=[course_slug, activity_slug])) else: form = new_form messages.add_message(request, messages.ERROR, 'Please correct the errors in the form.') return render( request, "submission/component_edit_single.html", { "course": course, "activity": activity, "component": component, "edit_id": edit_id, "form": form })
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 show_components_submission_history(request, course_slug, activity_slug, userid=None): if userid is None: userid = request.GET.get('userid') course = get_object_or_404(CourseOffering, slug=course_slug) activity = get_object_or_404(course.activity_set,slug = activity_slug, deleted=False) staff = False if userid is None: # can always see your own submissions userid = request.user.username else: # specifying a userid: must be course staff if is_course_staff_by_slug(request, course.slug): staff = True else: return ForbiddenResponse(request) member = get_object_or_404(Member, find_member(userid), offering=course) if activity.group: messages.add_message(request, messages.INFO, "This is a group submission. This history is based on submissions from all your group members.") gms = GroupMember.objects.filter(student=member, confirmed=True, activity=activity) submissions = GroupSubmission.objects.filter(activity=activity, group__groupmember__in=gms) else: submissions = StudentSubmission.objects.filter(activity=activity, member=member) # get all submission components component_list = select_all_components(activity, include_deleted=staff) all_submitted_components = [] for submission in submissions: c = get_submission_components(submission, activity, component_list) all_submitted_components.append({'sub':submission, 'comp':c}) return render(request, "submission/submission_history_view.html", {"course":course, "activity":activity,'userid':userid,'submitted_components': all_submitted_components})
def edit_single(request, course_slug, activity_slug): course = get_object_or_404(CourseOffering, slug=course_slug) activity = get_object_or_404(course.activity_set, slug=activity_slug, deleted=False) component_list = select_all_components(activity) #get component edit_id = request.GET.get('id') component = get_component(activity=activity, id=edit_id) if component is None: return NotFoundResponse(request) form = component.Type.ComponentForm(instance=component) #if form submitted if request.method == 'POST': new_form = component.Type.ComponentForm(request.POST) if new_form.is_valid(): new_component = new_form.save(commit=False) new_component.activity = activity new_component.id = component.id if new_component.position == None: count = len(select_all_components(activity)) new_component.position = count + 1 new_component.save() #LOG EVENT# l = LogEntry(userid=request.user.username, description=(u"edited component %s of %s") % (component.title, activity), related_object=new_component) l.save() messages.add_message(request, messages.SUCCESS, 'Component "' + new_component.title + '" successfully updated.') return HttpResponseRedirect(reverse(show_components, args=[course_slug, activity_slug])) else: form = new_form messages.add_message(request, messages.ERROR, 'Please correct the errors in the form.') return render(request, "submission/component_edit_single.html", {"course":course, "activity":activity, "component":component, "edit_id":edit_id, "form":form})
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 copy_setup_activities(course_copy_from, course_copy_to): from submission.models.code import CodeComponent from submission.models.codefile import CodefileComponent # copy Activities (and related content) all_activities = all_activities_filter(offering=course_copy_from) for activity in all_activities: Class = activity.__class__ new_activity = copy_activity(activity, course_copy_from, course_copy_to) save_copied_activity(new_activity, Class, course_copy_to) # should only apply to NumericActivity: others have no ActivityComponents for activity_component in ActivityComponent.objects.filter(numeric_activity_id=activity.id, deleted=False): new_activity_component = copy.deepcopy(activity_component) new_activity_component.id = None new_activity_component.pk = None new_activity_component.numeric_activity = new_activity new_activity_component.slug = None new_activity_component.save(force_insert=True) for common_problem in CommonProblem.objects.filter(activity_component=activity_component, deleted=False): new_common_problem = copy.deepcopy(common_problem) new_common_problem.id = None new_common_problem.pk = None new_common_problem.penalty = str(new_common_problem.penalty) new_common_problem.activity_component = new_activity_component new_common_problem.save(force_insert=True) for submission_component in select_all_components(activity): new_submission_component = copy.deepcopy(submission_component) new_submission_component.id = None new_submission_component.pk = None new_submission_component.activity = new_activity new_submission_component.slug = None if isinstance(new_submission_component, CodeComponent): # upgrade Code to Codefile while migrating new_submission_component = CodefileComponent.build_from_codecomponent(new_submission_component) # enforce tighter submission size limits if hasattr(new_submission_component, 'max_size'): if new_submission_component.max_size > settings.MAX_SUBMISSION_SIZE: new_submission_component.max_size = settings.MAX_SUBMISSION_SIZE new_submission_component.save(force_insert=True) for activity in CalLetterActivity.objects.filter(offering=course_copy_to): # fix up source and exam activities as best possible if activity.numeric_activity: try: na = NumericActivity.objects.get(offering=course_copy_to, name=activity.numeric_activity.name, deleted=False) except NumericActivity.DoesNotExist: na = NumericActivity.objects.filter(offering=course_copy_to, deleted=False)[0] activity.numeric_activity = na if activity.exam_activity: try: a = Activity.objects.get(offering=course_copy_to, name=activity.exam_activity.name, deleted=False) except Activity.DoesNotExist: a = Activity.objects.filter(offering=course_copy_to, deleted=False)[0] activity.exam_activity = a activity.save()
def _show_components_student(request, course_slug, activity_slug, userid=None, template="dashboard_student.html", staff=False): """ Show all the component submission history of this activity """ if userid == None: userid = request.user.username course = get_object_or_404(CourseOffering, slug=course_slug) activity = get_object_or_404(course.activity_set,slug=activity_slug, deleted=False) student = get_object_or_404(Person, find_userid_or_emplid(userid)) cansubmit = True submission, submitted_components = get_current_submission(student, activity, include_deleted=staff) if len(submitted_components) == 0: return NotFoundResponse(request) if submission and activity.due_date and activity.due_date < submission.created_at: late = submission.created_at - activity.due_date else: late = 0 if activity.group: gm = GroupMember.objects.filter(student__person=student, activity=activity, confirmed=True) if gm: group = gm[0].group member = gm[0].student else: group = None #cansubmit = False #messages.add_message(request, messages.INFO, "This is a group submission. You cannot submit since you aren't in a group.") else: group = None # activity should be submitable cansubmit = cansubmit and activity.submitable() if not cansubmit: messages.add_message(request, messages.ERROR, "This activity is not submittable.") return render(request, "submission/" + template, {"course":course, "activity":activity, "submission": submission, "submitted_components":submitted_components, "userid":userid, "late":late, "student":student, "group":group, "cansubmit":cansubmit}) # get all components of activity component_list = select_all_components(activity) component_list.sort() component_form_list=[] if request.method == 'POST': component_form_list = make_form_from_list(component_list, request=request) submitted_comp = [] # list all components which has content submitted in the POST not_submitted_comp = [] #list allcomponents which has no content submitted in the POST if not activity.group: new_sub = StudentSubmission() # the submission foreign key for newly submitted components new_sub.member = get_object_or_404(Member, offering__slug=course_slug, person__userid=request.user.username) elif gm: new_sub = GroupSubmission() new_sub.group = group new_sub.creator = member else: messages.add_message(request, messages.ERROR, "This is a group submission. You cannot submit since you aren't in a group.") return ForbiddenResponse(request) new_sub.activity = activity # begin validating uploaded data submitted_comp = [] not_submitted_comp = [] # validate forms one by one for data in component_form_list: component = data['comp'] form = data['form'] if form.is_valid(): sub = form.save(commit=False) sub.component = component submitted_comp.append(sub) else: # hack to replace the "required" message to something more appropriate for k,v in form.errors.items(): for i,e in enumerate(v): if e == "This field is required.": v[i] = "Nothing submitted." not_submitted_comp.append(component) # check duplicate filenames here all_ok = False while not all_ok: all_ok = True d = {} for c,s in submitted_components: d[c] = s and s.get_filename() for s in submitted_comp: d[s.component] = s.get_filename() # a list holding all file names file_name_list = [a[1] for a in d.items() if a[1] is not None] to_be_removed = [] for (i, s) in enumerate(submitted_comp): if file_name_list.count(s.get_filename()) > 1: all_ok = False to_be_removed.append(i) not_submitted_comp.append(s.component) #HACK: modify the 'errors' field in the form for data in component_form_list: if s.component == data['comp']: # assume we have only one field for submission form field_name = data['form'].fields.keys()[0] data['form']._errors[field_name] = ErrorList([u"This file has the same name as another file in your submission."]) # remove those has errors in submitted_comp to_be_removed.reverse() for t in to_be_removed: submitted_comp.pop(t) # all okay now # end validating, begin saving if len(submitted_comp) > 0: new_sub.save() for sub in submitted_comp: sub.submission = new_sub sub.save() #LOG EVENT# if activity.group: group_str = " as a member of group %s" % new_sub.group.name else: group_str = "" l = LogEntry(userid=request.user.username, description=u"submitted for %s %s%s" % (activity, sub.component.title, group_str), related_object=sub) l.save() if len(not_submitted_comp) == 0: messages.add_message(request, messages.SUCCESS, "Your submission was successful.") return HttpResponseRedirect(reverse(show_components, args=[course_slug, activity_slug])) return render(request, "submission/submission_error.html", {"course":course, "activity":activity, "component_list":component_form_list, "submitted_comp":submitted_comp, "not_submitted_comp":not_submitted_comp}) else: #not POST if activity.group and gm: messages.add_message(request, messages.INFO, "This is a group submission. You will submit on behalf of the group %s." % group.name) component_form_list = make_form_from_list(component_list) return render(request, "submission/" + template, {'component_form_list': component_form_list, "course": course, "activity": activity, "submission": submission, "submitted_components":submitted_components, "userid":userid, "late":late, "student":student, "group":group, "cansubmit":cansubmit, "is_staff":staff})
def copyCourseSetup(course_copy_from, course_copy_to): """ copy all the activities setup from one course to another copy numeric activities with their marking components, common problems and submission components """ with django.db.transaction.atomic(): from submission.models.code import CodeComponent from submission.models.codefile import CodefileComponent # copy things in offering's .config dict for f in course_copy_from.copy_config_fields: if f in course_copy_from.config: course_copy_to.config[f] = course_copy_from.config[f] course_copy_to.save() # copy Activities (and related content) all_activities = all_activities_filter(offering=course_copy_from) for activity in all_activities: Class = activity.__class__ new_activity = copy_activity(activity, course_copy_from, course_copy_to) save_copied_activity(new_activity, Class, course_copy_to) # should only apply to NumericActivity: others have no ActivityComponents for activity_component in ActivityComponent.objects.filter( numeric_activity=activity, deleted=False): new_activity_component = copy.deepcopy(activity_component) new_activity_component.id = None new_activity_component.pk = None new_activity_component.numeric_activity = new_activity new_activity_component.slug = None new_activity_component.save(force_insert=True) for common_problem in CommonProblem.objects.filter( activity_component=activity_component, deleted=False): new_common_problem = copy.deepcopy(common_problem) new_common_problem.id = None new_common_problem.pk = None new_common_problem.penalty = str( new_common_problem.penalty) new_common_problem.activity_component = new_activity_component new_common_problem.save(force_insert=True) for submission_component in select_all_components(activity): new_submission_component = copy.deepcopy(submission_component) new_submission_component.id = None new_submission_component.pk = None new_submission_component.activity = new_activity new_submission_component.slug = None if isinstance(new_submission_component, CodeComponent): # upgrade Code to Codefile while migrating new_submission_component = CodefileComponent.build_from_codecomponent( new_submission_component) # enforce tighter submission size limits if hasattr(new_submission_component, 'max_size'): if new_submission_component.max_size > settings.MAX_SUBMISSION_SIZE: new_submission_component.max_size = settings.MAX_SUBMISSION_SIZE new_submission_component.save(force_insert=True) for activity in CalLetterActivity.objects.filter( offering=course_copy_to): # fix up source and exam activities as best possible if activity.numeric_activity: try: na = NumericActivity.objects.get( offering=course_copy_to, name=activity.numeric_activity.name, deleted=False) except NumericActivity.DoesNotExist: na = NumericActivity.objects.filter( offering=course_copy_to, deleted=False)[0] activity.numeric_activity = na if activity.exam_activity: try: a = Activity.objects.get(offering=course_copy_to, name=activity.exam_activity.name, deleted=False) except Activity.DoesNotExist: a = Activity.objects.filter(offering=course_copy_to, deleted=False)[0] activity.exam_activity = a activity.save() # copy the Pages from pages.models import Page, PageFilesStorage, attachment_upload_to for p in Page.objects.filter(offering=course_copy_from): new_p = copy.deepcopy(p) new_p.id = None new_p.pk = None new_p.offering = course_copy_to while True: count = 0 orig_label = new_p.label try: new_p.save(force_insert=True) break except IntegrityError: count += 1 new_p.label = orig_label + "-" + str(count) # if there are release dates, adapt to new semester if new_p.releasedate(): week, wkday = course_copy_from.semester.week_weekday( new_p.releasedate()) new_date = course_copy_to.semester.duedate(week, wkday, None) new_p.set_releasedate(new_date) if new_p.editdate(): week, wkday = course_copy_from.semester.week_weekday( new_p.editdate()) new_date = course_copy_to.semester.duedate(week, wkday, None) new_p.set_editdate(new_date) new_p.save() v = p.current_version() new_v = copy.deepcopy(v) new_v.id = None new_v.pk = None new_v.page = new_p # collapse old version history new_v.wikitext = v.get_wikitext() new_v.diff = None new_v.diff_from = None new_v.comment = "Page migrated from %s" % (course_copy_from) if new_v.file_attachment: # copy the file (so we can safely remove old semesters' # files without leaving bad path reference) src = v.file_attachment.path path = attachment_upload_to(new_v, new_v.file_name) dst = PageFilesStorage.path(path) dstpath, dstfile = os.path.split(dst) while os.path.exists(os.path.join(dstpath, dstfile)): # handle duplicates by mangling the directory name dstpath += "_" dst = os.path.join(dstpath, dstfile) new_v.file_attachment = dst if not os.path.exists(dstpath): os.makedirs(dstpath) try: os.link(src, dst) except: # any problems with the hardlink: try simple copy import shutil shutil.copyfile(src, dst) new_v.save(force_insert=True)
def copyCourseSetup(course_copy_from, course_copy_to): """ copy all the activities setup from one course to another copy numeric activities with their marking components, common problems and submission components """ with django.db.transaction.atomic(): from submission.models.code import CodeComponent from submission.models.codefile import CodefileComponent # copy things in offering's .config dict for f in course_copy_from.copy_config_fields: if f in course_copy_from.config: course_copy_to.config[f] = course_copy_from.config[f] course_copy_to.save() # copy Activities (and related content) all_activities = all_activities_filter(offering=course_copy_from) for activity in all_activities: Class = activity.__class__ new_activity = copy_activity(activity, course_copy_from, course_copy_to) save_copied_activity(new_activity, Class, course_copy_to) # should only apply to NumericActivity: others have no ActivityComponents for activity_component in ActivityComponent.objects.filter(numeric_activity=activity, deleted=False): new_activity_component = copy.deepcopy(activity_component) new_activity_component.id = None new_activity_component.pk = None new_activity_component.numeric_activity = new_activity new_activity_component.slug = None new_activity_component.save(force_insert=True) for common_problem in CommonProblem.objects.filter(activity_component=activity_component, deleted=False): new_common_problem = copy.deepcopy(common_problem) new_common_problem.id = None new_common_problem.pk = None new_common_problem.penalty = str(new_common_problem.penalty) new_common_problem.activity_component = new_activity_component new_common_problem.save(force_insert=True) for submission_component in select_all_components(activity): new_submission_component = copy.deepcopy(submission_component) new_submission_component.id = None new_submission_component.pk = None new_submission_component.activity = new_activity new_submission_component.slug = None if isinstance(new_submission_component, CodeComponent): # upgrade Code to Codefile while migrating new_submission_component = CodefileComponent.build_from_codecomponent(new_submission_component) # enforce tighter submission size limits if hasattr(new_submission_component, 'max_size'): if new_submission_component.max_size > settings.MAX_SUBMISSION_SIZE: new_submission_component.max_size = settings.MAX_SUBMISSION_SIZE new_submission_component.save(force_insert=True) for activity in CalLetterActivity.objects.filter(offering=course_copy_to): # fix up source and exam activities as best possible if activity.numeric_activity: try: na = NumericActivity.objects.get(offering=course_copy_to, name=activity.numeric_activity.name, deleted=False) except NumericActivity.DoesNotExist: na = NumericActivity.objects.filter(offering=course_copy_to, deleted=False)[0] activity.numeric_activity = na if activity.exam_activity: try: a = Activity.objects.get(offering=course_copy_to, name=activity.exam_activity.name, deleted=False) except Activity.DoesNotExist: a = Activity.objects.filter(offering=course_copy_to, deleted=False)[0] activity.exam_activity = a activity.save() # copy the Pages from pages.models import Page, PageFilesStorage, attachment_upload_to for p in Page.objects.filter(offering=course_copy_from): new_p = copy.deepcopy(p) new_p.id = None new_p.pk = None new_p.offering = course_copy_to while True: count = 0 orig_label = new_p.label try: new_p.save(force_insert=True) break except IntegrityError: count += 1 new_p.label = orig_label + "-" + str(count) # if there are release dates, adapt to new semester if new_p.releasedate(): week, wkday = course_copy_from.semester.week_weekday(new_p.releasedate()) new_date = course_copy_to.semester.duedate(week, wkday, None) new_p.set_releasedate(new_date) if new_p.editdate(): week, wkday = course_copy_from.semester.week_weekday(new_p.editdate()) new_date = course_copy_to.semester.duedate(week, wkday, None) new_p.set_editdate(new_date) new_p.save() v = p.current_version() new_v = copy.deepcopy(v) new_v.id = None new_v.pk = None new_v.page = new_p # collapse old version history new_v.wikitext = v.get_wikitext() new_v.diff = None new_v.diff_from = None new_v.comment = "Page migrated from %s" % (course_copy_from) if new_v.file_attachment: # copy the file (so we can safely remove old semesters' # files without leaving bad path reference) src = v.file_attachment.path path = attachment_upload_to(new_v, new_v.file_name) dst = PageFilesStorage.path(path) dstpath, dstfile = os.path.split(dst) while os.path.exists(os.path.join(dstpath, dstfile)): # handle duplicates by mangling the directory name dstpath += "_" dst = os.path.join(dstpath, dstfile) new_v.file_attachment = dst if not os.path.exists(dstpath): os.makedirs(dstpath) try: os.link(src, dst) except: # any problems with the hardlink: try simple copy import shutil shutil.copyfile(src, dst) new_v.save(force_insert=True)