Пример #1
0
def delete_filetype(request, pk):
    """
    Delete a file type.

    :param request:
    :param pk: id of the file type
    :return:
    """
    obj = get_object_or_404(FileType, pk=pk)

    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            obj.delete()
            return render(
                request, 'base.html', {
                    'Message': 'File type deleted.',
                    'return': 'professionalskills:filetypelist',
                })
    else:
        form = ConfirmForm()

    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Confirm deletion of File type {}'.format(obj),
            'buttontext': 'Confirm'
        })
Пример #2
0
def terms_form(request):
    """
    Form for a user to accept the terms of use.

    :param request:
    :return:
    """
    try:
        # already accepted, redirect user to login
        obj = request.user.termsaccepted
        if obj.Stamp <= timezone.now():
            return HttpResponseRedirect('/')
    except UserAcceptedTerms.DoesNotExist:
        pass

    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            # user accepted terms. Possible already accepted terms in a parallel session, so get_or_create.
            UserAcceptedTerms.objects.get_or_create(User=request.user)
            return HttpResponseRedirect('/')
    else:
        form = ConfirmForm()

    return render(request, 'index/terms.html', {
        'form': form,
        'formtitle': 'I have read and accepted the Terms of Services',
        'buttontext': 'Confirm',
        'terms': Term.objects.all()
    })
Пример #3
0
def delete_filetype_aspect(request, pk):
    """
    Delete a file type grade aspect.

    :param request:
    :param pk: id of the file type aspect
    :return:
    """
    obj = get_object_or_404(StaffResponseFileAspect, pk=pk)
    pk = obj.File.pk  # store original linked File
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            obj.delete()
            return render(
                request, 'base.html', {
                    'Message': 'File type aspect deleted.',
                    'return': 'professionalskills:filetypeaspects',
                    'returnget': pk,
                })
    else:
        form = ConfirmForm()

    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Confirm deletion of file type aspect {}'.format(obj),
            'buttontext': 'Confirm'
        })
Пример #4
0
def delete_aspect(request, pk):
    """

    :param request:
    :param pk:
    :return:
    """
    aspect = get_object_or_404(GradeCategoryAspect, pk=pk)
    cat = aspect.Category.id
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            aspect.delete()
            return render(
                request, 'base.html', {
                    'Message': 'Grade category aspect deleted.',
                    'return': 'results:list_aspects',
                    'returnget': cat
                })
    else:
        form = ConfirmForm()
    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Delete grade category aspect?',
            'buttontext': 'Confirm'
        })
Пример #5
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
    })
Пример #6
0
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(),
        })
Пример #7
0
def confirm_mailing(request):
    if request.method == 'POST':
        mailing_obj = get_object_or_404(Mailing,
                                        id=request.POST.get('mailingid', None))
        form = ConfirmForm(request.POST)
        if form.is_valid():
            # loop over all collected email addresses to create a message
            mails = []
            for recipient in mailing_obj.RecipientsStaff.all(
            ) | mailing_obj.RecipientsStudents.all():
                mails.append({
                    'template': 'email/supportstaff_email.html',
                    'email': recipient.email,
                    'subject': mailing_obj.Subject,
                    'context': {
                        'message': mailing_obj.Message,
                        'name': recipient.usermeta.get_nice_name(),
                    }
                })
            EmailThreadTemplate(mails).start()
            mailing_obj.Sent = True
            mailing_obj.save()
            return render(request, "support/email_progress.html")
        raise PermissionDenied('The confirm checkbox was unchecked.')
    raise PermissionDenied("No post data supplied!")
Пример #8
0
def delete_file(request, pk):
    """
    Delete a public file

    :param request:
    :param pk: pk of the proposal to delete
    :return:
    """
    obj = get_object_or_404(PublicFile, pk=pk)
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            obj.delete()
            return render(request, "base.html", {
                "Message": "Public file removed!",
                "return": "index:index"
            })
    else:
        form = ConfirmForm()

    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Confirm deleting public file {}'.format(obj),
            'buttontext': 'Confirm'
        })
Пример #9
0
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,
    })
