Exemple #1
0
def get_valid_proposals():
    """
    Get all proposals that (non-private) students can be distributed to.

    :return:
    """
    return get_all_proposals().filter(Q(Status=4) & Q(Private__isnull=True))
Exemple #2
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 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,
        })
Exemple #4
0
def mail_track_heads_pending(stdout=False):
    """
    Mail track heads with their pending proposals. Can be used in an automated script with logging. In that case
     stdout can be set to True.

    :param stdout:
    :return:
    """
    for track in Track.objects.all():
        if stdout:
            print("Track: " + track.Name)
        if track.Head is not None:
            proposals = get_all_proposals().filter(
                Q(Status=3) & Q(Track=track))
            if proposals.count() > 0:
                e = track.Head.email
                context = {
                    'proposals': proposals,
                    'track': track,
                }
                send_mail("action required for track head",
                          "email/action_required_trackhead_email.html",
                          context, e)
                if stdout:
                    print("Sending proposals: " + str(list(proposals)))
            else:
                if stdout:
                    print("Track has no status 3 pending")
        else:
            if stdout:
                print("Track has no head")
Exemple #5
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})
Exemple #6
0
def list_published_api(request):
    """
    JSON list of all published proposals with some detail info.

    :param request:
    :return:
    """
    props = get_all_proposals().filter(Q(Status=4) & Q(Private__isnull=True))
    prop_list = []
    for prop in props:
        prop_list.append({
            "id":
            prop.id,
            "detaillink":
            reverse("proposals:details", args=[prop.id]),
            "title":
            prop.Title,
            "group":
            prop.Group.ShortName,
            "track":
            str(prop.Track),
            "reponsible":
            str(prop.ResponsibleStaff),
            "assistants": [str(u) for u in list(prop.Assistants.all())],
        })
    return JsonResponse(prop_list, safe=False)
Exemple #7
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
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
                      })
Exemple #9
0
def list_public_projects_titles_api(request):
    """
    Get all public proposals (=status 4) titles as JSON

    :param request:
    :return: JSON response
    """
    data = {}

    for prop in get_all_proposals().filter(Q(Status=4) & Q(Private__isnull=True)):
        data[prop.id] = prop.Title

    return JsonResponse(data)
Exemple #10
0
def get_pending_tag(user):
    """

    :param user:
    :return:
    """
    # <button> inside <a> is invalid HTML5. MetroUI does not work well with loading-pulse on non-button, so keep it.
    html = "<a href='" + reverse(
        "proposals:pending"
    ) + "'><button class=\"button danger loading-pulse\">Pending: {}</button></a>"
    num = 0
    if get_grouptype("2") in user.groups.all():
        num += get_all_proposals().filter(
            Q(Assistants__id=user.id) & Q(Status__exact=1)).count()
    if get_grouptype("1") in user.groups.all():
        num += get_all_proposals().filter(
            Q(ResponsibleStaff=user.id) & Q(Status__exact=2)).count()
    num += get_all_proposals().filter(
        Q(Track__Head=user.id) & Q(Status__exact=3)).count()
    if num == 0:
        return "No pending projects for your attention"
    else:
        return format_html(html.format(num))
Exemple #11
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})
Exemple #12
0
def list_public_projects_api(request):
    """
    Return all public proposals (=type 4) ordered by group as JSON

    :param request:
    :return: JSON response
    """
    data = {}

    for group in CapacityGroup.objects.all():
        data[group.ShortName] = {
            "name": group.ShortName,
            "projects": [prop.id for prop in
                         get_all_proposals().filter(Q(Status=4) & Q(Group=group) & Q(Private__isnull=True))]
        }

    return JsonResponse(data)
