Skip to content
Snippets Groups Projects
staff.py 8.84 KiB
Newer Older
from datetime import datetime

Ivan Procaccini's avatar
Ivan Procaccini committed
from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required
Ivan Procaccini's avatar
Ivan Procaccini committed
from app import messages
from app.constants import DEADLINE_FOR_STAFF_CHANGES
Ivan Procaccini's avatar
Ivan Procaccini committed
from app.database import db
from app.forms.project import ProjectForm
from app.models.category import Category
from app.models.person import Person
Ivan Procaccini's avatar
Ivan Procaccini committed
from app.models.project import Project
from app.models.shortlist import Shortlisting
from app.utils.decorators import only_before_deadline_for_staff, staff_only
bp = Blueprint("staff", __name__, url_prefix="/staff")


def shortlist_proposer(project):
    shortlisting = Shortlisting(student=project.on_behalf, project_id=project.id)
    db.session.add(shortlisting)
    db.session.commit()


def unshortlist_old_proposer(project: Project):
    shortlisting: Shortlisting = (
        Shortlisting.query.filter(Shortlisting.project_id == project.id)
        .filter_by(student=project.on_behalf)
        .first()
    )
    if shortlisting:
        db.session.delete(shortlisting)
        db.session.commit()


@bp.route("/projects")
    category_codes: list[str] = (
        Person.query.filter_by(username=current_user.username).first().categories
    )
    allow_edit = True if datetime.utcnow() <= DEADLINE_FOR_STAFF_CHANGES else False
    own_projects: list[Project]
        Project.query.filter_by(proposer=current_user.username)
        .filter(Project.category.in_(category_codes))
        .filter(Project.deleted.is_(None))
        .all()
    )
    other_projects: list[Project] = (
        Project.query.filter(Project.proposer != current_user.username)
        .filter(Project.category.in_(category_codes))
        "pages/staff/projects.html",
        own_projects=own_projects,
        other_projects=other_projects,
        allow_edit=allow_edit,
@bp.route("/projects/create", methods=["GET", "POST"])
@only_before_deadline_for_staff
def create_project():
    category_codes: list[str] = (
        Person.query.filter_by(username=current_user.username).first().categories
    )
    categories: list[Category] = Category.query.filter(
        Category.code.in_(category_codes)
    ).all()
    form = ProjectForm()
    form.category.choices = [("", "Choose an option")] + [
        (c.code, c.name) for c in categories
    ]
    if form.validate_on_submit():
        project = Project(
            title=form.title.data,
            description=form.description.data,
            background_skills=form.background_skills.data
            if form.background_skills.data not in [None, ""]
            else None,
            proposer=current_user.username,
            on_behalf=form.on_behalf.data if form.is_student_proposal.data else None,
            category=form.category.data,
            is_remote=(
                form.meeting_mode.data == "remote"
                if form.meeting_mode.data is not None
                else None
            ),
            is_part_time=(
                form.time_commitment.data == "part-time"
                if form.time_commitment.data is not None
                else None
            ),
            is_lab_based=(
                form.lab_usage.data == "lab-based"
                if form.lab_usage.data is not None
                else None
            ),
            proposed_start_date=form.proposed_start_date.data,
            duration_in_weeks=form.duration_in_weeks.data,
        )
        db.session.add(project)
        db.session.commit()
        if project.on_behalf:
            shortlist_proposer(project)
        return redirect(url_for("staff.projects"))
    elif request.method == "POST":
        if form.category.data not in category_codes:
            flash(messages.INVALID_CATEGORY)
    return render_template(
        "pages/project-form.html", form=form, person=Person(), categories=categories
    )
@bp.route("/projects/<project_id>/delete")
@login_required
@only_before_deadline_for_staff
def delete_project(project_id):
    can_delete = True
    category_codes: list[str] = (
        Person.query.filter_by(username=current_user.username).first().categories
    )
    project = Project.query.get(project_id)
    if not project:
        can_delete = False
    elif project.category not in category_codes:
        can_delete = False
    elif project.proposer != current_user.username:
        can_delete = False
        project.deleted = datetime.utcnow()
        db.session.commit()
    else:
        flash(messages.PROJECT_NOT_FOUND)
    return redirect(url_for("staff.projects"))


@bp.route("/projects/<project_id>/edit", methods=["GET", "POST"])
@login_required
@only_before_deadline_for_staff
def edit_project(project_id):
    # show_box is a boolean used to decide if we show the text box for student full name. by default this is not shown
    show_box = False
    person = None
    can_edit = True
    category_codes: list[str] = (
        Person.query.filter_by(username=current_user.username).first().categories
    )
    categories: list[Category] = Category.query.filter(
        Category.code.in_(category_codes)
    ).all()
    project = Project.query.get(project_id)
    if not project:
        can_edit = False
    elif project.category not in category_codes:
        can_edit = False
    if can_edit:
        form = ProjectForm()
        form.category.choices = [(c.code, c.name) for c in categories]
        on_behalf_changed = project.on_behalf != form.on_behalf.data
        if on_behalf_changed and project.on_behalf:
            unshortlist_old_proposer(project)
        if form.validate_on_submit():
            project.title = form.title.data
            project.description = form.description.data
            project.on_behalf = (
                form.on_behalf.data if form.is_student_proposal.data else None
            )
            project.category = form.category.data
            db.session.commit()
            if project.on_behalf and on_behalf_changed:
                shortlist_proposer(project)
            return redirect(url_for("staff.projects"))
        form = ProjectForm(obj=project)
        form.meeting_mode.data = "remote" if project.is_remote is True else ""
        form.time_commitment.data = "part-time" if project.is_part_time is True else ""
        form.lab_usage.data = "lab-based" if project.is_lab_based is True else ""
        form.category.choices = [(c.code, c.name) for c in categories]

        if project.on_behalf is not None:
            person = Person.query.filter(Person.username == project.on_behalf).first()
            if person:
                show_box = True

        return render_template(
            "pages/project-form.html",
            form=form,
            project=project,
            show_box=show_box,
            person=person,
        )
    flash(messages.PROJECT_NOT_FOUND)
    return redirect(url_for("staff.projects"))
@bp.route("/projects/<project_id>")
@login_required
@staff_only
def view_project(project_id):
    if project := Project.query.get(project_id):
        username = project.proposer

        person = Person.query.filter(Person.username == username).first() or Person(
            username=username, firstname="", lastname=""
        )

        return render_template(
            "pages/staff/project-view.html", project=project, person=person
        )
    flash(messages.PROJECT_NOT_FOUND)
    return redirect(url_for("staff.projects"))


@bp.route("/projects/shortlisted")
    own_projects: list[Shortlisting] = (
        Shortlisting.query.join(Project)
        .filter(Project.proposer == current_user.username)
        .order_by(Shortlisting.staff_ranking)
    for shortlisting in own_projects:
        person = Person.query.filter(
            Person.username == shortlisting.student
        ).first() or Person(username=shortlisting.student, firstname="", lastname="")

        full_names[shortlisting.id] = (
            person.full_name if person.full_name else person.username
        )

    return render_template(
        "pages/staff/shortlisted-projects.html",
        projects=own_projects,


@bp.route("/projects/rankings", methods=["PUT"])
@login_required
@only_before_deadline_for_staff
def update_ranking():
    shortlisting_ids = request.json
    current_shortlist: list[Shortlisting] = Shortlisting.query.join(Project).filter(
        and_(
            Shortlisting.id.in_(shortlisting_ids),
            Project.proposer == current_user.username,
        )
    )
    for shortlisting in current_shortlist:
        shortlisting.staff_ranking = shortlisting_ids.index(shortlisting.id) + 1