diff --git a/backend/app/coordinator/routes/groups.py b/backend/app/coordinator/routes/groups.py index 5f13583..2fbe993 100644 --- a/backend/app/coordinator/routes/groups.py +++ b/backend/app/coordinator/routes/groups.py @@ -2,7 +2,7 @@ from flask import abort, current_app from apiflask import APIBlueprint from flask_sqlalchemy import get_debug_queries -from ...students.models import Group, Student, YearGroup +from ...students.models import Group, Student, YearGroup, ProjectGradeSheet from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors from ..schemas import GroupSchema, GroupEditSchema, GroupsPaginationSchema, \ GroupCreateSchema, MessageSchema, GroupQuerySchema @@ -75,6 +75,11 @@ def create_group(year_group_id: int, data: dict) -> dict: students = db.session.query(Student).filter(Student.index.in_(students_indexes)).all() for student in students: group.students.append(student) + + db.session.commit() + + pgs = ProjectGradeSheet(group_id=group.id) + db.session.add(pgs) db.session.commit() return {"message": "Group was created!"} diff --git a/backend/app/project_supervisor/routes/__init__.py b/backend/app/project_supervisor/routes/__init__.py index aa01a5a..b7e3a18 100644 --- a/backend/app/project_supervisor/routes/__init__.py +++ b/backend/app/project_supervisor/routes/__init__.py @@ -1,7 +1,9 @@ from flask import Blueprint from .enrollments import bp as enrollments_bp +from .project_grade_sheet import bp as project_grade_sheet_bp bp = Blueprint("project_supervisor", __name__, url_prefix="/project_supervisor") bp.register_blueprint(enrollments_bp) +bp.register_blueprint(project_grade_sheet_bp) diff --git a/backend/app/project_supervisor/routes/project_grade_sheet.py b/backend/app/project_supervisor/routes/project_grade_sheet.py new file mode 100644 index 0000000..b0a5a97 --- /dev/null +++ b/backend/app/project_supervisor/routes/project_grade_sheet.py @@ -0,0 +1,68 @@ +from apiflask import APIBlueprint +from flask import abort + +from ...dependencies import db +from ..models import ProjectSupervisor +from ..schemas import ProjectSupervisorTermQuerySchema, MessageSchema, TemporaryProjectSupervisorSchema +from ...students.schemas import ProjectGradeSheetDetailFirstTermSchema, ProjectGradeSheetDetailSecondTermSchema, \ + ProjectGradeSheetEditFirstTermSchema, ProjectGradeSheetEditSecondTermSchema +from ...students.models import Group, ProjectGradeSheet + +bp = APIBlueprint("project_grade_sheet_for_project_supervisor", __name__, url_prefix="/project-grade-sheet") + + +@bp.get('/group//') +@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() + 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() + if group is None or len(group.project_grade_sheet) == 0: + abort(400, "Group doesn't exist!") + + pgs = group.project_grade_sheet[0] + if term == 1: + schema = ProjectGradeSheetDetailFirstTermSchema() + else: + schema = ProjectGradeSheetDetailSecondTermSchema() + + 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//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: + update_project_grade_sheet(group_id, query, data) + return {"message": "Your project grade sheet was updated!"} + + +@bp.patch('/group//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: + update_project_grade_sheet(group_id, query, data) + return {"message": "Your project grade sheet was updated!"} diff --git a/backend/app/project_supervisor/schemas.py b/backend/app/project_supervisor/schemas.py index e497085..5977a56 100644 --- a/backend/app/project_supervisor/schemas.py +++ b/backend/app/project_supervisor/schemas.py @@ -28,3 +28,7 @@ class TimeAvailabilityCreateSchema(Schema): # temporary class it will be removed in the future class TemporaryProjectSupervisorSchema(Schema): id = fields.Integer(required=True) + +class ProjectSupervisorTermQuerySchema(Schema): + id = fields.Integer(required=True) + term = fields.Integer(required=True, validate=validate.OneOf([1, 2])) diff --git a/backend/app/students/models.py b/backend/app/students/models.py index 29dcbb4..b594493 100644 --- a/backend/app/students/models.py +++ b/backend/app/students/models.py @@ -58,6 +58,73 @@ class Group(Base): return group_query +class ProjectGradeSheet(Base): + __tablename__ = 'project_grade_sheets' + + group_id = db.Column(db.Integer, db.ForeignKey('groups.id')) + group = db.relationship('Group', backref='project_grade_sheet', uselist=False, lazy='joined') + + presentation_required_content_1 = db.Column(db.Integer, default=0) + presentation_required_content_2 = db.Column(db.Integer, default=0) + presentation_was_compatible_1 = db.Column(db.Integer, default=0) + 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) + + documentation_project_vision_1 = db.Column(db.Integer, default=0) + documentation_project_vision_2 = db.Column(db.Integer, default=0) + documentation_requirements_1 = db.Column(db.Integer, default=0) + documentation_requirements_2 = db.Column(db.Integer, default=0) + documentation_for_clients_1 = db.Column(db.Integer, default=0) + documentation_for_clients_2 = db.Column(db.Integer, default=0) + documentation_for_developers_1 = db.Column(db.Integer, default=0) + documentation_for_developers_2 = db.Column(db.Integer, default=0) + documentation_license_1 = db.Column(db.Integer, default=0) + documentation_license_2 = db.Column(db.Integer, default=0) + + group_work_regularity_1 = db.Column(db.Integer, default=0) + group_work_regularity_2 = db.Column(db.Integer, default=0) + group_work_division_of_work_1 = db.Column(db.Integer, default=0) + group_work_division_of_work_2 = db.Column(db.Integer, default=0) + group_work_contact_with_client_1 = db.Column(db.Integer, default=0) + group_work_contact_with_client_2 = db.Column(db.Integer, default=0) + group_work_management_of_risk_1 = db.Column(db.Integer, default=0) + group_work_management_of_risk_2 = db.Column(db.Integer, default=0) + group_work_work_methodology_1 = db.Column(db.Integer, default=0) + group_work_work_methodology_2 = db.Column(db.Integer, default=0) + group_work_management_of_source_code_1 = db.Column(db.Integer, default=0) + group_work_management_of_source_code_2 = db.Column(db.Integer, default=0) + group_work_devops_1 = db.Column(db.Integer, default=0) + group_work_devops_2 = db.Column(db.Integer, default=0) + + products_project_complexity_of_product_1 = db.Column(db.Integer, default=0) + products_project_complexity_of_product_2 = db.Column(db.Integer, default=0) + products_project_access_to_application_1 = db.Column(db.Integer, default=0) + products_project_access_to_application_2 = db.Column(db.Integer, default=0) + products_project_security_issues_1 = db.Column(db.Integer, default=0) + products_project_security_issues_2 = db.Column(db.Integer, default=0) + products_project_access_to_test_application_1 = db.Column(db.Integer, default=0) + products_project_access_to_test_application_2 = db.Column(db.Integer, default=0) + products_project_acceptance_criteria_1 = db.Column(db.Integer, default=0) + products_project_acceptance_criteria_2 = db.Column(db.Integer, default=0) + products_project_expected_functionality_1 = db.Column(db.Integer, default=0) + products_project_expected_functionality_2 = db.Column(db.Integer, default=0) + products_project_promises_well_1 = db.Column(db.Integer, default=0) + products_project_promises_well_2 = db.Column(db.Integer, default=0) + products_project_has_been_implemented_1 = db.Column(db.Integer, default=0) + products_project_has_been_implemented_2 = db.Column(db.Integer, default=0) + products_project_is_useful_1 = db.Column(db.Integer, default=0) + products_project_is_useful_2 = db.Column(db.Integer, default=0) + products_project_prototype_1 = db.Column(db.Integer, default=0) + products_project_prototype_2 = db.Column(db.Integer, default=0) + products_project_tests_1 = db.Column(db.Integer, default=0) + products_project_tests_2 = db.Column(db.Integer, default=0) + products_project_technology_1 = db.Column(db.Integer, default=0) + products_project_technology_2 = db.Column(db.Integer, default=0) + + class Student(Person): __tablename__ = "students" diff --git a/backend/app/students/routes/__init__.py b/backend/app/students/routes/__init__.py index 561045e..06b34c0 100644 --- a/backend/app/students/routes/__init__.py +++ b/backend/app/students/routes/__init__.py @@ -1,11 +1,13 @@ from flask import Blueprint from .enrollments import bp as enrollments_bp +from .project_grade_sheet import bp as project_grade_sheet_bp from .registrations import bp as registrations_bp from .year_group import bp as year_group_bp bp = Blueprint("students", __name__, url_prefix="/students") bp.register_blueprint(enrollments_bp) +bp.register_blueprint(project_grade_sheet_bp) bp.register_blueprint(registrations_bp) bp.register_blueprint(year_group_bp) diff --git a/backend/app/students/routes/project_grade_sheet.py b/backend/app/students/routes/project_grade_sheet.py new file mode 100644 index 0000000..0d7a9de --- /dev/null +++ b/backend/app/students/routes/project_grade_sheet.py @@ -0,0 +1,35 @@ +from apiflask import APIBlueprint +from flask import abort + +from ..schemas import StudentIndexQueryTempSchema, ProjectGradeSheetDetailFirstTermSchema, \ + ProjectGradeSheetDetailSecondTermSchema +from ..models import Student, ProjectGradeSheet + +bp = APIBlueprint("project_grade_sheet", __name__, url_prefix="/project-grade-sheet") + + +@bp.get('/year-group//') +@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() + if st is None: + abort(404, "Not found student!") + #################################### + term = int(query.get('term')) + + groups = [g for g in st.groups if g.year_group_id == year_group_id] + if len(groups) == 0: + abort(404, "Not found group!") + group = groups[0] + + pgs = ProjectGradeSheet.query.filter(ProjectGradeSheet.group_id == group.id).first() + if pgs is None: + abort(404, "Not found project grade sheet!") + + if term == 1: + schema = ProjectGradeSheetDetailFirstTermSchema() + else: + schema = ProjectGradeSheetDetailSecondTermSchema() + + return schema.dump(pgs) diff --git a/backend/app/students/schemas.py b/backend/app/students/schemas.py index 503f29d..c4c40c5 100644 --- a/backend/app/students/schemas.py +++ b/backend/app/students/schemas.py @@ -1,4 +1,6 @@ -from marshmallow import fields, Schema +from marshmallow import fields, Schema, validate + +POINTS = [0, 1, 3, 4] class ProjectSupervisorSchema(Schema): @@ -69,3 +71,84 @@ class YearGroupQueryStudentSchema(Schema): index = fields.Integer(required=True) # it will be removed page = fields.Integer() per_page = fields.Integer() + + +class StudentIndexQueryTempSchema(Schema): + index = fields.Integer(required=True) # it will be removed + term = fields.Integer(required=True, validate=validate.OneOf([1, 2])) + + +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)) + + documentation_project_vision_1 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_requirements_1 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_for_clients_1 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_for_developers_1 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_license_1 = fields.Integer(validate=validate.OneOf(POINTS)) + + group_work_regularity_1 = fields.Integer(validate=validate.OneOf(POINTS)) + group_work_division_of_work_1 = fields.Integer(validate=validate.OneOf(POINTS)) + 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_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_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_promises_well_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)) + products_project_technology_1 = fields.Integer(validate=validate.OneOf(POINTS)) + + +class ProjectGradeSheetDetailFirstTermSchema(ProjectGradeSheetEditFirstTermSchema): + id = fields.Integer() + + +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)) + + documentation_project_vision_2 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_requirements_2 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_for_clients_2 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_for_developers_2 = fields.Integer(validate=validate.OneOf(POINTS)) + documentation_license_2 = fields.Integer(validate=validate.OneOf(POINTS)) + + group_work_regularity_2 = fields.Integer(validate=validate.OneOf(POINTS)) + group_work_division_of_work_2 = fields.Integer(validate=validate.OneOf(POINTS)) + 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_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_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_promises_well_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)) + products_project_technology_2 = fields.Integer(validate=validate.OneOf(POINTS)) + + +class ProjectGradeSheetDetailSecondTermSchema(ProjectGradeSheetEditSecondTermSchema): + id = fields.Integer() diff --git a/backend/migrations/versions/13d387439f43_.py b/backend/migrations/versions/13d387439f43_.py new file mode 100644 index 0000000..42dc936 --- /dev/null +++ b/backend/migrations/versions/13d387439f43_.py @@ -0,0 +1,89 @@ +"""empty message + +Revision ID: 13d387439f43 +Revises: 7deb011753b2 +Create Date: 2022-12-15 18:06:57.906228 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '13d387439f43' +down_revision = '7deb011753b2' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + 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') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('project_grade_sheets') + # ### end Alembic commands ###