Пример #10
0
def students_confirm(request):
    """
    Add a list of students to a specific timeslot

    :param request:
    :return:
    """
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            data = json.loads(request.POST.get('jsondata', None))
            if not data:
                raise PermissionDenied('Invalid data.')
            ts = get_object_or_404(TimeSlot, pk=data.get('ts'))
            cnt = 0
            for student in data['students']:
                s = get_object_or_404(User, pk=student, groups=None)
                s.usermeta.TimeSlot.add(ts)
                s.usermeta.save()
                cnt += 1
            return render(request, 'base.html', {
                'Message': f'{cnt} students set to {ts}.',
            })
        else:
            return render(request, 'base.html', {
                'Message': 'Please check confirm.',
            })
    else:
        raise PermissionDenied('Invalid request.')
Пример #11
0
def delete_timephase(request, timephase):
    """

    :param request:
    :param timephase: pk of timephase to delete
    :return:
    """
    name = 'Time phase'
    obj = get_object_or_404(TimePhase, pk=timephase)
    if obj.End < datetime.now().date():
        raise PermissionDenied('This TimePhase has already finished.')
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            obj.delete()
            return render(
                request, 'base.html', {
                    'Message': '{} deleted.'.format(name),
                    'return': 'timeline:list_timephases',
                    'returnget': obj.TimeSlot.pk
                })
    else:
        form = ConfirmForm()
    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Delete {}?'.format(name),
            'buttontext': 'Delete'
        })
Пример #12
0
def delete_category(request, pk):
    """

    :param request:
    :param pk:
    :return:
    """
    cat = get_object_or_404(GradeCategory, pk=pk)
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            for aspect in cat.aspects.all():
                aspect.delete()
            cat.delete()
            return render(request, 'base.html', {
                'Message': 'Grade category deleted.',
                'return': 'results:list_categories',
            })
    else:
        form = ConfirmForm()
    return render(request, 'GenericForm.html', {
        'form': form,
        'formtitle': 'Delete grade category?',
        'buttontext': 'Confirm'
    })
Пример #13
0
def delete_mailing_template(request, pk):
    """

    :param request:
    :param pk: pk of template
    :return:
    """
    name = 'Mailing template'
    obj = get_object_or_404(MailTemplate, pk=pk)
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            obj.delete()
            return render(
                request, 'base.html', {
                    'Message': '{} deleted.'.format(name),
                    'return': 'support:mailingtemplates'
                })
    else:
        form = ConfirmForm()
    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Delete {}?'.format(name),
            'buttontext': 'Delete'
        })
Пример #14
0
def osirisToMeta(request):
    write_errors = []
    try:
        data, log = read_osiris_xlsx()
    except:
        return render(
            request, 'base.html', {
                'Message':
                'Retrieving Osirisdata failed. Please upload a valid file.',
                'return': 'index:index',
            })

    if request.method == 'POST':
        count = 0
        form = ConfirmForm(request.POST)
        if form.is_valid():
            for p in data:
                try:
                    user = User.objects.get(email=p.email)
                except User.DoesNotExist:
                    write_errors.append('User {} skipped'.format(p.email))
                    continue
                try:
                    meta = user.usermeta
                except UserMeta.DoesNotExist:
                    meta = UserMeta()
                    user.usermeta = meta
                    meta.save()
                    user.save()
                if p.automotive:
                    meta.Study = 'Automotive'
                else:
                    meta.Study = 'Electrical Engineering'
                meta.Cohort = p.cohort
                meta.ECTS = p.ects
                meta.Studentnumber = p.idnumber
                meta.save()
                count += 1
            return render(
                request, 'base.html', {
                    'Message':
                    mark_safe('User meta updated for {} users. <br />'.format(
                        count) + print_list(write_errors)),
                    'return':
                    'osirisdata:list'
                })
    else:
        form = ConfirmForm()

    return render(
        request, 'osirisdata/osiris_to_meta_form.html', {
            'form': form,
            'formtitle': 'Confirm write to usermeta',
            'buttontext': 'Confirm'
        })
