Skip to content
Snippets Groups Projects
Commit 3a0af9ee authored by Andrea Callia D'Iddio's avatar Andrea Callia D'Iddio Committed by Andrea Callia D'Iddio
Browse files

Merge branch 'new-project-details' into 'master'

Feat: support new fields for meeting mode, proposed start date and duration.

See merge request !15
parents f13264d6 5176b1a4
No related branches found
No related tags found
No related merge requests found
Pipeline #373087 passed
......@@ -34,7 +34,7 @@ flask populate_dev
flask run
```
Then browse to <http://localhost:5000>.
Then browse to URL <http://localhost:5000>.
## Running the tests
......@@ -52,4 +52,4 @@ python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
pytest
```
\ No newline at end of file
```
......@@ -4,4 +4,4 @@ STUDENT = "student"
STAFF = "staff"
DEADLINE_FOR_STUDENT_CHANGES = datetime(2023, 2, 9, 19, 0)
DEADLINE_FOR_STAFF_CHANGES = datetime(2023, 2, 9, 19, 0)
DEADLINE_FOR_STAFF_CHANGES = datetime(2025, 2, 9, 19, 0)
from flask_wtf import FlaskForm
from wtforms import BooleanField, SelectField, StringField, TextAreaField
from wtforms import (
BooleanField,
DateField,
IntegerField,
SelectField,
StringField,
TextAreaField,
validators,
)
from wtforms.validators import DataRequired, ValidationError
......@@ -9,6 +17,18 @@ class ProjectForm(FlaskForm):
is_student_proposal = BooleanField("Student Proposal", default=False)
on_behalf = StringField("from", default=None)
category = SelectField("Category", coerce=str)
meeting_mode = SelectField(
"Meeting type",
choices=[("", "N/A"), ("remote", "Remote"), ("in-person", "In person")],
coerce=str,
validators=[validators.Optional()],
)
proposed_start_date = DateField(
"Proposed start date", validators=[validators.Optional()]
)
duration_in_weeks = IntegerField(
"Duration (in weeks)", validators=[validators.Optional()]
)
def validate_on_behalf(self, field):
if self.is_student_proposal.data and not field.data:
......@@ -20,3 +40,19 @@ class ProjectForm(FlaskForm):
if field == self.category and field.data == "":
self.category.errors += "Please choose a category for the project."
raise ValidationError("Please choose a category for the project.")
def validate_meeting_mode(self, field):
if field == self.category and field.data not in ["", "remote", "in-person"]:
self.meeting_mode.errors += "Invalid meeting mode."
raise ValidationError(
"The meeting mode for the project is invalid. Choose a valid value or leave it blank."
)
def validate_duration(self, field):
if all(
(field == self.duration_in_weeks, field.data is not None, field.data < 0)
):
self.meeting_mode.errors += "Invalid duration."
raise ValidationError(
"The duration for the project is invalid. Choose a valid value or leave it blank."
)
......@@ -4,6 +4,8 @@ from ..database import db
class Category(db.Model):
code = db.Column(db.String, primary_key=True)
title = db.Column(db.String)
deadline_for_student_changes = db.Column(db.DateTime)
deadline_for_staff_change = db.Column(db.DateTime)
@property
def name(self) -> str:
......
......@@ -10,6 +10,9 @@ class Project(db.Model):
title = db.Column(db.String)
description = db.Column(db.String)
category = db.Column(db.String, db.ForeignKey(Category.code), nullable=False)
is_remote = db.Column(db.Boolean)
proposed_start_date = db.Column(db.Date)
duration_in_weeks = db.Column(db.Integer)
timestamp = db.Column(db.DateTime, nullable=False, server_default=utcnow())
deleted = db.Column(db.DateTime)
......
......@@ -18,6 +18,15 @@
<section class="w3-section">
{{ form.category.label }} {{ form.category() }}
</section>
<section class="w3-section">
{{ form.meeting_mode.label }} {{ form.meeting_mode() }}
</section>
<section class="w3-section">
{{ form.proposed_start_date.label }} {{ form.proposed_start_date() }}
</section>
<section class="w3-section">
{{ form.duration_in_weeks.label }} {{ form.duration_in_weeks() }}
</section>
<section class="w3-section">
<div class="w3-quarter">
{{ form.is_student_proposal(class="w3-check", id="is-student-proposal") }}&ensp;{{ form.is_student_proposal.label }}
......
......@@ -19,6 +19,15 @@
{{ project_details_section('Title', project.title) }}
{{ project_details_section('Proposer', person.full_name if person.full_name else person.username) }}
{{ project_details_section('Description', project.description) }}
{% if project.is_remote %}
{{ project_details_section('Meeting mode', "Remote" if project.is_remote else "In person") }}
{% endif %}
{% if project.proposed_start_date %}
{{ project_details_section('Proposed start date', project.proposed_start_date) }}
{% endif %}
{% if project.duration_in_weeks %}
{{ project_details_section('Duration in weeks', project.duration_in_weeks) }}
{% endif %}
</div>
</div>
......
......@@ -19,6 +19,15 @@
{{ project_details_section('Title', project.title) }}
{{ project_details_section('Proposer', person.full_name if person.full_name else person.username) }}
{{ project_details_section('Description', project.description) }}
{% if project.is_remote %}
{{ project_details_section('Meeting mode', "Remote" if project.is_remote else "In person") }}
{% endif %}
{% if project.proposed_start_date %}
{{ project_details_section('Proposed start date', project.proposed_start_date) }}
{% endif %}
{% if project.duration_in_weeks %}
{{ project_details_section('Duration in weeks', project.duration_in_weeks) }}
{% endif %}
</div>
</div>
......
......@@ -85,6 +85,13 @@ def create_project():
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
),
proposed_start_date=form.proposed_start_date.data,
duration_in_weeks=form.duration_in_weeks.data,
)
db.session.add(project)
db.session.commit()
......@@ -159,6 +166,7 @@ def edit_project(project_id):
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.category.choices = [(c.code, c.name) for c in categories]
if project.on_behalf is not None:
......
"""Allow setting specific deadlines for each category.
Revision ID: 272f89c13a48
Revises: 4bcd9d51d569
Create Date: 2023-02-10 12:54:48.679922
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "272f89c13a48"
down_revision = "4bcd9d51d569"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"category",
sa.Column("deadline_for_student_changes", sa.DateTime(), nullable=True),
)
op.add_column(
"category", sa.Column("deadline_for_staff_change", sa.DateTime(), nullable=True)
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("category", "deadline_for_staff_change")
op.drop_column("category", "deadline_for_student_changes")
# ### end Alembic commands ###
"""Change duration to be specified in weeks.
Revision ID: 4bcd9d51d569
Revises: 6b9a5c131e23
Create Date: 2023-02-10 12:36:30.753392
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "4bcd9d51d569"
down_revision = "6b9a5c131e23"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"project", sa.Column("duration_in_weeks", sa.Integer(), nullable=True)
)
op.drop_column("project", "duration")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"project",
sa.Column(
"duration", postgresql.INTERVAL(), autoincrement=False, nullable=True
),
)
op.drop_column("project", "duration_in_weeks")
# ### end Alembic commands ###
"""Add new project details.
Revision ID: 6b9a5c131e23
Revises: e794d182f9d3
Create Date: 2023-02-10 11:22:43.453863
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "6b9a5c131e23"
down_revision = "e794d182f9d3"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("project", sa.Column("is_remote", sa.Boolean(), nullable=True))
op.add_column("project", sa.Column("proposed_start_date", sa.Date(), nullable=True))
op.add_column("project", sa.Column("duration", sa.Interval(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("project", "duration")
op.drop_column("project", "proposed_start_date")
op.drop_column("project", "is_remote")
# ### end Alembic commands ###
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment