def list_second_choice(request): """ list all students with a random distribution, without project and all non-full projects :param request: :return: """ props = get_all_proposals().filter( Status=4, Private__isnull=True).distinct().annotate( num_distr=Count('distributions')).filter( TimeSlot=get_timeslot(), num_distr__lt=F('NumStudentsMax')).order_by('Title') prop_obj = [[prop, get_share_link(prop.pk)] for prop in props] no_dist = get_all_students(undistributed=True).filter( distributions__isnull=True, applications__isnull=False).distinct() # filter students in this year with only applications in other year no_dist = [ s for s in no_dist if s.applications.filter(Proposal__TimeSlot=get_timeslot()).exists() ] return render( request, 'distributions/list_second_choice.html', { 'distributions': Distribution.objects.filter( TimeSlot=get_timeslot(), Application__isnull=True, Proposal__Private__isnull=True).order_by('Student'), 'no_dist': no_dist, 'proposals': prop_obj, })
def finalize_preview(request, pk, step=0): """ Edit grade for a category as indexed by step. For each student as given by pk. Also edit the individual aspects of each grade category. For trackheads and responsible staff :param request: :param pk: id of distribution :param step: number of step in the menu, index of category :return: """ ts = get_timeslot() if not hasattr(ts, 'resultoptions'): raise PermissionDenied("Results menu is not yet visible.") else: if not get_timeslot().resultoptions.Visible: raise PermissionDenied("Results menu is not yet visible.") dstr = get_object_or_404(Distribution, pk=pk) if not hasattr(dstr, 'presentationtimeslot'): raise PermissionDenied('This student does not have a presentation planned. Please plan it first.') if not request.user.is_superuser and \ request.user != dstr.Proposal.Track.Head and \ request.user != dstr.Proposal.ResponsibleStaff and \ get_grouptype('3') not in request.user.groups.all() and \ request.user not in dstr.presentationtimeslot.Presentations.Assessors.all(): raise PermissionDenied("You do not have the correct permissions to view print preview.") return render(request, "results/finalize_grades.html", { "dstr": dstr, "catresults": dstr.results.all(), "final": all(f.Final is True for f in dstr.results.all()) if dstr.results.all() else False, "finalgrade": dstr.TotalGradeRounded(), "preview": True, })
def manual(request): """ Support page to distribute students manually to projects. Uses ajax calls to change distributions. Same table included as in list appls/dists :param request: """ if get_timephase_number() < 4 or get_timephase_number() > 6: raise PermissionDenied( 'Distribution is not possible in this timephase') props = get_all_proposals().filter(Q(Status__exact=4)) \ .select_related('ResponsibleStaff__usermeta', 'Track', 'TimeSlot') \ .prefetch_related('Assistants__usermeta', 'Private__usermeta', 'applications__Student__usermeta', 'distributions__Student__usermeta') # includes students without applications. # also show undistributed in phase 6 studs = get_all_students(undistributed=True).exclude(distributions__TimeSlot=get_timeslot()) \ .select_related('usermeta') \ .prefetch_related('applications__Proposal').distinct() dists = Distribution.objects.filter(TimeSlot=get_timeslot()) \ .select_related('Student__usermeta', 'Proposal', 'Application__Student__usermeta') return render( request, 'distributions/manual_distribute.html', { 'proposals': props, 'undistributedStudents': studs, 'distributions': dists, 'hide_sidebar': True })
def copy(request, pk=None): """ Show a list of timeslots to import grades from. :param request: :param pk: :return: """ # do a copy if pk: ts = get_object_or_404(TimeSlot, pk=pk) if ts == get_timeslot(): raise PermissionDenied( "It is not possible to copy the grades from the current timeslot." ) if get_timeslot().gradecategories.exists(): return render( request, 'base.html', { 'Message': "The current timeslot already has grade categories." " Importing is not possible. " "Please remove the categories in the current timeslot before copying.", 'return': 'results:list_categories' }) if request.method == 'POST': form = ConfirmForm(request.POST) if form.is_valid(): for cat in ts.gradecategories.all(): old_id = cat.id old_aspects = cat.aspects.all() cat.id = None cat.TimeSlot = get_timeslot() cat.save() for aspect in old_aspects: aspect.id = None aspect.Category = cat aspect.save() return render( request, 'base.html', { 'Message': 'Finished importing!', 'return': 'results:list_categories' }) else: form = ConfirmForm() return render( request, 'GenericForm.html', { 'form': form, 'formtitle': 'Confirm copy grade categories and aspects', 'buttontext': 'Confirm' }) # list possible timeslots to copy from else: tss = TimeSlot.objects.filter(gradecategories__isnull=False).distinct() return render(request, "results/list_copy.html", { "tss": tss, 'ts': get_timeslot(), })
def get_distributions(user, timeslot=None): """ Function to return the distributions that a given staff user is allowed to see Type3 and 6 should see all distributions, to be able to mail them. :param user: The user to test :param timeslot: TimeSlot to get distributions from :return empty queryset on fail """ if get_grouptype('2u') in user.groups.all(): return Distribution.objects.none() if timeslot is None: timeslot = get_timeslot() if timeslot is None: return Distribution.objects.none( ) # similar to None, but can be used in chained filter. des_all = Distribution.objects.filter(TimeSlot=timeslot) if get_grouptype("3") in user.groups.all( ) or user.is_superuser or get_grouptype("6") in user.groups.all(): return des_all else: tracks = Track.objects.filter(Head=user) if planning_public() and timeslot == get_timeslot(): return des_all.filter( Q(Proposal__Track__in=tracks) | Q(Proposal__ResponsibleStaff=user) | Q(Proposal__Assistants__id=user.id) | Q(presentationtimeslot__Presentations__Assessors__id=user.id) ).distinct() else: return des_all.filter( Q(Proposal__Track__in=tracks) | Q(Proposal__ResponsibleStaff=user) | Q(Proposal__Assistants__id=user.id)).distinct()
def wrapper(*args, **kw): if 'pk' in kw: pk = int(kw['pk']) else: pk = int(args[1]) proj = get_cached_project(pk) request = args[0] # user needs to be logged in (so no need for login_required on top of this) if not request.user.is_authenticated: page = args[0].path return redirect_to_login( next=page, login_url='index:login', redirect_field_name='next', ) # support staf or superusers are always allowed to view if get_grouptype( "3") in request.user.groups.all() or request.user.is_superuser: return fn(*args, **kw) # user is staffmember and involved in the project if proj.ResponsibleStaff == request.user \ or request.user in proj.Assistants.all() \ or proj.Track.Head == request.user: return fn(*args, **kw) # group administrators can view proposal if group_administrator_status(proj, request.user) > 0: return fn(*args, **kw) # if project is published, non private and its the right time phase if proj.Status == 4: if not proj.Private.exists() or request.user in proj.Private.all( ): # only non-private proposals # else staff members are allowed to view public proposals in all timeslots and timephases # this includes assessors as they are type1 or type2. if request.user.groups.exists(): return fn(*args, **kw) # students view public proposals or private student views his proposal: Only in timephase after 2 elif get_timephase_number( ) > 2 and proj.TimeSlot == get_timeslot(): return fn(*args, **kw) # assessors are allowed to view status4 private projects if they have to assess it. elif planning_public() and \ proj.Private.exists() and \ request.user.groups.exists() and \ proj.TimeSlot == get_timeslot(): for dist in proj.distributions.all(): try: if request.user in dist.presentationtimeslot.Presentations.Assessors.all( ): return fn(*args, **kw) except PresentationTimeSlot.DoesNotExist: continue raise PermissionDenied( "You are not allowed to view this project page.")
def lti(request): if not get_timeslot(): return HttpResponse( 'Login is not available. The system is currently closed.', status=403) config = getattr(settings, 'PYLTI_CONFIG', dict()) consumers = config.get('consumers', dict()) params = dict(request.POST.items()) headers = request.META headers['X-Forwarded-Proto'] = headers['HTTP_X_FORWARDED_PROTO'] try: verify_request_common(consumers, request.build_absolute_uri(), request.method, headers, params) except LTIException as e: logger.error('LTI exception from canvas; {}'.format(e)) return HttpResponse("Signature Validation failed!", status=403) data = request.POST try: username = data['lis_person_sourcedid'] email = data['lis_person_contact_email_primary'] studentnumber = data['custom_canvas_user_login_id'] coursecode = data['context_label'] except KeyError as e: logger.error('Invalid post data from canvas; {}; {}'.format(data, e)) return HttpResponse("Missing data in POST", status=400) user = get_user(email, username) if user is None: user = User(email=email, username=username) user.save() try: meta = user.usermeta except UserMeta.DoesNotExist: meta = UserMeta(User=user) meta.Studentnumber = studentnumber if not meta.Overruled: if settings.COURSE_CODE_BEP in coursecode: meta.EnrolledBEP = True elif settings.COURSE_CODE_EXT in coursecode: meta.EnrolledBEP = True meta.EnrolledExt = True else: logger.warning( 'Course code not matched on BEP or EXT for user {}. Code was: {}' .format(user, coursecode)) meta.save() meta.TimeSlot.add(get_timeslot()) meta.save() user.save() log = CanvasLogin() log.Subject = user log.save() return redirect("{}/login/".format(settings.DOMAIN))
def get_all_students(undistributed=False): """ Return all active students in marketplace, used for instance for mailing. :param undistributed: Also return undistributed students in phase 6 and later. :return: user objects """ users = User.objects.filter( Q(usermeta__EnrolledBEP=True) & Q(groups=None) & Q(usermeta__TimeSlot=get_timeslot())).distinct() if get_timephase_number() < 6 or undistributed: return users else: # only students with a distributions return users.filter(Q(distributions__TimeSlot=get_timeslot()))
def save(self, commit=True): if commit: self.instance.TimeSlot = get_timeslot() self.instance.Public = False super().save(commit=True) self.instance.save() return self.instance
def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) super().__init__(*args, **kwargs) self.fields['ResponsibleStaff'].queryset = get_grouptype( '1').user_set.all() self.fields['Assistants'].queryset = get_grouptype('2').user_set.all() | \ get_grouptype('2u').user_set.all() | \ get_grouptype('1').user_set.all() self.fields[ 'ResponsibleStaff'].label_from_instance = self.user_label_from_instance self.fields[ 'Assistants'].label_from_instance = self.user_label_from_instance self.fields['addAssistantsEmail'].widget.attrs[ 'placeholder'] = "Add assistant via email address" self.fields['Private'].queryset = User.objects.filter(groups=None) # no user label_from_instance for private students because privacy. self.fields['addPrivatesEmail'].widget.attrs[ 'placeholder'] = "Add private student via email address" if get_timephase_number() == 1: self.fields['TimeSlot'].queryset = TimeSlot.objects.filter( End__gt=datetime.now()) self.fields['TimeSlot'].initial = get_timeslot() else: if self.request.user.is_superuser or get_grouptype( '3') in self.request.user.groups.all(): self.fields['TimeSlot'].queryset = TimeSlot.objects.all() else: # phase 2+, only add for future timeslot self.fields['TimeSlot'].queryset = TimeSlot.objects.filter( Begin__gt=datetime.now())
def setUp(self): self.app = 'professionalskills' super().setUp() # create a filetype for testing f = FileType(Name='testfiletype', TimeSlot=get_timeslot(), Deadline=datetime(year=2018, month=5, day=4)) f.id = 100 f.save() fa = StaffResponseFileAspect( Name='Testaspect;', Description='TestAspectDescription', File=f, ) fa.id = 100 fa.save() sf = StudentFile( Caption='TestFile', Distribution=self.distribution_random, Type=f, ) sf.id = 0 sf.save() g1 = StudentGroup(Number=0, PRV=f, Max=10, Start=timezone.now()) g1.id = 0 # g1.Members.add(self.users['t-p']) # g1.Members.add(self.users['r-s']) g1.save() g2 = StudentGroup(Number=1, PRV=f, Max=10, Start=timezone.now()) g2.id = 1 g2.save() g2.Members.add(self.users.get('r-s')) g2.Members.add(self.users.get('t-p')) g2.save()
def upgrade_status_api(request, pk): """ API call to increase the status of a proposal. :param request: :param pk: id of proposal :return: """ obj = get_object_or_404(Proposal, pk=pk) if obj.Status == 4: return HttpResponse("Already at final stage", status=403) if obj.Status == 3 and obj.nextyear(): return HttpResponse("Cannot publish proposal for future timeslot", status=403) elif get_timephase_number() > 2 and \ obj.TimeSlot == get_timeslot() and \ get_grouptype('3') not in request.user.groups.all(): return HttpResponse( "Proposal frozen in this timeslot. The timephase of editing has ended.", status=403) elif request.user in obj.Assistants.all() and obj.Status >= 2: return HttpResponse( "You are an assistant and not allowed to increase status further", status=403) # Done in can_edit decorator # elif obj.Track.Head != request.user and obj.Status > 2 and not get_grouptype('3') in request.user.groups.all(): # return HttpResponse("Not allowed to publish as non track head", status=403) else: oldstatus = obj.Status if oldstatus == 2: # per default go to publish from 4, 3 is only used if it is explicitly downgraded newstatus = 4 else: newstatus = obj.Status + 1 obj.Status = newstatus obj.save() mail_proposal_all(request, obj) notification = ProposalStatusChange() notification.Subject = obj notification.Actor = request.user notification.StatusFrom = oldstatus notification.StatusTo = newstatus notification.save() if obj.Status > 3: for assistant in obj.Assistants.all(): if get_grouptype("2u") in assistant.groups.all(): verify_assistant_fn(assistant) if obj.Status == 4: # put the object in cache if status goes from 3->4 cache.set('proposal_{}'.format(pk), obj, None) cache.delete('listproposalsbodyhtml') return HttpResponse(getStatStr(obj.Status))
def api_undistribute(request): """ AJAX call from manual distribute to undistribute :param request: :return: """ if get_timephase_number() < 4 or get_timephase_number() > 5: # Not in phase 6, because projects already started. raise PermissionDenied('Undistribution is not possible in this timephase') if request.method == 'POST': try: student = get_all_students(undistributed=True).get(pk=request.POST['student']) except User.DoesNotExist: return JsonResponse({'type': 'warning', 'txt': warningString + ' (User cannot be found)'}) try: dist = student.distributions.get(TimeSlot=get_timeslot()) except Distribution.DoesNotExist: return JsonResponse({'type': 'warning', 'txt': warningString + ' (Distribution cannot be found)'}) try: n = dist.delete() if n[0] == 1: return JsonResponse( {'type': 'success', 'txt': 'Undistributed Student ' + dist.Student.usermeta.get_nice_name()}) else: return JsonResponse({'type': 'warning', 'txt': warningString + ' (distributions not deleted)'}) except Exception as e: return JsonResponse({'type': 'warning', 'txt': warningString, 'exception': str(e)}) else: raise PermissionDenied('You don\'t know what you\'re doing!')
def save(self, commit=True): if commit: self.instance.PresentationOptions = get_timeslot( ).presentationoptions super().save(commit=True) self.instance.save() return self.instance
def add_file(request): """ For students to upload a file. Used for the hand in system. Responsibles, assistants and trackheads can then view the files of their students. support staff can see all student files. :param request: """ dist = get_object_or_404(Distribution, Student=request.user, TimeSlot=get_timeslot()) if request.method == 'POST': form = StudentFileForm(request.POST, request.FILES, request=request) if form.is_valid(): file = form.save(commit=False) file.Distribution = dist file.save() return render( request, 'base.html', { 'Message': 'File uploaded!', 'return': 'professionalskills:listownfiles' }) else: form = StudentFileForm(request=request) return render(request, 'GenericForm.html', { 'form': form, 'formtitle': 'Upload a file ', 'buttontext': 'Save' })
def edit_files(request): """ Edit public files. Only for supportstaff These files are shown on the homepage for every logged in user. :param request: """ form_set = modelformset_factory(PublicFile, form=PublicFileForm, can_delete=True, extra=0) qu = PublicFile.objects.filter(TimeSlot=get_timeslot()) formset = form_set(queryset=qu) if request.method == 'POST': formset = form_set(request.POST, request.FILES) if formset.is_valid(): formset.save() return render(request, "base.html", { "Message": "File changes saved!", "return": "index:index" }) return render( request, 'GenericForm.html', { 'formset': formset, 'formtitle': 'All public uploaded files', 'buttontext': 'Save changes' })
def list_categories(request): """ :param request: :return: """ ts = get_timeslot() cats = GradeCategory.objects.filter(TimeSlot=ts) ws = cats.aggregate(Sum('Weight'))['Weight__sum'] wsa = GradeCategoryAspect.objects.filter(Category__in=cats).count() if not hasattr(ts, 'resultoptions'): r = ResultOptions( TimeSlot=ts ) r.save() r = ts.resultoptions if request.method == "POST": form = MakeVisibleForm(request.POST, instance=r) if form.is_valid(): options = form.save() options.save() else: form = MakeVisibleForm(instance=r) return render(request, "results/list_categories.html", { "categories": GradeCategory.objects.filter(TimeSlot=ts), 'ts': ts, 'gsum': ws, 'asum': wsa, 'visible': r.Visible, 'form': form, })
def list_files_of_type(request, pk): """ Lists all files of one type of professional skill. :param request: :param pk: filetype to show delivered files for :return: """ ftype = get_object_or_404(FileType, pk=pk) if get_grouptype('3') in request.user.groups.all() or \ get_grouptype('6') in request.user.groups.all(): files = StudentFile.objects.filter(Type=ftype).distinct() elif get_grouptype('1') in request.user.groups.all() or \ get_grouptype('2') in request.user.groups.all(): # type1 or type2 dists = get_distributions(request.user, timeslot=get_timeslot()) if not dists: # raise PermissionDenied('You do not have any distributed students at this moment.') return render( request, 'base.html', context={ 'Message': 'You do not have any distributed students at this moment.' }) files = StudentFile.objects.filter(Type=ftype, Distribution__in=dists) # elif not request.user.groups.exists(): # files = StudentFile.objects.filter(Type=ftype, Distribution=request.user.distribution.get(TimeSlot=get_timeslot())) else: raise PermissionDenied('Not allowed.') return render(request, 'professionalskills/list_files.html', { 'type': ftype, 'files': files, })
def clean_DateTime(self): ts = get_timeslot() Phase = ts.timephases.get(Description=7) data = self.cleaned_data['DateTime'] if data == '' or data is None: return data if data.date( ) < ts.Begin: # allow presentations to be planned before phase 7, but not before timeslot. raise forms.ValidationError( "The date is before the begin of this time slot. Please choose a later date" ) elif data.date( ) > Phase.End: # presentations cannot be planned after phase 7. raise forms.ValidationError( "The date is after the presentations time phase. Please choose an earlier date" ) elif data.time() > time(hour=23): raise forms.ValidationError( "The start time is after 23:00, which is too late") elif data.time() < time(hour=7): raise forms.ValidationError( "The start time is before 7:00, which is too early") else: return data
def list_users(request, filter=False): """ List of all active users, including upgrade/downgrade button for staff and impersonate button for admins :param request: :return: """ if filter == 'all': users = User.objects.all() elif filter == 'current': users = User.objects.filter( Q(groups__isnull=False) | Q(usermeta__TimeSlot=get_timeslot())).distinct() else: # recent users = User.objects.filter( Q(groups__isnull=False) | Q(usermeta__TimeSlot__id__in=[ x.id for x in get_recent_timeslots() ]) | (Q(usermeta__TimeSlot=None) & Q(last_login__isnull=False))).distinct() return render( request, "support/list_users.html", { "users": users.prefetch_related('groups', 'usermeta', 'usermeta__TimeSlot', 'administratoredgroups'), 'hide_sidebar': True, })
def exports(request, download=None): ts = get_timeslot() if ts is None: # timeslot already finished, get last active timeslot. ts = TimeSlot.objects.order_by('End').last() if not download: return render(request, 'proposals/exports.html', context={'timeslot': ts}) else: if download == 'published': projects = Proposal.objects.filter(TimeSlot=ts, Status=4).order_by('Title') elif download == 'all': projects = Proposal.objects.filter(TimeSlot=ts).order_by('Title') elif download == 'nonfull': projects = Proposal.objects.annotate( num_distr=Count('distributions')).filter( TimeSlot=ts, Status=4, num_distr__lt=F('NumStudentsMax')).order_by('Title') elif download == 'nondistributed': projects = Proposal.objects.filter( TimeSlot=ts, Status=4, distributions__isnull=True).order_by('Title') else: raise PermissionDenied('Invalid download.') file = get_list_projects_xlsx(projects) response = HttpResponse(content=file) response[ 'Content-Disposition'] = 'attachment; filename=export-proposals-.xlsx'.format( download) response[ 'Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' return response
def delete_random_distributions(request): """ Delete all distributions who have had a random assigned project :param request: :return: """ dists = Distribution.objects.filter(TimeSlot=get_timeslot(), Application__isnull=True, Proposal__Private__isnull=True).order_by('Student') if request.method == 'POST': form = ConfirmForm(request.POST) if form.is_valid(): dists.delete() return render(request, 'base.html', { 'Message': 'Distributions deleted!' }) else: form = ConfirmForm() return render(request, 'distributions/delete_random_dists.html', { 'form': form, 'buttontext': 'Confirm', 'formtitle': 'Confirm deletion distributions of random assigned projects', 'distributions': dists, })
def get_applications(student): """ Get all applications of a student in this timeslot :param student: :return: """ return student.applications.filter(Proposal__TimeSlot=get_timeslot())
def get_presentation_student(user): """ Displays the presentation for a student :param user: :return: """ if not user.is_authenticated or user.groups.exists(): # anonymous or not student return False ts = get_timeslot() try: options = ts.presentationoptions except PresentationOptions.DoesNotExist: return "Your presentation is not yet planned." if options.Public or get_timephase_number() == 7: # check if user has presentations try: t = PresentationTimeSlot.objects.get(Q(Distribution__Student=user) & Q(Presentations__PresentationOptions__TimeSlot=ts)) except (PresentationTimeSlot.DoesNotExist, PresentationTimeSlot.MultipleObjectsReturned): return "Your presentation is not (yet) planned." start = timezone.localtime(t.DateTime).strftime("%A %d %B %Y %H:%M") room = t.Presentations.PresentationRoom url = reverse('presentations:presentationscalendar') title = "View all presentations" html = '<p>Your presentation is on {} in {}</p>' \ '<a href="{}" class ="button primary">{}</a></p>' st = format_html(html, start, room, url, title) return st else: return "Your presentation time slot will appear here when the planning becomes public."
def copy_project(request, pk): """ Copy a proposal from a previous timeslot. Only for staff that is allowed to see the proposal to copy. :param pk: the id of proposal to copy :param request: :return: """ if request.method == 'POST': form = ProposalFormCreate(request.POST, request=request) if form.is_valid(): prop = form.save() mail_proposal_all(request, prop) if prop.Private.all(): for std in prop.Private.all(): mail_proposal_private(prop, std, "A private proposal was created for you.") return render(request, "proposals/message_project.html", {"Message": "Proposal created!", "Proposal": prop}) else: old_proposal = get_object_or_404(Proposal, pk=pk) oldpk = old_proposal.pk old_proposal.pk = None # default timeslot. Overridden by form if this is not in phase 1. old_proposal.TimeSlot = get_timeslot() # Assistants and privates are removed, because m2m is not copied in this way. form = ProposalFormCreate(request=request, instance=old_proposal, copy=oldpk) if get_timephase_number() == 1: return render(request, 'GenericForm.html', {'form': form, 'formtitle': 'Edit copied proposal', 'buttontext': 'Create and go to next step'}) else: return render(request, 'GenericForm.html', {'form': form, 'formtitle': 'Edit copied proposal (For next timeslot)', 'buttontext': 'Create and go to next step'})
def get_all_applications(user): """ Get a users applications for this timeslot :param user: user to get applications for :return: """ return user.applications.filter(Proposal__TimeSlot=get_timeslot())
def can_respond_file(user, dist): if dist.TimeSlot == get_timeslot(): # current timeslot if user in dist.Proposal.Assistants.all() \ or user == dist.Proposal.ResponsibleStaff \ or user == dist.Proposal.Track.Head \ or get_grouptype('3') in user.groups.all(): return True return False
def get_list_distributions_xlsx(proposals): """ Excel export of proposals with distributions. Lists all proposals with their student. :param proposals: :return: """ wb = Workbook() # grab the active worksheet ws = wb.active ws.title = "distributions" ws['A1'] = "Proposals with students and staff from {}".format(settings.NAME_PRETTY) ws['A1'].style = 'Headline 2' ws['F1'] = "Exported on: " + str(datetime.now()) header = ["Title", "Track", "Research group", "Responsible", "Assistants", "Chosen", "Student Emails", "Staff Emails"] ws.column_dimensions['A'].width = 25 # title ws.column_dimensions['D'].width = 25 # responsible ws.column_dimensions['E'].width = 25 # assistants ws.column_dimensions['F'].width = 25 # chosen ws.column_dimensions['G'].width = 25 # student mail ws.column_dimensions['H'].width = 25 # staff mail ws.append(header) for col in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']: ws[col + '2'].style = 'Headline 3' for p in proposals: des = p.distributions.filter(TimeSlot=get_timeslot()) row = [p.Title, p.Track.__str__(), p.Group.__str__(), p.ResponsibleStaff.usermeta.get_nice_name()] assistants = '' for a in p.Assistants.all(): assistants += a.usermeta.get_nice_name() + "; " row.append(assistants) stds = '' stdsmail = '' for d in des: stdsmail += d.Student.email + '; ' try: stds += d.Student.usermeta.get_nice_fullname() + " (" + d.Student.usermeta.Studentnumber + "); " except: stds += d.Student.usermeta.get_nice_fullname() + "; " row.append(stds) row.append(stdsmail) emails = '{};'.format(p.ResponsibleStaff.email) for a in p.Assistants.all(): emails += '{};'.format(a.email) row.append(emails) # row[4].alignment = Alignment(wrapText=True) # row[5].alignment = Alignment(wrapText=True) ws.append(row) return save_virtual_workbook(wb)
def check_user(request, user): # insert checks on login here if user.is_superuser: return render(request, 'base.html', status=403, context={ 'Message': 'Superusers are not allowed to login via SSO. Please use 2FA login.'}) else: # block all except supportstaff if there is no timeslot # support staff needs login to be able to set a new timeslot or timephase. if not get_timeslot() and not get_grouptype('3') in user.groups.all(): # if there isn't a timeslot and not type3 return render(request, 'base.html', status=403, context={"Message": "Login is currently not available."}) # login functions for staff and students. if is_staff(user): set_level(user) if not user.groups.exists(): # existing staff member already have groups # new staff members get automatically type2staffunverified user.groups.add(get_grouptype("2u")) return True else: if not enrolled_osiris(user): return render(request, 'base.html', status=403, context={"Message": "You are not enrolled in our system yet. Please login once through canvas module BEP Marketplace"}) elif get_timephase_number() < 3: # if there isn't a timephase, this returns -1, so login is blocked. return render(request, 'base.html', status=403, context={"Message": "Student login is not available in " "this timephase."}) else: # student is enrolled in osiris. Set its usermeta from the osiris data data = osirisData() osirisdata = data.get(user.email) if osirisdata is not None: set_osiris(user, osirisdata) if get_timephase_number() > 5: # only students with project are allowed if not user.distributions.exists(): return render(request, 'base.html', status=403, context={"Message": "You don't have a project assigned" " to you, therefore login is not " "allowed in this timephase."}) if get_timeslot() not in user.usermeta.TimeSlot.all(): # user is not active in this timeslot # not in this timeslot so old user, canvas app sets timeslot # this security will fail if canvas does not close off old courses as it does now return render(request, 'base.html', status=403, context={"Message": "You already did your BEP once" ", login is not allowed."}) return True
def list_staff(request): """ List all staff with a distributed projects :param request: :return: """ def nint(nr): """ :param <int> nr: :return: """ if nr is None: return 0 else: return int(nr) staff = get_all_staff().filter( Q(groups=get_grouptype("2")) | Q(groups=get_grouptype("1"))).prefetch_related( 'proposalsresponsible', 'proposals') se = [] for s in staff: p1 = s.proposalsresponsible.filter(TimeSlot=get_timeslot()) p2 = s.proposals.filter(TimeSlot=get_timeslot()) pt1 = p1.count() pt2 = p2.count() pts = pt1 + pt2 dt1 = nint( p1.annotate(Count('distributions')).aggregate( Sum('distributions__count'))['distributions__count__sum']) dt2 = nint( p2.annotate(Count('distributions')).aggregate( Sum('distributions__count'))['distributions__count__sum']) dts = dt1 + dt2 se.append({ "user": s, "pt1": pt1, "pt2": pt2, "pts": pts, "dt1": dt1, "dt2": dt2, "dts": dts }) return render(request, 'support/list_staff.html', {"staff": se})