Пример #15
0
def assign(request, pk):
    """
    Assign all distributed students to one of the prv groups.

    :param request:
    :param pk:
    :return:
    """
    filetype = get_object_or_404(FileType, pk=pk)
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            if filetype.groups.all().aggregate(
                    Sum('Max'))['Max__sum'] < get_all_students().count():
                return render(
                    request, 'base.html', {
                        'Message':
                        'Groups capacity not sufficient. Groups are not changed.',
                        'return': 'professionalskills:listgroups',
                        'returnget': filetype.id
                    })
            for group in filetype.groups.all():
                group.Members.clear()
                group.save()
            students = list(get_all_students())
            totalstudents = len(students)
            random.shuffle(students)
            groups = list(filetype.groups.all())
            totaldistributed = 0
            while totaldistributed < totalstudents:
                for g in [g for g in groups if g.Members.count() < g.Max]:
                    try:
                        g.Members.add(students.pop(0))
                        totaldistributed += 1
                    except IndexError:
                        break
            for g in groups:
                g.save()

            return render(
                request, 'base.html', {
                    'Message': 'Students divided over the groups.',
                    'return': 'professionalskills:listgroups',
                    'returnget': filetype.id,
                })
    else:
        form = ConfirmForm()

    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle':
            'Confirm reshuffling students for {}'.format(filetype),
            'buttontext': 'Confirm'
        })
Пример #16
0
def mail_overdue_students(request):
    """
    Mail students that didn't handin file before the deadline

    :param request:
    :return:
    """
    timeslot = get_timeslot()
    prvs = FileType.objects.filter(TimeSlot=timeslot)
    dists = get_distributions(request.user, timeslot=timeslot)
    if request.method == "POST":
        form = ConfirmForm(request.POST)
        if form.is_valid():
            mails = []
            for dist in dists:
                missingtypes = []
                for ftype in prvs:
                    if ftype.deadline_passed() and not dist.files.filter(
                            Type=ftype).exists():
                        missingtypes.append(ftype)
                if len(missingtypes) > 0:
                    mails.append({
                        'template': 'email/overdueprvstudent.html',
                        'email': dist.Student.email,
                        'subject': 'Overdue professional skill delivery',
                        'context': {
                            'student': dist.Student,
                            'project': dist.Proposal,
                            'types': missingtypes,
                        }
                    })
            EmailThreadTemplate(mails).start()
            return render(request, "support/email_progress.html")
    else:
        form = ConfirmForm()

    # preview list of students.
    students = []
    for dist in dists:
        missing = False
        for ftype in prvs:
            if ftype.deadline_passed() and not dist.files.filter(
                    Type=ftype).exists():
                missing = True
                break
        if missing:
            students.append(dist.Student)
    return render(
        request, 'professionalskills/overdueprvform.html', {
            'form': form,
            'formtitle': 'Confirm mailing overdue students',
            'buttontext': 'Confirm',
            'students': students,
        })
Пример #17
0
def copy(request, pk, from_pk=None):
    """
    Show a list of timeslots to import rubrics from.

    :param request:
    :param pk: prv to copy grades to
    :param from_pk: prv to copy grades from
    :return:
    """
    prv = get_object_or_404(FileType, pk=pk)
    if prv.TimeSlot.is_finished():
        raise PermissionDenied('Old prvs cannot be changed.')
    if from_pk:  # do copy
        from_prv = get_object_or_404(FileType, pk=from_pk)
        if not from_prv.CheckedBySupervisor or not from_prv.aspects.exists():
            raise PermissionDenied("This file has no grading aspects.")

        if request.method == 'POST':
            form = ConfirmForm(request.POST)
            if form.is_valid():
                for aspect in from_prv.aspects.all():
                    aspect.id = None
                    aspect.File = prv
                    aspect.save()
                return render(
                    request, 'base.html', {
                        'Message': 'Finished importing!',
                        'return': 'professionalskills:list_aspects',
                        'returnget': prv.pk
                    })
        else:
            form = ConfirmForm()
        return render(
            request, 'GenericForm.html', {
                'form': form,
                'formtitle': f'Confirm import aspcects to {prv.Name}',
                'buttontext': 'Confirm'
            })

    else:  # show options list
        prvs = FileType.objects.filter(
            CheckedBySupervisor=True,
            aspects__isnull=False).exclude(pk=pk).distinct()
        return render(request, "professionalskills/list_copy.html", {
            "prvs": prvs,
            'prv': prv
        })
Пример #18
0
def delete_capacity_group(request, pk):
    obj = get_object_or_404(CapacityGroup, pk=pk)
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            obj.delete()
            return render(
                request, 'base.html', {
                    'Message': 'Capacity group {} deleted.'.format(obj),
                    'return': 'support:listcapacitygroups'
                })
    else:
        form = ConfirmForm()
    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Confirm deletion of {}'.format(obj),
            'buttontext': 'Delete'
        })
