Example #1
0
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})
Example #2
0
def automatic_options(request):
    """
    Option frontend for automatic() distribution

    :param request:
    :return: 302 to automatic() with correct get options
    """
    if get_timephase_number() < 4 or get_timephase_number() > 5:  # 4 or 5
        raise PermissionDenied('Distribution is not possible in this timephase')

    if request.method == 'POST':
        form = AutomaticDistributionOptionForm(request.POST)
        if form.is_valid():
            # redirect to automatic
            distribution_type = form.cleaned_data['distribution_type']
            print(distribution_type)
            distribute_random = form.cleaned_data['distribute_random']
            automotive_preference = form.cleaned_data['automotive_preference']
            return HttpResponseRedirect(reverse('distributions:distributeproposaloption',
                                                kwargs={'dist_type': distribution_type,
                                                        'distribute_random': distribute_random,
                                                        'automotive_preference': automotive_preference}
                                                ))
    else:
        form = AutomaticDistributionOptionForm()
    return render(request, 'GenericForm.html', {
        'form': form,
        'formtitle': 'Automatic distribution options',
        'buttontext': 'View result'
    })
Example #3
0
def list_students_xlsx(request):
    """
    Same as liststudents but as XLSX. The combination of students and grades is done in general_excel.

    :param request:
    """
    if get_timephase_number() < 0:
        if get_timeslot() is None:
            raise PermissionDenied("System is closed.")
    else:
        if get_timephase_number() < 4:
            raise PermissionDenied("Students are not yet distributed")
        if get_timephase_number() < 5 and not get_grouptype(
                "3") in request.user.groups.all():
            return render(
                request, "base.html", {
                    'Message':
                    "When the phase 'Distribution of projects is "
                    "finished, you can view your students here."
                })

    typ = GradeCategory.objects.filter(TimeSlot=get_timeslot())
    des = get_distributions(request.user)
    file = get_list_students_xlsx(des, typ)

    response = HttpResponse(content=file)
    response[
        'Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    response[
        'Content-Disposition'] = 'attachment; filename=students-grades.xlsx'
    return response
Example #4
0
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!')
Example #5
0
def can_downgrade_project_fn(user, proj):
    """
    Check if user can downgrade a project. upgrade is same as with can_edit_project_fn

    :param user:
    :param proj:
    :return:
    """
    if proj.prevyear():
        return False, "This is an old proposal. Changing history is not allowed."

    if proj.Status == 1:
        return False, "Already at first stage."

    # support staf, superusers are always allowed to downgrade
    if get_grouptype("3") in user.groups.all() \
            or user.is_superuser:
        return True, ""

    # proposals of this year, check timephases
    if proj.TimeSlot == get_timeslot():
        # if no timephase is enabled than forbid editing
        if get_timephase_number() < 0:
            return False, "No editing allowed, system is closed"

        # if timephase is after checking phase no editing is allowed, except for support staff
        if get_timephase_number() > 2 and not get_grouptype(
                "3") in user.groups.all():
            return False, "Proposal is frozen in this timeslot"

        # if status is 3 or 4 Responsible can downgrade 3-2 in timephase 1 only
        if proj.Status >= 3 and proj.ResponsibleStaff == user and get_timephase_number(
        ) == 1:
            return True, ""

        # Track head can downgrade in phase 1 and 2
        if get_timephase_number() <= 2 and (
                proj.Track.Head == user
                or group_administrator_status(proj, user) > 1):
            return True, ""
    else:
        # if status is 3 Responsible can downgrade 3-2 if not in this timeslot
        if proj.Status == 3 and proj.ResponsibleStaff == user:
            return True, ""

        # Track head is allowed all for not this timeslot
        if proj.Track.Head == user or group_administrator_status(proj,
                                                                 user) > 1:
            return True, ""

    # if status is 2 and user is assistant downgrade is allowed
    if proj.Status == 2 \
            and (user in proj.Assistants.all() or proj.ResponsibleStaff == user):
        return True, ""

    return False, "You are not allowed to downgrade this project."
