Skip to content

Commit 79eabf6

Browse files
feat: write api for sub todo
1 parent 2913801 commit 79eabf6

File tree

5 files changed

+171
-13
lines changed

5 files changed

+171
-13
lines changed
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
"""update todo and subtodo table
1+
"""refine db
22
3-
Revision ID: 9245f5673cb7
3+
Revision ID: f2d6aba28d7d
44
Revises: 80580dd7587b
5-
Create Date: 2024-12-06 10:53:59.113398
5+
Create Date: 2024-12-06 14:59:08.495370
66
77
"""
88
from alembic import op
@@ -11,7 +11,7 @@
1111

1212

1313
# revision identifiers, used by Alembic.
14-
revision = '9245f5673cb7'
14+
revision = 'f2d6aba28d7d'
1515
down_revision = '80580dd7587b'
1616
branch_labels = None
1717
depends_on = None

backend/app/api/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from fastapi import APIRouter
22

3-
from app.api.routes import items, login, private, users, utils
3+
from app.api.routes import items, login, private, users, utils, sub_todo
44
from app.core.config import settings
55

66
api_router = APIRouter()
77
api_router.include_router(login.router)
88
api_router.include_router(users.router)
99
api_router.include_router(utils.router)
1010
api_router.include_router(items.router)
11+
api_router.include_router(sub_todo.router)
1112

1213

1314
if settings.ENVIRONMENT == "local":

backend/app/api/routes/sub_todo.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import uuid
2+
from typing import Any
3+
4+
from fastapi import APIRouter, HTTPException
5+
from sqlmodel import func, select
6+
7+
from app.api.deps import CurrentUser, SessionDep
8+
from app.models import SubTodo, SubTodoCreate, SubTodoPublic, SubTodosPublic, SubTodoUpdate, Message, Todo
9+
10+
router = APIRouter(prefix="/subtodos", tags=["subtodos"])
11+
12+
13+
@router.get("/", response_model=SubTodosPublic)
14+
def read_sub_todos(
15+
session: SessionDep, current_user: CurrentUser, todo_id=uuid.UUID, skip: int = 0, limit: int = 100
16+
) -> Any:
17+
"""
18+
Retrieve sub todos.
19+
"""
20+
21+
if current_user.is_superuser:
22+
count_statement = select(func.count()).select_from(SubTodo)
23+
count = session.exec(count_statement).one()
24+
statement = select(SubTodo).offset(skip).limit(limit)
25+
todos = session.exec(statement).all()
26+
else:
27+
count_statement = (
28+
select(func.count())
29+
.select_from(SubTodo)
30+
.where(SubTodo.todo_id == todo_id)
31+
)
32+
count = session.exec(count_statement).one()
33+
statement = (
34+
select(SubTodo)
35+
.where(SubTodo.todo_id == todo_id)
36+
.offset(skip)
37+
.limit(limit)
38+
)
39+
todos = session.exec(statement).all()
40+
41+
return SubTodosPublic(data=todos, count=count)
42+
43+
44+
@router.get("/{id}", response_model=SubTodoPublic)
45+
def read_item(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) -> Any:
46+
"""
47+
Get sub todo by ID.
48+
"""
49+
sub_todo = session.get(SubTodo, id)
50+
if not sub_todo:
51+
raise HTTPException(status_code=404, detail="Sub todo not found")
52+
return sub_todo
53+
54+
@router.post("/", response_model=SubTodoPublic)
55+
def create_sub_todo(
56+
*, session: SessionDep, sub_todo_in: SubTodoCreate
57+
) -> Any:
58+
"""
59+
Create new sub todo.
60+
"""
61+
todo = session.get(Todo, sub_todo_in.todo_id)
62+
if not todo:
63+
raise HTTPException(status_code=404, detail="Todo not found")
64+
sub_todo = SubTodoCreate.model_validate(sub_todo_in)
65+
create_todo = SubTodo(**dict(sub_todo))
66+
session.add(create_todo)
67+
session.commit()
68+
session.refresh(create_todo)
69+
return create_todo
70+
71+
72+
# @router.put("/{id}", response_model=SubTodoPublic)
73+
# def update_sub_todo(
74+
# *,
75+
# session: SessionDep,
76+
# current_user: CurrentUser,
77+
# id: uuid.UUID,
78+
# todo_in: SubTodoUpdate,
79+
# ) -> Any:
80+
# """
81+
# Update an item.
82+
# """
83+
# sub_todo = session.get(SubTodo, id)
84+
# if not sub_todo:
85+
# raise HTTPException(status_code=404, detail="SubTodo not found")
86+
# if not current_user.is_superuser and (sub_todo.owner_id != current_user.id):
87+
# raise HTTPException(status_code=400, detail="Not enough permissions")
88+
# update_dict = todo_in.model_dump(exclude_unset=True)
89+
# sub_todo.sqlmodel_update(update_dict)
90+
# session.add(sub_todo)
91+
# session.commit()
92+
# session.refresh(sub_todo)
93+
# return sub_todo
94+
95+
@router.put("/{id}", response_model=SubTodoPublic)
96+
def update_sub_todo(
97+
*,
98+
session: SessionDep,
99+
current_user: CurrentUser,
100+
id: uuid.UUID,
101+
sub_todo_in: SubTodoUpdate,
102+
) -> Any:
103+
"""
104+
Update a sub todo by ID.
105+
"""
106+
# Fetch the sub todo by ID
107+
sub_todo = session.get(SubTodo, id)
108+
if not sub_todo:
109+
raise HTTPException(status_code=404, detail="SubTodo not found")
110+
111+
# Fetch the associated parent todo for ownership verification
112+
parent_todo = session.get(Todo, sub_todo.todo_id)
113+
if not parent_todo:
114+
raise HTTPException(status_code=404, detail="Parent Todo not found")
115+
116+
# Permission check: Ensure the user is the owner or a superuser
117+
if not current_user.is_superuser and (parent_todo.owner_id != current_user.id):
118+
raise HTTPException(status_code=403, detail="Not enough permissions")
119+
120+
# Update the SubTodo with the new data
121+
update_data = sub_todo_in.model_dump(exclude_unset=True)
122+
for key, value in update_data.items():
123+
setattr(sub_todo, key, value)
124+
125+
# Commit changes to the database
126+
session.add(sub_todo)
127+
session.commit()
128+
session.refresh(sub_todo)
129+
130+
return sub_todo
131+
132+
@router.delete("/{id}")
133+
def delete_sub_todo(
134+
session: SessionDep, current_user: CurrentUser, id: uuid.UUID
135+
) -> Message:
136+
"""
137+
Delete a sub todo.
138+
"""
139+
sub_todo = session.get(SubTodo, id)
140+
if not sub_todo:
141+
raise HTTPException(status_code=404, detail="SubTodo not found")
142+
todo = session.get(Todo, sub_todo.todo_id)
143+
if not todo:
144+
raise HTTPException(status_code=404, detail="Todo not found")
145+
if not current_user.is_superuser and (todo.owner_id != current_user.id):
146+
raise HTTPException(status_code=400, detail="Not enough permissions")
147+
session.delete(sub_todo)
148+
session.commit()
149+
return Message(message="SubTodo deleted successfully")