Пример #19
0
def mail_overdue_students(request):
    """
    Mail students that didn't handin file before the deadline

    :param request:
    :return:
    """
    if request.method == "POST":
        form = ConfirmForm(request.POST)
        if form.is_valid():
            mails = []
            for dist in get_distributions(request.user):
                missingtypes = []
                for ftype in FileType.objects.all():
                    if ftype.Deadline >= datetime.today().date():
                        continue  # only mail if the deadline has passed.
                    if dist.files.filter(Type=ftype).count() == 0:
                        missingtypes.append(ftype)
                if len(missingtypes) > 0:
                    mails.append({
                        'template': 'email/overdueprvstudent.html',
                        'email': dist.Student.email,
                        'subject': 'Overdue professional skill delivery',
                        'context': {
                            'student': dist.Student,
                            'project': dist.Proposal,
                            'types': missingtypes,
                        }
                    })

            EmailThreadTemplate(mails).start()

            return render(request, "support/email_progress.html")
    else:
        form = ConfirmForm()

    return render(
        request, 'GenericForm.html', {
            "form": form,
            "formtitle": "Confirm mailing overdue students",
            "buttontext": "Confirm"
        })
Пример #20
0
def students_applied(request):
    """
    Add students who applied in this timeslot to this timeslot

    :param request:
    :return:
    """
    timeslot = get_timeslot()
    if not timeslot:
        return render(
            request,
            'base.html',
            context={
                'Message':
                'There is no current active timeslot. This page is not available.'
            })

    students = User.objects.filter(
        groups=None, applications__Proposal__TimeSlot=timeslot).exclude(
            usermeta__TimeSlot=timeslot).distinct()
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            for s in students:
                s.usermeta.TimeSlot.add(timeslot)
                s.usermeta.save()
            return render(request,
                          'base.html',
                          context={'Message': 'Students added to timeslot.'})
    else:
        form = ConfirmForm()
    return render(
        request, 'timeline/timeslot_applied_users_form.html', {
            'form': form,
            'formtitle': f'Add students to timeslot {timeslot}',
            'students': students,
            'timeslot': timeslot,
            'buttontext': 'Confirm'
        })
Пример #21
0
def osirisToMeta(request):
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            data = osirisData()
            for p in data.getalldata():
                try:
                    user = User.objects.get(email=p.email)
                except User.DoesNotExist:
                    continue
                try:
                    meta = user.usermeta
                except UserMeta.DoesNotExist:
                    meta = UserMeta()
                    user.usermeta = meta
                    meta.save()
                    user.save()
                if p.automotive:
                    meta.Study = 'Automotive'
                else:
                    meta.Study = 'Eletrical Engineering'
                meta.Cohort = p.cohort
                meta.ECTS = p.ects
                meta.Studentnumber = p.idnumber
                meta.save()
            return render(request, 'base.html', {
                'Message': 'usermeta updated!',
                'return': 'osirisdata:list'
            })
    else:
        form = ConfirmForm()

    return render(
        request, 'GenericForm.html', {
            'form': form,
            'formtitle': 'Confirm rewrite usermeta',
            'buttontext': 'Confirm'
        })
Пример #22
0
def mail_distributions(request):
    """
    Mail all distributions to affected users

    :param request:
    :return:
    """
    if get_timephase_number() < 4 or get_timephase_number() > 5:
        # mailing is possible in phase 4 or 5
        raise PermissionDenied('Mailing distributions is not possible in this timephase')

    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            mails = []
            ts = get_timeslot()
            # iterate through projects, put students directly in the mail list
            for prop in get_all_proposals().filter(Q(distributions__isnull=False)):
                for dist in prop.distributions.filter(TimeSlot=ts):
                    mails.append({
                        'template': 'email/studentdistribution.html',
                        'email': dist.Student.email,
                        'subject': 'distribution',
                        'context': {
                            'project': prop,
                            'student': dist.Student,
                        }
                    })

            # iterate through all assistants and responsible
            for usr in get_all_staff().filter(Q(groups__name='type1staff') | Q(groups__name='type2staff')):
                if usr.proposals.filter(TimeSlot=get_timeslot()).exists():
                    mails.append({
                        'template': 'email/assistantdistribution.html',
                        'email': usr.email,
                        'subject': 'distribution',
                        'context': {
                            'supervisor': usr,
                            'projects': usr.proposals.filter(TimeSlot=get_timeslot()).distinct(),
                        }
                    })
                if usr.proposalsresponsible.filter(TimeSlot=get_timeslot()).exists():
                    mails.append({
                        'template': 'email/supervisordistribution.html',
                        'email': usr.email,
                        'subject': 'distribution',
                        'context': {
                            'supervisor': usr,
                            'projects': usr.proposalsresponsible.filter(TimeSlot=get_timeslot()).distinct(),
                        }
                    })

            # UNCOMMENT THIS TO MAIL NOT_DISTRIBUTED STUDENTS WITH 'action required'
            # for usr in get_all_students().filter(distributions__isnull=True):
            #     mails.append({
            #         'template': 'email/studentnodistribution.html',
            #         'email': usr.email,
            #         'subject': 'action required',
            #         'context': {
            #             'student': usr,
            #         }
            #     })
            EmailThreadTemplate(mails).start()

            return render(request, 'support/email_progress.html')
    else:
        form = ConfirmForm()

    return render(request, 'GenericForm.html', {
        'form': form,
        'formtitle': 'Confirm mailing distributions',
        'buttontext': 'Confirm'
    })