Example #6
0
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))
Example #7
0
    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())
Example #8
0
    def wrapper(*args, **kw):
        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',
            )

        # type 3 and 6 can always view professional skills.
        # Everyone can view it in phase 6 (execution) and later (presenting).
        if get_timephase_number() < 5 and \
                get_grouptype("3") not in request.user.groups.all() and \
                get_grouptype("6") not in request.user.groups.all():
            raise PermissionDenied(
                "Student files are not available in this phase")

        if not request.user.groups.exists(
        ) and not request.user.distributions.exists():
            raise PermissionDenied(
                "Student files are available after you are distributed to a project."
            )

        return fn(*args, **kw)
Example #9
0
def create_project(request):
    """
    Create a new proposal. Only for staff. Generating a new proposal for this timeslot is only allowed in the first
    timephase. In other timephases projects can only be generated for the next timeslot.

    :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)
            check_content_policy.CPVCheckThread(prop).start()
            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:
        init = {}
        if get_grouptype("1") in request.user.groups.all():
            init["ResponsibleStaff"] = request.user.id
        elif get_grouptype("2") in request.user.groups.all() or get_grouptype('2u'):
            init["Assistants"] = [request.user.id]
        form = ProposalFormCreate(request=request, initial=init)
    if get_timephase_number() == 1:
        return render(request, 'GenericForm.html', {'form': form,
                                                    'formtitle': 'Create new Proposal',
                                                    'buttontext': 'Create and go to next step'})
    else:
        return render(request, 'GenericForm.html', {'form': form,
                                                    'formtitle': 'Create new Proposal (For next timeslot)',
                                                    'buttontext': 'Create and go to next step'})
Example #10
0
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."
Example #11
0
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'})
Example #12
0
def mail_track_heads(request):
    """
    Mail all track heads with their actions

    :param request:
    :return:
    """
    if get_timephase_number() > 2:
        return render(request, "base.html",
                      {"Message": "Only possible in first two phases"})
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            mail_track_heads_pending()
            return render(request, "base.html",
                          {"Message": "Track Heads mailed!"})
    else:
        form = ConfirmForm()

    trackstats = {}
    for track in Track.objects.all():
        trackstats[str(track)] = {
            'pending':
            get_all_proposals().filter(Q(Status=3) & Q(Track=track)).count(),
            'head':
            track.Head.email,
        }
    return render(request, "support/TrackHeadSendConfirm.html", {
        'trackstats': trackstats,
        'form': form
    })
 def in_phase(u):
     if u.is_authenticated:
         if get_timephase_number() in phase_numbers:
             return True
         else:
             raise PermissionDenied(
                 "This page is not available in the current time phase.")
     return False
Example #14
0
def list_distributions_xlsx(request):
    """
    Same as supportListApplications but as XLSX
    """
    if get_timephase_number() < 3:
        raise PermissionDenied("There are no applications yet")
    elif get_timephase_number() > 4:
        projects = get_all_proposals().filter(Q(Status=4) & Q(distributions__isnull=False)).distinct()
    else:
        projects = get_all_proposals().filter(Status=4)
    # projects = projects.select_related('ResponsibleStaff', 'Track').prefetch_related('Assistants',
    #                                                                                  'distributions__Student__usermeta')
    file = get_list_distributions_xlsx(projects)
    response = HttpResponse(content=file)
    response['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    response['Content-Disposition'] = 'attachment; filename=marketplace-projects-distributions.xlsx'
    return response
Example #15
0
    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.")
Example #16
0
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
Example #17
0
 def is_student(u):
     if u.is_authenticated:
         if u.groups.exists():
             raise PermissionDenied(
                 "This page is only available for students.")
         if get_timephase_number() < 3:
             raise PermissionDenied(
                 "The system is not yet open for students.")
         return True
     return False
Example #18
0
def export_presentations(request):
    """
    Shows the presentations planning in an Excel file in the same way as was done before the marketplace

    :param request:
    :return: xlsx file
    """
    if get_timephase_number() <= 4:
        raise PermissionDenied("Projects are not yet distributed.")

    if get_timephase_number() != 7:
        if get_grouptype("3") not in request.user.groups.all():
            try:
                public = get_timeslot().presentationoptions.Public
            except:
                return render(
                    request, 'base.html',
                    {'Message': 'The Presentations are not yet planned.'})
            if not public:
                return render(request, 'base.html', {
                    'Message':
                    'The Presentations planning is not yet public'
                })

    sets = PresentationSet.objects.filter(
        PresentationOptions__TimeSlot=get_timeslot())
    if not sets:
        return render(
            request, "base.html", {
                "Message":
                "There is nothing planned yet. Please plan the presentations first."
            })

    file = get_list_presentations_xlsx(sets)
    response = HttpResponse(content=file)
    response[
        'Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    response[
        'Content-Disposition'] = 'attachment; filename=presentations-planning.xlsx'
    return response
Example #19
0
def api_redistribute(request):
    """
    AJAX call from manual distribute to change a distribution

    :param request:
    :return:
    """
    if get_timephase_number() < 4 or get_timephase_number() > 6:
        raise PermissionDenied('Distribution 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:
            # change Proposal
            dist.Proposal = get_cached_project(request.POST['propTo'])
            # change Application if user has Application
            try:
                dist.Application = get_all_applications(dist.Student).get(Proposal=dist.Proposal)
                appl_prio = dist.Application.Priority
            except Application.DoesNotExist:
                dist.Application = None
                appl_prio = -1
            dist.full_clean()
            dist.save()
        except Exception as e:
            return JsonResponse({'type': 'warning', 'txt': warningString, 'exception': str(e)})
        return JsonResponse(
            {'type': 'success', 'txt': 'Changed distributed Student ' + dist.Student.usermeta.get_nice_name() +
                                       ' to Proposal ' + dist.Proposal.Title, 'prio': appl_prio})
    else:
        raise PermissionDenied("You don't know what you're doing!")
Example #20
0
def prefetch(projects):
    """
    Prefetch interesting data for a list of projects.

    :param projects:
    :return:
    """
    projects = projects.select_related(
        'ResponsibleStaff__usermeta', 'Track__Head__usermeta', 'TimeSlot',
        'Group__Head__usermeta').prefetch_related('Assistants__usermeta')
    if get_timephase_number() > 4:
        projects = projects.prefetch_related(
            'distributions__Student__usermeta')
    return projects
Example #21
0
def list_applications_distributions(request):
    """
    Show a list of all active proposals with the applications and possibly distributions of students.
    Used for support staff as an overview.
    Same table include as in manual distribute
    """
    if get_timephase_number() < 3:
        raise PermissionDenied("There are no applications or distributions yet.")
    elif get_timephase_number() > 5:
        projects = get_all_proposals().filter(Q(Status=4) & Q(distributions__isnull=False)).distinct()
        projects = projects.select_related('ResponsibleStaff', 'Track').prefetch_related('Assistants',
                                                                                         'Private',
                                                                                         'distributions__Application',
                                                                                         'distributions__Student__usermeta')

    else:  # phase 3 & 4 & 5
        projects = get_all_proposals().filter(Status=4)
        projects = projects.select_related('ResponsibleStaff', 'Track').prefetch_related('Assistants',
                                                                                         'Private',
                                                                                         'applications__Student__usermeta',
                                                                                         'distributions__Application',
                                                                                         'distributions__Student__usermeta')

    return render(request, 'distributions/list_applications_distributions.html', {"proposals": projects})
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()))
Example #23
0
def download_all_of_type(request, pk):
    """
    Download all files for a given filetype

    :param request:
    :param pk: id of the filetype
    :return:
    """
    ftype = get_object_or_404(FileType, pk=pk)
    if ftype.TimeSlot == get_timeslot():  # current year download
        if get_timephase_number() < 5:  # only in phase 5, 6 and 7
            raise PermissionDenied(
                "This page is not available in the current time phase.")
    in_memory = BytesIO()
    dists = get_distributions(request.user, timeslot=ftype.TimeSlot)
    if not dists:
        raise PermissionDenied('You do not have any students.')
    with zipfile.ZipFile(in_memory, 'w') as archive:
        for file in ftype.files.filter(Distribution__in=dists):
            trck = file.Distribution.Proposal.Track
            fname = file.Distribution.Student.usermeta.get_nice_name().split(
            )[-1] + "".join(file.Distribution.Student.usermeta.get_nice_name().
                            split(' ')[:-1])
            try:
                with open(file.File.path, 'rb') as fstream:
                    archive.writestr(
                        '{}/{}.{}'.format(str(trck), fname,
                                          file.File.name.split('.')[-1]),
                        fstream.read())
            except (
                    IOError, ValueError
            ):  # happens if a file is referenced from database but does not exist on disk.
                return render(
                    request, 'base.html', {
                        'Message':
                        'These files cannot be downloaded, please contact support staff. (Error on file: "{}")'
                        .format(file)
                    })
    in_memory.seek(0)

    response = HttpResponse(content_type="application/zip")
    response['Content-Disposition'] = 'attachment; filename="{}.zip"'.format(
        str(ftype.Name))

    response.write(in_memory.read())

    return response
Example #24
0
    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().select_related('usermeta')
        self.fields['Assistants'].queryset = get_grouptype('2').user_set.all().select_related('usermeta') | \
                                             get_grouptype('2u').user_set.all().select_related('usermeta') | \
                                             get_grouptype('1').user_set.all().select_related('usermeta')
        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).select_related('usermeta')
        self.fields[
            'Private'].label_from_instance = self.user_label_from_instance

        # no user label_from_instance for private students because privacy.
        self.fields['addPrivatesEmail'].widget.attrs[
            'placeholder'] = "Add private student via email address"
        self.fields['TimeSlot'].empty_label = 'Future'
        if get_timephase_number() == 1:
            self.fields['TimeSlot'].queryset = TimeSlot.objects.filter(
                End__gt=datetime.now()).order_by('-Begin')
            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(
                ).order_by('-Begin')
                try:
                    self.fields['TimeSlot'].initial = TimeSlot.objects.filter(
                        Begin__gt=datetime.now()).order_by('-Begin')[
                            0]  # autofill to first next available timeslot.
                except IndexError:
                    self.fields['TimeSlot'].initial = None
            else:
                # phase 2+, only add for future timeslot
                tss = TimeSlot.objects.filter(
                    Begin__gt=datetime.now()).order_by('-Begin')
                self.fields['TimeSlot'].queryset = tss
                try:
                    self.fields['TimeSlot'].initial = tss[
                        0]  # autofill to first next available timeslot.
                except IndexError:
                    self.fields['TimeSlot'].initial = None
Example #25
0
def list_applications_distributions(request, timeslot=None):
    """
    Show a list of all active proposals with the applications and possibly distributions of students.
    Used for support staff as an overview.
    Same table include as in manual distribute
    """
    if not timeslot:
        if get_timephase_number() > 5:
            projects = get_all_proposals().filter(
                Q(Status=4) & Q(distributions__isnull=False)).distinct()
            projects = projects.select_related(
                'ResponsibleStaff',
                'Track').prefetch_related('Assistants', 'Private',
                                          'distributions__Application',
                                          'distributions__Student__usermeta')

        else:  # phase 3 & 4 & 5
            projects = get_all_proposals().filter(Status=4)
            projects = projects.select_related(
                'ResponsibleStaff',
                'Track').prefetch_related('Assistants', 'Private',
                                          'applications__Student__usermeta',
                                          'distributions__Application',
                                          'distributions__Student__usermeta')
        return render(request,
                      'distributions/list_applications_distributions.html',
                      {"proposals": projects})

    else:  # future
        ts = get_object_or_404(TimeSlot, pk=timeslot)
        projects = Proposal.objects.filter(TimeSlot=ts, Status=4)
        projects = projects.select_related(
            'ResponsibleStaff',
            'Track').prefetch_related('Assistants', 'Private',
                                      'applications__Student__usermeta',
                                      'distributions__Application',
                                      'distributions__Student__usermeta')

        return render(request,
                      'distributions/list_applications_distributions.html', {
                          "proposals": projects,
                          'timeslot': ts
                      })
Example #26
0
def get_all_proposals(old=False):
    """
    All proposals in this timeslot. Cached after timephase 5.

    :return:
    """
    if old:
        return Proposal.objects.all()

    if get_timephase_number() > 5:
        p = cache.get('all_proposals_objects')
        if p:
            return p
        else:
            p = Proposal.objects.filter(TimeSlot=get_timeslot()).distinct()
            cache.set('all_proposals_objects', p,
                      settings.STATIC_OBJECT_CACHE_DURATION)
            return p
    else:
        return Proposal.objects.filter(TimeSlot=get_timeslot()).distinct()
Example #27
0
    def wrapper(*args, **kw):
        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', )

        if get_timephase_number() != 3:
            raise PermissionDenied("Apply is not possible in this time phase.")
        if request.user.groups.exists():
            raise PermissionDenied("Only students can apply to proposals")
        if request.user.personal_proposal.filter(TimeSlot=get_timeslot()).exists():
            raise PermissionDenied("You cannot apply/retract because there is a private proposal for you.")
        if 'pk' in kw:
            pk = int(kw['pk'])
            prop = get_object_or_404(Project, pk=pk)
            if prop.Private.exists():
                raise PermissionDenied("This proposal is private. It is already assigned.")
        return fn(*args, **kw)
Example #28
0
def can_upgrade_project_fn(user, proj):
    """
    Check if user can upgrade a project.

    :param user:
    :param proj:
    :return:
    """
    allowed = can_edit_project_fn(user, proj, False)
    if allowed[0] is True:
        if proj.Status == 4:
            return False, "Already at final stage"
        if proj.Status == 3 and proj.nextyear():
            return False, "Cannot publish proposal for future timeslot"
        elif get_timephase_number() > 2 and \
                proj.TimeSlot == get_timeslot() and \
                get_grouptype('3') not in user.groups.all():
            return False, "Proposal frozen in this timeslot. The timephase of editing has ended."
        elif user in proj.Assistants.all() and proj.Status >= 2:
            return False, "You are an assistant and not allowed to increase status further"
        else:
            return True, ''
    return False, allowed[1]
Example #29
0
def calendar(request, own=False):
    """
    Calendar view of the presentations planning, public visible in phase 7, otherwise only if 'public==True'

    :param request:
    :param own:
    :return:
    """
    if get_timephase_number() < 7 and get_grouptype(
            "3") not in request.user.groups.all() and not planning_public():
        # in phase 5 and 6, planning is only visible for type3, except when it is set to public.
        return render(
            request, 'base.html',
            {'Message': 'The presentations planning is not yet public'})
    ts = get_timeslot()
    sets = PresentationSet.objects.filter(
        PresentationOptions__TimeSlot=ts).order_by('DateTime')
    if own:
        sets = sets.filter(
            Q(timeslots__Distribution__Proposal__ResponsibleStaff=request.user)
            | Q(timeslots__Distribution__Proposal__Assistants=request.user)
            | Q(Assessors=request.user)
            | Q(timeslots__Distribution__Student=request.user)).distinct()
    if not sets:
        if own:
            return render(
                request, "base.html", {
                    "Message":
                    "There are no presentations that you have to attend.",
                    'return': 'presentations:presentationscalendar'
                })
        else:
            return render(
                request, "base.html",
                {"Message": "The presentations are not yet planned."})

    # sets are ordered by datetime, so first set has the lowest time, this is where the calendar display starts
    begin = sets[0].DateTime.date()

    # To show a Make Public for the type3staff
    if get_grouptype(
            "3") in request.user.groups.all() and get_timephase_number() != 7:

        if not hasattr(ts, 'presentationoptions'):
            return render(
                request, "base.html", {
                    "Message":
                    "There are no presentation options yet, please <a class='button success' href='"
                    + reverse("presentations:presentationswizardstep1") +
                    "'>go back to step 1</a>"
                })
        options = ts.presentationoptions
        if request.method == "POST":
            form = MakePublicForm(request.POST, instance=options)
            if form.is_valid():
                options = form.save()
                options.save()
        else:
            form = MakePublicForm(instance=options)
        return render(request, "presentations/presentationsCalendar.html", {
            "sets": sets,
            "form": form,
            "beginCalendar": begin
        })

    # normal view for non-type3 staff
    return render(request, "presentations/presentationsCalendar.html", {
        "sets": sets,
        "beginCalendar": begin,
        "own": own
    })
Example #30
0
def detail_project(request, pk):
    """
    Detailview page for a given proposal. Displays all information for the proposal. Used for students to choose a
    proposal from, and for staff to check. For staff it shows edit and up/downgrade buttons. For students it shows a
    apply or retract button.
    The proposal is cached after the create phase (phase>4). The apply/retract button is not cached but inserted using
    a .format(). Proposals are not cached for staff
    Private proposals don't have a apply/retract button, because the template doesn't have the {} in it then.

    :param request:
    :param pk: pk of the project
    :return:
    """
    prop = get_cached_project(pk)

    # if student
    if not request.user.groups.exists():
        # make apply / retract button.
        if get_timephase_number() != 3:  # phase 3 is for students choosing projects.
            button = ''
        else:
            button = '<a href="{}" class="button {}">{}</a>'
            if get_all_applications(request.user).filter(
                    Proposal=prop).exists():  # if user has applied to this proposal
                button = button.format(reverse('students:retractapplication',
                                               args=[get_all_applications(request.user).filter(Proposal=prop)[0].id]),
                                       'danger',
                                       'Retract Application')
            else:  # show apply button
                button = button.format(reverse('students:confirmapply', args=[prop.id]), 'primary', 'Apply')

        # get proposal from cache, or put in cache
        cdata = cache.get("proposaldetail{}".format(pk))
        if cdata is None:
            data = {"proposal": prop,
                    "project": prop,
                    "user": request.user
                    }
            cdata = render_block_to_string("proposals/detail_project.html", 'body', data)
            cache.set('proposaldetail{}'.format(pk), cdata, None)

        tracking_visit_project(prop, request.user)  # always log visits from students
        return render(request, "proposals/detail_project.html", {
            "bodyhtml": cdata.format(button),
            'project': prop,
            'fav': prop.favorites.filter(User=request.user).exists()
        })  # send project for if statement in scripts.

    # if staff:
    else:
        data = {"proposal": prop,
                "project": prop,
                "Editlock": "Editing not possible"}
        if prop.Status == 4:  # published proposal in this timeslot
            # support staff can see applications
            if get_grouptype("3") in request.user.groups.all() and get_timephase_number() > 2:
                data['applications'] = prop.applications.all()
                # responsible / assistants can see distributions in distribution phase
            if get_timephase_number() >= 4:
                data['distributions'] = get_distributions(request.user).filter(Proposal=prop)
        allowed = can_edit_project_fn(request.user, prop, False)
        if allowed[0]:
            data['Editlock'] = False
        else:
            data['Editlock'] = allowed[1]
        data['fav'] = prop.favorites.filter(User=request.user).exists()
        data['cpv'] = cache.get('cpv_proj_{}'.format(prop.id))  # if cpv is not in cache, ignore
        return render(request, "proposals/detail_project.html", data)