Skip to content

Commit 55daca5

Browse files
authored
Feature/todo list (#285)
1 parent 5d48c1d commit 55daca5

File tree

12 files changed

+687
-23
lines changed

12 files changed

+687
-23
lines changed

app/database/models.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
JSON,
1010
Boolean,
1111
Column,
12+
Date,
1213
DateTime,
1314
Enum,
1415
Float,
@@ -78,6 +79,8 @@ class User(Base):
7879
back_populates="user",
7980
)
8081
comments = relationship("Comment", back_populates="user")
82+
tasks = relationship(
83+
"Task", cascade="all, delete", back_populates="owner")
8184

8285
features = relationship("Feature", secondary=UserFeature.__tablename__)
8386
oauth_credentials = relationship(
@@ -581,6 +584,21 @@ class InternationalDays(Base):
581584
international_day = Column(String, nullable=False)
582585

583586

587+
class Task(Base):
588+
__tablename__ = "tasks"
589+
590+
id = Column(Integer, primary_key=True, index=True)
591+
title = Column(String, nullable=False)
592+
description = Column(String, nullable=False)
593+
is_done = Column(Boolean, default=False)
594+
is_important = Column(Boolean, nullable=False)
595+
date = Column(Date, nullable=False)
596+
time = Column(Time, nullable=False)
597+
owner_id = Column(Integer, ForeignKey("users.id"))
598+
599+
owner = relationship("User", back_populates="tasks")
600+
601+
584602
# insert language data
585603

586604

app/internal/todo_list.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from datetime import date, time
2+
3+
from sqlalchemy.orm import Session
4+
5+
from app.database.models import Task
6+
from app.internal.utils import create_model
7+
8+
9+
def create_task(
10+
db: Session,
11+
title: str,
12+
description: str,
13+
date_str: date,
14+
time_str: time,
15+
owner_id: int,
16+
is_important: bool,
17+
) -> Task:
18+
"""Creates and saves a new task."""
19+
task = create_model(
20+
db,
21+
Task,
22+
title=title,
23+
description=description,
24+
date=date_str,
25+
time=time_str,
26+
owner_id=owner_id,
27+
is_important=is_important,
28+
is_done=False,
29+
)
30+
return task
31+
32+
33+
def by_id(db: Session, task_id: int) -> Task:
34+
task = db.query(Task).filter_by(id=task_id).one()
35+
return task

app/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def create_tables(engine, psql_environment):
8383
search,
8484
settings,
8585
telegram,
86+
todo_list,
8687
user,
8788
weekview,
8889
weight,
@@ -137,6 +138,7 @@ async def swagger_ui_redirect():
137138
search.router,
138139
settings.router,
139140
telegram.router,
141+
todo_list.router,
140142
user.router,
141143
weekview.router,
142144
weight.router,

app/routers/dayview.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44

55
from fastapi import APIRouter, Depends, HTTPException, Request
66

7-
from app.database.models import Event, User
7+
from app.database.models import Event, Task, User
88
from app.dependencies import get_db, templates
99
from app.internal import international_days, zodiac
1010
from app.internal.security.dependencies import current_user
11-
12-
# from app.internal.security.schema import CurrentUser
1311
from app.routers.user import get_all_user_events
1412

1513
router = APIRouter()
@@ -74,9 +72,9 @@ def _date_is_today(self) -> bool:
7472

7573
class EventsAttributes(DivAttributes):
7674
def __init__(
77-
self,
78-
event: Event,
79-
day: Union[bool, datetime] = False,
75+
self,
76+
event: Event,
77+
day: Union[bool, datetime] = False,
8078
) -> None:
8179
self.start_time = event.start
8280
self.end_time = event.end
@@ -136,9 +134,9 @@ def _check_multiday_event(self) -> Tuple[bool, bool]:
136134

137135

138136
def is_specific_time_event_in_day(
139-
event: Event,
140-
day: datetime,
141-
day_end: datetime,
137+
event: Event,
138+
day: datetime,
139+
day_end: datetime,
142140
) -> bool:
143141
if event.all_day:
144142
return False
@@ -150,9 +148,9 @@ def is_specific_time_event_in_day(
150148

151149

152150
def is_all_day_event_in_day(
153-
event: Event,
154-
day: datetime,
155-
day_end: datetime,
151+
event: Event,
152+
day: datetime,
153+
day_end: datetime,
156154
) -> bool:
157155
if not event.all_day:
158156
return False
@@ -164,9 +162,9 @@ def is_all_day_event_in_day(
164162

165163

166164
def get_events_and_attributes(
167-
day: datetime,
168-
session,
169-
user_id: int,
165+
day: datetime,
166+
session,
167+
user_id: int,
170168
) -> Iterator[Tuple[Event, EventsAttributes]]:
171169
events = get_all_user_events(session, user_id)
172170
day_end = day + timedelta(hours=24)
@@ -176,9 +174,9 @@ def get_events_and_attributes(
176174

177175

178176
def get_all_day_events(
179-
day: datetime,
180-
session,
181-
user_id: int,
177+
day: datetime,
178+
session,
179+
user_id: int,
182180
) -> Iterator[Event]:
183181
events = get_all_user_events(session, user_id)
184182
day_end = day + timedelta(hours=24)
@@ -189,11 +187,11 @@ def get_all_day_events(
189187

190188
@router.get("/day/{date}", include_in_schema=False)
191189
async def dayview(
192-
request: Request,
193-
date: str,
194-
view="day",
195-
session=Depends(get_db),
196-
user: User = Depends(current_user),
190+
request: Request,
191+
date: str,
192+
view="day",
193+
session=Depends(get_db),
194+
user: User = Depends(current_user),
197195
):
198196
try:
199197
day = datetime.strptime(date, "%Y-%m-%d")
@@ -212,6 +210,12 @@ async def dayview(
212210
)
213211
current_time_with_attrs = CurrentTimeAttributes(date=day)
214212
inter_day = international_days.get_international_day_per_day(session, day)
213+
tasks = (
214+
session.query(Task)
215+
.filter(Task.owner_id == user.user_id)
216+
.filter(Task.date == day.date())
217+
.order_by(Task.time)
218+
)
215219
month = day.strftime("%B").upper()
216220
return templates.TemplateResponse(
217221
"calendar_day_view.html",
@@ -221,9 +225,11 @@ async def dayview(
221225
"all_day_events": all_day_events,
222226
"month": month,
223227
"day": day.day,
228+
"date_str": date,
224229
"international_day": inter_day,
225230
"zodiac": zodiac_obj,
226231
"view": view,
227232
"current_time": current_time_with_attrs,
233+
"tasks": tasks,
228234
},
229235
)

app/routers/todo_list.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
from datetime import datetime
2+
3+
from fastapi import APIRouter, Depends, Form, status
4+
from fastapi.encoders import jsonable_encoder
5+
from fastapi.responses import JSONResponse, RedirectResponse
6+
from sqlalchemy.exc import SQLAlchemyError
7+
from sqlalchemy.orm import Session
8+
from starlette.requests import Request
9+
10+
from app.config import templates
11+
from app.dependencies import get_db
12+
from app.internal.todo_list import by_id, create_task
13+
from app.internal.utils import get_current_user
14+
15+
router = APIRouter(
16+
prefix="/task",
17+
tags=["task"],
18+
responses={status.HTTP_404_NOT_FOUND: {"description": "Not found"}},
19+
)
20+
21+
22+
@router.post("/delete")
23+
def delete_task(
24+
request: Request,
25+
task_id: int = Form(...),
26+
db: Session = Depends(get_db),
27+
) -> RedirectResponse:
28+
user = get_current_user(db)
29+
task = by_id(db, task_id)
30+
if task.owner_id != user.id:
31+
return templates.TemplateResponse(
32+
"calendar_day_view.html",
33+
{"task_id": task_id},
34+
status_code=status.HTTP_403_FORBIDDEN,
35+
)
36+
37+
date_str = task.date.strftime('%Y-%m-%d')
38+
try:
39+
# Delete task
40+
db.delete(task)
41+
42+
db.commit()
43+
44+
except (SQLAlchemyError, TypeError):
45+
return templates.TemplateResponse(
46+
"calendar_day_view.html",
47+
{"task_id": task_id},
48+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
49+
)
50+
return RedirectResponse(
51+
request.url_for("dayview", date=date_str),
52+
status_code=status.HTTP_302_FOUND,
53+
)
54+
55+
56+
@router.post("/add")
57+
async def add_task(
58+
request: Request,
59+
title: str = Form(...),
60+
description: str = Form(...),
61+
date_str: str = Form(...),
62+
time_str: str = Form(...),
63+
is_important: bool = Form(False),
64+
session: Session = Depends(get_db),
65+
) -> RedirectResponse:
66+
user = get_current_user(session)
67+
create_task(
68+
session,
69+
title,
70+
description,
71+
datetime.strptime(date_str, '%Y-%m-%d').date(),
72+
datetime.strptime(time_str, '%H:%M').time(),
73+
user.id,
74+
is_important,
75+
)
76+
return RedirectResponse(
77+
request.url_for("dayview", date=date_str),
78+
status_code=status.HTTP_303_SEE_OTHER,
79+
)
80+
81+
82+
@router.post("/edit")
83+
async def edit_task(
84+
request: Request,
85+
task_id: int = Form(...),
86+
title: str = Form(...),
87+
description: str = Form(...),
88+
date_str: str = Form(...),
89+
time_str: str = Form(...),
90+
is_important: bool = Form(False),
91+
session: Session = Depends(get_db),
92+
) -> RedirectResponse:
93+
task = by_id(session, task_id)
94+
task.title = title
95+
task.description = description
96+
task.date = datetime.strptime(date_str, '%Y-%m-%d').date()
97+
task.time = datetime.strptime(time_str, '%H:%M:%S').time()
98+
task.is_important = is_important
99+
session.commit()
100+
return RedirectResponse(
101+
request.url_for("dayview", date=date_str),
102+
status_code=status.HTTP_303_SEE_OTHER,
103+
)
104+
105+
106+
@router.post("/done/{task_id}")
107+
async def set_task_done(
108+
request: Request,
109+
task_id: int,
110+
session: Session = Depends(get_db),
111+
) -> RedirectResponse:
112+
task = by_id(session, task_id)
113+
task.is_done = True
114+
session.commit()
115+
return RedirectResponse(
116+
request.url_for("dayview", date=task.date.strftime('%Y-%m-%d')),
117+
status_code=status.HTTP_303_SEE_OTHER,
118+
)
119+
120+
121+
@router.post("/undone/{task_id}")
122+
async def set_task_undone(
123+
request: Request,
124+
task_id: int,
125+
session: Session = Depends(get_db),
126+
) -> RedirectResponse:
127+
task = by_id(session, task_id)
128+
task.is_done = False
129+
session.commit()
130+
return RedirectResponse(
131+
request.url_for("dayview", date=task.date.strftime('%Y-%m-%d')),
132+
status_code=status.HTTP_303_SEE_OTHER,
133+
)
134+
135+
136+
@router.get("/{task_id}")
137+
async def get_task(
138+
task_id: int,
139+
session: Session = Depends(get_db),
140+
) -> JSONResponse:
141+
task = by_id(session, task_id)
142+
data = jsonable_encoder(task)
143+
return JSONResponse(content=data)

0 commit comments

Comments
 (0)