From 60bcd9efc3d5347499ae564762c2c9a724451109 Mon Sep 17 00:00:00 2001 From: Thi1ef Date: Tue, 15 Jul 2025 01:04:11 +0300 Subject: [PATCH 1/3] Add new application features --- ...92b1544_add_feedback_column_application.py | 35 +++++++++ backend/app/models/applications.py | 2 + backend/app/routes/api/project/data_access.py | 41 ++++++++++- backend/app/routes/api/project/router.py | 23 ++++++ backend/app/routes/api/project/schemas.py | 5 +- backend/app/routes/api/project/service.py | 73 +++++++++++++++---- 6 files changed, 162 insertions(+), 17 deletions(-) create mode 100644 backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py diff --git a/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py b/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py new file mode 100644 index 0000000..7a7836f --- /dev/null +++ b/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py @@ -0,0 +1,35 @@ +"""add feedback column application + +Revision ID: 43d4892b1544 +Revises: 1722e1790137 +Create Date: 2025-07-14 18:38:01.464920 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "43d4892b1544" +down_revision: Union[str, Sequence[str], None] = "1722e1790137" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "applications", sa.Column("feedback", sa.Text(), nullable=True) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("applications", "feedback") + # ### end Alembic commands ### diff --git a/backend/app/models/applications.py b/backend/app/models/applications.py index c8bed2a..d15f491 100644 --- a/backend/app/models/applications.py +++ b/backend/app/models/applications.py @@ -6,6 +6,7 @@ func, Boolean, ForeignKey, + Text, PrimaryKeyConstraint, ) from dependencies.database import base @@ -18,6 +19,7 @@ class Application(base): user_id = Column(Integer, ForeignKey("users.id")) created_at = Column(DateTime, default=func.current_timestamp()) is_approved = Column(Boolean, nullable=True) + feedback = Column(Text, nullable=True) __table_args__ = ( Index("ix_applications_project_user", "project_id", "user_id", unique=True), diff --git a/backend/app/routes/api/project/data_access.py b/backend/app/routes/api/project/data_access.py index d01fd13..6faa848 100644 --- a/backend/app/routes/api/project/data_access.py +++ b/backend/app/routes/api/project/data_access.py @@ -2,7 +2,7 @@ from pydantic_core._pydantic_core import ValidationError from dependencies.database import DBSessionDep from models import Project, Application, ProjectMember -from sqlalchemy import select, update +from sqlalchemy import select, update, delete from routes.api.project.exceptions import ApplicationNotFoundError from routes.api.project.schemas import ( @@ -110,7 +110,7 @@ async def approve_application( Application.project_id == project_id, Application.user_id == approve_schema.user_id, ) - .values(is_approved=approve_schema.is_approved) + .values(is_approved=approve_schema.is_approved, feedback=approve_schema.feedback) .returning(Application) ) res = await self.db_session.execute(query) @@ -131,5 +131,42 @@ async def add_user_to_project( await self.db_session.flush() return ProjectMemberSchema.model_validate(project_member) + async def delete_project( + self, + project_id: int + ): + query = ( + delete(Project).where(Project.id == project_id) + ) + await self.db_session.execute(query) + await self.db_session.commit() + + async def get_applied_project( + self, + user_id: int + ) -> list[ApplicationSchema]: + query = ( + select(Application).where(Application.user_id == user_id) + ) + res = await self.db_session.execute(query) + applications = res.scalars().all() + return ( + [ApplicationSchema.model_validate(application) for application in applications] + if applications + else [] + ) + + async def cancel_application( + self, + project_id: int, + user_id: int + ): + query = ( + delete(Application).where(Application.user_id == user_id, Application.project_id == project_id) + ) + await self.db_session.execute(query) + await self.db_session.commit() + + ProjectsDataAccessDep = Annotated[ProjectsDataAccess, Depends(ProjectsDataAccess)] diff --git a/backend/app/routes/api/project/router.py b/backend/app/routes/api/project/router.py index 550f538..5c118ae 100644 --- a/backend/app/routes/api/project/router.py +++ b/backend/app/routes/api/project/router.py @@ -7,6 +7,7 @@ ApplicationSchema, ApproveApplicationSchema, ProjectMemberSchema, + MessageSchema ) from routes.api.project.service import ProjectServiceDep @@ -29,6 +30,12 @@ async def create_project( ) -> ProjectSchema: return await service.create_project(project_data, user) +@router.get("/all-applications") +async def get_applied_project( + user: AuthUserDep, + service: ProjectServiceDep +) -> list[ApplicationSchema]: + return await service.get_applied_project(user) @router.get("/{project_id}") async def get_project_by_id( @@ -74,3 +81,19 @@ async def approve_application( user: AuthUserDep, ) -> ProjectMemberSchema: return await service.approve_application(project_id, user, approve_schema) + +@router.delete("/{project_id}/applications/cancel") +async def cancel_application( + project_id: int, + service: ProjectServiceDep, + user: AuthUserDep +) -> MessageSchema: + return await service.cancel_application(project_id, user) + +@router.delete("/{project_id}") +async def delete_project( + project_id: int, + service: ProjectServiceDep, + user: AuthUserDep, +) -> MessageSchema: + return await service.delete_project(project_id, user) diff --git a/backend/app/routes/api/project/schemas.py b/backend/app/routes/api/project/schemas.py index 4bb3820..56a11c0 100644 --- a/backend/app/routes/api/project/schemas.py +++ b/backend/app/routes/api/project/schemas.py @@ -19,12 +19,15 @@ class ProjectSchema(NewProjectSchema): class Config: from_attributes = True +class MessageSchema(BaseModel): + msg: str class ApplicationSchema(BaseModel): project_id: int user_id: int is_approved: Optional[bool] created_at: datetime + feedback: Optional[str] class Config: from_attributes = True @@ -33,7 +36,7 @@ class Config: class ApproveApplicationSchema(BaseModel): is_approved: bool user_id: int - + feedback: Optional[str] class ProjectMemberSchema(BaseModel): project_id: int diff --git a/backend/app/routes/api/project/service.py b/backend/app/routes/api/project/service.py index 70dc1df..e76ab3f 100644 --- a/backend/app/routes/api/project/service.py +++ b/backend/app/routes/api/project/service.py @@ -1,3 +1,4 @@ +from email.message import Message from typing import Annotated from fastapi import Depends from fastapi.exceptions import HTTPException @@ -9,6 +10,7 @@ NewProjectSchema, ApplicationSchema, ApproveApplicationSchema, + MessageSchema ) from schemas.user import UserInDB @@ -29,9 +31,9 @@ async def get_projects(self) -> list[ProjectSchema]: return await self.data_access.get_all_projects() async def create_project( - self, - project_data: NewProjectSchema, - user: UserInDB, + self, + project_data: NewProjectSchema, + user: UserInDB, ) -> ProjectSchema: if user.role != UserRole.FOUNDER: raise HTTPException( @@ -47,10 +49,10 @@ async def create_project( return await self.data_access.create_project(project_data, user.id) async def get_project_applications( - self, - project_id: int, - user_id: int = None, - only_new: bool = False, + self, + project_id: int, + user_id: int = None, + only_new: bool = False, ) -> list[ApplicationSchema]: project = await self.data_access.get_project_by_id(project_id, user_id) if project is None: @@ -61,9 +63,9 @@ async def get_project_applications( return await self.data_access.get_project_applications(project_id, only_new) async def apply_to_project( - self, - project_id: int, - user_id: int, + self, + project_id: int, + user_id: int, ) -> ApplicationSchema: project = await self.data_access.get_project_by_id(project_id) if project is None: @@ -87,10 +89,10 @@ async def apply_to_project( return await self.data_access.apply_to_project(project_id, user_id) async def approve_application( - self, - project_id: int, - user: UserInDB, - approve_schema: ApproveApplicationSchema, + self, + project_id: int, + user: UserInDB, + approve_schema: ApproveApplicationSchema, ) -> ApplicationSchema: project = await self.data_access.get_project_by_id(project_id) if project is None: @@ -133,5 +135,48 @@ async def approve_application( application.user_id, ) + async def delete_project( + self, + project_id: int, + user: UserInDB + ) -> MessageSchema: + project = await self.data_access.get_project_by_id(project_id) + if project is None: + raise HTTPException( + status_code=404, + detail=f"Project with ID {project_id} not found." + ) + if project.ceo_id != user.id: + raise HTTPException( + status_code=403, + detail=f"Only the project CEO can delete." + ) + await self.data_access.delete_project(project_id) + return MessageSchema(msg=f'Project {project_id} was successfully deleted') + + async def get_applied_project( + self, + user: UserInDB + ) -> list[ApplicationSchema]: + return await self.data_access.get_applied_project(user.id) + + async def cancel_application( + self, + project_id, + user: UserInDB + ) -> MessageSchema: + application = await self.data_access.get_application_by_user_and_project_id(project_id, user.id) + if application is None: + raise HTTPException( + status_code=404, + detail=f"Application not found in project {project_id}.", + ) + if application.is_approved: + raise HTTPException( + status_code=403, + detail=f"Application was approved" + ) + await self.data_access.cancel_application(project_id, user.id) + return MessageSchema(msg=f"Application was successfully deleted") ProjectServiceDep = Annotated[ProjectService, Depends(ProjectService)] From 29c38d730284ab5eb36e24e20ad97b63a4f46139 Mon Sep 17 00:00:00 2001 From: Thi1ef Date: Tue, 15 Jul 2025 03:40:32 +0300 Subject: [PATCH 2/3] some fix and reformat code --- ...92b1544_add_feedback_column_application.py | 4 +- ...f57b2c75_change_date_to_datetime_format.py | 24 ++---- backend/app/routes/api/project/data_access.py | 65 ++++++---------- backend/app/routes/api/project/exceptions.py | 2 +- backend/app/routes/api/project/router.py | 23 +++--- backend/app/routes/api/project/schemas.py | 4 + backend/app/routes/api/project/service.py | 75 ++++++++----------- 7 files changed, 77 insertions(+), 120 deletions(-) diff --git a/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py b/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py index 7a7836f..a22c0b9 100644 --- a/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py +++ b/backend/app/migrations/versions/43d4892b1544_add_feedback_column_application.py @@ -22,9 +22,7 @@ def upgrade() -> None: """Upgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - op.add_column( - "applications", sa.Column("feedback", sa.Text(), nullable=True) - ) + op.add_column("applications", sa.Column("feedback", sa.Text(), nullable=True)) # ### end Alembic commands ### diff --git a/backend/app/migrations/versions/6e2af57b2c75_change_date_to_datetime_format.py b/backend/app/migrations/versions/6e2af57b2c75_change_date_to_datetime_format.py index 4a56db9..9a9851e 100644 --- a/backend/app/migrations/versions/6e2af57b2c75_change_date_to_datetime_format.py +++ b/backend/app/migrations/versions/6e2af57b2c75_change_date_to_datetime_format.py @@ -43,36 +43,24 @@ def upgrade() -> None: type_=sa.DateTime(), existing_nullable=True, ) - op.alter_column( - "projects", "is_public", existing_type=sa.BOOLEAN(), nullable=True - ) - op.alter_column( - "projects", "ceo_id", existing_type=sa.INTEGER(), nullable=True - ) + op.alter_column("projects", "is_public", existing_type=sa.BOOLEAN(), nullable=True) + op.alter_column("projects", "ceo_id", existing_type=sa.INTEGER(), nullable=True) op.alter_column( "projects", "is_opensource", existing_type=sa.BOOLEAN(), nullable=True ) - op.alter_column( - "projects", "is_dead", existing_type=sa.BOOLEAN(), nullable=True - ) + op.alter_column("projects", "is_dead", existing_type=sa.BOOLEAN(), nullable=True) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### - op.alter_column( - "projects", "is_dead", existing_type=sa.BOOLEAN(), nullable=False - ) + op.alter_column("projects", "is_dead", existing_type=sa.BOOLEAN(), nullable=False) op.alter_column( "projects", "is_opensource", existing_type=sa.BOOLEAN(), nullable=False ) - op.alter_column( - "projects", "ceo_id", existing_type=sa.INTEGER(), nullable=False - ) - op.alter_column( - "projects", "is_public", existing_type=sa.BOOLEAN(), nullable=False - ) + op.alter_column("projects", "ceo_id", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("projects", "is_public", existing_type=sa.BOOLEAN(), nullable=False) op.alter_column( "projects", "created_at", diff --git a/backend/app/routes/api/project/data_access.py b/backend/app/routes/api/project/data_access.py index 6faa848..4f1983a 100644 --- a/backend/app/routes/api/project/data_access.py +++ b/backend/app/routes/api/project/data_access.py @@ -15,7 +15,6 @@ from fastapi import Depends - class ProjectsDataAccess: def __init__(self, db_session: DBSessionDep): self.db_session = db_session @@ -46,11 +45,7 @@ async def get_project_by_title(self, title: str) -> ProjectSchema | None: async def get_all_projects(self) -> list[ProjectSchema]: res = await self.db_session.execute(select(Project)) projects = res.scalars().all() - return ( - [ProjectSchema.model_validate(project) for project in projects] - if projects - else [] - ) + return [ProjectSchema.model_validate(project) for project in projects] async def create_project( self, @@ -73,11 +68,7 @@ async def get_project_applications( query = query.where(Application.is_approved.is_(None)) res = await self.db_session.execute(query) applications = res.scalars().all() - return ( - [ApplicationSchema.model_validate(app) for app in applications] - if applications - else [] - ) + return [ApplicationSchema.model_validate(app) for app in applications] async def get_application_by_user_and_project_id( self, project_id: int, user_id: int @@ -110,7 +101,9 @@ async def approve_application( Application.project_id == project_id, Application.user_id == approve_schema.user_id, ) - .values(is_approved=approve_schema.is_approved, feedback=approve_schema.feedback) + .values( + is_approved=approve_schema.is_approved, feedback=approve_schema.feedback + ) .returning(Application) ) res = await self.db_session.execute(query) @@ -131,42 +124,26 @@ async def add_user_to_project( await self.db_session.flush() return ProjectMemberSchema.model_validate(project_member) - async def delete_project( - self, - project_id: int - ): - query = ( - delete(Project).where(Project.id == project_id) - ) - await self.db_session.execute(query) - await self.db_session.commit() + async def delete_project(self, project_id: int) -> bool: + query = delete(Project).where(Project.id == project_id) + res = await self.db_session.execute(query) + return res.rowcount > 0 - async def get_applied_project( - self, - user_id: int - ) -> list[ApplicationSchema]: - query = ( - select(Application).where(Application.user_id == user_id) - ) + async def get_user_applications(self, user_id: int) -> list[ApplicationSchema]: + query = select(Application).where(Application.user_id == user_id) res = await self.db_session.execute(query) applications = res.scalars().all() - return ( - [ApplicationSchema.model_validate(application) for application in applications] - if applications - else [] - ) - - async def cancel_application( - self, - project_id: int, - user_id: int - ): - query = ( - delete(Application).where(Application.user_id == user_id, Application.project_id == project_id) + return [ + ApplicationSchema.model_validate(application) + for application in applications + ] + + async def cancel_application(self, project_id: int, user_id: int) -> bool: + query = delete(Application).where( + Application.user_id == user_id, Application.project_id == project_id ) - await self.db_session.execute(query) - await self.db_session.commit() - + res = await self.db_session.execute(query) + return res.rowcount > 0 ProjectsDataAccessDep = Annotated[ProjectsDataAccess, Depends(ProjectsDataAccess)] diff --git a/backend/app/routes/api/project/exceptions.py b/backend/app/routes/api/project/exceptions.py index 5c0ccbf..30dbb97 100644 --- a/backend/app/routes/api/project/exceptions.py +++ b/backend/app/routes/api/project/exceptions.py @@ -1,4 +1,4 @@ class ApplicationNotFoundError(Exception): def __init__(self, message="No application found."): self.message = message - super().__init__(self.message) \ No newline at end of file + super().__init__(self.message) diff --git a/backend/app/routes/api/project/router.py b/backend/app/routes/api/project/router.py index 5c118ae..5f7e4d5 100644 --- a/backend/app/routes/api/project/router.py +++ b/backend/app/routes/api/project/router.py @@ -7,7 +7,7 @@ ApplicationSchema, ApproveApplicationSchema, ProjectMemberSchema, - MessageSchema + MessageSchema, ) from routes.api.project.service import ProjectServiceDep @@ -30,12 +30,13 @@ async def create_project( ) -> ProjectSchema: return await service.create_project(project_data, user) + @router.get("/all-applications") -async def get_applied_project( - user: AuthUserDep, - service: ProjectServiceDep +async def get_user_applications( + user: AuthUserDep, service: ProjectServiceDep ) -> list[ApplicationSchema]: - return await service.get_applied_project(user) + return await service.get_user_applications(user) + @router.get("/{project_id}") async def get_project_by_id( @@ -82,18 +83,18 @@ async def approve_application( ) -> ProjectMemberSchema: return await service.approve_application(project_id, user, approve_schema) + @router.delete("/{project_id}/applications/cancel") async def cancel_application( - project_id: int, - service: ProjectServiceDep, - user: AuthUserDep + project_id: int, service: ProjectServiceDep, user: AuthUserDep ) -> MessageSchema: return await service.cancel_application(project_id, user) + @router.delete("/{project_id}") async def delete_project( - project_id: int, - service: ProjectServiceDep, - user: AuthUserDep, + project_id: int, + service: ProjectServiceDep, + user: AuthUserDep, ) -> MessageSchema: return await service.delete_project(project_id, user) diff --git a/backend/app/routes/api/project/schemas.py b/backend/app/routes/api/project/schemas.py index 56a11c0..4cae04b 100644 --- a/backend/app/routes/api/project/schemas.py +++ b/backend/app/routes/api/project/schemas.py @@ -19,8 +19,11 @@ class ProjectSchema(NewProjectSchema): class Config: from_attributes = True + class MessageSchema(BaseModel): msg: str + success: bool + class ApplicationSchema(BaseModel): project_id: int @@ -38,6 +41,7 @@ class ApproveApplicationSchema(BaseModel): user_id: int feedback: Optional[str] + class ProjectMemberSchema(BaseModel): project_id: int user_id: int diff --git a/backend/app/routes/api/project/service.py b/backend/app/routes/api/project/service.py index e76ab3f..6294f40 100644 --- a/backend/app/routes/api/project/service.py +++ b/backend/app/routes/api/project/service.py @@ -10,7 +10,7 @@ NewProjectSchema, ApplicationSchema, ApproveApplicationSchema, - MessageSchema + MessageSchema, ) from schemas.user import UserInDB @@ -31,9 +31,9 @@ async def get_projects(self) -> list[ProjectSchema]: return await self.data_access.get_all_projects() async def create_project( - self, - project_data: NewProjectSchema, - user: UserInDB, + self, + project_data: NewProjectSchema, + user: UserInDB, ) -> ProjectSchema: if user.role != UserRole.FOUNDER: raise HTTPException( @@ -49,10 +49,10 @@ async def create_project( return await self.data_access.create_project(project_data, user.id) async def get_project_applications( - self, - project_id: int, - user_id: int = None, - only_new: bool = False, + self, + project_id: int, + user_id: int = None, + only_new: bool = False, ) -> list[ApplicationSchema]: project = await self.data_access.get_project_by_id(project_id, user_id) if project is None: @@ -63,9 +63,9 @@ async def get_project_applications( return await self.data_access.get_project_applications(project_id, only_new) async def apply_to_project( - self, - project_id: int, - user_id: int, + self, + project_id: int, + user_id: int, ) -> ApplicationSchema: project = await self.data_access.get_project_by_id(project_id) if project is None: @@ -89,10 +89,10 @@ async def apply_to_project( return await self.data_access.apply_to_project(project_id, user_id) async def approve_application( - self, - project_id: int, - user: UserInDB, - approve_schema: ApproveApplicationSchema, + self, + project_id: int, + user: UserInDB, + approve_schema: ApproveApplicationSchema, ) -> ApplicationSchema: project = await self.data_access.get_project_by_id(project_id) if project is None: @@ -135,48 +135,37 @@ async def approve_application( application.user_id, ) - async def delete_project( - self, - project_id: int, - user: UserInDB - ) -> MessageSchema: + async def delete_project(self, project_id: int, user: UserInDB) -> MessageSchema: project = await self.data_access.get_project_by_id(project_id) if project is None: raise HTTPException( - status_code=404, - detail=f"Project with ID {project_id} not found." + status_code=404, detail=f"Project with ID {project_id} not found." ) if project.ceo_id != user.id: raise HTTPException( - status_code=403, - detail=f"Only the project CEO can delete." + status_code=403, detail=f"Only the project CEO can delete." ) - await self.data_access.delete_project(project_id) - return MessageSchema(msg=f'Project {project_id} was successfully deleted') + res = await self.data_access.delete_project(project_id) + return MessageSchema( + msg=f"Project {project_id} was successfully deleted", success=res + ) - async def get_applied_project( - self, - user: UserInDB - ) -> list[ApplicationSchema]: - return await self.data_access.get_applied_project(user.id) + async def get_user_applications(self, user: UserInDB) -> list[ApplicationSchema]: + return await self.data_access.get_user_applications(user.id) - async def cancel_application( - self, - project_id, - user: UserInDB - ) -> MessageSchema: - application = await self.data_access.get_application_by_user_and_project_id(project_id, user.id) + async def cancel_application(self, project_id, user: UserInDB) -> MessageSchema: + application = await self.data_access.get_application_by_user_and_project_id( + project_id, user.id + ) if application is None: raise HTTPException( status_code=404, detail=f"Application not found in project {project_id}.", ) if application.is_approved: - raise HTTPException( - status_code=403, - detail=f"Application was approved" - ) - await self.data_access.cancel_application(project_id, user.id) - return MessageSchema(msg=f"Application was successfully deleted") + raise HTTPException(status_code=400, detail=f"Application was approved") + res = await self.data_access.cancel_application(project_id, user.id) + return MessageSchema(msg=f"Application was successfully deleted", success=res) + ProjectServiceDep = Annotated[ProjectService, Depends(ProjectService)] From af059bd576e7bbf1915a7602cae42e38779a842b Mon Sep 17 00:00:00 2001 From: Thi1ef Date: Tue, 15 Jul 2025 16:17:20 +0300 Subject: [PATCH 3/3] rename response schema and fixed service return --- backend/app/routes/api/project/data_access.py | 3 +-- backend/app/routes/api/project/router.py | 10 ++++----- backend/app/routes/api/project/schemas.py | 5 ++--- backend/app/routes/api/project/service.py | 21 ++++++++++--------- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/backend/app/routes/api/project/data_access.py b/backend/app/routes/api/project/data_access.py index 4f1983a..8eb3722 100644 --- a/backend/app/routes/api/project/data_access.py +++ b/backend/app/routes/api/project/data_access.py @@ -138,12 +138,11 @@ async def get_user_applications(self, user_id: int) -> list[ApplicationSchema]: for application in applications ] - async def cancel_application(self, project_id: int, user_id: int) -> bool: + async def delete_application(self, project_id: int, user_id: int) -> bool: query = delete(Application).where( Application.user_id == user_id, Application.project_id == project_id ) res = await self.db_session.execute(query) return res.rowcount > 0 - ProjectsDataAccessDep = Annotated[ProjectsDataAccess, Depends(ProjectsDataAccess)] diff --git a/backend/app/routes/api/project/router.py b/backend/app/routes/api/project/router.py index 5f7e4d5..46c3c73 100644 --- a/backend/app/routes/api/project/router.py +++ b/backend/app/routes/api/project/router.py @@ -7,7 +7,7 @@ ApplicationSchema, ApproveApplicationSchema, ProjectMemberSchema, - MessageSchema, + ActionResponse, ) from routes.api.project.service import ProjectServiceDep @@ -85,10 +85,10 @@ async def approve_application( @router.delete("/{project_id}/applications/cancel") -async def cancel_application( +async def delete_application( project_id: int, service: ProjectServiceDep, user: AuthUserDep -) -> MessageSchema: - return await service.cancel_application(project_id, user) +) -> ActionResponse: + return await service.delete_application(project_id, user) @router.delete("/{project_id}") @@ -96,5 +96,5 @@ async def delete_project( project_id: int, service: ProjectServiceDep, user: AuthUserDep, -) -> MessageSchema: +) -> ActionResponse: return await service.delete_project(project_id, user) diff --git a/backend/app/routes/api/project/schemas.py b/backend/app/routes/api/project/schemas.py index 4cae04b..943a642 100644 --- a/backend/app/routes/api/project/schemas.py +++ b/backend/app/routes/api/project/schemas.py @@ -20,11 +20,10 @@ class Config: from_attributes = True -class MessageSchema(BaseModel): - msg: str +class ActionResponse(BaseModel): + message : str success: bool - class ApplicationSchema(BaseModel): project_id: int user_id: int diff --git a/backend/app/routes/api/project/service.py b/backend/app/routes/api/project/service.py index 6294f40..bf999bb 100644 --- a/backend/app/routes/api/project/service.py +++ b/backend/app/routes/api/project/service.py @@ -2,6 +2,8 @@ from typing import Annotated from fastapi import Depends from fastapi.exceptions import HTTPException +from pyexpat.errors import messages + from models.users import UserRole from routes.api.project.data_access import ProjectsDataAccessDep from routes.api.project.exceptions import ApplicationNotFoundError @@ -10,7 +12,7 @@ NewProjectSchema, ApplicationSchema, ApproveApplicationSchema, - MessageSchema, + ActionResponse, ) from schemas.user import UserInDB @@ -135,7 +137,7 @@ async def approve_application( application.user_id, ) - async def delete_project(self, project_id: int, user: UserInDB) -> MessageSchema: + async def delete_project(self, project_id: int, user: UserInDB) -> ActionResponse: project = await self.data_access.get_project_by_id(project_id) if project is None: raise HTTPException( @@ -146,14 +148,13 @@ async def delete_project(self, project_id: int, user: UserInDB) -> MessageSchema status_code=403, detail=f"Only the project CEO can delete." ) res = await self.data_access.delete_project(project_id) - return MessageSchema( - msg=f"Project {project_id} was successfully deleted", success=res - ) + msg = "Project was successfully deleted" if res else "Project not found" + return ActionResponse(message=msg, success=res) async def get_user_applications(self, user: UserInDB) -> list[ApplicationSchema]: return await self.data_access.get_user_applications(user.id) - async def cancel_application(self, project_id, user: UserInDB) -> MessageSchema: + async def delete_application(self, project_id, user: UserInDB) -> ActionResponse: application = await self.data_access.get_application_by_user_and_project_id( project_id, user.id ) @@ -163,9 +164,9 @@ async def cancel_application(self, project_id, user: UserInDB) -> MessageSchema: detail=f"Application not found in project {project_id}.", ) if application.is_approved: - raise HTTPException(status_code=400, detail=f"Application was approved") - res = await self.data_access.cancel_application(project_id, user.id) - return MessageSchema(msg=f"Application was successfully deleted", success=res) - + raise HTTPException(status_code=400, detail="Application was approved") + res = await self.data_access.detele_application(project_id, user.id) + msg = "Application was successfully deleted" if res else "Failed to delete application" + return ActionResponse(message=msg, success=res) ProjectServiceDep = Annotated[ProjectService, Depends(ProjectService)]