Exemple #13
0
def mailing(request, pk=None):
    """
    Mailing list to mail users.

    :param request:
    :param pk: optional key of a mailing template
    :return:
    """
    if request.method == 'POST':
        form = ChooseMailingList(
            request.POST,
            staff_options=mail_staff_options,
            student_options=mail_student_options,
        )
        if form.is_valid():
            recipients_staff = set()
            recipients_students = set()
            # staff
            if form.cleaned_data['SaveTemplate']:
                t = MailTemplate(
                    RecipientsStaff=json.dumps(form.cleaned_data['Staff']),
                    RecipientsStudents=json.dumps(
                        form.cleaned_data['Students']),
                    Message=form.cleaned_data['Message'],
                    Subject=form.cleaned_data['Subject'],
                )
                t.save()

            ts = form.cleaned_data['TimeSlot']
            for s in form.cleaned_data['Staff']:
                try:
                    # staff selected by group
                    recipients_staff.update(
                        Group.objects.get(name=s).user_set.all())
                    if s not in [
                            'type3staff', 'type4staff', 'type5staff',
                            'type6staff'
                    ]:
                        # if user group is not a support type, mail only users with project in this year.
                        for staff in list(recipients_staff):
                            if not staff.proposalsresponsible.filter(
                                    TimeSlot=ts).exists(
                                    ) and not staff.proposals.filter(
                                        TimeSlot=ts).exists():
                                # user has no project in the selected timeslots.
                                recipients_staff.remove(staff)
                except Group.DoesNotExist:
                    # not a group object, staff selected by custom options.
                    projs = get_all_proposals(old=True).filter(TimeSlot=ts)
                    if s == 'staffnonfinishedproj':
                        for proj in projs.filter(Status__lt=3).distinct():
                            recipients_staff.add(proj.ResponsibleStaff)
                            recipients_staff.update(proj.Assistants.all())
                    elif s == 'distributedstaff':
                        for proj in projs.filter(
                                distributions__isnull=False).distinct():
                            recipients_staff.add(proj.ResponsibleStaff)
                            recipients_staff.update(proj.Assistants.all())
                    elif s == 'staffnostudents':
                        for proj in projs.filter(
                                distributions__isnull=True).distinct():
                            recipients_staff.add(proj.ResponsibleStaff)
                            recipients_staff.update(proj.Assistants.all())
                    elif s == 'assessors':
                        dists = Distribution.objects.filter(
                            TimeSlot=ts).distinct()
                        for d in dists:
                            try:
                                recipients_staff.update(
                                    d.presentationtimeslot.Presentations.
                                    Assessors.all())
                            except PresentationTimeSlot.DoesNotExist:
                                continue
                        recipients_staff.update(
                            User.objects.filter(
                                tracks__isnull=False))  # add trackheads
            # students
            students = User.objects.filter(
                Q(usermeta__TimeSlot=ts) & Q(usermeta__EnrolledBEP=True)
                & Q(groups=None))
            for s in form.cleaned_data['Students']:
                if s == 'all':
                    recipients_students.update(students)
                elif s == '10ectsstd':
                    recipients_students.update(
                        students.filter(usermeta__EnrolledExt=False))
                elif s == '15ectsstd':
                    recipients_students.update(
                        students.filter(usermeta__EnrolledExt=True))
                elif s == 'distributedstd':
                    recipients_students.update(
                        students.filter(distributions__isnull=False,
                                        distributions__TimeSlot=ts).distinct())

            # always send copy to admins
            for user in User.objects.filter(is_superuser=True):
                recipients_staff.add(user)
            # always send copy to self
            if request.user not in recipients_students or \
                    request.user not in recipients_staff:
                recipients_staff.update([request.user])

            mailing_obj = Mailing(
                Subject=form.cleaned_data['Subject'],
                Message=form.cleaned_data['Message'],
            )
            mailing_obj.save()
            mailing_obj.RecipientsStaff.set(recipients_staff)
            mailing_obj.RecipientsStudents.set(recipients_students)
            context = {
                'form': ConfirmForm(initial={'confirm': True}),
                'template': form.cleaned_data['SaveTemplate'],
                'mailing': mailing_obj,
            }
            return render(request,
                          "support/email_confirm.html",
                          context=context)
    else:
        initial = None
        if pk:
            template = get_object_or_404(MailTemplate, pk=pk)
            initial = {
                'Message': template.Message,
                'Subject': template.Subject,
                'Staff': json.loads(template.RecipientsStaff),
                'Students': json.loads(template.RecipientsStudents),
            }
        form = ChooseMailingList(initial=initial,
                                 staff_options=mail_staff_options,
                                 student_options=mail_student_options)
    return render(
        request, "GenericForm.html", {
            "form": form,
            "formtitle": "Send mailing list",
            "buttontext": "Go to confirm",
        })
Exemple #14
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,
    })
Exemple #15
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'
    })
Exemple #16
0
        p.StudentsTaskDescription = "students have to do stuff woop woop"
        p.Track = random.choice(trackobjs)
        # p.Private = None
        # p.Image = random.choice(["niels.png", "crying.png"])
        # p.Status = random.choice(Proposal.StatusOptions)[0]
        p.Status = 4
        p.TimeSlot = get_timeslot()
        p.save()  # save already to activate the manytomany field of assistants
        numphd = random.choice([1, 2])
        ass1 = random.choice(phds)
        p.Assistants.add(ass1)
        if numphd == 2:
            phds.remove(ass1)
            ass2 = random.choice(phds)
            p.Assistants.add(ass2)
            phds.append(ass1)
        p.save()
        print('{} created'.format(p))
    except:
        print(str(i) + "th proposal not created")

print("generating applications")
secure_random = random.SystemRandom()
projects = get_all_proposals()
for i in range(0, NUMSTDS):
    for c in range(1, settings.MAX_NUM_APPLICATIONS + 1):
        app = Application(Priority=c,
                          Proposal=secure_random.choice(projects),
                          Student=User.objects.get(username='******'.format(i)))
        app.save()