add black, isort and flake8 package, format code, fix examination schedule and enrollments functionality, add chairman of committee field for TermOfDefence entity

This commit is contained in:
dominik24c 2023-01-14 17:38:03 +01:00
parent 038f9da93d
commit 40274dc8d7
62 changed files with 2711 additions and 1428 deletions

View File

@ -25,8 +25,24 @@ Run tests
```bash
pytest
```
Run all tests in specific python module
```bash
pytest ./tests/unit_tests/test_file.py
```
Run one test inside of python module
```bash
pytest ./tests/unit_tests/test_file.py::test_function_name
```
***
### Format code:
```bash
black path/to/file.py
isort path/to/file.py
flake8
```
### Useful commands:
Add new package
```bash
@ -45,15 +61,19 @@ flask startapp NAME_OF_APP
```
Above command create package structure:
\
Create serializer in `__schemas__.py` file:
```python3
class ExampleSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = MODEL_NAME
Create serializer in `schemas.py` file:
```python3
from marshmallow import Schema, fields
class ExampleSchema(Schema):
id = fields.Integer()
name = fields.Str()
```
\
Create models in `__models__.py` file:
Create models in `models.py` file:
```python3
from ..dependencies import db
class Example(db.Model):
__tablename__ = "examples"
@ -61,7 +81,7 @@ class Example(db.Model):
title = db.Column(db.String(255), unique=True)
```
\
Create api routes in ```__routes__.py``` file. You only have to register blueprint in `app/__init__.py`.
Create api routes in ```routes.py``` file. You only have to register blueprint in `app/__init__.py`.
```python3
from flask import Blueprint, make_response, jsonify, Response

View File

@ -1,26 +1,26 @@
import os
from apiflask import APIFlask
from flask_migrate import Migrate
from flask_cors import CORS
from flask_migrate import Migrate
from .api import api_bp
from .commands.clear_db import clear_db
from .commands.startapp import startapp
from .config import config
from .dependencies import db, ma
from .commands.startapp import startapp
from .commands.clear_db import clear_db
from .utils import import_models
from .api import api_bp
from .errors import register_error_handlers
from .utils import import_models
def create_app(config_name: str = '') -> APIFlask:
def create_app(config_name: str = "") -> APIFlask:
if config_name is None:
config_name = os.environ.get("FLASK_ENV")
app = APIFlask(__name__, docs_path='/')
app = APIFlask(__name__, docs_path="/")
app.config.from_object(config.get(config_name) or config.get("development"))
if app.config['ENABLE_CORS']:
if app.config["ENABLE_CORS"]:
CORS(app)
db.init_app(app)

View File

@ -1,10 +1,11 @@
from flask import Blueprint
from .coordinator.routes import bp as coordinator_bp
from .examination_schedule.routes import bp as examination_schedules_bp
from .project_supervisor.routes import bp as project_supervisor_bp
from .students.routes import bp as students_bp
from .examination_schedule.routes import bp as examination_schedules_bp
api_bp = Blueprint('api', __name__, url_prefix='/api')
api_bp = Blueprint("api", __name__, url_prefix="/api")
# register blueprints here
api_bp.register_blueprint(coordinator_bp)

View File

@ -2,6 +2,21 @@ from enum import Enum
class ModeGroups(str, Enum):
STATIONARY = 's'
NON_STATIONARY = 'n'
ENGLISH_SPEAKING_STATIONARY = 'e'
STATIONARY = "s"
NON_STATIONARY = "n"
ENGLISH_SPEAKING_STATIONARY = "e"
class EnrollmentsMode(str, Enum):
"""
What means specific values?:
INIT - project supervisor set your availability time for this examination
schedule, students cannot use examination schedule
OPEN - students can assign to term of defence, project supervisor cannot
use examination schedule
CLOSE - students and project supervisor cannot use examination schedule
"""
INIT = "i"
OPEN = "o"
CLOSE = "c"

View File

@ -1,5 +1,5 @@
from marshmallow import fields, Schema
from marshmallow import Schema, fields
class MessageSchema(Schema):
message = fields.Str()
message = fields.Str()

View File

@ -10,11 +10,13 @@ class PaginationResponse(TypedDict):
max_pages: int
def order_by_column_name(query: BaseQuery, model_field: str, order_by_col_name: Union[str, None]) -> BaseQuery:
def order_by_column_name(
query: BaseQuery, model_field: str, order_by_col_name: Union[str, None]
) -> BaseQuery:
if order_by_col_name is not None:
if order_by_col_name == 'asc':
if order_by_col_name == "asc":
query = query.order_by(model_field)
elif order_by_col_name == 'desc':
elif order_by_col_name == "desc":
query = query.order_by(desc(model_field))
return query
@ -27,12 +29,12 @@ def paginate_models(page: int, query: BaseQuery, per_page=10) -> PaginationRespo
else:
query = query.paginate(page=default_page, per_page=per_page, error_out=False)
return {
'items': query.items,
'max_pages': query.pages
}
return {"items": query.items, "max_pages": query.pages}
def is_allowed_extensions(filename: str):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']
return (
"." in filename
and filename.rsplit(".", 1)[1].lower()
in current_app.config["ALLOWED_EXTENSIONS"]
)

View File

@ -1,10 +1,10 @@
from flask.cli import with_appcontext
from click import command
from flask.cli import with_appcontext
from ..dependencies import db
@command('clear_db')
@command("clear_db")
@with_appcontext
def clear_db() -> None:
"""Clear database"""

View File