backend/app/crud.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from sqlmodel import Session, select
55

66
from app.core.security import get_password_hash, verify_password
7-
from app.models import Item, ItemCreate, User, UserCreate, UserUpdate
7+
from app.models import Item, ItemCreate, User, UserCreate, UserUpdate, SubTodoCreate
88

99

1010
def create_user(*, session: Session, user_create: UserCreate) -> User:
@@ -52,3 +52,10 @@ def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -
5252
session.commit()
5353
session.refresh(db_item)
5454
return db_item
55+
56+
def create_subtodo(*, session: Session, item_in: SubTodoCreate, owner_id: uuid.UUID) -> Item:
57+
db_item = Item.model_validate(item_in, update={"owner_id": owner_id})
58+
session.add(db_item)
59+
session.commit()
60+
session.refresh(db_item)
61+
return db_item

backend/app/models.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class Todo(TodoBase, table=True):
131131
owner_id: uuid.UUID = Field(
132132
foreign_key="user.id", nullable=False, ondelete="CASCADE"
133133
)
134-
status: str = Field(max_length=255)
134+
status: str = Field(default="in_progress", max_length=255)
135135
owner: User | None = Relationship(back_populates="todos")
136136
subtodos: list["SubTodo"] = Relationship(back_populates="todo")
137137

@@ -142,12 +142,12 @@ class TodoCreate(TodoBase):
142142
class TodoUpdate(TodoBase):
143143
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
144144
desc: str | None = Field(default=None, max_length=255)
145-
status: str = Field(max_length=255)
145+
status: str | None = Field(default=None, max_length=255)
146146

147147
class TodoPublic(TodoBase):
148148
id: uuid.UUID
149149
owner_id: uuid.UUID
150-
status: StatusEnum
150+
status: str
151151

152152
class TodosPublic(SQLModel):
153153
data: list[TodoPublic]
@@ -158,27 +158,28 @@ class SubTodoBase(SQLModel):
158158
title: str = Field(min_length=1, max_length=255)
159159
desc: str = Field(max_length=255)
160160

161+
161162
class SubTodo(SubTodoBase, table=True):
162163
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
163164
todo_id: uuid.UUID = Field(
164165
foreign_key="todo.id", nullable=False, ondelete="CASCADE"
165166
)
166-
status: str = Field(max_length=255)
167+
status: str = Field(default="in_progress", max_length=255)
167168
todo: Todo | None = Relationship(back_populates="subtodos")
168169

169170
class SubTodoCreate(SubTodoBase):
170-
pass
171+
todo_id: uuid.UUID
171172

172173
# Properties to receive on item update
173174
class SubTodoUpdate(SubTodoBase):
174175
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
175176
desc: str | None = Field(default=None, max_length=255)
176-
status: StatusEnum | None = Field(default=None)
177+
status: str | None = Field(default=None, max_length=255)
177178

178179
class SubTodoPublic(SubTodoBase):
179180
id: uuid.UUID
180181
todo_id: uuid.UUID
181-
status: StatusEnum
182+
status: str
182183

183184
class SubTodosPublic(SQLModel):
184185
data: list[SubTodoPublic]

0 commit comments

Comments
 (0)