Newer
Older
from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from sqlalchemy import and_
from app.constants import DEADLINE_FOR_STAFF_CHANGES
from app.forms.project import ProjectForm
from app.models.category import Category
from app.models.person import Person
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")

Andrea Callia D'Iddio
committed
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()
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
Andrea Callia D'Iddio
committed
own_projects: list[Project] = (
Project.query.filter_by(proposer=current_user.username)
.filter(Project.category.in_(category_codes))
.filter(Project.deleted.is_(None))
.all()
)
Andrea Callia D'Iddio
committed
other_projects: list[Project] = (
Project.query.filter(Project.proposer != current_user.username)
.filter(Project.category.in_(category_codes))
Andrea Callia D'Iddio
committed
.filter(Project.deleted.is_(None))
.all()
)
return render_template(
Andrea Callia D'Iddio
committed
"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
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.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,

Andrea Callia D'Iddio
committed
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
),

Andrea Callia D'Iddio
committed
proposed_start_date=form.proposed_start_date.data,
duration_in_weeks=form.duration_in_weeks.data,
)
db.session.add(project)
db.session.commit()

Andrea Callia D'Iddio
committed
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")
@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
Andrea Callia D'Iddio
committed
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
# 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.category.choices = [(c.code, c.name) for c in categories]

Andrea Callia D'Iddio
committed
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

Andrea Callia D'Iddio
committed
if project.on_behalf and on_behalf_changed:
shortlist_proposer(project)
return redirect(url_for("staff.projects"))
form = ProjectForm(obj=project)

Andrea Callia D'Iddio
committed
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"))
Andrea Callia D'Iddio
committed
@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")
Andrea Callia D'Iddio
committed
@login_required
Andrea Callia D'Iddio
committed
def projects_ranking():
own_projects: list[Shortlisting] = (
Andrea Callia D'Iddio
committed
Shortlisting.query.join(Project)
.filter(Project.proposer == current_user.username)
.order_by(Shortlisting.staff_ranking)
Andrea Callia D'Iddio
committed
.all()
)
full_names = {}
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",
full_names=full_names,
)
@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
db.session.commit()
return "", 204