@ -1,9 +1,9 @@
import os
import re
from flask import current_app
from click import command, argument
from click import argument, command
from click.exceptions import ClickException
from flask import current_app
from flask.cli import with_appcontext
@ -14,7 +14,7 @@ def startapp(name: str) -> None:
"""Create the application structure"""
if not re.match("^[a-zA-Z].*$", name):
raise ClickException(f"The name argument must be type of string!")
raise ClickException("The name argument must be type of string!")
app_dir = current_app.config["SRC_DIR"] / name
if os.path.exists(app_dir):
@ -26,12 +26,14 @@ def startapp(name: str) -> None:
Below you write a schema of model
Example:
from marshmallow import Schema
class ExampleSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = MODEL_NAME
\'\'\'
from ..dependencies import ma
from marshmallow import Schema
"""
model_content = """\'\'\'
@ -43,8 +45,7 @@ class Example(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(255), unique=True)
\'\'\'
\'\'\'
from ..dependencies import db
"""

View File

@ -8,11 +8,11 @@ class Config:
BASE_DIR = Path(__file__).resolve().parent.parent
SRC_DIR = BASE_DIR / "app"
EXCLUDED_DIRS = ["__pycache__", "commands"]
TIMEZONE = 'Europe/Warsaw'
TIMEZONE = "Europe/Warsaw"
ENABLE_CORS = os.environ.get('ENABLE_CORS') or False
ENABLE_CORS = os.environ.get("ENABLE_CORS") or False
ALLOWED_EXTENSIONS = {'csv'}
ALLOWED_EXTENSIONS = {"csv"}
MAX_CONTENT_LENGTH = 10 * 1024 * 1024 # 10 MB
SQLALCHEMY_TRACK_MODIFICATIONS = False
@ -20,8 +20,8 @@ class Config:
LIMIT_STUDENTS_PER_GROUP = 5
LIMIT_MEMBERS_PER_COMMITTEE = 3
DESCRIPTION = 'System PRI'
OPENAPI_VERSION = '3.0.2'
DESCRIPTION = "System PRI"
OPENAPI_VERSION = "3.0.2"
# Weights for project grade sheet
PRESENTATION_WEIGHT_FIRST_TERM = 1.5

View File

@ -1,8 +1,6 @@
class CSVException(Exception):
"""Main csv exception"""
pass
class InvalidNameOrTypeHeaderException(CSVException):
"""Throw if csv file has invalid name or type of header"""
pass

View File

@ -1,13 +1,23 @@
from flask import abort
from datetime import datetime
from typing import List
from flask import abort
from sqlalchemy import and_, or_
from ...base.mode import EnrollmentsMode
from ...dependencies import db
from ...examination_schedule.models import ExaminationSchedule, TermOfDefence
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group
def get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id: int,
term_of_defence_id: int) -> ExaminationSchedule:
td = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id).first()
def get_term_of_defence_by_id_and_examination_schedule_id(
examination_schedule_id: int, term_of_defence_id: int
) -> ExaminationSchedule:
td = TermOfDefence.query.filter(
TermOfDefence.id == term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id,
).first()
if td is None:
abort(404, "Not found examination schedule or term of defence!")
return td
@ -27,9 +37,12 @@ def check_the_group_has_assigned_to_term_of_defence(group_id: int) -> TermOfDefe
return td
def set_new_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int,
group_id: int) -> TermOfDefence:
td = get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id, term_of_defence_id)
def set_new_group_to_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int, group_id: int
) -> TermOfDefence:
td = get_term_of_defence_by_id_and_examination_schedule_id(
examination_schedule_id, term_of_defence_id
)
get_group_by_id(group_id)
check_the_group_has_assigned_to_term_of_defence(group_id)
td.group_id = group_id
@ -37,7 +50,95 @@ def set_new_group_to_term_of_defence(examination_schedule_id: int, term_of_defen
def get_examination_schedule_by_id(examination_schedule_id: int) -> ExaminationSchedule:
ex = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first()
ex = ExaminationSchedule.query.filter(
ExaminationSchedule.id == examination_schedule_id
).first()
if ex is None:
abort(404, "Not found examination schedule!")
return ex
def get_and_check_the_project_supervisors_exists_in_db(
year_group_id: int, project_supervisors_ids: List[int]
) -> List[ProjectSupervisor]:
project_supervisors = (
ProjectSupervisor.query.filter(
or_(*[ProjectSupervisor.id == i for i in project_supervisors_ids])
)
.filter(ProjectSupervisor.year_group_id == year_group_id)
.all()
)
if len(project_supervisors) != len(project_supervisors_ids):
abort(400, "Project Supervisors didn't exist!")
return project_supervisors
def validate_enrollments_date(
ex_start_date: datetime, ex_end_date: datetime, duration_time: int, data: dict
) -> None:
start_date = data.get("start_date")
end_date = data.get("end_date")
if not (
ex_start_date.timestamp() <= start_date.timestamp()
and ex_end_date.timestamp() >= end_date.timestamp()
):
abort(400, "Invalid date range!")
if end_date <= start_date:
abort(400, "End date must be greater than start date!")
delta_time = end_date - start_date
delta_time_in_minutes = delta_time.total_seconds() / 60
print(delta_time_in_minutes, duration_time, delta_time_in_minutes != duration_time)
if delta_time_in_minutes % duration_time != 0:
abort(400, "Invalid duration time!")
def check_the_term_of_defence_not_exists_in_chosen_date_range(
examination_schedule_id: int, data: dict
) -> None:
start_date = data.get("start_date")
end_date = data.get("end_date")
td = (
TermOfDefence.query.filter(
TermOfDefence.examination_schedule_id == examination_schedule_id
)
.filter(
or_(
and_(
TermOfDefence.start_date >= start_date,
TermOfDefence.start_date < end_date,
TermOfDefence.end_date >= end_date,
),
and_(
TermOfDefence.start_date <= start_date,
TermOfDefence.end_date > start_date,
TermOfDefence.end_date <= end_date,
),
)
)
.first()
)
if td is not None:
abort(400, "This term of defence is taken! You choose other date!")
def set_enrollments_mode(
examination_schedule_id: int, mode: EnrollmentsMode, action_name: str
) -> dict:
examination_schedule = (
db.session.query(ExaminationSchedule)
.filter(ExaminationSchedule.id == examination_schedule_id)
.first()
)
if examination_schedule is None:
abort(404, "Not found examination schedule!")
examination_schedule.open_enrollments = mode.value
db.session.commit()
return {"message": f"You {action_name} enrollments for this examination schedule!"}

View File

@ -1,12 +1,12 @@
from flask import Blueprint
from .examination_schedule import bp as examination_schedule_bp
from .enrollments import bp as enrollments_bp
from .examination_schedule import bp as examination_schedule_bp
from .groups import bp as groups_bp
from .project_supervisor import bp as project_supervisor_bp
from .students import bp as students_bp
from .year_group import bp as year_group_bp
from .workloads import bp as workloads_bp
from .year_group import bp as year_group_bp
bp = Blueprint("coordinator", __name__, url_prefix="/coordinator")

View File

@ -1,68 +1,53 @@
import datetime
from apiflask import APIBlueprint
from flask import abort, current_app
from sqlalchemy import or_, and_
from flask_sqlalchemy import get_debug_queries
from flask import abort
from ..schemas import TermOfDefenceSchema, TemporaryAvailabilityListSchema, AssignedGroupToTermOfDefenceListSchema, \
GroupIdSchema
from ...examination_schedule.models import TermOfDefence, TemporaryAvailability
from ...students.models import YearGroup
from ...project_supervisor.models import ProjectSupervisor
from ...dependencies import db
from ...base.schemas import MessageSchema
from ...dependencies import db
from ...examination_schedule.models import TemporaryAvailability, TermOfDefence
from ..query.enrollments import (
check_the_term_of_defence_not_exists_in_chosen_date_range,
get_and_check_the_project_supervisors_exists_in_db,
get_examination_schedule_by_id,
get_term_of_defence_by_id_and_examination_schedule_id,
set_new_group_to_term_of_defence,
validate_enrollments_date,
)
from ..schemas.enrollments import (
AssignedGroupToTermOfDefenceListSchema,
TemporaryAvailabilityListSchema,
TermOfDefenceSchema,
)
from ..schemas.groups import GroupIdSchema
from ..utils import generate_range_dates
from ..query.enrollments import get_term_of_defence_by_id_and_examination_schedule_id, set_new_group_to_term_of_defence, \
get_examination_schedule_by_id
bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments")
@bp.post('/<int:examination_schedule_id>/add')
@bp.post("/<int:examination_schedule_id>/add")
@bp.input(TermOfDefenceSchema)
@bp.output(MessageSchema)
def create_term_of_defence(examination_schedule_id: int, data: dict) -> dict:
if not data:
abort(400, "You have passed empty data!")
chairman_of_committee_id = data.pop("chairman_of_committee")
project_supervisors_ids = data.pop("project_supervisors")
if chairman_of_committee_id not in project_supervisors_ids:
abort(400, "Invalid id of chairman committee!")
ex = get_examination_schedule_by_id(examination_schedule_id)
yg_id = ex.year_group_id
project_supervisors_ids = data.pop('project_supervisors')
project_supervisors = ProjectSupervisor.query.filter(
or_(*[ProjectSupervisor.id == i for i in project_supervisors_ids])).filter(YearGroup.id == yg_id).all()
if len(project_supervisors) != len(project_supervisors_ids):
abort(404, "Project Supervisors didn't exist!")
start_date = data['start_date']
end_date = data['end_date']
if not (ex.start_date.timestamp() <= start_date.timestamp() and ex.end_date.timestamp() >= end_date.timestamp()):
abort(400, "Invalid date range!")
if end_date <= start_date:
abort(400, "End date must be greater than start date!")
delta_time = end_date - start_date
delta_time_in_minutes = delta_time.total_seconds() / 60
if delta_time_in_minutes != ex.duration_time:
abort(400, "Invalid duration time!")
td = TermOfDefence.query.filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
filter(
or_(and_(TermOfDefence.start_date >= start_date,
TermOfDefence.start_date < end_date,
TermOfDefence.end_date >= end_date),
and_(TermOfDefence.start_date <= start_date,
TermOfDefence.end_date > start_date,
TermOfDefence.end_date <= end_date))).first()
if td is not None:
abort(400, "This term of defence is taken! You choose other date!")
td = TermOfDefence(**data, examination_schedule_id=examination_schedule_id)
project_supervisors = get_and_check_the_project_supervisors_exists_in_db(
ex.year_group_id, project_supervisors_ids
)
validate_enrollments_date(ex.start_date, ex.end_date, ex.duration_time, data)
check_the_term_of_defence_not_exists_in_chosen_date_range(
examination_schedule_id, data
)
td = TermOfDefence(
**data,
examination_schedule_id=examination_schedule_id,
chairman_of_committee=chairman_of_committee_id
)
db.session.add(td)
db.session.commit()
for p in project_supervisors:
@ -71,110 +56,76 @@ def create_term_of_defence(examination_schedule_id: int, data: dict) -> dict:
return {"message": "Term of defence was created!"}
@bp.post('/<int:examination_schedule_id>/add-term-of-defences/')
@bp.post("/<int:examination_schedule_id>/add-term-of-defences/")
@bp.input(TermOfDefenceSchema)
@bp.output(MessageSchema)
def create_many_term_of_defences(examination_schedule_id: int, data: dict) -> dict:
if not data:
abort(400, "You have passed empty data!")
chairman_of_committee_id = data.pop("chairman_of_committee")
project_supervisors_ids = data.pop("project_supervisors")
if chairman_of_committee_id not in project_supervisors_ids:
abort(400, "Invalid id of chairman committee!")
ex = get_examination_schedule_by_id(examination_schedule_id)
yg_id = ex.year_group_id
project_supervisors_ids = data.pop('project_supervisors')
project_supervisors = ProjectSupervisor.query.filter(
or_(*[ProjectSupervisor.id == i for i in project_supervisors_ids])).filter(YearGroup.id == yg_id).all()
if len(project_supervisors) != len(project_supervisors_ids):
abort(404, "Project Supervisors didn't exist!")
start_date = data['start_date']
end_date = data['end_date']
if not (ex.start_date.timestamp() <= start_date.timestamp() and ex.end_date.timestamp() >= end_date.timestamp()):
abort(400, "Invalid date range!")
if end_date <= start_date:
abort(400, "End date must be greater than start date!")
delta_time = end_date - start_date
delta_time_in_minutes = delta_time.total_seconds() / 60
if delta_time_in_minutes % ex.duration_time != 0:
abort(400, "Invalid duration time!")
td = TermOfDefence.query.filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
filter(
or_(and_(TermOfDefence.start_date >= start_date,
TermOfDefence.start_date < end_date,
TermOfDefence.end_date >= end_date),
and_(TermOfDefence.start_date <= start_date,
TermOfDefence.end_date > start_date,
TermOfDefence.end_date <= end_date))).first()
if td is not None:
abort(400, "This term of defence is taken! You choose other date!")
project_supervisors = get_and_check_the_project_supervisors_exists_in_db(
ex.year_group_id, project_supervisors_ids
)
validate_enrollments_date(ex.start_date, ex.end_date, ex.duration_time, data)
check_the_term_of_defence_not_exists_in_chosen_date_range(
examination_schedule_id, data
)
# create many here
start_date = data.get("start_date")
end_date = data.get("end_date")
dates = generate_range_dates(start_date, end_date, ex.duration_time)
for start_date in dates:
end_date = start_date + datetime.timedelta(minutes=ex.duration_time)
td = TermOfDefence(start_date=start_date, end_date=end_date, examination_schedule_id=examination_schedule_id)
td = TermOfDefence(
start_date=start_date,
end_date=end_date,
examination_schedule_id=examination_schedule_id,
chairman_of_committee=chairman_of_committee_id,
)
td.members_of_committee = project_supervisors
db.session.add(td)
db.session.commit()
return {"message": "Term of defences was created!"}
@bp.put('/<int:examination_schedule_id>/update/<int:term_of_defence_id>/')
@bp.put("/<int:examination_schedule_id>/update/<int:term_of_defence_id>/")
@bp.input(TermOfDefenceSchema)
@bp.output(MessageSchema)
def update_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict:
if not data:
abort(400, "You have passed empty data!")
def update_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
chairman_of_committee_id = data.pop("chairman_of_committee")
project_supervisors_ids = data.pop("project_supervisors")
if chairman_of_committee_id not in project_supervisors_ids:
abort(400, "Invalid id of chairman committee!")
td_query = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id)
td_query = TermOfDefence.query.filter(
TermOfDefence.id == term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id,
)
td = td_query.first()
if td is None:
abort(404, "Not found term of defence!")
ex = td.examination_schedule
yg_id = ex.year_group_id
project_supervisors_ids = data.pop('project_supervisors')
project_supervisors = ProjectSupervisor.query.filter(
or_(*[ProjectSupervisor.id == i for i in project_supervisors_ids])).filter(YearGroup.id == yg_id).all()
ex = get_examination_schedule_by_id(examination_schedule_id)
if len(project_supervisors) != len(project_supervisors_ids):
abort(404, "Project Supervisors didn't exist!")
start_date = data['start_date']
end_date = data['end_date']
if not (ex.start_date.timestamp() <= start_date.timestamp() and ex.end_date.timestamp() >= end_date.timestamp()):
abort(400, "Invalid date range!")
if end_date <= start_date:
abort(400, "End date must be greater than start date!")
delta_time = end_date - start_date
delta_time_in_minutes = delta_time.total_seconds() / 60
if delta_time_in_minutes != ex.duration_time:
abort(400, "Invalid duration time!")
term_of_defence = TermOfDefence.query.filter(TermOfDefence.id != term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id). \
filter(
or_(and_(TermOfDefence.start_date >= start_date,
TermOfDefence.start_date < end_date,
TermOfDefence.end_date >= end_date),
and_(TermOfDefence.start_date <= start_date,
TermOfDefence.end_date > start_date,
TermOfDefence.end_date <= end_date))).first()
if term_of_defence is not None:
abort(400, "This term of defence is taken! You choose other date!")
project_supervisors = get_and_check_the_project_supervisors_exists_in_db(
ex.year_group_id, project_supervisors_ids
)
validate_enrollments_date(ex.start_date, ex.end_date, ex.duration_time, data)
check_the_term_of_defence_not_exists_in_chosen_date_range(
examination_schedule_id, data
)
td_query.update(data)
td.members_of_committee = []
td.chairman_of_committee = chairman_of_committee_id
db.session.commit()
for p in project_supervisors:
td.members_of_committee.append(p)
@ -183,75 +134,102 @@ def update_term_of_defence(examination_schedule_id: int, term_of_defence_id: int
return {"message": "Term of defence was updated!"}
@bp.delete('/<int:examination_schedule_id>/delete/<int:term_of_defence_id>/')
@bp.delete("/<int:examination_schedule_id>/delete/<int:term_of_defence_id>/")
@bp.output(MessageSchema)
def delete_term_of_defence(examination_schedule_id: int, term_of_defence_id: int) -> dict:
td = get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id, term_of_defence_id)
def delete_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int
) -> dict:
td = get_term_of_defence_by_id_and_examination_schedule_id(
examination_schedule_id, term_of_defence_id
)
db.session.delete(td)
db.session.commit()
return {"message": "Term of defence was deleted!"}
@bp.get('/<int:examination_schedule_id>/term-of-defences/')
@bp.get("/<int:examination_schedule_id>/term-of-defences/")
@bp.output(AssignedGroupToTermOfDefenceListSchema)
def list_of_term_of_defences(examination_schedule_id: int) -> dict:
get_examination_schedule_by_id(examination_schedule_id)
td = TermOfDefence.query. \
join(TermOfDefence.members_of_committee, isouter=True). \
filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
all()
td = (
TermOfDefence.query.join(TermOfDefence.members_of_committee, isouter=True)
.filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
.all()
)
return {"term_of_defences": td}
@bp.get('/<int:examination_schedule_id>/temporary-availabilities/')
@bp.get("/<int:examination_schedule_id>/temporary-availabilities/")
@bp.output(TemporaryAvailabilityListSchema)
def list_of_temporary_availability(examination_schedule_id: int) -> dict:
get_examination_schedule_by_id(examination_schedule_id)
td = TemporaryAvailability.query. \
filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id). \
join(TemporaryAvailability.project_supervisor). \
all()
td = (
TemporaryAvailability.query.filter(
TemporaryAvailability.examination_schedule_id == examination_schedule_id
)
.join(TemporaryAvailability.project_supervisor)
.all()
)
return {"temporary_availabilities": td}
@bp.get('/<int:examination_schedule_id>/assigned-group-to-term-of-defences/')
@bp.get("/<int:examination_schedule_id>/assigned-group-to-term-of-defences/")
@bp.output(AssignedGroupToTermOfDefenceListSchema)
def list_of_assigned_group_to_term_of_defences(examination_schedule_id: int) -> dict:
get_examination_schedule_by_id(examination_schedule_id)
td = TermOfDefence.query. \
join(TermOfDefence.members_of_committee, isouter=True). \
join(TermOfDefence.group). \
filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
filter(TermOfDefence.group_id.isnot(None)). \
all()
td = (
TermOfDefence.query.join(TermOfDefence.members_of_committee, isouter=True)
.join(TermOfDefence.group)
.filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
.filter(TermOfDefence.group_id.isnot(None))
.all()
)
return {"term_of_defences": td}
@bp.post('/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/')
@bp.post(
"/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/"
)
@bp.input(GroupIdSchema)
@bp.output(MessageSchema)
def add_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict:
set_new_group_to_term_of_defence(examination_schedule_id, term_of_defence_id, data.get("group_id"))
def add_group_to_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
set_new_group_to_term_of_defence(
examination_schedule_id, term_of_defence_id, data.get("group_id")
)
db.session.commit()
return {"message": "Group was added to term of defences!"}
@bp.delete('/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/')
@bp.delete(
"/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/"
)
@bp.output(MessageSchema)
def delete_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int) -> dict:
td = get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id, term_of_defence_id)
def delete_group_to_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int
) -> dict:
td = get_term_of_defence_by_id_and_examination_schedule_id(
examination_schedule_id, term_of_defence_id
)
td.group_id = None
db.session.commit()
return {"message": "Group was deleted from term of defences!"}
@bp.put('/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/')
@bp.put(
"/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/"
)
@bp.input(GroupIdSchema)
@bp.output(MessageSchema)
def update_group_for_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict:
set_new_group_to_term_of_defence(examination_schedule_id, term_of_defence_id, data.get("group_id"))
def update_group_for_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
set_new_group_to_term_of_defence(
examination_schedule_id, term_of_defence_id, data.get("group_id")
)
db.session.commit()
return {"message": "Group for term of defence was updated!"}

View File

@ -1,34 +1,40 @@
import datetime
from apiflask import APIBlueprint
from flask import abort, Response, make_response, current_app
from flask import Response, abort, current_app, make_response
from ...base.mode import EnrollmentsMode
from ...base.schemas import MessageSchema
from ...base.utils import paginate_models
from ...base.mode import ModeGroups
from ...dependencies import db
from ...examination_schedule.models import ExaminationSchedule, TermOfDefence
from ...students.models import Group, YearGroup
from ...project_supervisor.models import ProjectSupervisor
from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, \
ExaminationSchedulesQuerySchema, ExaminationSchedulesPaginationSchema
from ..utils import generate_examination_schedule_pdf_file
from ...base.schemas import MessageSchema
from ...students.models import Group, YearGroup
from ..query.enrollments import set_enrollments_mode
from ..schemas.examination_schedule import (
ExaminationScheduleSchema,
ExaminationSchedulesPaginationSchema,
ExaminationSchedulesQuerySchema,
)
from ..utils import generate_examination_schedule_pdf_file, get_duration_time
bp = APIBlueprint("examination_schedule", __name__, url_prefix="/examination_schedule")
@bp.get('/<int:year_group_id>/')
@bp.input(ExaminationSchedulesQuerySchema, location='query')
@bp.get("/<int:year_group_id>/")
@bp.input(ExaminationSchedulesQuerySchema, location="query")
@bp.output(ExaminationSchedulesPaginationSchema)
def list_examination_schedule(year_group_id: int, query: dict) -> dict:
page = query.get('page')
per_page = query.get('per_page')
es_query = ExaminationSchedule.query.filter(ExaminationSchedule.year_group_id == year_group_id)
page = query.get("page")
per_page = query.get("per_page")
es_query = ExaminationSchedule.query.filter(
ExaminationSchedule.year_group_id == year_group_id
)
data = paginate_models(page, es_query, per_page)
return {'examination_schedules': data['items'], 'max_pages': data['max_pages']}
return {"examination_schedules": data["items"], "max_pages": data["max_pages"]}
@bp.post('/<int:year_group_id>/')
@bp.post("/<int:year_group_id>/")
@bp.input(ExaminationScheduleSchema)
@bp.output(MessageSchema, status_code=201)
def create_examination_schedule(year_group_id: int, data: dict) -> dict:
@ -36,29 +42,28 @@ def create_examination_schedule(year_group_id: int, data: dict) -> dict:
if yg is None:
abort(404, "Year group doesn't exist!")
if data['start_date'] > data['end_date']:
if data.get("start_date") > data.get("end_date"):
abort(400, "Invalid data! End date must be greater than start date!")
duration_time = None
if yg.mode == ModeGroups.NON_STATIONARY.value:
duration_time = 20
elif yg.mode in [ModeGroups.STATIONARY.value, ModeGroups.ENGLISH_SPEAKING_STATIONARY.value]:
duration_time = 30
duration_time = get_duration_time(yg.mode)
if duration_time is None:
abort(400, "Invalid mode of year group!")
abort(400, "Invalid duration time!")
examination_schedule = ExaminationSchedule(**data, year_group_id=year_group_id, duration_time=duration_time)
examination_schedule = ExaminationSchedule(
**data, year_group_id=year_group_id, duration_time=duration_time
)
db.session.add(examination_schedule)
db.session.commit()
return {"message": "Examination schedule was created!"}
@bp.put('/<int:id>/')
@bp.put("/<int:examination_schedule_id>/")
@bp.input(ExaminationScheduleSchema)
@bp.output(MessageSchema)
def update_examination_schedule(id: int, data: dict) -> dict:
examination_schedule_query = db.session.query(ExaminationSchedule).filter(ExaminationSchedule.id == id)
def update_examination_schedule(examination_schedule_id: int, data: dict) -> dict:
examination_schedule_query = db.session.query(ExaminationSchedule).filter(
ExaminationSchedule.id == examination_schedule_id
)
examination_schedule = examination_schedule_query.first()
if examination_schedule is None:
@ -68,10 +73,14 @@ def update_examination_schedule(id: int, data: dict) -> dict:
return {"message": "Examination schedule was updated!"}
@bp.delete('/<int:id>/')
@bp.delete("/<int:examination_schedule_id>/")
@bp.output(MessageSchema)
def delete_examination_schedule(id: int) -> dict:
examination_schedule = db.session.query(ExaminationSchedule).filter(ExaminationSchedule.id == id).first()
def delete_examination_schedule(examination_schedule_id: int) -> dict:
examination_schedule = (
db.session.query(ExaminationSchedule)
.filter(ExaminationSchedule.id == examination_schedule_id)
.first()
)
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
db.session.delete(examination_schedule)
@ -79,52 +88,56 @@ def delete_examination_schedule(id: int) -> dict:
return {"message": "Examination schedule was deleted!"}
@bp.put('/<int:id>/date/')
@bp.input(ExaminationScheduleUpdateSchema)
@bp.put("/<int:examination_schedule_id>/open-enrollments/")
@bp.output(MessageSchema)
def set_date_of_examination_schedule(id: int, data: dict) -> dict:
examination_schedule_query = db.session.query(ExaminationSchedule).filter(ExaminationSchedule.id == id)
examination_schedule = examination_schedule_query.first()
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
if data['start_date_for_enrollment_students'] > data['end_date_for_enrollment_students']:
abort(400, "Invalid data! End date must be greater than start date!")
examination_schedule_query.update(data)
db.session.commit()
return {"message": "You set date of examination schedule!"}
def open_enrollments(examination_schedule_id: int) -> dict:
return set_enrollments_mode(examination_schedule_id, EnrollmentsMode.OPEN, "open")
@bp.post('/<int:examination_schedule_id>/download/')
@bp.put("/<int:examination_schedule_id>/close-enrollments/")
@bp.output(MessageSchema)
def close_enrollments(examination_schedule_id: int) -> dict:
return set_enrollments_mode(examination_schedule_id, EnrollmentsMode.CLOSE, "close")
@bp.post("/<int:examination_schedule_id>/download/")
def download_examination_schedule(examination_schedule_id: int) -> Response:
examination_schedule = db.session.query(ExaminationSchedule). \
filter(ExaminationSchedule.id == examination_schedule_id).first()
examination_schedule = (
db.session.query(ExaminationSchedule)
.filter(ExaminationSchedule.id == examination_schedule_id)
.first()
)
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
distinct_dates = db.session.query(db.func.Date(TermOfDefence.start_date)).distinct().all()
distinct_dates = (
db.session.query(db.func.Date(TermOfDefence.start_date)).distinct().all()
)
nested_term_of_defences = []
for d in distinct_dates:
date_tmp = datetime.datetime.strptime(d[0], "%Y-%m-%d").date()
term_of_defences = db.session.query(TermOfDefence). \
join(Group, isouter=True).join(ProjectSupervisor, isouter=True). \
filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
filter(TermOfDefence.group_id.isnot(None)). \
filter(db.func.Date(TermOfDefence.start_date) == date_tmp). \
all()
term_of_defences = (
db.session.query(TermOfDefence)
.join(Group, isouter=True)
.join(ProjectSupervisor, isouter=True)
.filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
.filter(TermOfDefence.group_id.isnot(None))
.filter(db.func.Date(TermOfDefence.start_date) == date_tmp)
.all()
)
if len(term_of_defences) > 0:
nested_term_of_defences.append(term_of_defences)
# print(nested_term_of_defences)
base_dir = current_app.config.get('BASE_DIR')
pdf = generate_examination_schedule_pdf_file(examination_schedule.title, nested_term_of_defences, base_dir)
base_dir = current_app.config.get("BASE_DIR")
pdf = generate_examination_schedule_pdf_file(
examination_schedule.title, nested_term_of_defences, base_dir
)
title = examination_schedule.title.replace("-", "_").split()
filename = "_".join(title)
response = make_response(pdf)
response.headers['Content-Type'] = 'application/pdf'
response.headers['Content-Disposition'] = 'attachment; filename=%s.pdf' % filename
response.headers["Content-Type"] = "application/pdf"
response.headers["Content-Disposition"] = "attachment; filename=%s.pdf" % filename
return response

View File

@ -1,61 +1,72 @@
from flask import abort
from apiflask import APIBlueprint
from flask_sqlalchemy import get_debug_queries
from flask import abort
from ...students.models import Group, Student, YearGroup, ProjectGradeSheet
from ...project_supervisor.models import ProjectSupervisor
from ..schemas import GroupEditSchema, GroupsPaginationSchema, GroupCreateSchema, GroupQuerySchema, DetailGroupSchema
from ...dependencies import db
from ...base.utils import paginate_models
from ...base.schemas import MessageSchema
from ...base.utils import paginate_models
from ...dependencies import db
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group, ProjectGradeSheet, Student, YearGroup
from ..schemas.groups import (
GroupCreateSchema,
GroupEditSchema,
GroupQuerySchema,
GroupsPaginationSchema,
)
from ..schemas.students import DetailGroupSchema
from ..utils import attach_points_for_first_and_second_term_to_group_models
bp = APIBlueprint("groups", __name__, url_prefix="/groups")
@bp.get("/<int:year_group_id>/")
@bp.input(GroupQuerySchema, location='query')
@bp.input(GroupQuerySchema, location="query")
@bp.output(GroupsPaginationSchema)
def list_groups(year_group_id: int, query: dict) -> dict:
search_name = query.get('name')
page = query.get('page')
per_page = query.get('per_page')
search_name = query.get("name")
page = query.get("page")
per_page = query.get("per_page")
groups_query = Group.search_by_name(year_group_id, search_name)
data = paginate_models(page, groups_query, per_page)
items = data['items']
items = data["items"]
attach_points_for_first_and_second_term_to_group_models(items)
return {
"groups": items,
"max_pages": data['max_pages']
}
return {"groups": items, "max_pages": data["max_pages"]}
@bp.post("/<int:year_group_id>/")
@bp.input(GroupCreateSchema)
@bp.output(MessageSchema, status_code=201)
def create_group(year_group_id: int, data: dict) -> dict:
name = data['name']
students_ids = data['students']
project_supervisor_id = data['project_supervisor_id']
name = data["name"]
students_ids = data["students"]
project_supervisor_id = data["project_supervisor_id"]
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
if yg is None:
abort(404, "Not found year group!")
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
project_supervisor = ProjectSupervisor.query.filter_by(
id=project_supervisor_id
).first()
if project_supervisor is None:
abort(404, f"Not found project supervisor!")
abort(404, "Not found project supervisor!")
group = Group(name=name, project_supervisor_id=project_supervisor_id, year_group_id=year_group_id)
group = Group(
name=name,
project_supervisor_id=project_supervisor_id,
year_group_id=year_group_id,
)
students_without_groups = db.session.query(Student, Group). \
join(Group, Student.groups). \
filter(Group.year_group_id == year_group_id). \
filter(db.or_(*[Student.id == st_id for st_id in students_ids])).all()
students_without_groups = (
db.session.query(Student, Group)
.join(Group, Student.groups)
.filter(Group.year_group_id == year_group_id)
.filter(db.or_(*[Student.id == st_id for st_id in students_ids]))
.all()
)
if len(students_without_groups) > 0:
abort(400, "One or more students have already belonged to group!")
@ -81,7 +92,7 @@ def create_group(year_group_id: int, data: dict) -> dict:
def detail_group(group_id: int) -> Group:
group = Group.query.filter_by(id=group_id).first()
if group is None:
abort(404, f"Not found group!")
abort(404, "Not found group!")
return group
@ -90,7 +101,7 @@ def detail_group(group_id: int) -> Group:
def delete_group(group_id: int) -> dict:
group = Group.query.filter_by(id=group_id).first()
if group is None:
abort(404, f"Not found group!")
abort(404, "Not found group!")
group.students = []
db.session.delete(group)
@ -103,29 +114,31 @@ def delete_group(group_id: int) -> dict:
@bp.output(MessageSchema)
def edit_group(group_id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
abort(400, "You have passed empty data!")
group_query = Group.query.filter_by(id=group_id)
group = group_query.first()
if group is None:
abort(404, f"Not found group!")
abort(404, "Not found group!")
students_ids = data.get('students')
name = data.get('name')
project_supervisor_id = data.get('project_supervisor_id')
students_ids = data.get("students")
name = data.get("name")
project_supervisor_id = data.get("project_supervisor_id")
if students_ids is not None:
students = db.session.query(Student).filter(Student.id.in_(students_ids)).all()
if len(students_ids) != len(students):
abort(404, 'Not found students!')
abort(404, "Not found students!")
group.students = students
if name is not None:
group.name = name
if project_supervisor_id is not None:
ps = ProjectSupervisor.query.filter(ProjectSupervisor.id == project_supervisor_id).first()
ps = ProjectSupervisor.query.filter(
ProjectSupervisor.id == project_supervisor_id
).first()
if ps is None:
abort(404, "Not found project supervisor!")
group.project_supervisor_id = project_supervisor_id

View File

@ -1,37 +1,40 @@
from flask import abort
from apiflask import APIBlueprint
from flask_sqlalchemy import get_debug_queries
from flask import abort
from ...base.schemas import MessageSchema
from ...base.utils import paginate_models
from ...dependencies import db
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group, YearGroup
from ..schemas import ProjectSupervisorSchema, ProjectSupervisorEditSchema, ProjectSupervisorsPaginationSchema, \
ProjectSupervisorCreateSchema, MessageWithIdSchema, ProjectSupervisorQuerySchema
from ...base.schemas import MessageSchema
from ...dependencies import db
from ...base.utils import paginate_models
from ..schemas.project_supervisor import (
ProjectSupervisorCreateSchema,
ProjectSupervisorEditSchema,
ProjectSupervisorQuerySchema,
ProjectSupervisorSchema,
ProjectSupervisorsPaginationSchema,
)
from ..schemas.students import MessageWithIdSchema
bp = APIBlueprint("project_supervisor", __name__, url_prefix="/project_supervisor")
@bp.get("/<int:year_group_id>/")
@bp.input(ProjectSupervisorQuerySchema, location='query')
@bp.input(ProjectSupervisorQuerySchema, location="query")
@bp.output(ProjectSupervisorsPaginationSchema)
def list_project_supervisors(year_group_id: int, query: dict) -> dict:
fullname = query.get('fullname')
order_by_first_name = query.get('order_by_first_name')
order_by_last_name = query.get('order_by_last_name')
page = query.get('page')
per_page = query.get('per_page')
fullname = query.get("fullname")
order_by_first_name = query.get("order_by_first_name")
order_by_last_name = query.get("order_by_last_name")
page = query.get("page")
per_page = query.get("per_page")
project_supervisor_query = ProjectSupervisor.search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
year_group_id, fullname, order_by_first_name, order_by_last_name)
project_supervisor_query = ProjectSupervisor.search_by_fullname(
year_group_id, fullname, order_by_first_name, order_by_last_name
)
data = paginate_models(page, project_supervisor_query, per_page)
# print(get_debug_queries()[0])
return {
"project_supervisors": data['items'],
"max_pages": data['max_pages']
}
return {"project_supervisors": data["items"], "max_pages": data["max_pages"]}
@bp.post("/<int:year_group_id>/")
@ -42,8 +45,11 @@ def create_project_supervisor(year_group_id: int, data: dict) -> dict:
if year_group is None:
abort(404, "Not found year group!")
email = data['email']
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.email == email).first()
email = data["email"]
project_supervisor = ProjectSupervisor.query.filter(
ProjectSupervisor.email == email,
ProjectSupervisor.year_group_id == year_group_id,
).first()
if project_supervisor is not None:
abort(400, "Project Supervisor has already exists!")
@ -57,20 +63,26 @@ def create_project_supervisor(year_group_id: int, data: dict) -> dict:
@bp.get("/<int:project_supervisor_id>/detail/")
@bp.output(ProjectSupervisorSchema)
def detail_project_supervisor(project_supervisor_id: int) -> ProjectSupervisor:
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
project_supervisor = ProjectSupervisor.query.filter_by(
id=project_supervisor_id
).first()
if project_supervisor is None:
abort(404, 'Not found project supervisor!')
abort(404, "Not found project supervisor!")
return project_supervisor
@bp.delete("/<int:project_supervisor_id>/")
@bp.output(MessageSchema)
def delete_project_supervisor(project_supervisor_id: int) -> dict:
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
project_supervisor = ProjectSupervisor.query.filter_by(
id=project_supervisor_id
).first()
if project_supervisor is None:
abort(404, "Not found project supervisor!")
count_groups = len(Group.query.filter(Group.project_supervisor_id == project_supervisor.id).all())
count_groups = len(
Group.query.filter(Group.project_supervisor_id == project_supervisor.id).all()
)
if count_groups > 0:
abort(400, "Project Supervisor has at least one group!")
@ -84,13 +96,15 @@ def delete_project_supervisor(project_supervisor_id: int) -> dict:
@bp.output(MessageSchema)
def edit_project_supervisor(project_supervisor_id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
abort(400, "You have passed empty data!")
project_supervisor_query = ProjectSupervisor.query.filter_by(id=project_supervisor_id)
project_supervisor_query = ProjectSupervisor.query.filter_by(
id=project_supervisor_id
)
project_supervisor = project_supervisor_query.first()
if project_supervisor is None:
abort(404, f"Not found project supervisor!")
abort(404, "Not found project supervisor!")
project_supervisor_query.update(data)
db.session.commit()
@ -104,19 +118,34 @@ def copy_project_supervisors_from_last_year_group(year_group_id: int) -> dict:
if year_group is None:
abort(404, "Not found year group!")
last_year_group = YearGroup.query.filter(YearGroup.mode == year_group.mode). \
filter(YearGroup.id != year_group_id).filter(YearGroup.created_at < year_group.created_at). \
order_by(db.desc(YearGroup.created_at)).first()
last_year_group = (
YearGroup.query.filter(YearGroup.mode == year_group.mode)
.filter(YearGroup.id != year_group_id)
.filter(YearGroup.created_at < year_group.created_at)
.order_by(db.desc(YearGroup.created_at))
.first()
)
if last_year_group is None:
abort(400, "The latest year group doesn't exist!")
project_supervisors = ProjectSupervisor.query.filter(ProjectSupervisor.year_group_id == last_year_group.id).all()
current_project_supervisors_email_in_new_year_group = [ps.email for ps in ProjectSupervisor.query.filter(
ProjectSupervisor.year_group_id == year_group_id).all()]
project_supervisors = ProjectSupervisor.query.filter(
ProjectSupervisor.year_group_id == last_year_group.id
).all()
current_project_supervisors_email_in_new_year_group = [
ps.email
for ps in ProjectSupervisor.query.filter(
ProjectSupervisor.year_group_id == year_group_id
).all()
]
for ps in project_supervisors:
if ps.email not in current_project_supervisors_email_in_new_year_group:
new_ps = ProjectSupervisor(first_name=ps.first_name, last_name=ps.last_name, email=ps.email,
limit_group=ps.limit_group, year_group_id=year_group_id)
new_ps = ProjectSupervisor(
first_name=ps.first_name,
last_name=ps.last_name,
email=ps.email,
limit_group=ps.limit_group,
year_group_id=year_group_id,
)
db.session.add(new_ps)
db.session.commit()

View File

@ -1,44 +1,51 @@
from random import randint
from itertools import islice
from random import randint
from flask import Response, abort
from apiflask import APIBlueprint
from flask import Response, abort
from sqlalchemy import or_
from flask_sqlalchemy import get_debug_queries
from ...students.models import Student, Group, YearGroup
from ...project_supervisor.models import ProjectSupervisor
from ..schemas import StudentSchema, StudentEditSchema, StudentsPaginationSchema, YearGroupInfoQuery, \
StudentCreateSchema, FileSchema, StudentQuerySchema, StudentListFileDownloaderSchema
from ...dependencies import db
from ..utils import parse_csv, generate_csv
from ..exceptions import InvalidNameOrTypeHeaderException
from ...base.utils import paginate_models, is_allowed_extensions
from ...base.schemas import MessageSchema
from ...base.utils import is_allowed_extensions, paginate_models
from ...dependencies import db
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group, Student, YearGroup
from ..exceptions import InvalidNameOrTypeHeaderException
from ..schemas.students import (
FileSchema,
StudentCreateSchema,
StudentEditSchema,
StudentListFileDownloaderSchema,
StudentQuerySchema,
StudentSchema,
StudentsPaginationSchema,
YearGroupInfoQuery,
)
from ..utils import generate_csv, parse_csv
bp = APIBlueprint("students", __name__, url_prefix="/students")
@bp.get("/<int:year_group_id>/")
@bp.input(StudentQuerySchema, location='query')
@bp.input(StudentQuerySchema, location="query")
@bp.output(StudentsPaginationSchema)
def list_students(year_group_id: int, query: dict) -> dict:
# add filter by year group
fullname = query.get('fullname')
order_by_first_name = query.get('order_by_first_name')
order_by_last_name = query.get('order_by_last_name')
page = query.get('page')
per_page = query.get('per_page')
fullname = query.get("fullname")
order_by_first_name = query.get("order_by_first_name")
order_by_last_name = query.get("order_by_last_name")
page = query.get("page")
per_page = query.get("per_page")
student_query = Student.search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
year_group_id, fullname, order_by_first_name, order_by_last_name)
student_query = (
Student.search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
year_group_id, fullname, order_by_first_name, order_by_last_name
)
)
data = paginate_models(page, student_query, per_page)
# print(get_debug_queries()[0])
return {
"students": data['items'],
"max_pages": data['max_pages']
}
return {"students": data["items"], "max_pages": data["max_pages"]}
@bp.get("/<int:student_id>/detail/")
@ -66,13 +73,13 @@ def delete_student(student_id: int) -> dict:
@bp.output(MessageSchema)
def edit_student(student_id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
abort(400, "You have passed empty data!")
student_query = Student.query.filter(Student.id==student_id)
student_query = Student.query.filter(Student.id == student_id)
student = student_query.first()
if student is None:
abort(404, 'Not found student!')
abort(404, "Not found student!")
student_query.update(data)
db.session.commit()
@ -84,20 +91,21 @@ def edit_student(student_id: int, data: dict) -> dict:
@bp.input(StudentCreateSchema)
@bp.output(MessageSchema)
def create_student(data: dict) -> dict:
index = data['index']
yg_id = data['year_group_id']
del data['year_group_id']
index = data["index"]
yg_id = data["year_group_id"]
del data["year_group_id"]
student = Student.query.filter(Student.index == index, Student.year_group_id == yg_id).first()
student = Student.query.filter(
Student.index == index, Student.year_group_id == yg_id
).first()
if student is not None:
abort(400, "Student has already assigned to this year group!")
# add student to the chosen year group
year_group = YearGroup.query.filter(YearGroup.id == yg_id).first()
if year_group is None:
abort(404, "Not found year group!")
dummy_email = f'student{randint(1, 300_000)}@gmail.com'
dummy_email = f"student{randint(1, 300_000)}@gmail.com"
student = Student(**data, email=dummy_email, year_group_id=yg_id)
db.session.add(student)
db.session.commit()
@ -106,18 +114,18 @@ def create_student(data: dict) -> dict:
@bp.post("/upload/")
@bp.input(YearGroupInfoQuery, location='query')
@bp.input(FileSchema, location='form_and_files')
@bp.input(YearGroupInfoQuery, location="query")
@bp.input(FileSchema, location="form_and_files")
@bp.output(MessageSchema)
def upload_students(query: dict, file: dict) -> dict:
"""Add only Students to chosen year group if students exist in db and assigned to correct year group,
they will be omitted"""
year_group_id = query.get('year_group_id')
"""Add only Students to chosen year group if students exist in db and
assigned to correct year group, they will be omitted"""
year_group_id = query.get("year_group_id")
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
if yg is None:
abort(404, "Not found year group!")
uploaded_file = file.get('file')
uploaded_file = file.get("file")
if uploaded_file and is_allowed_extensions(uploaded_file.filename):
try:
students = parse_csv(uploaded_file, year_group_id)
@ -128,10 +136,19 @@ def upload_students(query: dict, file: dict) -> dict:
if len(list_of_students) == 0:
break
students_in_db = Student.query.filter(or_(Student.index == s.index for s in list_of_students)).\
filter(Student.year_group_id==year_group_id).all()
students_in_db = (
Student.query.filter(
or_(Student.index == s.index for s in list_of_students)
)
.filter(Student.year_group_id == year_group_id)
.all()
)
student_index_in_db = [s.index for s in students_in_db]
students_not_exists_in_db = list(filter(lambda s: s.index not in student_index_in_db, list_of_students))
students_not_exists_in_db = list(
filter(
lambda s: s.index not in student_index_in_db, list_of_students
)
)
db.session.add_all(students_not_exists_in_db)
db.session.commit()
@ -144,17 +161,23 @@ def upload_students(query: dict, file: dict) -> dict:
@bp.post("/download/")
@bp.input(StudentListFileDownloaderSchema, location='query')
@bp.input(StudentListFileDownloaderSchema, location="query")
def download_students(query: dict) -> Response:
year_group_id = query.get('year_group_id')
students_and_groups = db.session.query(Student, Group).join(Group, Student.groups). \
filter(Group.year_group_id == year_group_id). \
join(ProjectSupervisor).all()
year_group_id = query.get("year_group_id")
students_and_groups = (
db.session.query(Student, Group)
.join(Group, Student.groups)
.filter(Group.year_group_id == year_group_id)
.join(ProjectSupervisor)
.all()
)
if len(students_and_groups) == 0:
abort(404, "Not found students!")
csv_file = generate_csv(students_and_groups)
response = Response(csv_file, mimetype='text/csv')
response.headers.set("Content-Disposition", "attachment", filename="students_list.csv")
response = Response(csv_file, mimetype="text/csv")
response.headers.set(
"Content-Disposition", "attachment", filename="students_list.csv"
)
return response

View File

@ -1,32 +1,38 @@
from apiflask import APIBlueprint
from flask import abort
from flask_sqlalchemy import get_debug_queries
from ...dependencies import db
from ...examination_schedule.models import ExaminationSchedule, TermOfDefence
from ...project_supervisor.models import ProjectSupervisor
from ..schemas import WorkloadSchema
from ..schemas.examination_schedule import WorkloadSchema
bp = APIBlueprint("workloads", __name__, url_prefix="/")
@bp.get('/examination_schedule/<int:examination_schedule_id>/workloads/')
@bp.get("/examination_schedule/<int:examination_schedule_id>/workloads/")
@bp.output(WorkloadSchema)
def workloads_statistics(examination_schedule_id: int) -> dict:
es = ExaminationSchedule.query.filter_by(id=examination_schedule_id).first()
if es is None:
abort(404, "Not found examination schedule!")
statistics = db.session.query(
ProjectSupervisor.first_name + " " + ProjectSupervisor.last_name,
db.func.count(TermOfDefence.group_id),
db.func.count(TermOfDefence.id),
).join(TermOfDefence.members_of_committee). \
group_by(ProjectSupervisor.id).all()
statistics = (
db.session.query(
ProjectSupervisor.first_name + " " + ProjectSupervisor.last_name,
db.func.count(TermOfDefence.group_id),
db.func.count(TermOfDefence.id),
)
.join(TermOfDefence.members_of_committee)
.group_by(ProjectSupervisor.id)
.all()
)
# print(statistics)
# print(len(statistics))
# print(get_debug_queries())
workloads = ({"full_name": s[0], "groups_assigned_to_his_committee": s[1],
"assigned_to_committee": s[2]} for s in statistics)
return {'workloads': workloads}
workloads = (
{
"full_name": s[0],
"groups_assigned_to_his_committee": s[1],
"assigned_to_committee": s[2],
}
for s in statistics
)
return {"workloads": workloads}

View File

@ -1,22 +1,28 @@
from flask import abort
from apiflask import APIBlueprint
from flask import abort
from ...students.models import YearGroup
from ..schemas import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema
from ...dependencies import db
from ...base.utils import paginate_models
from ...base.schemas import MessageSchema
from ...base.utils import paginate_models
from ...dependencies import db
from ...students.models import YearGroup
from ..schemas.year_group import (
YearGroupPaginationSchema,
YearGroupQuerySchema,
YearGroupSchema,
)
bp = APIBlueprint("year_group", __name__, url_prefix="/year-group")
@bp.post('/')
@bp.post("/")
@bp.input(YearGroupSchema)
@bp.output(MessageSchema, status_code=201)
def create_year_group(data: dict) -> dict:
name = data['name']
mode = data['mode']
year_group = YearGroup.query.filter(YearGroup.name == name, YearGroup.mode == mode).first()
name = data["name"]
mode = data["mode"]
year_group = YearGroup.query.filter(
YearGroup.name == name, YearGroup.mode == mode
).first()
if year_group is not None:
abort(400, "Year group has already exists!")
@ -27,38 +33,37 @@ def create_year_group(data: dict) -> dict:
return {"message": "Year group was created!"}
@bp.get('/')
@bp.input(YearGroupQuerySchema, location='query')
@bp.get("/")
@bp.input(YearGroupQuerySchema, location="query")
@bp.output(YearGroupPaginationSchema)
def list_of_year_groups(query: dict) -> dict:
page = query.get('page')
per_page = query.get('per_page')
page = query.get("page")
per_page = query.get("per_page")
year_group_query = YearGroup.query.order_by(db.desc(YearGroup.created_at))
data = paginate_models(page, year_group_query, per_page)
return {
"year_groups": data['items'],
"max_pages": data['max_pages']
}
return {"year_groups": data["items"], "max_pages": data["max_pages"]}
@bp.put('/<int:id>/')
@bp.put("/<int:id>/")
@bp.input(YearGroupSchema)
@bp.output(MessageSchema)
def update_year_of_group(id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
abort(400, "You have passed empty data!")
year_group_query = YearGroup.query.filter(YearGroup.id == id)
year_group = year_group_query.first()
if year_group is None:
abort(404, 'Not found year group!')
abort(404, "Not found year group!")
name = data['name']
mode = data['mode']
year_group = YearGroup.query.filter(YearGroup.name == name, YearGroup.mode == mode, YearGroup.id != id).first()
name = data["name"]
mode = data["mode"]
year_group = YearGroup.query.filter(
YearGroup.name == name, YearGroup.mode == mode, YearGroup.id != id
).first()
if year_group is not None:
abort(400, "Year group has already exists!")
@ -68,12 +73,12 @@ def update_year_of_group(id: int, data: dict) -> dict:
return {"message": "Year group was updated!"}
@bp.delete('/<int:id>/')
@bp.delete("/<int:id>/")
@bp.output(MessageSchema, status_code=202)
def delete_year_of_group(id: int) -> dict:
year_group = YearGroup.query.filter_by(id=id).first()
if year_group is None:
abort(404, f"Year group doesn't exist!")
abort(404, "Year group doesn't exist!")
db.session.delete(year_group)
db.session.commit()
return {"message": "Year group was deleted!"}

View File

@ -1,11 +0,0 @@
from .enrollments import TermOfDefenceSchema, TermOfDefenceListSchema, TemporaryAvailabilityListSchema, \
AssignedGroupToTermOfDefenceListSchema
from .examination_schedule import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, \
ExaminationSchedulesPaginationSchema, ExaminationSchedulesQuerySchema, WorkloadSchema
from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema, GroupIdSchema
from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisorsPaginationSchema, \
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema
from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \
StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageWithIdSchema, FileSchema, \
StudentQuerySchema, YearGroupInfoQuery, DetailGroupSchema
from .year_group import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema

View File

@ -4,9 +4,16 @@ from ..validators import validate_datetime_greater_than_now
class TermOfDefenceSchema(Schema):
start_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
end_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
project_supervisors = fields.List(fields.Integer(required=True), validate=validate.Length(3, 3))
start_date = fields.DateTime(
validate=validate_datetime_greater_than_now, required=True
)
end_date = fields.DateTime(
validate=validate_datetime_greater_than_now, required=True
)
project_supervisors = fields.List(
fields.Integer(required=True), validate=validate.Length(3, 3)
)
chairman_of_committee = fields.Integer(required=True)
class ProjectSupervisorForTermOfDefenceSchema(Schema):
@ -19,30 +26,35 @@ class TermOfDefenceItemSchema(Schema):
id = fields.Integer()
start_date = fields.DateTime()
end_date = fields.DateTime()
members_of_committee = fields.List(fields.Nested(ProjectSupervisorForTermOfDefenceSchema))
members_of_committee = fields.List(
fields.Nested(ProjectSupervisorForTermOfDefenceSchema)
)
chairman_of_committee = fields.Integer()
class TermOfDefenceListSchema(Schema):
term_of_defences = fields.List(fields.Nested(TermOfDefenceItemSchema))
class StudentDataItemSchema(Schema):
class StudentDataItemAssignedGroupSchema(Schema):
index = fields.Integer()
first_name = fields.Str()
last_name = fields.Str()
class GroupDataItemSchema(Schema):
class GroupDataItemAssignedGroupSchema(Schema):
name = fields.Str()
students = fields.List(fields.Nested(StudentDataItemSchema))
students = fields.List(fields.Nested(StudentDataItemAssignedGroupSchema))
class AssignedGroupToTermOfDefenceItemSchema(TermOfDefenceItemSchema):
group = fields.Nested(GroupDataItemSchema)
class AssignedGroupToTermOfDefenceDataItemSchema(TermOfDefenceItemSchema):
group = fields.Nested(GroupDataItemAssignedGroupSchema)
class AssignedGroupToTermOfDefenceListSchema(Schema):
term_of_defences = fields.List(fields.Nested(AssignedGroupToTermOfDefenceItemSchema))
term_of_defences = fields.List(
fields.Nested(AssignedGroupToTermOfDefenceDataItemSchema)
)
class ProjectSupervisorForTemporaryAvailabilitySchema(Schema):
@ -58,4 +70,6 @@ class TemporaryAvailabilityItemSchema(Schema):
class TemporaryAvailabilityListSchema(Schema):
temporary_availabilities = fields.List(fields.Nested(TemporaryAvailabilityItemSchema))
temporary_availabilities = fields.List(
fields.Nested(TemporaryAvailabilityItemSchema)
)

View File

@ -1,30 +1,30 @@
from marshmallow import fields, validate, Schema
from marshmallow import Schema, fields, validate
from ..validators import validate_datetime_greater_than_now
class ExaminationScheduleSchema(Schema):
title = fields.Str(validate=validate.Length(min=1, max=100), required=True)
start_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
end_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
class ExaminationScheduleUpdateSchema(Schema):
start_date_for_enrollment_students = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
end_date_for_enrollment_students = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
start_date = fields.DateTime(
validate=validate_datetime_greater_than_now, required=True
)
end_date = fields.DateTime(
validate=validate_datetime_greater_than_now, required=True
)
class ExaminationScheduleListItemSchema(Schema):
id = fields.Integer()
title = fields.Str()
open_enrollments = fields.String()
start_date = fields.DateTime()
end_date = fields.DateTime()
start_date_for_enrollment_students = fields.DateTime()
end_date_for_enrollment_students = fields.DateTime()
class ExaminationSchedulesPaginationSchema(Schema):
examination_schedules = fields.List(fields.Nested(ExaminationScheduleListItemSchema))
examination_schedules = fields.List(
fields.Nested(ExaminationScheduleListItemSchema)
)
max_pages = fields.Integer()

View File

@ -1,6 +1,5 @@
from marshmallow import Schema, fields, validate
from ..validators import validate_index
from .students import GroupSchema

View File

@ -1,6 +1,12 @@
from marshmallow import fields, validate, Schema
from marshmallow import Schema, fields, validate
from .students import ProjectSupervisorSchema
class ProjectSupervisorSchema(Schema):
id = fields.Integer()
first_name = fields.Str()
last_name = fields.Str()
email = fields.Str()
limit_group = fields.Integer()
class ProjectSupervisorQuerySchema(Schema):
@ -19,7 +25,9 @@ class ProjectSupervisorsPaginationSchema(Schema):
class ProjectSupervisorCreateSchema(Schema):
first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True)
last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True)
email = fields.Str(validate=[validate.Length(min=1, max=255), validate.Email()], required=True)
email = fields.Str(
validate=[validate.Length(min=1, max=255), validate.Email()], required=True
)
limit_group = fields.Integer(required=True)
@ -27,4 +35,4 @@ class ProjectSupervisorEditSchema(Schema):
first_name = fields.Str(validate=validate.Length(min=1, max=255))
last_name = fields.Str(validate=validate.Length(min=1, max=255))
email = fields.Str(validate=[validate.Length(min=0, max=255), validate.Email()])
limit_group = fields.Integer()
limit_group = fields.Integer()

View File

@ -1,15 +1,9 @@
from marshmallow import fields, validate, Schema
from marshmallow import Schema, fields, validate
from ...dependencies import ma
from ...students.models import Student, Group
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group, Student
from ..validators import validate_index
class ProjectSupervisorSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = ProjectSupervisor
include_relationships = False
from .project_supervisor import ProjectSupervisorSchema
class GroupSchema(ma.SQLAlchemyAutoSchema):
@ -45,7 +39,6 @@ class StudentCreateSchema(ma.Schema):
class StudentEditSchema(ma.Schema):
first_name = fields.Str(validate=validate.Length(min=1, max=255))
last_name = fields.Str(validate=validate.Length(min=1, max=255))
pesel = fields.Str(validate=validate.Length(min=0, max=11))
index = fields.Integer(validate=validate_index)
@ -55,7 +48,7 @@ class MessageWithIdSchema(ma.Schema):
class FileSchema(ma.Schema):
file = fields.Raw(metadata={'type': 'file'}, required=True)
file = fields.Raw(metadata={"type": "file"}, required=True)
class StudentQuerySchema(ma.Schema):
@ -69,8 +62,12 @@ class StudentQuerySchema(ma.Schema):
class YearGroupInfoQuery(Schema):
year_group_id = fields.Integer(required=True)
class DetailGroupSchema(ma.SQLAlchemyAutoSchema):
project_supervisor = fields.Nested(ProjectSupervisorSchema)
students = fields.List(fields.Nested(StudentSchema), validate=validate.Length(min=1, max=255))
students = fields.List(
fields.Nested(StudentSchema), validate=validate.Length(min=1, max=255)
)
class Meta:
model = Group
model = Group

View File

@ -1,4 +1,4 @@
from marshmallow import Schema, fields, validate, ValidationError
from marshmallow import Schema, ValidationError, fields, validate
from ...base.mode import ModeGroups
@ -10,7 +10,7 @@ def validate_mode(value: str) -> str:
class YearGroupSchema(Schema):
name = fields.Str(validate=validate.Regexp(r'^\d{4}\/\d{4}$'), required=True)
name = fields.Str(validate=validate.Regexp(r"^\d{4}\/\d{4}$"), required=True)
mode = fields.Str(validate=validate_mode, required=True)

View File

@ -3,54 +3,85 @@ import json
from collections import defaultdict
from datetime import datetime, timedelta
from io import BytesIO
from typing import Generator, Union, Any, List, Tuple, TextIO
from pathlib import Path
from typing import Any, Generator, List, TextIO, Tuple, Union
import pandas as pd
from flask import current_app
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm, inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Table
from reportlab.lib.units import inch, mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import PageBreak, Paragraph, SimpleDocTemplate, Table
from werkzeug.datastructures import FileStorage
from .exceptions import InvalidNameOrTypeHeaderException
from ..students.models import Student, Group, ProjectGradeSheet
from ..base.mode import ModeGroups
from ..examination_schedule.models import TermOfDefence
from ..students.models import Group, ProjectGradeSheet, Student
from .exceptions import InvalidNameOrTypeHeaderException
def check_columns(df: pd.DataFrame) -> bool:
headers = set(df.keys().values)
column_names = ['NAZWISKO', 'IMIE', 'INDEKS', 'EMAIL']
column_types = ['object', 'object', 'int', 'object']
return all((column_name in headers for column_name in column_names)) and \
all((str(df.dtypes[column_name]).startswith(column_type) for column_name, column_type in
zip(column_names, column_types)))
column_names = ["NAZWISKO", "IMIE", "INDEKS", "EMAIL"]
column_types = ["object", "object", "int", "object"]
return all((column_name in headers for column_name in column_names)) and all(
(
str(df.dtypes[column_name]).startswith(column_type)
for column_name, column_type in zip(column_names, column_types)
)
)
def parse_csv(file: Union[FileStorage, TextIO], year_group_id: int) -> Generator[Student, Any, None]:
def parse_csv(
file: Union[FileStorage, TextIO], year_group_id: int
) -> Generator[Student, Any, None]:
df = pd.read_csv(file)
if not check_columns(df):
raise InvalidNameOrTypeHeaderException
students = (Student(last_name=dict(item.items())['NAZWISKO'],
first_name=dict(item.items())['IMIE'],
index=dict(item.items())['INDEKS'],
email=dict(item.items())['EMAIL'],
year_group_id=year_group_id)
for _, item in df.iterrows())
students = (
Student(
last_name=dict(item.items())["NAZWISKO"],
first_name=dict(item.items())["IMIE"],
index=dict(item.items())["INDEKS"],
email=dict(item.items())["EMAIL"],
year_group_id=year_group_id,
)
for _, item in df.iterrows()
)
return students
def generate_csv(students_and_groups: List[Tuple[Student, Group]]) -> str:
headers = ['INDEKS', 'IMIE', 'NAZWISKO', 'EMAIL', 'CDYD_KOD', 'PRZ_KOD', 'TZAJ_KOD', 'GR_NR', 'PRG_KOD']
data = [(student.index, student.first_name, student.last_name, student.email,
group.cdyd_kod, group.prz_kod, group.tzaj_kod, group.project_supervisor_id,
None) for student, group in students_and_groups]
headers = [
"INDEKS",
"IMIE",
"NAZWISKO",
"EMAIL",
"CDYD_KOD",
"PRZ_KOD",
"TZAJ_KOD",
"GR_NR",
"PRG_KOD",
]
data = [
(
student.index,
student.first_name,
student.last_name,
student.email,
group.cdyd_kod,
group.prz_kod,
group.tzaj_kod,
group.project_supervisor_id,
None,
)
for student, group in students_and_groups
]
dataframe = defaultdict(list)
for row in data:
for idx, item in enumerate(row):
@ -60,8 +91,9 @@ def generate_csv(students_and_groups: List[Tuple[Student, Group]]) -> str:
return df.to_csv(index=False)
def generate_range_dates(start_date: datetime, end_date: datetime, step_in_minutes: int) -> \
Generator[Union[datetime, timedelta], Any, None]:
def generate_range_dates(
start_date: datetime, end_date: datetime, step_in_minutes: int
) -> Generator[Union[datetime, timedelta], Any, None]:
current_date = copy.copy(start_date)
while True:
next_date = current_date + timedelta(minutes=step_in_minutes)
@ -72,10 +104,19 @@ def generate_range_dates(start_date: datetime, end_date: datetime, step_in_minut
current_date = copy.copy(next_date)
def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences: List[List[TermOfDefence]],
base_dir: Path) -> bytes:
def generate_examination_schedule_pdf_file(
title: str, nested_term_of_defences: List[List[TermOfDefence]], base_dir: Path
) -> bytes:
pagesize = (297 * mm, 210 * mm)
headers = ["lp.", "Godzina", "Nazwa projektu", "Opiekun", "Zespol", "Komisja", "Uwagi"]
headers = [
"lp.",
"Godzina",
"Nazwa projektu",
"Opiekun",
"Zespol",
"Komisja",
"Uwagi",
]
pdf_buffer = BytesIO()
my_doc = SimpleDocTemplate(
pdf_buffer,
@ -84,13 +125,13 @@ def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences:
leftMargin=1 * inch,
rightMargin=1 * inch,
bottomMargin=1 * inch,
title=title
title=title,
)
pdfmetrics.registerFont(TTFont('Lato', base_dir / 'fonts' / 'Lato.ttf'))
pdfmetrics.registerFont(TTFont("Lato", base_dir / "fonts" / "Lato.ttf"))
style = getSampleStyleSheet()
bodyText = style['BodyText']
bodyText.fontName = 'Lato'
bodyText = style["BodyText"]
bodyText.fontName = "Lato"
normal = style["Heading1"]
normal.alignment = TA_CENTER
flowables = []
@ -99,7 +140,7 @@ def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences:
for term_of_defences in nested_term_of_defences:
if len(term_of_defences) == 0:
continue
date = datetime.strftime(term_of_defences[0].start_date.date(), '%d.%m.%Y')
date = datetime.strftime(term_of_defences[0].start_date.date(), "%d.%m.%Y")
paragraph_1 = Paragraph(f"{title} ~ {date}", normal)
flowables.append(paragraph_1)
data = [headers]
@ -107,7 +148,7 @@ def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences:
for idx, td in enumerate(term_of_defences, start=1):
new_date = td.start_date + timedelta(hours=2)
group_name = td.group.name if td.group is not None else ""
if group_name != '':
if group_name != "":
ps = td.group.project_supervisor
project_supervisor_fullname = f"{ps.first_name[0]}. {ps.last_name}"
students = td.group.students
@ -120,27 +161,40 @@ def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences:
members = td.members_of_committee
# print(members)
if len(members) == 0:
committee = ''
committee = ""
else:
members_iter = (f"{m.first_name[0]} {m.last_name}" for m in members)
committee = ", ".join(members_iter)
data.append([str(idx), new_date.strftime("%H:%M"),
Paragraph(group_name, bodyText),
Paragraph(project_supervisor_fullname, bodyText),
Paragraph(team, bodyText),
Paragraph(committee, bodyText),
])
data.append(
[
str(idx),
new_date.strftime("%H:%M"),
Paragraph(group_name, bodyText),
Paragraph(project_supervisor_fullname, bodyText),
Paragraph(team, bodyText),
Paragraph(committee, bodyText),
]
)
# print(data)
table = Table(data=data,
style=[
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#A6F1A6")),
('BACKGROUND', (0, 0), (1, -1), colors.HexColor("#A6F1A6"))
],
colWidths=[0.25 * inch, 0.7 * inch, 1.6 * inch, 1.5 * inch, 2.5 * inch, 2.2 * inch, 2 * inch]
)
table = Table(
data=data,
style=[
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#A6F1A6")),
("BACKGROUND", (0, 0), (1, -1), colors.HexColor("#A6F1A6")),
],
colWidths=[
0.25 * inch,
0.7 * inch,
1.6 * inch,
1.5 * inch,
2.5 * inch,
2.2 * inch,
2 * inch,
],
)
flowables.append(table)
flowables.append(PageBreak())
@ -150,8 +204,20 @@ def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences:
return pdf_value
def get_duration_time(mode: str) -> int:
duration_time = None
if mode == ModeGroups.NON_STATIONARY.value:
duration_time = 20
elif mode in [
ModeGroups.STATIONARY.value,
ModeGroups.ENGLISH_SPEAKING_STATIONARY.value,
]:
duration_time = 30
return duration_time
def load_weight_for_project_grade_sheet() -> Union[dict, None]:
base_dir = current_app.config.get('BASE_DIR')
base_dir = current_app.config.get("BASE_DIR")
config_dir = base_dir / "config"
with open(config_dir / "weights_project_grade_sheet.json") as f:
@ -160,7 +226,9 @@ def load_weight_for_project_grade_sheet() -> Union[dict, None]:
return data
def calculate_points_for_one_term(weights: dict, project_grade_sheets: List[ProjectGradeSheet]) -> list:
def calculate_points_for_one_term(
weights: dict, project_grade_sheets: List[ProjectGradeSheet]
) -> list:
terms = []
for pgs in project_grade_sheets:
if pgs is None:
@ -168,28 +236,30 @@ def calculate_points_for_one_term(weights: dict, project_grade_sheets: List[Proj
continue
first_term_points = {
'nominator': 0,
'denominator': 0,
"nominator": 0,
"denominator": 0,
}
second_term_points = {
'nominator': 0,
'denominator': 0,
"nominator": 0,
"denominator": 0,
}
for weight_key, weight_value in weights.items():
points = first_term_points if weight_key.endswith('1') else second_term_points
points = (
first_term_points if weight_key.endswith("1") else second_term_points
)
try:
attribute_value = getattr(pgs, weight_key)
except AttributeError:
attribute_value = 0
points['nominator'] += attribute_value * weight_value * 1 / 4
points['denominator'] += weight_value
points["nominator"] += attribute_value * weight_value * 1 / 4
points["denominator"] += weight_value
try:
fp = first_term_points['nominator'] / first_term_points['denominator']
fp = first_term_points["nominator"] / first_term_points["denominator"]
except ZeroDivisionError:
fp = 0
try:
sp = second_term_points['nominator'] / second_term_points['denominator']
sp = second_term_points["nominator"] / second_term_points["denominator"]
except ZeroDivisionError:
sp = 0

View File

@ -1,6 +1,5 @@
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
ma = Marshmallow()

View File

@ -8,6 +8,6 @@ def register_error_handlers(app: APIFlask):
@app.errorhandler(HTTPException)
def handle_http_exception(e):
response = e.get_response()
response.data = json.dumps({'error': e.description})
response.content_type = 'application/json'
response.data = json.dumps({"error": e.description})
response.content_type = "application/json"
return response

View File

@ -1,18 +1,22 @@
from ..dependencies import db
from ..base.mode import EnrollmentsMode
from ..base.models import Base
from ..dependencies import db
class ExaminationSchedule(Base):
__tablename__ = 'examination_schedules'
__tablename__ = "examination_schedules"
title = db.Column(db.String(100), unique=True, nullable=False)
duration_time = db.Column(db.Integer, nullable=False) # in minutes
start_date_for_enrollment_students = db.Column(db.DateTime)
end_date_for_enrollment_students = db.Column(db.DateTime)
open_enrollments = db.Column(
db.String(1), default=EnrollmentsMode.INIT.value, nullable=False
)
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'), nullable=False)
year_group = db.relationship('YearGroup', backref='examination_schedules')
year_group_id = db.Column(
db.Integer, db.ForeignKey("year_groups.id"), nullable=False
)
year_group = db.relationship("YearGroup", backref="examination_schedules")
committee = db.Table(
@ -23,23 +27,42 @@ committee = db.Table(
class TermOfDefence(Base):
__tablename__ = 'term_of_defences'
__tablename__ = "term_of_defences"
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
examination_schedule_id = db.Column(db.Integer, db.ForeignKey('examination_schedules.id'))
examination_schedule = db.relationship('ExaminationSchedule', backref='term_of_defences')
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
group = db.relationship("Group", uselist=False, backref='term_of_defence', lazy='joined')
members_of_committee = db.relationship("ProjectSupervisor", secondary=committee, lazy='joined')
examination_schedule_id = db.Column(
db.Integer, db.ForeignKey("examination_schedules.id")
)
examination_schedule = db.relationship(
"ExaminationSchedule", backref="term_of_defences"
)
group_id = db.Column(db.Integer, db.ForeignKey("groups.id"))
group = db.relationship(
"Group", uselist=False, backref="term_of_defence", lazy="joined"
)
members_of_committee = db.relationship(
"ProjectSupervisor", secondary=committee, lazy="joined"
)
chairman_of_committee = db.Column(
db.Integer, db.ForeignKey("project_supervisors.id")
)
class TemporaryAvailability(Base):
__tablename__ = 'temporary_availabilities'
__tablename__ = "temporary_availabilities"
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
examination_schedule_id = db.Column(db.Integer, db.ForeignKey('examination_schedules.id'), nullable=False)
examination_schedule = db.relationship("ExaminationSchedule", backref='temporary_availabilities')
project_supervisor_id = db.Column(db.Integer, db.ForeignKey('project_supervisors.id'), nullable=False)
project_supervisor = db.relationship("ProjectSupervisor", backref='temporary_availabilities')
examination_schedule_id = db.Column(
db.Integer, db.ForeignKey("examination_schedules.id"), nullable=False
)
examination_schedule = db.relationship(
"ExaminationSchedule", backref="temporary_availabilities"
)
project_supervisor_id = db.Column(
db.Integer, db.ForeignKey("project_supervisors.id"), nullable=False
)
project_supervisor = db.relationship(
"ProjectSupervisor", backref="temporary_availabilities"
)

View File

@ -1,8 +1,8 @@
from flask_sqlalchemy import BaseQuery
from ..dependencies import db
from ..base.models import Person, Base
from ..base.models import Base, Person
from ..base.utils import order_by_column_name
from ..dependencies import db
from ..students.models import YearGroup
@ -10,27 +10,41 @@ class ProjectSupervisor(Base, Person):
__tablename__ = "project_supervisors"
limit_group = db.Column(db.Integer, default=3, nullable=False)
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
year_group = db.relationship('YearGroup', backref='project_supervisors')
is_coordinator = db.Column(db.Boolean, default=False, nullable=False)
year_group_id = db.Column(db.Integer, db.ForeignKey("year_groups.id"))
year_group = db.relationship("YearGroup", backref="project_supervisors")
__table__args = db.UniqueConstraint(
"email", "year_group_id", name="uc_email_year_group_id"
)
@classmethod
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, year_group_id: int = None,
fullname: str = None,
order_by_first_name: str = None,
order_by_last_name: str = None) -> BaseQuery:
def search_by_fullname(
cls,
year_group_id: int = None,
fullname: str = None,
order_by_first_name: str = None,
order_by_last_name: str = None,
) -> BaseQuery:
project_supervisors_query = cls.query
if year_group_id is not None:
project_supervisors_query = project_supervisors_query. \
filter(ProjectSupervisor.year_group_id == year_group_id)
project_supervisors_query = project_supervisors_query.filter(
ProjectSupervisor.year_group_id == year_group_id
)
if fullname is not None:
project_supervisors_query = project_supervisors_query.filter(
(ProjectSupervisor.first_name + ' ' + ProjectSupervisor.last_name).like(f'{fullname}%'))
(ProjectSupervisor.first_name + " " + ProjectSupervisor.last_name).like(
f"{fullname}%"
)
)
project_supervisors_query = order_by_column_name(project_supervisors_query, ProjectSupervisor.first_name,
order_by_first_name)
project_supervisors_query = order_by_column_name(project_supervisors_query, ProjectSupervisor.last_name,
order_by_last_name)
project_supervisors_query = order_by_column_name(
project_supervisors_query, ProjectSupervisor.first_name, order_by_first_name
)
project_supervisors_query = order_by_column_name(
project_supervisors_query, ProjectSupervisor.last_name, order_by_last_name
)
return project_supervisors_query

View File

@ -0,0 +1,31 @@
from flask import abort
from ...dependencies import db
from ...students.models import Group, ProjectGradeSheet
from ..models import ProjectSupervisor
def update_project_grade_sheet(group_id: int, query: dict, data: dict) -> None:
project_supervisor_id = query.get("id")
project_supervisor = ProjectSupervisor.query.filter(
ProjectSupervisor.id == project_supervisor_id
).first()
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
####################################
if len(data) == 0:
abort(400, "You passed empty data!")
group = Group.query.filter(
Group.project_supervisor_id == project_supervisor_id, Group.id == group_id
).first()
if group is None:
abort(400, "You cannot update project grade sheet! It's not your group!")
pgs_query = ProjectGradeSheet.query.filter(ProjectGradeSheet.group_id == group_id)
if pgs_query.first() is None:
abort(404, "Not found project grade sheet!")
pgs_query.update(data)
db.session.commit()

View File

@ -1,53 +1,92 @@
from datetime import datetime
from apiflask import APIBlueprint
from flask import abort
from sqlalchemy import and_, or_
from ..schemas import TimeAvailabilityCreateSchema, TemporaryProjectSupervisorSchema, \
ListOfFreeTimesSchema, ListOfTermOfDefenceSchema
from ...dependencies import db
from ..models import ProjectSupervisor
from ...examination_schedule.models import ExaminationSchedule, TemporaryAvailability, TermOfDefence
from ...base.mode import EnrollmentsMode
from ...base.schemas import MessageSchema
from ...dependencies import db
from ...examination_schedule.models import (
ExaminationSchedule,
TemporaryAvailability,
TermOfDefence,
)
from ..models import ProjectSupervisor
from ..schemas import (
ListOfFreeTimesSchema,
ListOfTermOfDefenceSchema,
TemporaryProjectSupervisorSchema,
TimeAvailabilityCreateSchema,
)
bp = APIBlueprint("enrollments", __name__, url_prefix="/")
@bp.post('/<int:examination_schedule_id>/enrollments/')
@bp.post("/<int:examination_schedule_id>/enrollments/")
@bp.input(TimeAvailabilityCreateSchema)
@bp.output(MessageSchema)
def set_your_free_time_to_examination_schedule(examination_schedule_id: int, data: dict) -> dict:
def set_your_free_time_to_examination_schedule(
examination_schedule_id: int, data: dict
) -> dict:
# this code will be removed
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.id == data['project_supervisor_id']).first()
project_supervisor = ProjectSupervisor.query.filter(
ProjectSupervisor.id == data["project_supervisor_id"]
).first()
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
print(project_supervisor)
################
es = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first()
if es is None:
examination_schedule = ExaminationSchedule.query.filter(
ExaminationSchedule.id == examination_schedule_id
).first()
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
if examination_schedule.year_group_id != project_supervisor.year_group_id:
abort(400, "You are not assigned to this year group!")
sd = data['start_date']
ed = data['end_date']
if examination_schedule.open_enrollments != EnrollmentsMode.INIT.value:
abort(400, "Enrollments has started or closed! You have been delayed!")
sd = data.get("start_date")
ed = data.get("end_date")
if sd > ed:
abort(400, "Invalid data! End date must be greater than start date!")
if not (es.start_date.timestamp() <= sd.timestamp() and es.end_date.timestamp() >= ed.timestamp()):
abort(400, "Date range is not within the examination schedule!")
start_date = examination_schedule.start_date
end_date = examination_schedule.end_date
if not (
start_date.timestamp() <= sd.timestamp()
and end_date.timestamp() >= ed.timestamp()
):
abort(400, "Invalid date range!")
now = datetime.utcnow()
if es.start_date_for_enrollment_students is not None and \
es.start_date_for_enrollment_students.timestamp() < now.timestamp():
abort(403, "Enrollment has started! You cannot set your free time!")
ta = TemporaryAvailability.query.filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id). \
filter(TemporaryAvailability.project_supervisor_id == project_supervisor.id). \
filter(or_(and_(TemporaryAvailability.start_date >= sd, TemporaryAvailability.start_date < ed,
TemporaryAvailability.end_date >= ed),
and_(TemporaryAvailability.start_date <= sd, TemporaryAvailability.end_date > sd,
TemporaryAvailability.end_date <= ed))).first()
ta = (
TemporaryAvailability.query.filter(
TemporaryAvailability.examination_schedule_id == examination_schedule_id
)
.filter(TemporaryAvailability.project_supervisor_id == project_supervisor.id)
.filter(
or_(
and_(
TemporaryAvailability.start_date >= sd,
TemporaryAvailability.start_date < ed,
TemporaryAvailability.end_date >= ed,
),
and_(
TemporaryAvailability.start_date <= sd,
TemporaryAvailability.end_date > sd,
TemporaryAvailability.end_date <= ed,
),
)
)
.first()
)
if ta is not None:
abort(400, "Invalid date ranges. You set your free time in this date range! Choose another date!")
abort(
400,
"Invalid date ranges. You set your free time "
"in this date range! Choose another date!",
)
ta = TemporaryAvailability(**data, examination_schedule_id=examination_schedule_id)
db.session.add(ta)
@ -56,19 +95,37 @@ def set_your_free_time_to_examination_schedule(examination_schedule_id: int, dat
return {"message": "You have just assigned your free time!"}
@bp.delete('/<int:examination_schedule_id>/enrollments/<int:temporary_availability_id>/')
@bp.delete(
"/<int:examination_schedule_id>/enrollments/<int:temporary_availability_id>/"
)
@bp.input(TemporaryProjectSupervisorSchema)
@bp.output(MessageSchema)
def delete_your_free_time_from_examination_schedule(examination_schedule_id: int, temporary_availability_id: int,
data: dict) -> dict:
def delete_your_free_time_from_examination_schedule(
examination_schedule_id: int, temporary_availability_id: int, data: dict
) -> dict:
# this code will be removed
project_supervisor = db.session.query(ProjectSupervisor).filter(ProjectSupervisor.id == data['id']).first()
project_supervisor = (
db.session.query(ProjectSupervisor)
.filter(ProjectSupervisor.id == data["id"])
.first()
)
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
################
ta = TemporaryAvailability.query.filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id,
TemporaryAvailability.project_supervisor_id == project_supervisor.id,
TemporaryAvailability.id == temporary_availability_id).first()
examination_schedule = ExaminationSchedule.query.filter(
ExaminationSchedule.id == examination_schedule_id
).first()
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
if examination_schedule.open_enrollments != EnrollmentsMode.INIT.value:
abort(400, "Enrollments has started or closed! You have been delayed!")
ta = TemporaryAvailability.query.filter(
TemporaryAvailability.examination_schedule_id == examination_schedule_id,
TemporaryAvailability.project_supervisor_id == project_supervisor.id,
TemporaryAvailability.id == temporary_availability_id,
).first()
if ta is None:
abort(404, "Your free time doesn't exist!")
@ -78,49 +135,65 @@ def delete_your_free_time_from_examination_schedule(examination_schedule_id: int
return {"message": "You have just removed your free time!"}
@bp.get('/<int:examination_schedule_id>/temporary-availabilities/')
@bp.input(TemporaryProjectSupervisorSchema, location='query')
@bp.get("/<int:examination_schedule_id>/temporary-availabilities/")
@bp.input(TemporaryProjectSupervisorSchema, location="query")
@bp.output(ListOfFreeTimesSchema)
def list_enrollments_for_project_supervisor(examination_schedule_id: int, data: dict) -> dict:
def list_enrollments_for_project_supervisor(
examination_schedule_id: int, data: dict
) -> dict:
# this code will be removed
project_supervisor = db.session.query(ProjectSupervisor).filter(ProjectSupervisor.id == data['id']).first()
project_supervisor = (
db.session.query(ProjectSupervisor)
.filter(ProjectSupervisor.id == data["id"])
.first()
)
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
################
es = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id,
ExaminationSchedule.year_group_id).first()
if es is None:
examination_schedule = ExaminationSchedule.query.filter(
ExaminationSchedule.id == examination_schedule_id
).first()
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
now = datetime.utcnow()
start_date = es.start_date_for_enrollment_students
if start_date is not None and start_date.timestamp() < now.timestamp():
abort(403, "Forbidden! Enrollment has just started! You cannot assign to the exam committees!")
if examination_schedule.open_enrollments != EnrollmentsMode.INIT.value:
abort(400, "Enrollments has started or closed! You have been delayed!")
# list of your term of defences first enrollment
ta = TemporaryAvailability.query.filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id,
TemporaryAvailability.project_supervisor_id == project_supervisor.id).all()
ta = TemporaryAvailability.query.filter(
TemporaryAvailability.examination_schedule_id == examination_schedule_id,
TemporaryAvailability.project_supervisor_id == project_supervisor.id,
).all()
return {"free_times": ta}
@bp.get('/<int:examination_schedule_id>/term-of-defences/')
@bp.input(TemporaryProjectSupervisorSchema, location='query')
@bp.get("/<int:examination_schedule_id>/term-of-defences/")
@bp.input(TemporaryProjectSupervisorSchema, location="query")
@bp.output(ListOfTermOfDefenceSchema)
def list_created_term_of_defences_by_coordinator_for_project_supervisor(examination_schedule_id: int,
data: dict) -> dict:
def list_created_term_of_defences_by_coordinator_for_project_supervisor(
examination_schedule_id: int, data: dict
) -> dict:
# this code will be removed
project_supervisor = db.session.query(ProjectSupervisor).filter(ProjectSupervisor.id == data['id']).first()
project_supervisor = (
db.session.query(ProjectSupervisor)
.filter(ProjectSupervisor.id == data["id"])
.first()
)
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
################
es = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id,
ExaminationSchedule.year_group_id).first()
es = ExaminationSchedule.query.filter(
ExaminationSchedule.id == examination_schedule_id,
ExaminationSchedule.year_group_id,
).first()
if es is None:
abort(404, "Examination schedule doesn't exist!")
# list of your free times first enrollment
td = TermOfDefence.query.join(TermOfDefence.members_of_committee). \
filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
filter_by(id=project_supervisor.id).all()
td = (
TermOfDefence.query.join(TermOfDefence.members_of_committee)
.filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
.filter_by(id=project_supervisor.id)
.all()
)
return {"term_of_defences": td}

View File

@ -1,29 +1,43 @@
from apiflask import APIBlueprint
from flask import abort
from ...dependencies import db
from ..models import ProjectSupervisor
from ..schemas import ProjectSupervisorTermQuerySchema, TemporaryProjectSupervisorSchema
from ...students.schemas import ProjectGradeSheetDetailFirstTermSchema, ProjectGradeSheetDetailSecondTermSchema, \
ProjectGradeSheetEditFirstTermSchema, ProjectGradeSheetEditSecondTermSchema
from ...students.models import Group, ProjectGradeSheet
from ...base.schemas import MessageSchema
from ...students.models import Group
from ...students.schemas import (
ProjectGradeSheetDetailFirstTermSchema,
ProjectGradeSheetDetailSecondTermSchema,
ProjectGradeSheetEditFirstTermSchema,
ProjectGradeSheetEditSecondTermSchema,
)
from ..models import ProjectSupervisor
from ..query.project_grade_sheet import update_project_grade_sheet
from ..schemas import ProjectSupervisorTermQuerySchema, TemporaryProjectSupervisorSchema
bp = APIBlueprint("project_grade_sheet_for_project_supervisor", __name__, url_prefix="/project-grade-sheet")
bp = APIBlueprint(
"project_grade_sheet_for_project_supervisor",
__name__,
url_prefix="/project-grade-sheet",
)
@bp.get('/group/<int:group_id>/')
@bp.input(ProjectSupervisorTermQuerySchema, location='query')
@bp.get("/group/<int:group_id>/")
@bp.input(ProjectSupervisorTermQuerySchema, location="query")
def detail_project_grade_sheet(group_id: int, query: dict) -> dict:
project_supervisor_id = query.get('id')
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.id == project_supervisor_id).first()
project_supervisor_id = query.get("id")
project_supervisor = ProjectSupervisor.query.filter(
ProjectSupervisor.id == project_supervisor_id
).first()
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
####################################
term = query.get('term')
group = Group.query.filter(Group.project_supervisor_id == project_supervisor_id, Group.id == group_id).first()
term = query.get("term")
group = Group.query.filter(
Group.project_supervisor_id == project_supervisor_id, Group.id == group_id
).first()
if group is None or len(group.project_grade_sheet) == 0:
abort(400, "Group doesn't exist!")
abort(
400, "Group doesn't exist or you are not project supervisor of that group!"
)
pgs = group.project_grade_sheet[0]
if term == 1:
@ -34,36 +48,23 @@ def detail_project_grade_sheet(group_id: int, query: dict) -> dict:
return schema.dump(pgs)
def update_project_grade_sheet(group_id: int, query: dict, data: dict) -> None:
project_supervisor_id = query.get('id')
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.id == project_supervisor_id).first()
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
####################################
if len(data) == 0:
abort(400, "You passed empty data!")
pgs_query = ProjectGradeSheet.query.filter(ProjectGradeSheet.group_id == group_id)
if pgs_query.first() is None:
abort(404, "Not found project grade sheet!")
pgs_query.update(data)
db.session.commit()
@bp.patch('/group/<int:group_id>/first-term/')
@bp.input(TemporaryProjectSupervisorSchema, location='query')
@bp.input(ProjectGradeSheetEditFirstTermSchema, location='json')
@bp.patch("/group/<int:group_id>/first-term/")
@bp.input(TemporaryProjectSupervisorSchema, location="query")
@bp.input(ProjectGradeSheetEditFirstTermSchema, location="json")
@bp.output(MessageSchema)
def update_project_grade_sheet_for_first_term(group_id: int, query: dict, data: dict) -> dict:
def update_project_grade_sheet_for_first_term(
group_id: int, query: dict, data: dict
) -> dict:
update_project_grade_sheet(group_id, query, data)
return {"message": "Your project grade sheet was updated!"}
@bp.patch('/group/<int:group_id>/second-term/')
@bp.input(TemporaryProjectSupervisorSchema, location='query')
@bp.input(ProjectGradeSheetEditSecondTermSchema, location='json')
@bp.patch("/group/<int:group_id>/second-term/")
@bp.input(TemporaryProjectSupervisorSchema, location="query")
@bp.input(ProjectGradeSheetEditSecondTermSchema, location="json")
@bp.output(MessageSchema)
def update_project_grade_sheet_for_second_term(group_id: int, query: dict, data: dict) -> dict:
def update_project_grade_sheet_for_second_term(
group_id: int, query: dict, data: dict
) -> dict:
update_project_grade_sheet(group_id, query, data)
return {"message": "Your project grade sheet was updated!"}

View File

@ -1,4 +1,4 @@
from marshmallow import fields, validate, Schema
from marshmallow import Schema, fields, validate
class FreeTimeSchema(Schema):
@ -18,7 +18,9 @@ class ListOfTermOfDefenceSchema(Schema):
class TimeAvailabilityCreateSchema(Schema):
start_date = fields.DateTime(required=True)
end_date = fields.DateTime(required=True)
project_supervisor_id = fields.Integer(required=True) # temporary field it will be removed in the future
project_supervisor_id = fields.Integer(
required=True
) # temporary field it will be removed in the future
# temporary class it will be removed in the future

View File

@ -2,59 +2,63 @@ from datetime import datetime
from flask_sqlalchemy import BaseQuery
from ..dependencies import db
from ..base.models import Person, Base
from ..base.models import Base, Person
from ..base.utils import order_by_column_name
from ..dependencies import db
from ..examination_schedule.models import TermOfDefence
class YearGroup(Base):
__tablename__ = 'year_groups'
__tablename__ = "year_groups"
name = db.Column(db.String(50), nullable=False)
mode = db.Column(db.String(1), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
__table__args = (
db.UniqueConstraint('name', 'mode', name='uc_name_mode_year_group')
)
__table__args = db.UniqueConstraint("name", "mode", name="uc_name_mode_year_group")
students_groups = db.Table('students_groups',
db.Column('group_id', db.ForeignKey('groups.id'), nullable=False),
db.Column('student_id', db.ForeignKey('students.id'), nullable=False))
students_groups = db.Table(
"students_groups",
db.Column("group_id", db.ForeignKey("groups.id"), nullable=False),
db.Column("student_id", db.ForeignKey("students.id"), nullable=False),
)
class Group(Base):
__tablename__ = "groups"
name = db.Column(db.String(60), nullable=False)
cdyd_kod = db.Column(db.String(60), default='2022/SZ')
prz_kod = db.Column(db.String(60), default='06-DPRILI0')
tzaj_kod = db.Column(db.String(60), default='LAB')
project_supervisor_id = db.Column(db.Integer, db.ForeignKey('project_supervisors.id'))
project_supervisor = db.relationship('ProjectSupervisor', backref='groups')
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
year_group = db.relationship('YearGroup', backref='groups', lazy='joined')
cdyd_kod = db.Column(db.String(60), default="2022/SZ")
prz_kod = db.Column(db.String(60), default="06-DPRILI0")
tzaj_kod = db.Column(db.String(60), default="LAB")
project_supervisor_id = db.Column(
db.Integer, db.ForeignKey("project_supervisors.id")
)
project_supervisor = db.relationship("ProjectSupervisor", backref="groups")
year_group_id = db.Column(db.Integer, db.ForeignKey("year_groups.id"))
year_group = db.relationship("YearGroup", backref="groups", lazy="joined")
points_for_first_term = db.Column(db.Integer, default=0, nullable=False)
points_for_second_term = db.Column(db.Integer, default=0, nullable=False)
students = db.relationship('Student', secondary=students_groups, back_populates='groups')
students = db.relationship(
"Student", secondary=students_groups, back_populates="groups"
)
@classmethod
def search_by_name(cls, year_group_id: int, search_name: str = None) -> BaseQuery:
group_query = cls.query.filter(Group.year_group_id == year_group_id)
if search_name is not None:
group_query = group_query.filter(Group.name.like(f'{search_name}%'))
group_query = group_query.filter(Group.name.like(f"{search_name}%"))
return group_query
class ProjectGradeSheet(Base):
__tablename__ = 'project_grade_sheets'
__tablename__ = "project_grade_sheets"
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
group = db.relationship('Group', backref='project_grade_sheet', uselist=False)
group_id = db.Column(db.Integer, db.ForeignKey("groups.id"))
group = db.relationship("Group", backref="project_grade_sheet", uselist=False)
presentation_required_content_1 = db.Column(db.Integer, default=0)
presentation_required_content_2 = db.Column(db.Integer, default=0)
@ -62,8 +66,12 @@ class ProjectGradeSheet(Base):
presentation_was_compatible_2 = db.Column(db.Integer, default=0)
presentation_showing_1 = db.Column(db.Integer, default=0)
presentation_showing_2 = db.Column(db.Integer, default=0)
presentation_answers_to_questions_from_committee_1 = db.Column(db.Integer, default=0)
presentation_answers_to_questions_from_committee_2 = db.Column(db.Integer, default=0)
presentation_answers_to_questions_from_committee_1 = db.Column(
db.Integer, default=0
)
presentation_answers_to_questions_from_committee_2 = db.Column(
db.Integer, default=0
)
documentation_project_vision_1 = db.Column(db.Integer, default=0)
documentation_project_vision_2 = db.Column(db.Integer, default=0)
@ -121,20 +129,36 @@ class Student(Base, Person):
__tablename__ = "students"
index = db.Column(db.Integer, nullable=False)
groups = db.relationship('Group', secondary=students_groups, back_populates='students')
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
year_group = db.relationship('YearGroup', backref='students')
groups = db.relationship(
"Group", secondary=students_groups, back_populates="students"
)
year_group_id = db.Column(db.Integer, db.ForeignKey("year_groups.id"))
year_group = db.relationship("YearGroup", backref="students")
__table__args = db.UniqueConstraint(
"index", "year_group_id", name="uc_index_year_group_id"
)
@classmethod
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, year_group_id: int, fullname: str = None,
order_by_first_name: str = None,
order_by_last_name: str = None) -> BaseQuery:
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
cls,
year_group_id: int,
fullname: str = None,
order_by_first_name: str = None,
order_by_last_name: str = None,
) -> BaseQuery:
student_query = cls.query.filter(Student.year_group_id == year_group_id)
if fullname is not None:
student_query = student_query.filter((Student.first_name + ' ' + Student.last_name).like(f'{fullname}%'))
student_query = student_query.filter(
(Student.first_name + " " + Student.last_name).like(f"{fullname}%")
)
student_query = order_by_column_name(student_query, Student.first_name, order_by_first_name)
student_query = order_by_column_name(student_query, Student.last_name, order_by_last_name)
student_query = order_by_column_name(
student_query, Student.first_name, order_by_first_name
)
student_query = order_by_column_name(
student_query, Student.last_name, order_by_last_name
)
return student_query

View File

@ -1,46 +1,69 @@
import datetime
from apiflask import APIBlueprint
from flask import abort
from ..schemas import TemporaryStudentSchema, ExaminationScheduleListSchema, \
TermOfDefenceStudentListSchema
from ...base.schemas import MessageSchema
from ...dependencies import db
from ..models import Student, Group, TermOfDefence
from ...examination_schedule.models import ExaminationSchedule
from ...project_supervisor.models import ProjectSupervisor
from ...base.schemas import MessageSchema
from ..models import Group, Student, TermOfDefence
from ..schemas import (
ExaminationScheduleListSchema,
TemporaryStudentSchema,
TermOfDefenceStudentListSchema,
)
bp = APIBlueprint("enrollments", __name__, url_prefix="/")
@bp.post('/<int:examination_schedule_id>/enrollments/<int:term_of_defence_id>/')
@bp.post("/<int:examination_schedule_id>/enrollments/<int:term_of_defence_id>/")
@bp.input(TemporaryStudentSchema)
@bp.output(MessageSchema)
def assign_your_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict:
def assign_your_group_to_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
# this code will be removed
student = Student.query.filter(Student.index == data['student_index']).first()
student = Student.query.filter(Student.id == data.get("student_id")).first()
if student is None:
abort(404, "Student doesn't exist!")
abort(404, "Not found student!")
################
term_of_defence = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id).first()
ex = term_of_defence.examination_schedule
if term_of_defence is None or ex is None:
term_of_defence = (
TermOfDefence.query.filter(
TermOfDefence.id == term_of_defence_id,
TermOfDefence.examination_schedule_id == examination_schedule_id,
)
.join(ExaminationSchedule)
.first()
)
if term_of_defence is None or (ex := term_of_defence.examination_schedule) is None:
abort(400, "Term of defence not found!")
g = Group.query.join(ProjectSupervisor).filter(Group.year_group_id == ex.year_group_id). \
join(Group.students).filter_by(index=student.index).first()
g = (
Group.query.join(ProjectSupervisor)
.filter(Group.year_group_id == ex.year_group_id)
.join(Group.students)
.filter_by(index=student.index)
.first()
)
if g is None or g.project_supervisor is None:
abort(400, "You don't have a group or your group doesn't have an assigned project supervisor!")
abort(
400,
"You don't have a group or your group doesn't"
" have an assigned project supervisor!",
)
defence = TermOfDefence.query.filter(TermOfDefence.group_id == g.id,
TermOfDefence.examination_schedule_id == examination_schedule_id).first()
defence = TermOfDefence.query.filter(
TermOfDefence.group_id == g.id,
TermOfDefence.examination_schedule_id == examination_schedule_id,
).first()
if defence is not None:
abort(400, "Your group has already assigned to any exam date!")
td = TermOfDefence.query.join(TermOfDefence.members_of_committee). \
filter_by(id=g.project_supervisor_id).first()
td = (
TermOfDefence.query.join(TermOfDefence.members_of_committee)
.filter_by(id=g.project_supervisor_id)
.first()
)
if td is None:
abort(400, "Your project supervisor is not in committee!")
@ -51,25 +74,35 @@ def assign_your_group_to_term_of_defence(examination_schedule_id: int, term_of_d
return {"message": "You have just assigned the group for this exam date!"}
@bp.delete('/<int:examination_schedule_id>/enrollments/<int:term_of_defence_id>/')
@bp.delete("/<int:examination_schedule_id>/enrollments/<int:term_of_defence_id>/")
@bp.input(TemporaryStudentSchema)
@bp.output(MessageSchema)
def delete_your_group_from_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict:
def delete_your_group_from_term_of_defence(
examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
# this code will be removed
student = Student.query.filter(Student.index == data['student_index']).first()
student = Student.query.filter(Student.id == data.get("student_id")).first()
if student is None:
abort(404, "Student doesn't exist!")
abort(404, "Not found student!")
################
term_of_defence = TermOfDefence.query.join(ExaminationSchedule).filter(TermOfDefence.id == term_of_defence_id). \
filter(TermOfDefence.examination_schedule_id == examination_schedule_id).first()
term_of_defence = (
TermOfDefence.query.join(ExaminationSchedule)
.filter(TermOfDefence.id == term_of_defence_id)
.filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
.first()
)
ex = term_of_defence.examination_schedule
if term_of_defence is None:
abort(404, "Term of defence doesn't exist!")
group = Group.query.filter(Group.year_group_id == ex.year_group_id). \
join(Group.students).filter_by(index=student.index).first()
group = (
Group.query.filter(Group.year_group_id == ex.year_group_id)
.join(Group.students)
.filter_by(index=student.index)
.first()
)
if group.id != term_of_defence.group_id:
abort(400, "You are not assigned to this group!")
@ -80,41 +113,51 @@ def delete_your_group_from_term_of_defence(examination_schedule_id: int, term_of
return {"message": "You have just removed the group for this exam date!"}
@bp.get('/examination-schedule/year-group/<int:year_group_id>/')
@bp.input(TemporaryStudentSchema, location='query')
@bp.get("/examination-schedule/year-group/<int:year_group_id>/")
@bp.input(TemporaryStudentSchema, location="query")
@bp.output(ExaminationScheduleListSchema)
def list_examination_schedule(year_group_id: int, data: dict) -> dict:
# this code will be removed
student = Student.query.filter(Student.index == data['student_index']).first()
student = Student.query.filter(Student.id == data.get("student_id")).first()
if student is None:
abort(404, "Student doesn't exist!")
abort(404, "Not found student!")
################
# in the future filter after the mode of examination schedule if we will have authorization module
now = datetime.datetime.utcnow()
examination_schedules = ExaminationSchedule.query. \
filter(ExaminationSchedule.year_group_id == year_group_id). \
filter(ExaminationSchedule.start_date_for_enrollment_students < now). \
filter(ExaminationSchedule.end_date_for_enrollment_students > now).all()
return {'examination_schedules': examination_schedules}
examination_schedules = ExaminationSchedule.query.filter(
ExaminationSchedule.year_group_id == year_group_id
).all()
return {"examination_schedules": examination_schedules}
@bp.get('/examination-schedule/<int:examination_schedule_id>/enrollments/')
@bp.input(TemporaryStudentSchema, location='query')
@bp.get("/examination-schedule/<int:examination_schedule_id>/enrollments/")
@bp.input(TemporaryStudentSchema, location="query")
@bp.output(TermOfDefenceStudentListSchema)
def list_term_of_defences(examination_schedule_id: int, data: dict) -> dict:
# this code will be removed
student = Student.query.filter(Student.index == data['student_index']).first()
student = Student.query.filter(Student.id == data.get("student_id")).first()
if student is None:
abort(404, "Student doesn't exist!")
abort(404, "Not found student!")
################
# in the future filter after the mode of examination schedule if we will have authorization module
now = datetime.datetime.utcnow()
term_of_defences = TermOfDefence.query.filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
join(ExaminationSchedule, isouter=True). \
filter(ExaminationSchedule.start_date_for_enrollment_students < now). \
filter(ExaminationSchedule.end_date_for_enrollment_students > now).all()
term_of_defences = (
TermOfDefence.query.filter(
TermOfDefence.examination_schedule_id == examination_schedule_id
)
.join(ExaminationSchedule, isouter=True)
.all()
)
term_of_defences = list(filter(lambda n: len([d.id for d in n.members_of_committee if d.id == student.groups[0].project_supervisor.id]) > 0, term_of_defences))
term_of_defences = list(
filter(
lambda n: len(
[
d.id
for d in n.members_of_committee
if d.id == student.groups[0].project_supervisor.id
]
)
> 0,
term_of_defences,
)
)
return {'term_of_defences': term_of_defences}
return {"term_of_defences": term_of_defences}

View File

@ -1,22 +1,25 @@
from apiflask import APIBlueprint
from flask import abort
from ..schemas import StudentIndexQueryTempSchema, ProjectGradeSheetDetailFirstTermSchema, \
ProjectGradeSheetDetailSecondTermSchema
from ..models import Student, ProjectGradeSheet
from ..models import ProjectGradeSheet, Student
from ..schemas import (
ProjectGradeSheetDetailFirstTermSchema,
ProjectGradeSheetDetailSecondTermSchema,
StudentIndexQueryTempSchema,
)
bp = APIBlueprint("project_grade_sheet", __name__, url_prefix="/project-grade-sheet")
@bp.get('/year-group/<int:year_group_id>/')
@bp.input(StudentIndexQueryTempSchema, location='query')
@bp.get("/year-group/<int:year_group_id>/")
@bp.input(StudentIndexQueryTempSchema, location="query")
def detail_project_grade_sheet(year_group_id: int, query: dict) -> dict:
index = query.get('index')
st = Student.query.filter(Student.index == index).first()
student_id = query.get("student_id")
st = Student.query.filter(Student.id == student_id).first()
if st is None:
abort(404, "Not found student!")
####################################
term = int(query.get('term'))
term = int(query.get("term"))
groups = [g for g in st.groups if g.year_group_id == year_group_id]
if len(groups) == 0:

View File

@ -1,36 +1,34 @@
from apiflask import APIBlueprint
from ...base.utils import paginate_models
from ...dependencies import db
from ...project_supervisor.models import ProjectSupervisor
from ..models import Group
from ...dependencies import db
from ..schemas import ProjectSupervisorQuerySchema, ProjectSupervisorPaginationSchema
from ...base.utils import paginate_models
from ..schemas import ProjectSupervisorPaginationSchema, ProjectSupervisorQuerySchema
bp = APIBlueprint("registrations", __name__, url_prefix="/registrations")
@bp.get('/<int:year_group_id>/')
@bp.input(ProjectSupervisorQuerySchema, location='query')
@bp.get("/<int:year_group_id>/")
@bp.input(ProjectSupervisorQuerySchema, location="query")
@bp.output(ProjectSupervisorPaginationSchema)
def list_available_groups(year_group_id: int, query: dict) -> dict:
page = query.get('page')
per_page = query.get('per_page')
page = query.get("page")
per_page = query.get("per_page")
available_groups = (ProjectSupervisor.limit_group - db.func.count(Group.id))
ps_query = db.session. \
query(ProjectSupervisor, available_groups). \
join(Group, isouter=True). \
filter(ProjectSupervisor.year_group_id == year_group_id).\
group_by(ProjectSupervisor.id)
available_groups = ProjectSupervisor.limit_group - db.func.count(Group.id)
ps_query = (
db.session.query(ProjectSupervisor, available_groups)
.join(Group, isouter=True)
.filter(ProjectSupervisor.year_group_id == year_group_id)
.group_by(ProjectSupervisor.id)
)
data = paginate_models(page, ps_query, per_page)
project_supervisors = []
for project_supervisor, available_groups in data['items']:
setattr(project_supervisor, 'available_groups', available_groups)
for project_supervisor, available_groups in data["items"]:
setattr(project_supervisor, "available_groups", available_groups)
project_supervisors.append(project_supervisor)
return {
"project_supervisors": project_supervisors,
"max_pages": data['max_pages']
}
return {"project_supervisors": project_supervisors, "max_pages": data["max_pages"]}

View File

@ -1,9 +1,9 @@
from marshmallow import fields, Schema, validate
from marshmallow import Schema, fields, validate
POINTS = [0, 1, 3, 4]
class ProjectSupervisorSchema(Schema):
class ProjectSupervisorWithAvailableGroupsSchema(Schema):
first_name = fields.Str()
last_name = fields.Str()
email = fields.Str()
@ -11,7 +11,9 @@ class ProjectSupervisorSchema(Schema):
class ProjectSupervisorPaginationSchema(Schema):
project_supervisors = fields.List(fields.Nested(ProjectSupervisorSchema))
project_supervisors = fields.List(
fields.Nested(ProjectSupervisorWithAvailableGroupsSchema)
)
max_pages = fields.Integer()
@ -21,18 +23,19 @@ class ProjectSupervisorQuerySchema(Schema):
class TemporaryStudentSchema(Schema):
student_index = fields.Integer(required=True)
student_id = fields.Integer(required=True)
class ExaminationScheduleSchema(Schema):
class ExaminationScheduleStudentSchema(Schema):
id = fields.Integer()
title = fields.Str()
start_date = fields.DateTime()
end_date = fields.DateTime()
open_enrollments = fields.Boolean()
class ExaminationScheduleListSchema(Schema):
examination_schedules = fields.List(fields.Nested(ExaminationScheduleSchema))
examination_schedules = fields.List(fields.Nested(ExaminationScheduleStudentSchema))
class ProjectSupervisorCommitteeSchema(Schema):
@ -64,11 +67,13 @@ class AssignedGroupToTermOfDefenceItemSchema(TermOfDefenceStudentItemSchema):
class TermOfDefenceStudentListSchema(Schema):
term_of_defences = fields.List(fields.Nested(AssignedGroupToTermOfDefenceItemSchema))
term_of_defences = fields.List(
fields.Nested(AssignedGroupToTermOfDefenceItemSchema)
)
class StudentIndexQueryTempSchema(Schema):
index = fields.Integer(required=True) # it will be removed
student_id = fields.Integer(required=True) # it will be removed
term = fields.Integer(required=True, validate=validate.OneOf([1, 2]))
@ -76,7 +81,9 @@ class ProjectGradeSheetEditFirstTermSchema(Schema):
presentation_required_content_1 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_was_compatible_1 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_showing_1 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_answers_to_questions_from_committee_1 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_answers_to_questions_from_committee_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
documentation_project_vision_1 = fields.Integer(validate=validate.OneOf(POINTS))
documentation_requirements_1 = fields.Integer(validate=validate.OneOf(POINTS))
@ -89,17 +96,31 @@ class ProjectGradeSheetEditFirstTermSchema(Schema):
group_work_contact_with_client_1 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_management_of_risk_1 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_work_methodology_1 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_management_of_source_code_1 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_management_of_source_code_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
group_work_devops_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_complexity_of_product_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_access_to_application_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_complexity_of_product_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_access_to_application_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_security_issues_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_access_to_test_application_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_acceptance_criteria_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_expected_functionality_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_access_to_test_application_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_acceptance_criteria_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_expected_functionality_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_promises_well_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_has_been_implemented_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_has_been_implemented_1 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_is_useful_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_prototype_1 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_tests_1 = fields.Integer(validate=validate.OneOf(POINTS))
@ -114,7 +135,9 @@ class ProjectGradeSheetEditSecondTermSchema(Schema):
presentation_required_content_2 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_was_compatible_2 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_showing_2 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_answers_to_questions_from_committee_2 = fields.Integer(validate=validate.OneOf(POINTS))
presentation_answers_to_questions_from_committee_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
documentation_project_vision_2 = fields.Integer(validate=validate.OneOf(POINTS))
documentation_requirements_2 = fields.Integer(validate=validate.OneOf(POINTS))
@ -127,17 +150,31 @@ class ProjectGradeSheetEditSecondTermSchema(Schema):
group_work_contact_with_client_2 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_management_of_risk_2 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_work_methodology_2 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_management_of_source_code_2 = fields.Integer(validate=validate.OneOf(POINTS))
group_work_management_of_source_code_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
group_work_devops_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_complexity_of_product_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_access_to_application_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_complexity_of_product_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_access_to_application_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_security_issues_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_access_to_test_application_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_acceptance_criteria_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_expected_functionality_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_access_to_test_application_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_acceptance_criteria_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_expected_functionality_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_promises_well_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_has_been_implemented_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_has_been_implemented_2 = fields.Integer(
validate=validate.OneOf(POINTS)
)
products_project_is_useful_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_prototype_2 = fields.Integer(validate=validate.OneOf(POINTS))
products_project_tests_2 = fields.Integer(validate=validate.OneOf(POINTS))

View File

@ -1,5 +1,5 @@
import os
import importlib
import os
import warnings
from flask import current_app
@ -7,8 +7,8 @@ from flask import current_app
def get_app_directories() -> list:
directories = []
src_dir = current_app.config['SRC_DIR']
excluded_dirs = current_app.config['EXCLUDED_DIRS']
src_dir = current_app.config["SRC_DIR"]
excluded_dirs = current_app.config["EXCLUDED_DIRS"]
for dirname in os.listdir(src_dir):
path = src_dir / dirname

View File

@ -1,4 +1,5 @@
from dotenv import load_dotenv
from app import create_app
load_dotenv()

View File

@ -3,9 +3,8 @@ from __future__ import with_statement
import logging
from logging.config import fileConfig
from flask import current_app
from alembic import context
from flask import current_app
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
@ -14,17 +13,17 @@ config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
logger = logging.getLogger("alembic.env")
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
config.set_main_option(
'sqlalchemy.url',
str(current_app.extensions['migrate'].db.get_engine().url).replace(
'%', '%%'))
target_metadata = current_app.extensions['migrate'].db.metadata
"sqlalchemy.url",
str(current_app.extensions["migrate"].db.get_engine().url).replace("%", "%%"),
)
target_metadata = current_app.extensions["migrate"].db.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
@ -45,9 +44,7 @@ def run_migrations_offline():
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True
)
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
@ -65,20 +62,20 @@ def run_migrations_online():
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
if getattr(config.cmd_opts, "autogenerate", False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
logger.info("No changes in schema detected.")
connectable = current_app.extensions['migrate'].db.get_engine()
connectable = current_app.extensions["migrate"].db.get_engine()
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
**current_app.extensions["migrate"].configure_args
)
with context.begin_transaction():

View File

@ -1,196 +0,0 @@
"""empty message
Revision ID: 3fd120fc5e12
Revises:
Create Date: 2023-01-14 00:03:06.327441
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3fd120fc5e12'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('year_groups',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=50), nullable=False),
sa.Column('mode', sa.String(length=1), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_table('examination_schedules',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('title', sa.String(length=100), nullable=False),
sa.Column('duration_time', sa.Integer(), nullable=False),
sa.Column('start_date_for_enrollment_students', sa.DateTime(), nullable=True),
sa.Column('end_date_for_enrollment_students', sa.DateTime(), nullable=True),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('end_date', sa.DateTime(), nullable=False),
sa.Column('year_group_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('title')
)
op.create_table('project_supervisors',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('first_name', sa.String(length=255), nullable=False),
sa.Column('last_name', sa.String(length=255), nullable=False),
sa.Column('email', sa.String(length=120), nullable=False),
sa.Column('limit_group', sa.Integer(), nullable=False),
sa.Column('year_group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_project_supervisors_email'), 'project_supervisors', ['email'], unique=False)
op.create_index(op.f('ix_project_supervisors_first_name'), 'project_supervisors', ['first_name'], unique=False)
op.create_index(op.f('ix_project_supervisors_last_name'), 'project_supervisors', ['last_name'], unique=False)
op.create_table('students',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('first_name', sa.String(length=255), nullable=False),
sa.Column('last_name', sa.String(length=255), nullable=False),
sa.Column('email', sa.String(length=120), nullable=False),
sa.Column('index', sa.Integer(), nullable=False),
sa.Column('year_group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_students_email'), 'students', ['email'], unique=False)
op.create_index(op.f('ix_students_first_name'), 'students', ['first_name'], unique=False)
op.create_index(op.f('ix_students_last_name'), 'students', ['last_name'], unique=False)
op.create_table('groups',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=60), nullable=False),
sa.Column('cdyd_kod', sa.String(length=60), nullable=True),
sa.Column('prz_kod', sa.String(length=60), nullable=True),
sa.Column('tzaj_kod', sa.String(length=60), nullable=True),
sa.Column('project_supervisor_id', sa.Integer(), nullable=True),
sa.Column('year_group_id', sa.Integer(), nullable=True),
sa.Column('points_for_first_term', sa.Integer(), nullable=False),
sa.Column('points_for_second_term', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('temporary_availabilities',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('end_date', sa.DateTime(), nullable=False),
sa.Column('examination_schedule_id', sa.Integer(), nullable=False),
sa.Column('project_supervisor_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['examination_schedule_id'], ['examination_schedules.id'], ),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('project_grade_sheets',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('group_id', sa.Integer(), nullable=True),
sa.Column('presentation_required_content_1', sa.Integer(), nullable=True),
sa.Column('presentation_required_content_2', sa.Integer(), nullable=True),
sa.Column('presentation_was_compatible_1', sa.Integer(), nullable=True),
sa.Column('presentation_was_compatible_2', sa.Integer(), nullable=True),
sa.Column('presentation_showing_1', sa.Integer(), nullable=True),
sa.Column('presentation_showing_2', sa.Integer(), nullable=True),
sa.Column('presentation_answers_to_questions_from_committee_1', sa.Integer(), nullable=True),
sa.Column('presentation_answers_to_questions_from_committee_2', sa.Integer(), nullable=True),
sa.Column('documentation_project_vision_1', sa.Integer(), nullable=True),
sa.Column('documentation_project_vision_2', sa.Integer(), nullable=True),
sa.Column('documentation_requirements_1', sa.Integer(), nullable=True),
sa.Column('documentation_requirements_2', sa.Integer(), nullable=True),
sa.Column('documentation_for_clients_1', sa.Integer(), nullable=True),
sa.Column('documentation_for_clients_2', sa.Integer(), nullable=True),
sa.Column('documentation_for_developers_1', sa.Integer(), nullable=True),
sa.Column('documentation_for_developers_2', sa.Integer(), nullable=True),
sa.Column('documentation_license_1', sa.Integer(), nullable=True),
sa.Column('documentation_license_2', sa.Integer(), nullable=True),
sa.Column('group_work_regularity_1', sa.Integer(), nullable=True),
sa.Column('group_work_regularity_2', sa.Integer(), nullable=True),
sa.Column('group_work_division_of_work_1', sa.Integer(), nullable=True),
sa.Column('group_work_division_of_work_2', sa.Integer(), nullable=True),
sa.Column('group_work_contact_with_client_1', sa.Integer(), nullable=True),
sa.Column('group_work_contact_with_client_2', sa.Integer(), nullable=True),
sa.Column('group_work_management_of_risk_1', sa.Integer(), nullable=True),
sa.Column('group_work_management_of_risk_2', sa.Integer(), nullable=True),
sa.Column('group_work_work_methodology_1', sa.Integer(), nullable=True),
sa.Column('group_work_work_methodology_2', sa.Integer(), nullable=True),
sa.Column('group_work_management_of_source_code_1', sa.Integer(), nullable=True),
sa.Column('group_work_management_of_source_code_2', sa.Integer(), nullable=True),
sa.Column('group_work_devops_1', sa.Integer(), nullable=True),
sa.Column('group_work_devops_2', sa.Integer(), nullable=True),
sa.Column('products_project_complexity_of_product_1', sa.Integer(), nullable=True),
sa.Column('products_project_complexity_of_product_2', sa.Integer(), nullable=True),
sa.Column('products_project_access_to_application_1', sa.Integer(), nullable=True),
sa.Column('products_project_access_to_application_2', sa.Integer(), nullable=True),
sa.Column('products_project_security_issues_1', sa.Integer(), nullable=True),
sa.Column('products_project_security_issues_2', sa.Integer(), nullable=True),
sa.Column('products_project_access_to_test_application_1', sa.Integer(), nullable=True),
sa.Column('products_project_access_to_test_application_2', sa.Integer(), nullable=True),
sa.Column('products_project_acceptance_criteria_1', sa.Integer(), nullable=True),
sa.Column('products_project_acceptance_criteria_2', sa.Integer(), nullable=True),
sa.Column('products_project_expected_functionality_1', sa.Integer(), nullable=True),
sa.Column('products_project_expected_functionality_2', sa.Integer(), nullable=True),
sa.Column('products_project_promises_well_1', sa.Integer(), nullable=True),
sa.Column('products_project_promises_well_2', sa.Integer(), nullable=True),
sa.Column('products_project_has_been_implemented_1', sa.Integer(), nullable=True),
sa.Column('products_project_has_been_implemented_2', sa.Integer(), nullable=True),
sa.Column('products_project_is_useful_1', sa.Integer(), nullable=True),
sa.Column('products_project_is_useful_2', sa.Integer(), nullable=True),
sa.Column('products_project_prototype_1', sa.Integer(), nullable=True),
sa.Column('products_project_prototype_2', sa.Integer(), nullable=True),
sa.Column('products_project_tests_1', sa.Integer(), nullable=True),
sa.Column('products_project_tests_2', sa.Integer(), nullable=True),
sa.Column('products_project_technology_1', sa.Integer(), nullable=True),
sa.Column('products_project_technology_2', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('students_groups',
sa.Column('group_id', sa.Integer(), nullable=False),
sa.Column('student_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.ForeignKeyConstraint(['student_id'], ['students.id'], )
)
op.create_table('term_of_defences',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('end_date', sa.DateTime(), nullable=False),
sa.Column('examination_schedule_id', sa.Integer(), nullable=True),
sa.Column('group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['examination_schedule_id'], ['examination_schedules.id'], ),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('committees',
sa.Column('term_of_defence_id', sa.Integer(), nullable=True),
sa.Column('project_supervisor_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.ForeignKeyConstraint(['term_of_defence_id'], ['term_of_defences.id'], )
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('committees')
op.drop_table('term_of_defences')
op.drop_table('students_groups')
op.drop_table('project_grade_sheets')
op.drop_table('temporary_availabilities')
op.drop_table('groups')
op.drop_index(op.f('ix_students_last_name'), table_name='students')
op.drop_index(op.f('ix_students_first_name'), table_name='students')
op.drop_index(op.f('ix_students_email'), table_name='students')
op.drop_table('students')
op.drop_index(op.f('ix_project_supervisors_last_name'), table_name='project_supervisors')
op.drop_index(op.f('ix_project_supervisors_first_name'), table_name='project_supervisors')
op.drop_index(op.f('ix_project_supervisors_email'), table_name='project_supervisors')
op.drop_table('project_supervisors')
op.drop_table('examination_schedules')
op.drop_table('year_groups')
# ### end Alembic commands ###

View File

@ -0,0 +1,313 @@
"""empty message
Revision ID: 559c8f18a125
Revises:
Create Date: 2023-01-14 15:25:59.137169
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "559c8f18a125"
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"year_groups",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("name", sa.String(length=50), nullable=False),
sa.Column("mode", sa.String(length=1), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"examination_schedules",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("title", sa.String(length=100), nullable=False),
sa.Column("duration_time", sa.Integer(), nullable=False),
sa.Column("open_enrollments", sa.String(length=1), nullable=False),
sa.Column("start_date", sa.DateTime(), nullable=False),
sa.Column("end_date", sa.DateTime(), nullable=False),
sa.Column("year_group_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["year_group_id"],
["year_groups.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("title"),
)
op.create_table(
"project_supervisors",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("first_name", sa.String(length=255), nullable=False),
sa.Column("last_name", sa.String(length=255), nullable=False),
sa.Column("email", sa.String(length=120), nullable=False),
sa.Column("limit_group", sa.Integer(), nullable=False),
sa.Column("is_coordinator", sa.Boolean(), nullable=False),
sa.Column("year_group_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["year_group_id"],
["year_groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_project_supervisors_email"),
"project_supervisors",
["email"],
unique=False,
)
op.create_index(
op.f("ix_project_supervisors_first_name"),
"project_supervisors",
["first_name"],
unique=False,
)
op.create_index(
op.f("ix_project_supervisors_last_name"),
"project_supervisors",
["last_name"],
unique=False,
)
op.create_table(
"students",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("first_name", sa.String(length=255), nullable=False),
sa.Column("last_name", sa.String(length=255), nullable=False),
sa.Column("email", sa.String(length=120), nullable=False),
sa.Column("index", sa.Integer(), nullable=False),
sa.Column("year_group_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["year_group_id"],
["year_groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_students_email"), "students", ["email"], unique=False)
op.create_index(
op.f("ix_students_first_name"), "students", ["first_name"], unique=False
)
op.create_index(
op.f("ix_students_last_name"), "students", ["last_name"], unique=False
)
op.create_table(
"groups",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("name", sa.String(length=60), nullable=False),
sa.Column("cdyd_kod", sa.String(length=60), nullable=True),
sa.Column("prz_kod", sa.String(length=60), nullable=True),
sa.Column("tzaj_kod", sa.String(length=60), nullable=True),
sa.Column("project_supervisor_id", sa.Integer(), nullable=True),
sa.Column("year_group_id", sa.Integer(), nullable=True),
sa.Column("points_for_first_term", sa.Integer(), nullable=False),
sa.Column("points_for_second_term", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["project_supervisor_id"],
["project_supervisors.id"],
),
sa.ForeignKeyConstraint(
["year_group_id"],
["year_groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"temporary_availabilities",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("start_date", sa.DateTime(), nullable=False),
sa.Column("end_date", sa.DateTime(), nullable=False),
sa.Column("examination_schedule_id", sa.Integer(), nullable=False),
sa.Column("project_supervisor_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["examination_schedule_id"],
["examination_schedules.id"],
),
sa.ForeignKeyConstraint(
["project_supervisor_id"],
["project_supervisors.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"project_grade_sheets",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("group_id", sa.Integer(), nullable=True),
sa.Column("presentation_required_content_1", sa.Integer(), nullable=True),
sa.Column("presentation_required_content_2", sa.Integer(), nullable=True),
sa.Column("presentation_was_compatible_1", sa.Integer(), nullable=True),
sa.Column("presentation_was_compatible_2", sa.Integer(), nullable=True),
sa.Column("presentation_showing_1", sa.Integer(), nullable=True),
sa.Column("presentation_showing_2", sa.Integer(), nullable=True),
sa.Column(
"presentation_answers_to_questions_from_committee_1",
sa.Integer(),
nullable=True,
),
sa.Column(
"presentation_answers_to_questions_from_committee_2",
sa.Integer(),
nullable=True,
),
sa.Column("documentation_project_vision_1", sa.Integer(), nullable=True),
sa.Column("documentation_project_vision_2", sa.Integer(), nullable=True),
sa.Column("documentation_requirements_1", sa.Integer(), nullable=True),
sa.Column("documentation_requirements_2", sa.Integer(), nullable=True),
sa.Column("documentation_for_clients_1", sa.Integer(), nullable=True),
sa.Column("documentation_for_clients_2", sa.Integer(), nullable=True),
sa.Column("documentation_for_developers_1", sa.Integer(), nullable=True),
sa.Column("documentation_for_developers_2", sa.Integer(), nullable=True),
sa.Column("documentation_license_1", sa.Integer(), nullable=True),
sa.Column("documentation_license_2", sa.Integer(), nullable=True),
sa.Column("group_work_regularity_1", sa.Integer(), nullable=True),
sa.Column("group_work_regularity_2", sa.Integer(), nullable=True),
sa.Column("group_work_division_of_work_1", sa.Integer(), nullable=True),
sa.Column("group_work_division_of_work_2", sa.Integer(), nullable=True),
sa.Column("group_work_contact_with_client_1", sa.Integer(), nullable=True),
sa.Column("group_work_contact_with_client_2", sa.Integer(), nullable=True),
sa.Column("group_work_management_of_risk_1", sa.Integer(), nullable=True),
sa.Column("group_work_management_of_risk_2", sa.Integer(), nullable=True),
sa.Column("group_work_work_methodology_1", sa.Integer(), nullable=True),
sa.Column("group_work_work_methodology_2", sa.Integer(), nullable=True),
sa.Column(
"group_work_management_of_source_code_1", sa.Integer(), nullable=True
),
sa.Column(
"group_work_management_of_source_code_2", sa.Integer(), nullable=True
),
sa.Column("group_work_devops_1", sa.Integer(), nullable=True),
sa.Column("group_work_devops_2", sa.Integer(), nullable=True),
sa.Column(
"products_project_complexity_of_product_1", sa.Integer(), nullable=True
),
sa.Column(
"products_project_complexity_of_product_2", sa.Integer(), nullable=True
),
sa.Column(
"products_project_access_to_application_1", sa.Integer(), nullable=True
),
sa.Column(
"products_project_access_to_application_2", sa.Integer(), nullable=True
),
sa.Column("products_project_security_issues_1", sa.Integer(), nullable=True),
sa.Column("products_project_security_issues_2", sa.Integer(), nullable=True),
sa.Column(
"products_project_access_to_test_application_1", sa.Integer(), nullable=True
),
sa.Column(
"products_project_access_to_test_application_2", sa.Integer(), nullable=True
),
sa.Column(
"products_project_acceptance_criteria_1", sa.Integer(), nullable=True
),
sa.Column(
"products_project_acceptance_criteria_2", sa.Integer(), nullable=True
),
sa.Column(
"products_project_expected_functionality_1", sa.Integer(), nullable=True
),
sa.Column(
"products_project_expected_functionality_2", sa.Integer(), nullable=True
),
sa.Column("products_project_promises_well_1", sa.Integer(), nullable=True),
sa.Column("products_project_promises_well_2", sa.Integer(), nullable=True),
sa.Column(
"products_project_has_been_implemented_1", sa.Integer(), nullable=True
),
sa.Column(
"products_project_has_been_implemented_2", sa.Integer(), nullable=True
),
sa.Column("products_project_is_useful_1", sa.Integer(), nullable=True),
sa.Column("products_project_is_useful_2", sa.Integer(), nullable=True),
sa.Column("products_project_prototype_1", sa.Integer(), nullable=True),
sa.Column("products_project_prototype_2", sa.Integer(), nullable=True),
sa.Column("products_project_tests_1", sa.Integer(), nullable=True),
sa.Column("products_project_tests_2", sa.Integer(), nullable=True),
sa.Column("products_project_technology_1", sa.Integer(), nullable=True),
sa.Column("products_project_technology_2", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"students_groups",
sa.Column("group_id", sa.Integer(), nullable=False),
sa.Column("student_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(
["student_id"],
["students.id"],
),
)
op.create_table(
"term_of_defences",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("start_date", sa.DateTime(), nullable=False),
sa.Column("end_date", sa.DateTime(), nullable=False),
sa.Column("examination_schedule_id", sa.Integer(), nullable=True),
sa.Column("group_id", sa.Integer(), nullable=True),
sa.Column("chairman_of_committee", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["chairman_of_committee"],
["project_supervisors.id"],
),
sa.ForeignKeyConstraint(
["examination_schedule_id"],
["examination_schedules.id"],
),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"committees",
sa.Column("term_of_defence_id", sa.Integer(), nullable=True),
sa.Column("project_supervisor_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["project_supervisor_id"],
["project_supervisors.id"],
),
sa.ForeignKeyConstraint(
["term_of_defence_id"],
["term_of_defences.id"],
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("committees")
op.drop_table("term_of_defences")
op.drop_table("students_groups")
op.drop_table("project_grade_sheets")
op.drop_table("temporary_availabilities")
op.drop_table("groups")
op.drop_index(op.f("ix_students_last_name"), table_name="students")
op.drop_index(op.f("ix_students_first_name"), table_name="students")
op.drop_index(op.f("ix_students_email"), table_name="students")
op.drop_table("students")
op.drop_index(
op.f("ix_project_supervisors_last_name"), table_name="project_supervisors"
)
op.drop_index(
op.f("ix_project_supervisors_first_name"), table_name="project_supervisors"
)
op.drop_index(
op.f("ix_project_supervisors_email"), table_name="project_supervisors"
)
op.drop_table("project_supervisors")
op.drop_table("examination_schedules")
op.drop_table("year_groups")
# ### end Alembic commands ###

2
backend/pyproject.toml Normal file
View File

@ -0,0 +1,2 @@
[tool.isort]
profile = "black"

View File

@ -14,3 +14,6 @@ python-dotenv==0.21.0
factory_boy>=3.2.1,<3.3.0
reportlab>=3.6.12,<3.7.0
gunicorn>=20.1.0,<20.2.0
black>=22.12.0,<22.13.0
flake8>=6.0.0,<6.1.0
isort>=5.11.4,<5.12.0

10
backend/setup.cfg Normal file
View File

@ -0,0 +1,10 @@
[flake8]
max-line-length = 88
extend-ignore =
E203,
exclude =
migrations,
__pycache__,
tests
per-file-ignores = **/*/models.py:F401

View File

@ -3,8 +3,8 @@ from typing import Generator
import pytest
from apiflask import APIFlask
from flask import Flask
from flask.testing import FlaskClient
from flask.ctx import AppContext
from flask.testing import FlaskClient
from app import create_app
from app.dependencies import db

View File

@ -1,13 +1,13 @@
import datetime
from factory import alchemy, Sequence
from factory import Sequence, alchemy
from factory.faker import Faker
from factory.fuzzy import FuzzyInteger, FuzzyDateTime
from factory.fuzzy import FuzzyDateTime, FuzzyInteger
from app.dependencies import db
from app.students.models import Student, Group, YearGroupStudents
from app.project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
from app.examination_schedule.models import ExaminationSchedule
from app.project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
from app.students.models import Group, Student, YearGroupStudents
class ProjectSupervisorFactory(alchemy.SQLAlchemyModelFactory):
@ -15,9 +15,9 @@ class ProjectSupervisorFactory(alchemy.SQLAlchemyModelFactory):
model = ProjectSupervisor
sqlalchemy_session = db.session
first_name = Faker('first_name')
last_name = Faker('last_name')
email = Faker('email')
first_name = Faker("first_name")
last_name = Faker("last_name")
email = Faker("email")
class YearGroupProjectSupervisorsFactory(alchemy.SQLAlchemyModelFactory):
@ -33,7 +33,7 @@ class GroupFactory(alchemy.SQLAlchemyModelFactory):
model = Group
sqlalchemy_session = db.session
name = Sequence(lambda n: f'Group-{n}')
name = Sequence(lambda n: f"Group-{n}")
points_for_first_term = FuzzyInteger(1, 5)
points_for_second_term = FuzzyInteger(1, 5)
@ -43,9 +43,9 @@ class StudentFactory(alchemy.SQLAlchemyModelFactory):
model = Student
sqlalchemy_session = db.session
first_name = Faker('first_name')
last_name = Faker('last_name')
email = Faker('email')
first_name = Faker("first_name")
last_name = Faker("last_name")
email = Faker("email")
index = Sequence(lambda n: 400_000 + n)
@ -60,9 +60,13 @@ class ExaminationScheduleFactory(alchemy.SQLAlchemyModelFactory):
model = ExaminationSchedule
sqlalchemy_session = db.session
title = Sequence(lambda n: f'Examination schedule {n}')
title = Sequence(lambda n: f"Examination schedule {n}")
duration_time = 30
start_date = FuzzyDateTime(datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc),
datetime.datetime(2020, 1, 5, tzinfo=datetime.timezone.utc))
end_date = FuzzyDateTime(datetime.datetime(2020, 1, 10, tzinfo=datetime.timezone.utc),
datetime.datetime(2020, 1, 20, tzinfo=datetime.timezone.utc))
start_date = FuzzyDateTime(
datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc),
datetime.datetime(2020, 1, 5, tzinfo=datetime.timezone.utc),
)
end_date = FuzzyDateTime(
datetime.datetime(2020, 1, 10, tzinfo=datetime.timezone.utc),
datetime.datetime(2020, 1, 20, tzinfo=datetime.timezone.utc),
)

View File

@ -1,30 +1,44 @@
from typing import List
from .factory import ProjectSupervisorFactory, YearGroupProjectSupervisorsFactory, \
StudentFactory, YearGroupStudentsFactory, GroupFactory, ExaminationScheduleFactory
from app.dependencies import db
from app.project_supervisor.models import YearGroup, ProjectSupervisor
from app.students.models import Group, Student, YearGroupStudents
from app.examination_schedule.models import ExaminationSchedule
from app.base.mode import ModeGroups
from app.dependencies import db
from app.examination_schedule.models import ExaminationSchedule
from app.project_supervisor.models import ProjectSupervisor, YearGroup
from app.students.models import Group, Student, YearGroupStudents
from .factory import (
ExaminationScheduleFactory,
GroupFactory,
ProjectSupervisorFactory,
StudentFactory,
YearGroupProjectSupervisorsFactory,
YearGroupStudentsFactory,
)
def create_year_group(data: dict = None) -> YearGroup:
if data is None:
data = {'mode': ModeGroups.STATIONARY.value, 'name': '2022/2023'}
data = {"mode": ModeGroups.STATIONARY.value, "name": "2022/2023"}
yg = YearGroup(**data)
db.session.add(yg)
db.session.commit()
return yg
def create_project_supervisors(yg: YearGroup, amount: int, limit_group: int = 3) -> List[ProjectSupervisorFactory]:
def create_project_supervisors(
yg: YearGroup, amount: int, limit_group: int = 3
) -> List[ProjectSupervisorFactory]:
ps = [ProjectSupervisorFactory() for _ in range(amount)]
db.session.add_all(ps)
db.session.commit()
db.session.add_all(
[YearGroupProjectSupervisorsFactory(limit_group=limit_group, year_group_id=yg.id, project_supervisor_id=p.id)
for p in ps])
[
YearGroupProjectSupervisorsFactory(
limit_group=limit_group, year_group_id=yg.id, project_supervisor_id=p.id
)
for p in ps
]
)
db.session.commit()
return ps
@ -47,7 +61,12 @@ def create_students(yg: YearGroup, amount: int) -> List[StudentFactory]:
students = [StudentFactory() for _ in range(amount)]
db.session.add_all(students)
db.session.commit()
db.session.add_all([YearGroupStudentsFactory(year_group_id=yg.id, student_index=s.index) for s in students])
db.session.add_all(
[
YearGroupStudentsFactory(year_group_id=yg.id, student_index=s.index)
for s in students
]
)
db.session.commit()
return students
@ -57,7 +76,9 @@ def create_student(data: dict, year_group_id: int = None) -> Student:
db.session.add(st)
db.session.commit()
if year_group_id is not None:
db.session.add(YearGroupStudents(year_group_id=year_group_id, student_index=st.index))
db.session.add(
YearGroupStudents(year_group_id=year_group_id, student_index=st.index)
)
db.session.commit()
return st
@ -76,8 +97,12 @@ def create_group(data: dict, yg: YearGroup) -> Group:
return group
def create_examination_schedules(yg: YearGroup, amount: int) -> List[ExaminationScheduleFactory]:
examination_schedules = [ExaminationScheduleFactory(year_group_id=yg.id) for _ in range(amount)]
def create_examination_schedules(
yg: YearGroup, amount: int
) -> List[ExaminationScheduleFactory]:
examination_schedules = [
ExaminationScheduleFactory(year_group_id=yg.id) for _ in range(amount)
]
db.session.add_all(examination_schedules)
db.session.commit()
return examination_schedules

View File

@ -1,28 +1,42 @@
import datetime
from ...utils import _test_case_client, _test_case_client_without_response, assert_model_changes
from ...fake_data import create_examination_schedule, create_year_group, create_examination_schedules
from app.dependencies import db
valid_data = {
'title': 'examination schedule summer',
'start_date': datetime.datetime.now() + datetime.timedelta(days=5),
'end_date': datetime.datetime.now() + datetime.timedelta(days=10),
'duration_time': 30
from ...fake_data import (
create_examination_schedule,
create_examination_schedules,
create_year_group,
)
from ...utils import (
_test_case_client,
_test_case_client_without_response,
assert_model_changes,
)
valid_data = {
"title": "examination schedule summer",
"start_date": datetime.datetime.now() + datetime.timedelta(days=5),
"end_date": datetime.datetime.now() + datetime.timedelta(days=10),
"duration_time": 30,
}
ex_data = {
'title': 'new title',
'start_date': (datetime.datetime.now() + datetime.timedelta(days=5)).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
'end_date': (datetime.datetime.now() + datetime.timedelta(days=10)).strftime("%Y-%m-%dT%H:%M:%S.000Z")
"title": "new title",
"start_date": (datetime.datetime.now() + datetime.timedelta(days=5)).strftime(
"%Y-%m-%dT%H:%M:%S.000Z"
),
"end_date": (datetime.datetime.now() + datetime.timedelta(days=10)).strftime(
"%Y-%m-%dT%H:%M:%S.000Z"
),
}
enrollments_data = {
'start_date_for_enrollment_students': (datetime.datetime.now() + datetime.timedelta(days=2)).strftime(
"%Y-%m-%dT%H:%M:%S.000Z"),
'end_date_for_enrollment_students': (datetime.datetime.now() + datetime.timedelta(days=4)).strftime(
"%Y-%m-%dT%H:%M:%S.000Z")
"start_date_for_enrollment_students": (
datetime.datetime.now() + datetime.timedelta(days=2)
).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
"end_date_for_enrollment_students": (
datetime.datetime.now() + datetime.timedelta(days=4)
).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
}
@ -31,89 +45,169 @@ def test_list_examination_schedules(test_app_with_context) -> None:
year_group = create_year_group()
create_examination_schedules(year_group, 34)
url = f'/api/coordinator/examination_schedule/{year_group.id}/?per_page=10'
data = _test_case_client_without_response(client, url, None, 200, method='get')
assert data.get('max_pages') == 4
assert len(data.get('examination_schedules')) == 10
url = f"/api/coordinator/examination_schedule/{year_group.id}/?per_page=10"
data = _test_case_client_without_response(client, url, None, 200, method="get")
assert data.get("max_pages") == 4
assert len(data.get("examination_schedules")) == 10
def test_delete_examination_schedule(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
year_group = create_year_group()
ex = create_examination_schedules(year_group, 1)[0]
_test_case_client(client, f'/api/coordinator/examination_schedule/{ex.id}/', None,
'Examination schedule was deleted!', 200, method='delete')
_test_case_client(
client,
f"/api/coordinator/examination_schedule/{ex.id}/",
None,
"Examination schedule was deleted!",
200,
method="delete",
)
def test_delete_examination_schedule_if_examination_schedule_doesnt_exist(test_app_with_context) -> None:
def test_delete_examination_schedule_if_examination_schedule_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/examination_schedule/32/', None,
"Examination schedule doesn't exist!", 404, method='delete', key='error')
_test_case_client(
client,
"/api/coordinator/examination_schedule/32/",
None,
"Examination schedule doesn't exist!",
404,
method="delete",
key="error",
)
def test_update_examination_schedule(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
year_group = create_year_group()
ex = create_examination_schedules(year_group, 1)[0]
_test_case_client(client, f'/api/coordinator/examination_schedule/{ex.id}/', ex_data,
'Examination schedule was updated!', 200, method='put')
_test_case_client(
client,
f"/api/coordinator/examination_schedule/{ex.id}/",
ex_data,
"Examination schedule was updated!",
200,
method="put",
)
assert_model_changes(ex, ex_data)
def test_update_examination_schedule_schedule_if_examination_schedule_doesnt_exist(test_app_with_context) -> None:
def test_update_examination_schedule_schedule_if_examination_schedule_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/examination_schedule/32/', ex_data,
"Examination schedule doesn't exist!", 404, method='put', key='error')
_test_case_client(
client,
"/api/coordinator/examination_schedule/32/",
ex_data,
"Examination schedule doesn't exist!",
404,
method="put",
key="error",
)
def test_set_date_of_examination_schedule(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
year_group = create_year_group()
ex = create_examination_schedule(valid_data, year_group)
_test_case_client(client, f'/api/coordinator/examination_schedule/{ex.id}/date/', enrollments_data,
'You set date of examination schedule!', 200, method='put')
_test_case_client(
client,
f"/api/coordinator/examination_schedule/{ex.id}/date/",
enrollments_data,
"You set date of examination schedule!",
200,
method="put",
)
assert_model_changes(ex, enrollments_data)
def test_set_date_of_examination_schedule_if_examination_schedule_doesnt_exist(test_app_with_context) -> None:
def test_set_date_of_examination_schedule_if_examination_schedule_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/examination_schedule/43/date/', enrollments_data,
'Examination schedule doesn\'t exist!', 404, method='put', key='error')
_test_case_client(
client,
"/api/coordinator/examination_schedule/43/date/",
enrollments_data,
"Examination schedule doesn't exist!",
404,
method="put",
key="error",
)
def test_set_date_of_examination_schedule_with_invalid_date(test_app_with_context) -> None:
def test_set_date_of_examination_schedule_with_invalid_date(
test_app_with_context,
) -> None:
invalid_dates = {
'start_date_for_enrollment_students': enrollments_data['end_date_for_enrollment_students'],
'end_date_for_enrollment_students': enrollments_data['start_date_for_enrollment_students'],
"start_date_for_enrollment_students": enrollments_data[
"end_date_for_enrollment_students"
],
"end_date_for_enrollment_students": enrollments_data[
"start_date_for_enrollment_students"
],
}
with test_app_with_context.test_client() as client:
year_group = create_year_group()
ex = create_examination_schedule(valid_data, year_group)
_test_case_client(client, f'/api/coordinator/examination_schedule/{ex.id}/date/', invalid_dates,
'Invalid data! End date must be greater than start date!', 400, method='put', key='error')
_test_case_client(
client,
f"/api/coordinator/examination_schedule/{ex.id}/date/",
invalid_dates,
"Invalid data! End date must be greater than start date!",
400,
method="put",
key="error",
)
def test_create_project_supervisors(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
year_group = create_year_group()
_test_case_client(client, f'/api/coordinator/examination_schedule/{year_group.id}/', ex_data,
'Examination schedule was created!', 201, method='post')
_test_case_client(
client,
f"/api/coordinator/examination_schedule/{year_group.id}/",
ex_data,
"Examination schedule was created!",
201,
method="post",
)
def test_create_project_supervisors_if_year_group_doesnt_exist(test_app_with_context) -> None:
def test_create_project_supervisors_if_year_group_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/examination_schedule/33/', ex_data,
'Year group doesn\'t exist!', 404, method='post', key='error')
_test_case_client(
client,
"/api/coordinator/examination_schedule/33/",
ex_data,
"Year group doesn't exist!",
404,
method="post",
key="error",
)
def test_create_project_supervisors_with_invalid_dates(test_app_with_context) -> None:
invalid_data = {
'title': 'examination schedule winter',
'start_date': ex_data['end_date'],
'end_date': ex_data['start_date']
"title": "examination schedule winter",
"start_date": ex_data["end_date"],
"end_date": ex_data["start_date"],
}
with test_app_with_context.test_client() as client:
year_group = create_year_group()
_test_case_client(client, f'/api/coordinator/examination_schedule/{year_group.id}/', invalid_data,
'Invalid data! End date must be greater than start date!', 400, method='post', key='error')
_test_case_client(
client,
f"/api/coordinator/examination_schedule/{year_group.id}/",
invalid_data,
"Invalid data! End date must be greater than start date!",
400,
method="post",
key="error",
)

View File

@ -2,24 +2,32 @@ import copy
from flask import current_app
from ...utils import _test_case_client, _test_case_client_without_response, assert_model_changes, _test_case_group
from ...fake_data import create_year_group, create_groups, create_group, create_students, create_project_supervisors
from app.dependencies import db
from app.students.models import Group
from app.project_supervisor.models import YearGroupProjectSupervisors
from app.students.models import Group
valid_data = {
'name': 'System Pri'
}
from ...fake_data import (
create_group,
create_groups,
create_project_supervisors,
create_students,
create_year_group,
)
from ...utils import (
_test_case_client,
_test_case_client_without_response,
_test_case_group,
assert_model_changes,
)
new_data = {
'name': 'Mobile app'
}
valid_data = {"name": "System Pri"}
new_data = {"name": "Mobile app"}
invalid_data = {
'name': 'Mobile app v2',
'students': [123_344, 455_444],
'project_supervisor_id': 1
"name": "Mobile app v2",
"students": [123_344, 455_444],
"project_supervisor_id": 1,
}
@ -27,39 +35,69 @@ def test_list_groups(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
create_groups(yg, 33)
data = _test_case_client_without_response(client, f'/api/coordinator/groups/{yg.id}/?per_page=10', None, 200,
method='get')
assert data.get('max_pages') == 4
assert len(data.get('groups')) == 10
data = _test_case_client_without_response(
client,
f"/api/coordinator/groups/{yg.id}/?per_page=10",
None,
200,
method="get",
)
assert data.get("max_pages") == 4
assert len(data.get("groups")) == 10
def test_detail_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
group = create_group(valid_data, yg)
data = _test_case_client_without_response(client, f'/api/coordinator/groups/{group.id}/detail/', None, 200,
method='get')
data = _test_case_client_without_response(
client,
f"/api/coordinator/groups/{group.id}/detail/",
None,
200,
method="get",
)
assert_model_changes(group, data)
def test_detail_group_if_group_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/groups/11/detail/', None, 'Not found group!', 404, method='get',
key='error')
_test_case_client(
client,
"/api/coordinator/groups/11/detail/",
None,
"Not found group!",
404,
method="get",
key="error",
)
def test_delete_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
group = create_group(valid_data, yg)
_test_case_client(client, f'/api/coordinator/groups/{group.id}/', None, 'Group was deleted!', 202,
method='delete')
_test_case_client(
client,
f"/api/coordinator/groups/{group.id}/",
None,
"Group was deleted!",
202,
method="delete",
)
def test_delete_group_if_group_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/groups/32/', None, 'Not found group!', 404, method='delete',
key='error')
_test_case_client(
client,
"/api/coordinator/groups/32/",
None,
"Not found group!",
404,
method="delete",
key="error",
)
def test_edit_group(test_app_with_context) -> None:
@ -69,11 +107,17 @@ def test_edit_group(test_app_with_context) -> None:
students = create_students(yg, 3)
ps = create_project_supervisors(yg, 1)[0]
group = create_group(valid_data, yg)
data['students'] = [student.index for student in students]
data['project_supervisor_id'] = ps.id
data["students"] = [student.index for student in students]
data["project_supervisor_id"] = ps.id
_test_case_client(client, f'/api/coordinator/groups/{group.id}/', data, 'Group was updated!', 200,
method='put')
_test_case_client(
client,
f"/api/coordinator/groups/{group.id}/",
data,
"Group was updated!",
200,
method="put",
)
_test_case_group(group, data)
@ -83,18 +127,32 @@ def test_edit_group_with_invalid_project_supervisor_id(test_app_with_context) ->
yg = create_year_group()
students = create_students(yg, 3)
group = create_group(valid_data, yg)
data['students'] = [student.index for student in students]
data['project_supervisor_id'] = 10
_test_case_client(client, f'/api/coordinator/groups/{group.id}/', data, 'Not found project supervisor!', 404,
method='put', key='error')
data["students"] = [student.index for student in students]
data["project_supervisor_id"] = 10
_test_case_client(
client,
f"/api/coordinator/groups/{group.id}/",
data,
"Not found project supervisor!",
404,
method="put",
key="error",
)
def test_edit_group_with_invalid_data(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
group = create_group(valid_data, yg)
data = {'students': [123_4356, 243_533, 434_343]}
_test_case_client(client, f'/api/coordinator/groups/{group.id}/', data, 'Validation error', 400, method='put')
data = {"students": [123_4356, 243_533, 434_343]}
_test_case_client(
client,
f"/api/coordinator/groups/{group.id}/",
data,
"Validation error",
400,
method="put",
)
def test_edit_group_with_invalid_student_indexes(test_app_with_context) -> None:
@ -102,21 +160,42 @@ def test_edit_group_with_invalid_student_indexes(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
group = create_group(valid_data, yg)
data['students'] = [123_456, 243_533, 434_343]
_test_case_client(client, f'/api/coordinator/groups/{group.id}/', data, 'Not found students!', 404,
method='put', key='error')
data["students"] = [123_456, 243_533, 434_343]
_test_case_client(
client,
f"/api/coordinator/groups/{group.id}/",
data,
"Not found students!",
404,
method="put",
key="error",
)
def test_edit_group_if_group_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/groups/333/', new_data, 'Not found group!', 404,
method='put', key='error')
_test_case_client(
client,
"/api/coordinator/groups/333/",
new_data,
"Not found group!",
404,
method="put",
key="error",
)
def test_edit_group_if_you_pass_empty_data(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/groups/333/', {}, 'You have passed empty data!', 400,
method='put', key='error')
_test_case_client(
client,
"/api/coordinator/groups/333/",
{},
"You have passed empty data!",
400,
method="put",
key="error",
)
def test_create_group(test_app_with_context) -> None:
@ -125,26 +204,46 @@ def test_create_group(test_app_with_context) -> None:
yg = create_year_group()
students = create_students(yg, 3)
ps = create_project_supervisors(yg, 1)[0]
data['students'] = [student.index for student in students]
data['project_supervisor_id'] = ps.id
data["students"] = [student.index for student in students]
data["project_supervisor_id"] = ps.id
_test_case_client(client, f'/api/coordinator/groups/{yg.id}/', data, 'Group was created!', 201,
method='post')
_test_case_client(
client,
f"/api/coordinator/groups/{yg.id}/",
data,
"Group was created!",
201,
method="post",
)
assert Group.query.count() == 1
_test_case_group(Group.query.first(), data)
def test_create_group_if_year_group_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/groups/22/', invalid_data, 'Not found year group!', 404,
method='post', key='error')
_test_case_client(
client,
"/api/coordinator/groups/22/",
invalid_data,
"Not found year group!",
404,
method="post",
key="error",
)
def test_create_group_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
_test_case_client(client, f'/api/coordinator/groups/{yg.id}/', invalid_data, 'Not found project supervisor!',
404, method='post', key='error')
_test_case_client(
client,
f"/api/coordinator/groups/{yg.id}/",
invalid_data,
"Not found project supervisor!",
404,
method="post",
key="error",
)
def test_create_group_if_you_exceed_the_group_limit(test_app_with_context) -> None:
@ -152,14 +251,21 @@ def test_create_group_if_you_exceed_the_group_limit(test_app_with_context) -> No
with test_app_with_context.test_client() as client:
yg = create_year_group()
ps = create_project_supervisors(yg, 1)[0]
data['project_supervisor_id'] = ps.id
limit_group = current_app.config.get('LIMIT_STUDENTS_PER_GROUP')
data["project_supervisor_id"] = ps.id
limit_group = current_app.config.get("LIMIT_STUDENTS_PER_GROUP")
data['students'].extend([999_000 + i for i in range(limit_group + 4)])
data["students"].extend([999_000 + i for i in range(limit_group + 4)])
_test_case_client(client, f'/api/coordinator/groups/{yg.id}/', data,
f"Too much students you want add to group, The group can have only {limit_group}"
" students", 400, method='post', key='error')
_test_case_client(
client,
f"/api/coordinator/groups/{yg.id}/",
data,
f"Too much students you want add to group, The group can have only {limit_group}"
" students",
400,
method="post",
key="error",
)
def test_create_group_if_students_doesnt_exist(test_app_with_context) -> None:
@ -167,36 +273,55 @@ def test_create_group_if_students_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
ps = create_project_supervisors(yg, 1)[0]
data['project_supervisor_id'] = ps.id
_test_case_client(client, f'/api/coordinator/groups/{yg.id}/', data, "Not found students!", 404, method='post',
key='error')
data["project_supervisor_id"] = ps.id
_test_case_client(
client,
f"/api/coordinator/groups/{yg.id}/",
data,
"Not found students!",
404,
method="post",
key="error",
)
def test_create_group_if_at_least_one_student_belong_to_other_group(test_app_with_context) -> None:
def test_create_group_if_at_least_one_student_belong_to_other_group(
test_app_with_context,
) -> None:
data = copy.deepcopy(invalid_data)
with test_app_with_context.test_client() as client:
yg = create_year_group()
ps = create_project_supervisors(yg, 1)[0]
group = create_group(valid_data, yg)
data['project_supervisor_id'] = ps.id
data["project_supervisor_id"] = ps.id
student = create_students(yg, 1)[0]
group.students.append(student)
db.session.commit()
data['students'].extend([student.index])
_test_case_client(client, f'/api/coordinator/groups/{yg.id}/', data,
"One or more students have already belonged to group!", 400, method='post', key='error')
data["students"].extend([student.index])
_test_case_client(
client,
f"/api/coordinator/groups/{yg.id}/",
data,
"One or more students have already belonged to group!",
400,
method="post",
key="error",
)
def test_create_group_if_limit_of_group_was_exceed_for_project_supervisor(test_app_with_context) -> None:
def test_create_group_if_limit_of_group_was_exceed_for_project_supervisor(
test_app_with_context,
) -> None:
data = copy.deepcopy(invalid_data)
with test_app_with_context.test_client() as client:
yg = create_year_group()
ps = create_project_supervisors(yg, 1)[0]
data['project_supervisor_id'] = ps.id
data["project_supervisor_id"] = ps.id
ygps = YearGroupProjectSupervisors.query.filter_by(project_supervisor_id=ps.id, year_group_id=yg.id). \
first()
ygps = YearGroupProjectSupervisors.query.filter_by(
project_supervisor_id=ps.id, year_group_id=yg.id
).first()
limit_group = ygps.limit_group
groups = create_groups(yg, limit_group)
@ -204,6 +329,12 @@ def test_create_group_if_limit_of_group_was_exceed_for_project_supervisor(test_a
group.project_supervisor_id = ps.id
db.session.commit()
_test_case_client(client, f'/api/coordinator/groups/{yg.id}/', data,
"Can't create new group, project supervisor achieved a limit of groups",
400, method='post', key='error')
_test_case_client(
client,
f"/api/coordinator/groups/{yg.id}/",
data,
"Can't create new group, project supervisor achieved a limit of groups",
400,
method="post",
key="error",
)

View File

@ -1,27 +1,40 @@
from ...utils import _test_case_client, _test_case_client_without_response, assert_model_changes
from ...fake_data import create_project_supervisors, create_year_group, create_dummy_group, create_dummy_ps
from app.dependencies import db
from app.project_supervisor.models import YearGroupProjectSupervisors
from ...fake_data import (
create_dummy_group,
create_dummy_ps,
create_project_supervisors,
create_year_group,
)
from ...utils import (
_test_case_client,
_test_case_client_without_response,
assert_model_changes,
)
valid_data = {
'first_name': 'John',
'last_name': 'Smith',
'email': 'johnsmith@gmail.com'
"first_name": "John",
"last_name": "Smith",
"email": "johnsmith@gmail.com",
}
year_group_ps_data = {
'limit_group': 3
}
year_group_ps_data = {"limit_group": 3}
def test_list_project_supervisors(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
year_group = create_year_group()
create_project_supervisors(year_group, 25)
data = _test_case_client_without_response(client, '/api/coordinator/project_supervisor/?per_page=10', None, 200,
method='get')
assert data.get('max_pages') == 3
assert len(data.get('project_supervisors')) == 10
data = _test_case_client_without_response(
client,
"/api/coordinator/project_supervisor/?per_page=10",
None,
200,
method="get",
)
assert data.get("max_pages") == 3
assert len(data.get("project_supervisors")) == 10
def test_list_project_supervisors_by_year_group(test_app_with_context) -> None:
@ -30,184 +43,343 @@ def test_list_project_supervisors_by_year_group(test_app_with_context) -> None:
year_group_2 = create_year_group()
create_project_supervisors(year_group, 12)
create_project_supervisors(year_group_2, 24)
data = _test_case_client_without_response(client,
f'/api/coordinator/project_supervisor/{year_group.id}/?per_page=10',
None, 200, method='get')
assert data.get('max_pages') == 2
assert len(data.get('project_supervisors')) == 10
data = _test_case_client_without_response(
client,
f"/api/coordinator/project_supervisor/{year_group.id}/?per_page=10",
None,
200,
method="get",
)
assert data.get("max_pages") == 2
assert len(data.get("project_supervisors")) == 10
def test_create_project_supervisors(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/project_supervisor/', valid_data,
'Project Supervisor was created!', 201, method='post')
_test_case_client(
client,
"/api/coordinator/project_supervisor/",
valid_data,
"Project Supervisor was created!",
201,
method="post",
)
def test_create_project_supervisors_with_invalid_data(test_app_with_context) -> None:
data = {
'first_name': 'John',
'last_name': 'Smith',
'email': 'johnsmitl.com'
}
data = {"first_name": "John", "last_name": "Smith", "email": "johnsmitl.com"}
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/project_supervisor/', data,
'Validation error', 400, method='post')
_test_case_client(
client,
"/api/coordinator/project_supervisor/",
data,
"Validation error",
400,
method="post",
)
def test_create_project_supervisors_if_project_supervisor_has_already_exist(test_app_with_context) -> None:
def test_create_project_supervisors_if_project_supervisor_has_already_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
create_dummy_ps(valid_data)
_test_case_client(client, '/api/coordinator/project_supervisor/', valid_data,
'Project Supervisor has already exists!', 400, method='post', key='error')
_test_case_client(
client,
"/api/coordinator/project_supervisor/",
valid_data,
"Project Supervisor has already exists!",
400,
method="post",
key="error",
)
def test_detail_project_supervisor(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
data = _test_case_client_without_response(client, f'/api/coordinator/project_supervisor/{ps.id}/detail/',
None, 200, method='get')
data = _test_case_client_without_response(
client,
f"/api/coordinator/project_supervisor/{ps.id}/detail/",
None,
200,
method="get",
)
assert_model_changes(ps, data)
def test_detail_project_supervisor_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
def test_detail_project_supervisor_if_project_supervisor_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/project_supervisor/23/detail/', None,
'Not found project supervisor!', 404, method='get', key='error')
_test_case_client(
client,
"/api/coordinator/project_supervisor/23/detail/",
None,
"Not found project supervisor!",
404,
method="get",
key="error",
)
def test_delete_project_supervisor(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/', None,
'Project Supervisor was deleted!', 200, method='delete')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/",
None,
"Project Supervisor was deleted!",
200,
method="delete",
)
def test_delete_project_supervisor_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
def test_delete_project_supervisor_if_project_supervisor_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/project_supervisor/23/', None,
'Not found project supervisor!', 404, method='delete', key='error')
_test_case_client(
client,
"/api/coordinator/project_supervisor/23/",
None,
"Not found project supervisor!",
404,
method="delete",
key="error",
)
def test_delete_project_supervisor_if_project_supervisor_has_got_at_least_one_group(test_app_with_context) -> None:
def test_delete_project_supervisor_if_project_supervisor_has_got_at_least_one_group(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
create_dummy_group({'name': 'new project'}, ps.id)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/', None,
'Project Supervisor has at least one group!', 400, method='delete', key='error')
create_dummy_group({"name": "new project"}, ps.id)
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/",
None,
"Project Supervisor has at least one group!",
400,
method="delete",
key="error",
)
def test_edit_project_supervisor(test_app_with_context) -> None:
new_data = {
'first_name': 'Albert',
'last_name': 'Einstein',
'email': 'albertmc2@gmail.com'
"first_name": "Albert",
"last_name": "Einstein",
"email": "albertmc2@gmail.com",
}
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/', new_data,
'Project Supervisor was updated!', 200, method='put')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/",
new_data,
"Project Supervisor was updated!",
200,
method="put",
)
assert_model_changes(ps, new_data)
def test_edit_project_supervisor_with_invalid_data(test_app_with_context) -> None:
invalid_data = {
'first_name': 'Mark',
'last_name': 'Smith',
'email': 'invalidemail'
}
invalid_data = {"first_name": "Mark", "last_name": "Smith", "email": "invalidemail"}
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/', invalid_data,
'Validation error', 400, method='put')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/",
invalid_data,
"Validation error",
400,
method="put",
)
def test_edit_project_supervisor_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
def test_edit_project_supervisor_if_project_supervisor_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/project_supervisor/2332/', valid_data,
'Not found project supervisor!', 404, method='put', key='error')
_test_case_client(
client,
"/api/coordinator/project_supervisor/2332/",
valid_data,
"Not found project supervisor!",
404,
method="put",
key="error",
)
def test_add_project_supervisor_to_year_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
yg = create_year_group()
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/',
year_group_ps_data, "Project Supervisor was added to year group!", 201, method='post')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/",
year_group_ps_data,
"Project Supervisor was added to year group!",
201,
method="post",
)
def test_add_project_supervisor_to_year_group_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
def test_add_project_supervisor_to_year_group_if_project_supervisor_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
_test_case_client(client, f'/api/coordinator/project_supervisor/454/year-group/{yg.id}/', year_group_ps_data,
"Not found project supervisor!", 404, method='post', key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/454/year-group/{yg.id}/",
year_group_ps_data,
"Not found project supervisor!",
404,
method="post",
key="error",
)
def test_add_project_supervisor_to_year_group_if_year_group_doesnt_exist(test_app_with_context) -> None:
def test_add_project_supervisor_to_year_group_if_year_group_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/2/', year_group_ps_data,
"Not found year group!", 404, method='post', key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/2/",
year_group_ps_data,
"Not found year group!",
404,
method="post",
key="error",
)
def test_add_project_supervisor_to_year_group_if_project_supervisor_has_already_assigned_to_year_group(
test_app_with_context) -> None:
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
yg = create_year_group()
ygps = YearGroupProjectSupervisors(project_supervisor_id=ps.id, year_group_id=yg.id, limit_group=2)
ygps = YearGroupProjectSupervisors(
project_supervisor_id=ps.id, year_group_id=yg.id, limit_group=2
)
db.session.add(ygps)
db.session.commit()
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/',
year_group_ps_data, "Project supervisor is assigned to this year group!", 400, method='post',
key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/",
year_group_ps_data,
"Project supervisor is assigned to this year group!",
400,
method="post",
key="error",
)
def test_delete_project_supervisor_to_year_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
yg = create_year_group()
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/', None,
"Project Supervisor was removed from this year group!", 200, method='delete')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/",
None,
"Project Supervisor was removed from this year group!",
200,
method="delete",
)
assert len(YearGroupProjectSupervisors.query.all()) == 0
def test_delete_project_supervisor_to_year_group_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
def test_delete_project_supervisor_to_year_group_if_project_supervisor_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
_test_case_client(client, f'/api/coordinator/project_supervisor/5/year-group/{yg.id}/', None,
"Not found project supervisor!", 404, method='delete', key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/5/year-group/{yg.id}/",
None,
"Not found project supervisor!",
404,
method="delete",
key="error",
)
def test_delete_project_supervisor_to_year_group_if_year_group_doesnt_exist(test_app_with_context) -> None:
def test_delete_project_supervisor_to_year_group_if_year_group_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/23/', None,
"Not found year group!", 404, method='delete', key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/23/",
None,
"Not found year group!",
404,
method="delete",
key="error",
)
def test_update_limit_of_group_for_project_supervisor(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
yg = create_year_group()
ygps = YearGroupProjectSupervisors(project_supervisor_id=ps.id, year_group_id=yg.id, limit_group=6)
ygps = YearGroupProjectSupervisors(
project_supervisor_id=ps.id, year_group_id=yg.id, limit_group=6
)
db.session.add(ygps)
db.session.commit()
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/',
year_group_ps_data, "Limit of group was changed!", 200, method='put')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/{yg.id}/",
year_group_ps_data,
"Limit of group was changed!",
200,
method="put",
)
assert_model_changes(ygps, year_group_ps_data)
def test_update_limit_of_group_for_project_supervisor_if_project_supervisor_doesnt_exist(test_app_with_context) -> None:
def test_update_limit_of_group_for_project_supervisor_if_project_supervisor_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
_test_case_client(client, f'/api/coordinator/project_supervisor/34/year-group/{yg.id}/',
year_group_ps_data, "Not found project supervisor!", 404, method='put', key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/34/year-group/{yg.id}/",
year_group_ps_data,
"Not found project supervisor!",
404,
method="put",
key="error",
)
def test_update_limit_of_group_for_project_supervisor_if_year_group_doesnt_exist(test_app_with_context) -> None:
def test_update_limit_of_group_for_project_supervisor_if_year_group_doesnt_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
ps = create_dummy_ps(valid_data)
_test_case_client(client, f'/api/coordinator/project_supervisor/{ps.id}/year-group/34/',
year_group_ps_data, "Not found year group!", 404, method='put', key='error')
_test_case_client(
client,
f"/api/coordinator/project_supervisor/{ps.id}/year-group/34/",
year_group_ps_data,
"Not found year group!",
404,
method="put",
key="error",
)

View File

@ -1,26 +1,26 @@
import copy
from ...utils import _test_case_client, _test_case_client_without_response, assert_model_changes
from ...fake_data import create_year_group, create_students, create_student
from ...fake_data import create_student, create_students, create_year_group
from ...utils import (
_test_case_client,
_test_case_client_without_response,
assert_model_changes,
)
valid_data = {
'first_name': 'Albert',
'last_name': 'Rose',
'index': 234_343,
'email': 'albert@gmail.com'
"first_name": "Albert",
"last_name": "Rose",
"index": 234_343,
"email": "albert@gmail.com",
}
new_data = {
'first_name': 'Martin',
'last_name': 'Green',
'pesel': '93030312894'
}
new_data = {"first_name": "Martin", "last_name": "Green", "pesel": "93030312894"}
data_to_create_student = {
'first_name': 'Albert',
'last_name': 'Marcus',
'pesel': '93030312896',
'index': 123_456
"first_name": "Albert",
"last_name": "Marcus",
"pesel": "93030312896",
"index": 123_456,
}
@ -28,94 +28,174 @@ def test_list_students(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
create_students(yg, 45)
data = _test_case_client_without_response(client, f'/api/coordinator/students/{yg.id}/?per_page=10', None, 200,
method='get')
assert data.get('max_pages') == 5
assert len(data.get('students')) == 10
data = _test_case_client_without_response(
client,
f"/api/coordinator/students/{yg.id}/?per_page=10",
None,
200,
method="get",
)
assert data.get("max_pages") == 5
assert len(data.get("students")) == 10
def test_detail_student(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
st = create_student(valid_data, yg.id)
data = _test_case_client_without_response(client, f'/api/coordinator/students/{st.index}/detail/', None, 200,
method='get')
data = _test_case_client_without_response(
client,
f"/api/coordinator/students/{st.index}/detail/",
None,
200,
method="get",
)
assert_model_changes(st, data)
def test_detail_student_if_student_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/students/43/detail/', None, 'Not found student!', 404,
method='get', key='error')
_test_case_client(
client,
"/api/coordinator/students/43/detail/",
None,
"Not found student!",
404,
method="get",
key="error",
)
def test_delete_student(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
st = create_student(valid_data, yg.id)
_test_case_client(client, f'/api/coordinator/students/{st.index}/', None, 'Student was deleted!', 202,
method='delete')
_test_case_client(
client,
f"/api/coordinator/students/{st.index}/",
None,
"Student was deleted!",
202,
method="delete",
)
def test_delete_student_if_student_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/students/43/', None, 'Not found student!', 404,
method='delete', key='error')
_test_case_client(
client,
"/api/coordinator/students/43/",
None,
"Not found student!",
404,
method="delete",
key="error",
)
def test_edit_student(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group()
st = create_student(valid_data, yg.id)
_test_case_client(client, f'/api/coordinator/students/{st.index}/', new_data, 'Student was updated!', 200,
method='put')
_test_case_client(
client,
f"/api/coordinator/students/{st.index}/",
new_data,
"Student was updated!",
200,
method="put",
)
def test_edit_student_with_invalid_data(test_app_with_context) -> None:
data = copy.copy(new_data)
data['pesel'] = '43333333333433443'
data["pesel"] = "43333333333433443"
with test_app_with_context.test_client() as client:
yg = create_year_group()
st = create_student(valid_data, yg.id)
_test_case_client(client, f'/api/coordinator/students/{st.index}/', data, 'Validation error', 400, method='put')
_test_case_client(
client,
f"/api/coordinator/students/{st.index}/",
data,
"Validation error",
400,
method="put",
)
def test_edit_student_if_student_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/students/54/', new_data, 'Not found student!', 404, method='put',
key='error')
_test_case_client(
client,
"/api/coordinator/students/54/",
new_data,
"Not found student!",
404,
method="put",
key="error",
)
def test_create_student(test_app_with_context) -> None:
data = copy.copy(data_to_create_student)
with test_app_with_context.test_client() as client:
yg = create_year_group()
data['year_group_id'] = yg.id
_test_case_client(client, '/api/coordinator/students/', data, 'Student was created!', 200, method='post')
data["year_group_id"] = yg.id
_test_case_client(
client,
"/api/coordinator/students/",
data,
"Student was created!",
200,
method="post",
)
def test_create_student_with_invalid_data(test_app_with_context) -> None:
data = copy.copy(data_to_create_student)
data['pesel'] = '434343434343344'
data["pesel"] = "434343434343344"
with test_app_with_context.test_client() as client:
yg = create_year_group()
data['year_group_id'] = yg.id
_test_case_client(client, '/api/coordinator/students/', data, 'Validation error', 400, method='post')
data["year_group_id"] = yg.id
_test_case_client(
client,
"/api/coordinator/students/",
data,
"Validation error",
400,
method="post",
)
def test_create_student_if_year_group_doesnt_exist(test_app_with_context) -> None:
data = copy.copy(data_to_create_student)
with test_app_with_context.test_client() as client:
data['year_group_id'] = 34
_test_case_client(client, '/api/coordinator/students/', data, 'Not found year group!', 404, method='post',
key='error')
data["year_group_id"] = 34
_test_case_client(
client,
"/api/coordinator/students/",
data,
"Not found year group!",
404,
method="post",
key="error",
)
def test_create_student_if_student_has_already_assigned_to_this_group(test_app_with_context) -> None:
def test_create_student_if_student_has_already_assigned_to_this_group(
test_app_with_context,
) -> None:
data = copy.copy(data_to_create_student)
with test_app_with_context.test_client() as client:
yg = create_year_group()
create_student(data, yg.id)
data['year_group_id'] = yg.id
_test_case_client(client, '/api/coordinator/students/', data, 'You are assigned to this year group!', 400,
method='post', key='error')
data["year_group_id"] = yg.id
_test_case_client(
client,
"/api/coordinator/students/",
data,
"You are assigned to this year group!",
400,
method="post",
key="error",
)

View File

@ -1,99 +1,154 @@
from ...fake_data import create_year_group
from ...utils import _test_case_client_without_response, _test_case_client, assert_model_changes
from app.base.mode import ModeGroups
valid_data = {
'mode': ModeGroups.STATIONARY.value,
'name': '2022/2023'
}
from ...fake_data import create_year_group
from ...utils import (
_test_case_client,
_test_case_client_without_response,
assert_model_changes,
)
new_data = {
'mode': ModeGroups.NON_STATIONARY.value,
'name': '2021/2022'
}
valid_data = {"mode": ModeGroups.STATIONARY.value, "name": "2022/2023"}
example_data = {
'mode': ModeGroups.STATIONARY.value,
'name': '2021/2022'
}
new_data = {"mode": ModeGroups.NON_STATIONARY.value, "name": "2021/2022"}
example_data = {"mode": ModeGroups.STATIONARY.value, "name": "2021/2022"}
def test_create_year_group(test_client) -> None:
_test_case_client(test_client, '/api/coordinator/year-group/', valid_data, 'Year group was created!', 201)
_test_case_client(
test_client,
"/api/coordinator/year-group/",
valid_data,
"Year group was created!",
201,
)
def test_create_year_group_with_invalid_name(test_client) -> None:
data = {
'mode': ModeGroups.STATIONARY.value,
'name': '2022/203232a'
}
_test_case_client(test_client, '/api/coordinator/year-group/', data, 'Validation error', 400)
data = {"mode": ModeGroups.STATIONARY.value, "name": "2022/203232a"}
_test_case_client(
test_client, "/api/coordinator/year-group/", data, "Validation error", 400
)
def test_create_year_group_with_invalid_mode(test_client) -> None:
data = {
'mode': 'xxxx',
'name': '2022/2033'
}
_test_case_client(test_client, '/api/coordinator/year-group/', data, 'Validation error', 400)
data = {"mode": "xxxx", "name": "2022/2033"}
_test_case_client(
test_client, "/api/coordinator/year-group/", data, "Validation error", 400
)
def test_create_year_group_if_year_group_already_exists(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
create_year_group(valid_data)
_test_case_client(client, '/api/coordinator/year-group/', valid_data, 'Year group has already exists!', 400,
'error')
_test_case_client(
client,
"/api/coordinator/year-group/",
valid_data,
"Year group has already exists!",
400,
"error",
)
def test_delete_year_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group(valid_data)
_test_case_client(client, f'/api/coordinator/year-group/{yg.id}/', None, 'Year group was deleted!', 202,
method='delete')
_test_case_client(
client,
f"/api/coordinator/year-group/{yg.id}/",
None,
"Year group was deleted!",
202,
method="delete",
)
def test_delete_year_group_if_year_group_not_exists(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/coordinator/year-group/1/', None, 'Year group doesn\'t exist!', 404,
method='delete', key='error')
_test_case_client(
client,
"/api/coordinator/year-group/1/",
None,
"Year group doesn't exist!",
404,
method="delete",
key="error",
)
def test_update_year_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group(valid_data)
_test_case_client(client, f'/api/coordinator/year-group/{yg.id}/', new_data, "Year group was updated!", 200,
method='put', key='message')
_test_case_client(
client,
f"/api/coordinator/year-group/{yg.id}/",
new_data,
"Year group was updated!",
200,
method="put",
key="message",
)
assert_model_changes(yg, new_data)
def test_update_year_group_with_invalid_data(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
yg = create_year_group(valid_data)
_test_case_client(client, f'/api/coordinator/year-group/{yg.id}/', {'name': '', 'mode': ''}, 'Validation error',
400, method='put', key='message')
_test_case_client(
client,
f"/api/coordinator/year-group/{yg.id}/",
{"name": "", "mode": ""},
"Validation error",
400,
method="put",
key="message",
)
assert_model_changes(yg, valid_data)
def test_update_year_group_with_invalid_route_param_year_group_id(test_app_with_context) -> None:
def test_update_year_group_with_invalid_route_param_year_group_id(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, f'/api/coordinator/year-group/23/', new_data, 'Not found year group!',
404, method='put', key='error')
_test_case_client(
client,
f"/api/coordinator/year-group/23/",
new_data,
"Not found year group!",
404,
method="put",
key="error",
)
def test_update_year_group_with_valid_data_and_year_group_which_has_already_exist(test_app_with_context) -> None:
def test_update_year_group_with_valid_data_and_year_group_which_has_already_exist(
test_app_with_context,
) -> None:
with test_app_with_context.test_client() as client:
create_year_group(new_data)
yg = create_year_group(valid_data)
_test_case_client(client, f'/api/coordinator/year-group/{yg.id}/', new_data, 'Year group has already exists!',
400, method='put', key='error')
_test_case_client(
client,
f"/api/coordinator/year-group/{yg.id}/",
new_data,
"Year group has already exists!",
400,
method="put",
key="error",
)
assert_model_changes(yg, valid_data)
def test_list_year_group(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
ygs = [create_year_group(data) for data in (valid_data, new_data, example_data)]
data = _test_case_client_without_response(client, '/api/coordinator/year-group/', None, 200, method='get')
assert data.get('max_pages') == 1
for year_group_data in data.get('year_groups'):
yg_id = year_group_data.get('id')
assert_model_changes(list(filter(lambda yg: yg.id == yg_id, ygs))[0], year_group_data)
data = _test_case_client_without_response(
client, "/api/coordinator/year-group/", None, 200, method="get"
)
assert data.get("max_pages") == 1
for year_group_data in data.get("year_groups"):
yg_id = year_group_data.get("id")
assert_model_changes(
list(filter(lambda yg: yg.id == yg_id, ygs))[0], year_group_data
)

View File

@ -1,13 +1,16 @@
from ...utils import _test_case_client_without_response, assert_model_changes
from ...fake_data import create_year_group, create_project_supervisors, create_groups
from app.dependencies import db
from ...fake_data import create_groups, create_project_supervisors, create_year_group
from ...utils import _test_case_client_without_response, assert_model_changes
def test_list_year_group_for_specific_student(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
year_group = create_year_group()
amount_of_project_supervisors = 3
project_supervisors = create_project_supervisors(year_group, amount_of_project_supervisors)
project_supervisors = create_project_supervisors(
year_group, amount_of_project_supervisors
)
groups = create_groups(year_group, 6)
for i in range(1, 4):
@ -16,12 +19,14 @@ def test_list_year_group_for_specific_student(test_app_with_context) -> None:
gr.project_supervisor_id = project_supervisors[i - 1].id
db.session.commit()
url = f'/api/students/registrations/{year_group.id}/?per_page=10'
data = _test_case_client_without_response(client, url, None, 200, method='get')
assert data.get('max_pages') == 1
project_supervisors_data = data.get('project_supervisors')
url = f"/api/students/registrations/{year_group.id}/?per_page=10"
data = _test_case_client_without_response(client, url, None, 200, method="get")
assert data.get("max_pages") == 1
project_supervisors_data = data.get("project_supervisors")
assert len(project_supervisors_data) == amount_of_project_supervisors
for ps, expected_available_groups in zip(project_supervisors, [2, 1, 0]):
ps_dict = list(filter(lambda p: p.get('email') == ps.email, project_supervisors_data))[0]
assert ps_dict.get('available_groups') == expected_available_groups
ps_dict = list(
filter(lambda p: p.get("email") == ps.email, project_supervisors_data)
)[0]
assert ps_dict.get("available_groups") == expected_available_groups

View File

@ -1,32 +1,17 @@
from ...utils import _test_case_client, _test_case_client_without_response
from ...fake_data import create_year_group, create_student
from app.base.mode import ModeGroups
from app.dependencies import db
from app.students.models import YearGroupStudents
valid_data = {
'first_name': 'Dominic',
'last_name': 'Mozart',
'index': 123_345
}
from ...fake_data import create_student, create_year_group
from ...utils import _test_case_client, _test_case_client_without_response
valid_data = {"first_name": "Dominic", "last_name": "Mozart", "index": 123_345}
year_group_data = [
{
'name': '2022/2023',
'mode': ModeGroups.STATIONARY.value
},
{
'name': '2021/2022',
'mode': ModeGroups.STATIONARY.value
},
{
'name': '2023/2024',
'mode': ModeGroups.NON_STATIONARY.value
},
{
'name': '1997/1998',
'mode': ModeGroups.NON_STATIONARY.value
},
{"name": "2022/2023", "mode": ModeGroups.STATIONARY.value},
{"name": "2021/2022", "mode": ModeGroups.STATIONARY.value},
{"name": "2023/2024", "mode": ModeGroups.NON_STATIONARY.value},
{"name": "1997/1998", "mode": ModeGroups.NON_STATIONARY.value},
]
@ -35,16 +20,25 @@ def test_list_year_group_for_specific_student(test_app_with_context) -> None:
year_groups = [create_year_group(data) for data in year_group_data]
student = create_student(valid_data)
for yg in year_groups[:-1]:
db.session.add(YearGroupStudents(year_group_id=yg.id, student_index=student.index))
db.session.add(
YearGroupStudents(year_group_id=yg.id, student_index=student.index)
)
db.session.commit()
url = f'/api/students/year-group/?per_page=10&index={student.index}'
data = _test_case_client_without_response(client, url, None, 200, method='get')
assert data.get('max_pages') == 1
assert len(data.get('year_groups')) == len(year_groups) - 1
url = f"/api/students/year-group/?per_page=10&index={student.index}"
data = _test_case_client_without_response(client, url, None, 200, method="get")
assert data.get("max_pages") == 1
assert len(data.get("year_groups")) == len(year_groups) - 1
def test_list_year_group_if_student_doesnt_exist(test_app_with_context) -> None:
with test_app_with_context.test_client() as client:
_test_case_client(client, '/api/students/year-group/?per_page=10&index=23', None, 'Not found student!', 404,
method='get', key='error')
_test_case_client(
client,
"/api/students/year-group/?per_page=10&index=23",
None,
"Not found student!",
404,
method="get",
key="error",
)

View File

@ -4,80 +4,107 @@ import pandas as pd
import pytest
from flask import current_app
from app.dependencies import db
from app.base.utils import is_allowed_extensions, order_by_column_name, paginate_models
from app.coordinator.utils import check_columns, parse_csv, generate_range_dates, generate_csv
from app.coordinator.exceptions import InvalidNameOrTypeHeaderException
from app.students.models import Student, Group
from app.coordinator.utils import (
check_columns,
generate_csv,
generate_range_dates,
parse_csv,
)
from app.dependencies import db
from app.students.models import Group, Student
def test_is_allowed_extensions(test_app) -> None:
with test_app.app_context():
for ext in current_app.config.get('ALLOWED_EXTENSIONS'):
assert is_allowed_extensions(f'file.{ext}') is True
for ext in current_app.config.get("ALLOWED_EXTENSIONS"):
assert is_allowed_extensions(f"file.{ext}") is True
def test_is_allowed_extensions_with_invalid_extensions(test_app) -> None:
with test_app.app_context():
assert is_allowed_extensions('file.invalid_ext') is False
assert is_allowed_extensions('file') is False
assert is_allowed_extensions("file.invalid_ext") is False
assert is_allowed_extensions("file") is False
def test_order_by_column_name_ascending_mode(test_app) -> None:
with test_app.app_context():
query = order_by_column_name(Student.query, 'index', 'desc')
query = order_by_column_name(Student.query, "index", "desc")
assert 'ORDER BY students."index"' in str(query)
def test_order_by_column_name_descending_mode(test_app) -> None:
with test_app.app_context():
query = order_by_column_name(Student.query, 'index', 'desc')
query = order_by_column_name(Student.query, "index", "desc")
assert 'ORDER BY students."index" DESC' in str(query)
def test_paginate_models(test_app_ctx_with_db) -> None:
with test_app_ctx_with_db:
st = Student(index=123456, first_name='Dominic', last_name='Smith', pesel='99010109876', email='xxx@gmail.com')
st1 = Student(index=123457, first_name='John', last_name='Newton', pesel='99010109871', email='zzz@gmail.com')
st = Student(
index=123456,
first_name="Dominic",
last_name="Smith",
pesel="99010109876",
email="xxx@gmail.com",
)
st1 = Student(
index=123457,
first_name="John",
last_name="Newton",
pesel="99010109871",
email="zzz@gmail.com",
)
db.session.add_all([st, st1])
db.session.commit()
result = paginate_models(1, Student.query, 1)
items = result.get('items', [])
max_pages = result.get('max_pages', 0)
items = result.get("items", [])
max_pages = result.get("max_pages", 0)
assert len(items) == 1
assert max_pages == 2
def test_check_columns() -> None:
dummy_data = {'NAZWISKO': ['Smith'], 'IMIE': ['Dominic'], 'INDEKS': [343433], 'PESEL': [90020178654],
'EMAIL': ['domsmi@gmail.com']}
dummy_data = {
"NAZWISKO": ["Smith"],
"IMIE": ["Dominic"],
"INDEKS": [343433],
"PESEL": [90020178654],
"EMAIL": ["domsmi@gmail.com"],
}
df = pd.DataFrame(data=dummy_data)
assert check_columns(df) is True
def test_check_columns_with_invalid_column_names() -> None:
dummy_data = {'col1': [1, 2], 'col2': [2, 3]}
dummy_data = {"col1": [1, 2], "col2": [2, 3]}
df = pd.DataFrame(data=dummy_data)
assert check_columns(df) is False
def test_check_columns_with_invalid_column_types() -> None:
dummy_data = {'NAZWISKO': [999], 'IMIE': ['Dominic'], 'INDEKS': [343433], 'PESEL': [90020178654],
'EMAIL': ['domsmi@gmail.com']}
dummy_data = {
"NAZWISKO": [999],
"IMIE": ["Dominic"],
"INDEKS": [343433],
"PESEL": [90020178654],
"EMAIL": ["domsmi@gmail.com"],
}
df = pd.DataFrame(data=dummy_data)
assert check_columns(df) is False
def get_path_to_fake_data(filename: str) -> str:
base_dir = current_app.config.get('BASE_DIR', '/')
return base_dir / 'tmp_data' / filename
base_dir = current_app.config.get("BASE_DIR", "/")
return base_dir / "tmp_data" / filename
def test_parse_csv(test_app) -> None:
with test_app.app_context():
with open(get_path_to_fake_data('students.csv')) as f:
with open(get_path_to_fake_data("students.csv")) as f:
students = sorted(list(parse_csv(f)), key=lambda s: s.index)
indexes = [452790 + i for i in range(3)]
assert len(students) == len(indexes)
@ -87,14 +114,14 @@ def test_parse_csv(test_app) -> None:
def test_parse_csv_with_invalid_column_header_name_in_csv_file(test_app) -> None:
with test_app.app_context():
with open(get_path_to_fake_data('students_column_name.csv')) as f:
with open(get_path_to_fake_data("students_column_name.csv")) as f:
with pytest.raises(InvalidNameOrTypeHeaderException):
parse_csv(f)
def test_parse_csv_with_invalid_column_type_in_csv_file(test_app) -> None:
with test_app.app_context():
with open(get_path_to_fake_data('students_column_type.csv')) as f:
with open(get_path_to_fake_data("students_column_type.csv")) as f:
with pytest.raises(InvalidNameOrTypeHeaderException):
parse_csv(f)
@ -114,12 +141,27 @@ def test_generate_range_dates() -> None:
def test_generate_csv(test_app_ctx_with_db) -> None:
students_data = [
{'first_name': 'Dominic', 'last_name': 'Smith', 'email': 'xxe@gmail.com', 'index': 123456,
'pesel': '98070234293'},
{'first_name': 'Matthew', 'last_name': 'Cash', 'email': 'zze@gmail.com', 'index': 123455,
'pesel': '98070234291'},
{'first_name': 'Martin', 'last_name': 'Rose', 'email': 'nne@gmail.com', 'index': 123446,
'pesel': '98070234223'},
{
"first_name": "Dominic",
"last_name": "Smith",
"email": "xxe@gmail.com",
"index": 123456,
"pesel": "98070234293",
},
{
"first_name": "Matthew",
"last_name": "Cash",
"email": "zze@gmail.com",
"index": 123455,
"pesel": "98070234291",
},
{
"first_name": "Martin",
"last_name": "Rose",
"email": "nne@gmail.com",
"index": 123446,
"pesel": "98070234223",
},
]
with test_app_ctx_with_db:
@ -135,7 +177,11 @@ def test_generate_csv(test_app_ctx_with_db) -> None:
db.session.add_all([gr1, gr2])
db.session.commit()
students_and_groups = [(students[0], gr1), (students[1], gr1), (students[2], gr2)]
students_and_groups = [
(students[0], gr1),
(students[1], gr1),
(students[2], gr2),
]
generated_csv = generate_csv(students_and_groups)
for data in students_data:
for value in data.values():

View File

@ -3,7 +3,10 @@ import datetime
import pytest
from marshmallow import ValidationError
from app.coordinator.validators import validate_index, validate_datetime_greater_than_now
from app.coordinator.validators import (
validate_datetime_greater_than_now,
validate_index,
)
def test_validate_index() -> None:

View File

@ -15,8 +15,13 @@ def assert_model_changes(model: db.Model, expected_data: dict) -> None:
assert value == val
def _test_case_client_without_response(test_client: FlaskClient, url: str, data: Union[dict, None], status_code: int,
method: str = 'post') -> dict:
def _test_case_client_without_response(
test_client: FlaskClient,
url: str,
data: Union[dict, None],
status_code: int,
method: str = "post",
) -> dict:
method_func = getattr(test_client, method)
if data is not None:
response = method_func(url, json=data)
@ -27,16 +32,25 @@ def _test_case_client_without_response(test_client: FlaskClient, url: str, data:
return response.json
def _test_case_client(test_client: FlaskClient, url: str, data: Union[dict, None], message: str, status_code: int,
key: str = 'message', method: str = 'post') -> None:
response_data = _test_case_client_without_response(test_client, url, data, status_code, method)
def _test_case_client(
test_client: FlaskClient,
url: str,
data: Union[dict, None],
message: str,
status_code: int,
key: str = "message",
method: str = "post",
) -> None:
response_data = _test_case_client_without_response(
test_client, url, data, status_code, method
)
assert key in response_data.keys()
assert response_data.get(key) == message
def _test_case_group(group: Group, data: dict) -> None:
assert group.name == data['name']
assert group.project_supervisor_id == data['project_supervisor_id']
assert group.name == data["name"]
assert group.project_supervisor_id == data["project_supervisor_id"]
for st in group.students:
assert st.index in data['students']
assert st.index in data["students"]