Пример #23
0
def automatic(request, dist_type, distribute_random=1, automotive_preference=1):
    """
    After automatic distribution, this pages shows how good the automatic distribution is. At this point a type3staff
     member can choose to apply the distributions. Later, this distribution can be edited using manual distributions.

    :param request:
    :param dist_type: which type automatic distribution is used.
    :param distribute_random: Whether to distribute leftover students to random projects
    :param automotive_preference: Distribute automotive students first to automotive people
    :return:
    """
    if get_timephase_number() < 4 or get_timephase_number() > 5:  # 4 or 5
        raise PermissionDenied('Distribution is not possible in this timephase')

    if int(dist_type) == 1:
        typename = 'calculated by student'
    elif int(dist_type) == 2:
        typename = 'calculated by project'
    else:
        raise PermissionDenied("Invalid type")

    if distribute_random not in [0, 1]:
        raise PermissionDenied("Invalid option type random")
    if automotive_preference not in [0, 1]:
        raise PermissionDenied("Invalid option type automotive")

    dists = []  # list to store actual user and proposal objects, instead of just ID's like distobjs.
    if request.method == 'POST':
        jsondata = request.POST.get('jsondata', None)
        if jsondata is None:
            return render(request, 'base.html', {'Message': 'Invalid POST data'})
        distobjs = json.loads(jsondata)  # json blob with all dists with studentID projectID and preference
        for obj in distobjs:
            dists.append({
                'student': User.objects.get(pk=obj['StudentID']),
                'proposal': get_all_proposals().get(pk=obj['ProjectID']),
                'preference': obj['Preference'],
            })

        form = ConfirmForm(request.POST)
        if form.is_valid():
            # delete all old distributions of this timeslot
            Distribution.objects.filter(TimeSlot=get_timeslot()).delete()
            # save the stuff
            for dist in dists:
                dstdbobj = Distribution()
                dstdbobj.Student = dist['student']
                dstdbobj.Proposal = dist['proposal']
                dstdbobj.TimeSlot = get_timeslot()
                if dist['preference'] > 0:
                    try:
                        dstdbobj.Application = \
                            Application.objects.filter(Q(Student=dist['student']) &
                                                       Q(Proposal=dist['proposal']) &
                                                       Q(Priority=dist['preference']) &
                                                       Q(Proposal__TimeSlot=get_timeslot()))[0]
                    except Application.DoesNotExist:
                        dstdbobj.Application = None
                else:
                    dstdbobj.Application = None
                dstdbobj.save()

            return render(request, 'base.html', {
                'Message': 'Distributions saved!',
                'return': 'distributions:SupportListApplicationsDistributions',
            })

    else:
        # Handle the most common errors beforehand with a nice message
        error_list = ''
        stds = get_all_students().filter(personal_proposal__isnull=False, personal_proposal__TimeSlot=get_timeslot()).distinct().prefetch_related('personal_proposal')
        for u in stds:  # all private students
            ps = u.personal_proposal.filter(TimeSlot=get_timeslot())
            if ps.count() > 1:  # more than one private proposal.
                error_list += "<li>User {} has multiple private proposals ({}). Please resolve this!</li>" \
                    .format(u, print_list(ps))
            if ps.first().Status != 4:
                error_list += "<li>User {} has private proposals which is not yet public. Please upgrade proposal {} to public!</li>" \
                    .format(u, ps.first())
        if error_list:
            return render(request, 'base.html', {
                'Message': '<h1>Automatic distribution cannot start</h1><p>The following error(s) occurred:</p><ul>{}</ul>'
                          .format(error_list),
                'return': 'distributions:SupportListApplicationsDistributions',
            })

        form = ConfirmForm()
        # run the algorithms
        # catch any remaining errors of the algorithm with a broad exception exception.
        try:
            if int(dist_type) == 1:  # from student
                distobjs = distribution.calculate_1_from_student(
                    distribute_random=distribute_random,
                    automotive_preference=automotive_preference)
            elif int(dist_type) == 2:  # from project
                distobjs = distribution.calculate_2_from_project(
                    distribute_random=distribute_random,
                    automotive_preference=automotive_preference)
        # invalid types are catched at the begin of the function
        except Exception as e:
            return render(request, "base.html", {
                'Message': '<h1>Automatic distribution cannot start</h1><p>The following error(s) occurred:</p>{}'
                          .format(e)})

    # convert to django models from db
    # and make scatter chart data
    scatter = []
    undistributed = list(get_all_students())  # get all students and remove all distributed students later.
    for obj in distobjs:  # distobjs is saved as json blob in the view, to use later on for distributions.
        student = get_all_students().get(pk=obj.StudentID)
        undistributed.remove(student)  # remove distributed student from undistributed students list.
        scatter.append({  # data for scatterplot ECTS vs Preference
            'x': student.usermeta.ECTS,
            'y': obj.Preference,
        })
        try:
            proposal = get_all_proposals().get(pk=obj.ProjectID)
        except Proposal.DoesNotExist:
            raise Exception("Proposal id {} cannot be found in all_proposals!".format(obj.ProjectID))
        dists.append({
            'student': student,
            'proposal': proposal,
            'preference': obj.Preference,
        })
    # show undistributed students also in the table. (Not added to json blob)
    for obj in undistributed:
        dists.append({
            'student': obj,
            'proposal': None,
            'preference': 'Not distributed. ' + ('With' if obj.applications.filter(Proposal__TimeSlot=get_timeslot()).exists() else 'No') + ' applications.',
        })

    cohorts = distribution.get_cohorts()

    # make headers for table and find cohort for each student.
    columns = ['Total']
    # calculate stats per cohort
    prefs = {  # first column with total
        'Total': [obj['preference'] for obj in dists if obj['proposal'] is not None],
    }
    for c in cohorts:  # all next columns in table for each cohort. First column is totals.
        columns.append(c)
        prefs[c] = []
        for obj in dists:
            if obj['proposal'] is not None:  # do not count not-distributed students in the stats.
                if obj['student'].usermeta.Cohort == int(c):
                    prefs[c].append(obj['preference'])  # list of all individual preferences in this cohort

    # make a table
    table = []
    pref_options = list(range(-1, settings.MAX_NUM_APPLICATIONS + 1))  # all options for preference.
    for pref in pref_options:  # each preference, each row.
        # set first column
        if pref == -1:  # random distributed students.
            this_row = ['Random']
        elif pref == 0:
            this_row = ['Private']  # private proposals
        else:
            this_row = ['#' + str(pref), ]  # application preference
        # set other columns
        for c in columns:  # add columns to the row.
            num = prefs[c].count(pref)
            try:
                this_row.append('{}% ({})'.format(round(num / len(prefs[c]) * 100), num))
            except ZeroDivisionError:
                this_row.append('{}% ({})'.format(0, 0))

        # add row the the table
        table.append(this_row)
    # one but last row with totals.
    this_row = ['Total Distributed']
    for c in columns:
        this_row.append(len(prefs[c]))
    table.append(this_row)

    # last row with undistributed.
    this_row = ['Not Distributed', len(undistributed)]
    for c in columns[1:]:  # skip total column, is already added.
        this_row.append(len([u for u in undistributed if u.usermeta.Cohort == c]))
    table.append(this_row)

    # show the tables for testing.
    if settings.TESTING:
        return columns, table, dists

    data = [obj.as_json() for obj in distobjs]
    return render(request, 'distributions/automatic_distribute.html', {
        'typename': typename,
        'distributions': dists,
        'form': form,
        'stats': table,
        'stats_header': columns,
        'scatter': scatter,
        'jsondata': json.dumps(data),
        'distribute_random': distribute_random,
        'automotive_preference': automotive_preference,
    })