Skip to content
Snippets Groups Projects

Support specific deadline for each category of project.

Merged Andrea Callia D'Iddio requested to merge specific-deadline-for-category into master
All threads resolved!
2 files
+ 10
11
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 105
46
@@ -5,14 +5,13 @@ from flask_login import current_user, login_required
from sqlalchemy import and_
from app import messages
from app.constants import DEADLINE_FOR_STAFF_CHANGES
from app.database import db
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
from app.utils.decorators import staff_only
bp = Blueprint("staff", __name__, url_prefix="/staff")
@@ -41,14 +40,21 @@ def 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]
categories: list[Category] = Category.query.filter(
Category.code.in_(category_codes)
).all()
deadlines: dict[str, datetime] = {
c.code: c.deadline_for_staff_change for c in categories
}
own_projects: list[Project] = (
Project.query.filter_by(proposer=current_user.username)
.filter(Project.category.in_(category_codes))
.filter(Project.deleted.is_(None))
.all()
)
allow_edit = {
p.id: datetime.utcnow() <= deadlines[p.category] for p in own_projects
}
other_projects: list[Project] = (
Project.query.filter(Project.proposer != current_user.username)
.filter(Project.category.in_(category_codes))
@@ -60,13 +66,17 @@ def projects():
own_projects=own_projects,
other_projects=other_projects,
allow_edit=allow_edit,
allow_create=datetime.utcnow() <= max(deadlines.values()),
)
def equals_or_none(first, second):
return first == second if first is not None else None
@bp.route("/projects/create", methods=["GET", "POST"])
@login_required
@staff_only
@only_before_deadline_for_staff
def create_project():
category_codes: list[str] = (
Person.query.filter_by(username=current_user.username).first().categories
@@ -74,7 +84,8 @@ def create_project():
categories: list[Category] = Category.query.filter(
Category.code.in_(category_codes)
).all()
form = ProjectForm()
project = Project(proposer=current_user.username)
form = ProjectForm(obj=project)
form.category.choices = [("", "Choose an option")] + [
(c.code, c.name) for c in categories
]
@@ -82,34 +93,23 @@ def create_project():
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,
background_skills=form.background_skills.data,
proposer=current_user.username,
on_behalf=form.on_behalf.data if form.is_student_proposal.data else None,
on_behalf=form.on_behalf.data,
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
),
is_remote=equals_or_none(form.meeting_mode.data, "remote"),
is_part_time=equals_or_none(form.time_commitment.data, "part-time"),
is_lab_based=equals_or_none(form.lab_usage.data, "lab-based"),
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)
if Category.query.get(form.category.data).is_deadline_expired_for_staff:
flash(messages.DEADLINE_EXPIRED_FOR_STAFF)
else:
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:
@@ -122,20 +122,22 @@ def create_project():
@bp.route("/projects/<project_id>/delete")
@login_required
@staff_only
@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
if can_delete:
if (project is not None) and Category.query.get(
project.category
).is_deadline_expired_for_staff:
flash(messages.DEADLINE_EXPIRED_FOR_STAFF)
elif all(
(
(project is not None),
(project.category in category_codes),
(project.proposer == current_user.username),
)
):
project.deleted = datetime.utcnow()
db.session.commit()
else:
@@ -146,12 +148,12 @@ def delete_project(project_id):
@bp.route("/projects/<project_id>/edit", methods=["GET", "POST"])
@login_required
@staff_only
@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
deadline_expired = False
category_codes: list[str] = (
Person.query.filter_by(username=current_user.username).first().categories
)
@@ -163,6 +165,9 @@ def edit_project(project_id):
can_edit = False
elif project.category not in category_codes:
can_edit = False
elif Category.query.get(project.category).is_deadline_expired_for_staff:
can_edit = False
deadline_expired = True
if can_edit:
form = ProjectForm()
form.category.choices = [(c.code, c.name) for c in categories]
@@ -172,18 +177,44 @@ def edit_project(project_id):
if form.validate_on_submit():
project.title = form.title.data
project.description = form.description.data
project.background_skills = form.background_skills.data
project.on_behalf = (
form.on_behalf.data if form.is_student_proposal.data else None
)
project.category = form.category.data
project.is_remote = equals_or_none(form.meeting_mode.data, "remote")
project.is_part_time = equals_or_none(
form.time_commitment.data, "part-time"
)
project.is_lab_based = equals_or_none(form.lab_usage.data, "lab-based")
project.proposed_start_date = form.proposed_start_date.data
project.duration_in_weeks = form.duration_in_weeks.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.meeting_mode.data = (
"remote"
if project.is_remote is True
else "in-person"
if project.is_remote is False
else ""
)
form.time_commitment.data = (
"part-time"
if project.is_part_time is True
else "full-time"
if project.is_part_time is False
else ""
)
form.lab_usage.data = (
"lab-based"
if project.is_lab_based is True
else "non-lab-based"
if project.is_lab_based is False
else ""
)
form.category.choices = [(c.code, c.name) for c in categories]
if project.on_behalf is not None:
@@ -198,7 +229,10 @@ def edit_project(project_id):
show_box=show_box,
person=person,
)
flash(messages.PROJECT_NOT_FOUND)
if deadline_expired:
flash(messages.DEADLINE_EXPIRED_FOR_STAFF)
else:
flash(messages.PROJECT_NOT_FOUND)
return redirect(url_for("staff.projects"))
@@ -231,7 +265,15 @@ def projects_ranking():
.all()
)
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()
full_names = {}
projects_by_category = {c: [] for c in category_codes}
for shortlisting in own_projects:
person = Person.query.filter(
@@ -242,9 +284,13 @@ def projects_ranking():
person.full_name if person.full_name else person.username
)
if shortlisting.project.category in projects_by_category:
projects_by_category[shortlisting.project.category].append(shortlisting)
return render_template(
"pages/staff/shortlisted-projects.html",
projects=own_projects,
projects=projects_by_category,
categories=categories,
full_names=full_names,
)
@@ -252,7 +298,6 @@ def projects_ranking():
@bp.route("/projects/rankings", methods=["PUT"])
@login_required
@staff_only
@only_before_deadline_for_staff
def update_ranking():
shortlisting_ids = request.json
current_shortlist: list[Shortlisting] = Shortlisting.query.join(Project).filter(
@@ -261,7 +306,21 @@ def update_ranking():
Project.proposer == current_user.username,
)
)
deadline_errors = False
for shortlisting in current_shortlist:
shortlisting.staff_ranking = shortlisting_ids.index(shortlisting.id) + 1
current_ranking = shortlisting.staff_ranking
new_ranking = shortlisting_ids.index(shortlisting.id) + 1
if Category.query.get(
Project.query.get(shortlisting.project_id).category
).is_deadline_expired_for_staff:
if current_ranking != new_ranking:
deadline_errors = True
else:
shortlisting.staff_ranking = new_ranking
if deadline_errors:
flash(messages.DEADLINE_EXPIRED_FOR_STAFF)
return redirect(url_for(f"{current_user.role}.projects"))
else:
flash(messages.SUCCESSFUL_STAFF_RANKING_UPDATE)
db.session.commit()
return "", 204
Loading