From 839728cf6604d2c27f560552b7729b2f072e5c71 Mon Sep 17 00:00:00 2001 From: Borgia Leiva Date: Tue, 10 Feb 2026 11:37:30 +0100 Subject: [PATCH 1/6] Made ProjectUser return full project info --- schemas/projects_users.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/schemas/projects_users.py b/schemas/projects_users.py index 4ea25a2..36c74fd 100644 --- a/schemas/projects_users.py +++ b/schemas/projects_users.py @@ -1,10 +1,11 @@ from typing import List from pydantic import ConfigDict -from schemas.projects import ProjectBase +from schemas.projects import ProjectFull from schemas.users import UserBase class ProjectUserBase(UserBase): model_config = ConfigDict(from_attributes=True) - projects: List[ProjectBase] + projects: List[ProjectFull] + \ No newline at end of file From 48b2bcfa7007ab00659f1e620fa0a00b86737552 Mon Sep 17 00:00:00 2001 From: Borgia Leiva Date: Tue, 10 Feb 2026 11:37:59 +0100 Subject: [PATCH 2/6] Refactored me.py, minor changes --- routers/me.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/routers/me.py b/routers/me.py index 44f78fe..b5cd2b3 100644 --- a/routers/me.py +++ b/routers/me.py @@ -4,12 +4,15 @@ from jose import JWTError, jwt import models from routers import auth -import schemas.users as user_schemas -import schemas.projects_users as projects_users_schemas + +from schemas.users import UserBase +from schemas.projects import ProjectBase +from schemas.projects_users import ProjectUserBase + router = APIRouter(prefix="/me", tags=["me"]) -@router.get("/", response_model=projects_users_schemas.ProjectUserBase, tags=["me", "users"]) +@router.get("/", response_model=ProjectUserBase, tags=["me", "users"]) def get_me(request: Request, db: db_dependency): """Get current authenticated user""" user = auth.get_user_from_jwt(request, db) From 1b84af00253a7d6a34d52f1f1b06ac4f37ddff11 Mon Sep 17 00:00:00 2001 From: Borgia Leiva Date: Tue, 10 Feb 2026 11:47:36 +0100 Subject: [PATCH 3/6] Fixed confusion in project creation between sqlalchemy and pydantic model --- routers/projects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/projects.py b/routers/projects.py index be25752..db30d18 100644 --- a/routers/projects.py +++ b/routers/projects.py @@ -115,14 +115,14 @@ def create_project(project: ProjectCreate, request:Request, db: db_dependency): return db_project -@router.post("/{project_id}/tasks", response_model=ProjectTaskCreate, tags=["tasks"]) +@router.post("/{project_id}/tasks", response_model=ProjectTaskBase, tags=["tasks"]) def create_project_task(project_id: int, task: TaskCreate, db: db_dependency, request: Request): """Create a new task in a specified project""" user = get_user_from_jwt(request, db) db_project = get_project_by_id_for_user(user, project_id, db) - db_task = ProjectTaskCreate( + db_task = Task( title=task.title, description=task.description, status=task.status, From 0f0b27b2d98a8131094acf03f690652e6377472a Mon Sep 17 00:00:00 2001 From: Borgia Leiva Date: Tue, 10 Feb 2026 12:36:40 +0100 Subject: [PATCH 4/6] change user addition to project to be based off of email address and not user id --- routers/projects.py | 16 ++++++++-------- schemas/projects.py | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/routers/projects.py b/routers/projects.py index db30d18..c99b5fb 100644 --- a/routers/projects.py +++ b/routers/projects.py @@ -4,7 +4,7 @@ from typing import List, Annotated from database import db_dependency from schemas.tasks import TaskBase, TaskCreate, TaskUpdate -from schemas.projects import ProjectBase, ProjectCreate, ProjectUpdate, ProjectAddUsers, ProjectRemoveUsers, ProjectFull +from schemas.projects import ProjectBase, ProjectCreate, ProjectUpdate, ProjectAddUser, ProjectRemoveUsers, ProjectFull from schemas.users import UserBase from schemas.projects_users import ProjectUserBase from schemas.projects_tasks import ProjectTaskBase, ProjectTaskCreate @@ -134,17 +134,17 @@ def create_project_task(project_id: int, task: TaskCreate, db: db_dependency, re db.refresh(db_task) return db_task -@router.post("/{project_id}/users", response_model=ProjectAddUsers, tags=["users"]) -def add_project_user(project_id: int, user_data: ProjectAddUsers, db: db_dependency, request: Request): - """Add users to a specified project using their IDs""" +@router.post("/{project_id}/users", response_model=ProjectAddUser, tags=["users"]) +def add_project_user(project_id: int, user_data: ProjectAddUser, db: db_dependency, request: Request): + """Add a user to a specified project using their email address""" user = get_user_from_jwt(request, db) db_project = get_project_by_id_for_user(user, project_id, db) - for user_id in user_data.user_ids: - db_user = db.query(UserBase).filter(getattr(UserBase, "id") == user_id).first() - if db_user: - db_project.users.append(db_user) + db_user = db.query(UserBase).filter(getattr(UserBase, "email") == user_data.user_email).first() + if db_user: + db_project.users.append(db_user) + db.commit() db.refresh(db_project) return db_project diff --git a/schemas/projects.py b/schemas/projects.py index 592cea8..28c2ad4 100644 --- a/schemas/projects.py +++ b/schemas/projects.py @@ -26,6 +26,9 @@ class ProjectUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None +class ProjectAddUser(BaseModel): + user_email: str + class ProjectAddUsers(BaseModel): user_ids: List[int] = [] From 8d7537b602108edd17f9d93900e72cf7e17468e4 Mon Sep 17 00:00:00 2001 From: Borgia Leiva Date: Tue, 10 Feb 2026 13:02:01 +0100 Subject: [PATCH 5/6] Fixed adding collaborator --- routers/projects.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/routers/projects.py b/routers/projects.py index c99b5fb..fc87a81 100644 --- a/routers/projects.py +++ b/routers/projects.py @@ -80,7 +80,7 @@ def get_project_user(project_id: int, user_id: int, db: db_dependency, request: db_project : ProjectBase = get_project_by_id_for_user(user, project_id, db) - db_user = db.query(UserBase).filter(getattr(UserBase, "id") == user_id).first() + db_user = db.query(User).filter(User.id == user_id).first() if db_user is None or db_user not in db_project.users: raise HTTPException(status_code=404, detail="User not found in the specified project") return db_user @@ -91,7 +91,7 @@ def get_project_tasks(project_id: int, request:Request, db: db_dependency): user = get_user_from_jwt(request, db) db_project = get_project_by_id_for_user(user, project_id, db) - db_tasks = db.query(TaskBase).filter(getattr(TaskBase, "project_id") == project_id).all() + db_tasks = db.query(Task).filter(Task.project_id == project_id).all() return db_tasks @router.post("/", response_model=ProjectCreate) @@ -134,16 +134,21 @@ def create_project_task(project_id: int, task: TaskCreate, db: db_dependency, re db.refresh(db_task) return db_task -@router.post("/{project_id}/users", response_model=ProjectAddUser, tags=["users"]) +@router.post("/{project_id}/users", response_model=ProjectFull, tags=["users"]) def add_project_user(project_id: int, user_data: ProjectAddUser, db: db_dependency, request: Request): """Add a user to a specified project using their email address""" user = get_user_from_jwt(request, db) - db_project = get_project_by_id_for_user(user, project_id, db) - db_user = db.query(UserBase).filter(getattr(UserBase, "email") == user_data.user_email).first() - if db_user: + db_user = db.query(User).filter(User.email == user_data.user_email).first() + + if not db_user: + raise HTTPException(status_code=404, detail="User with the specified email not found") + + if db_user not in db_project.users: db_project.users.append(db_user) + else: + raise HTTPException(status_code=400, detail="User is already a member of the project") db.commit() db.refresh(db_project) @@ -156,7 +161,7 @@ def remove_user_from_project(project_id: int, user_id: int, db: db_dependency, r db_project = get_project_by_id_for_user(user, project_id, db) - db_user = db.query(UserBase).filter(getattr(UserBase, "id") == user_id).first() + db_user = db.query(User).filter(User.id == user_id).first() if db_user is None or db_user not in db_project.users: raise HTTPException(status_code=404, detail="User not found in the specified project") From 95d37bd3784617004063e8d9a725e6df28d66a4d Mon Sep 17 00:00:00 2001 From: Borgia Leiva Date: Tue, 10 Feb 2026 13:40:13 +0100 Subject: [PATCH 6/6] Added samsple frontends repos in docs description --- main.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/main.py b/main.py index ba3776f..da64346 100644 --- a/main.py +++ b/main.py @@ -28,6 +28,13 @@ It allows users to manage projects, tasks, and user assignments with proper auth ## Source Code The source code for this API can be found on [GitHub](https://github.com/a-mayb3/Kanban_clone_backend) or [my forgejo instance](https://git.vollex.cc/a-mayb3/Kanban_clone_backend). + +## Other projects +Here are some frontend implementations for this API: +- [KanbanCloneAngular](https://github.com/a-mayb3/KanbanCloneAngular) - Angular frontend +- [KanbanCloneAndroid](https://github.com/a-mayb3/KanbanCloneAndroid) - Android frontend + + """ global_logger = logging.getLogger()