Skip to content

Commit 53c1b08

Browse files
authored
Feat: Event View Backend (#159)
* Moved the route to top of file
1 parent 26b1a58 commit 53c1b08

File tree

7 files changed

+210
-158
lines changed

7 files changed

+210
-158
lines changed

app/internal/event.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import re
22

33
from fastapi import HTTPException
4-
54
from starlette.status import HTTP_400_BAD_REQUEST
65

76
ZOOM_REGEX = re.compile(r'https://.*?\.zoom.us/[a-z]/.[^.,\b\s]+')

app/internal/quotes/load_quotes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ def is_quotes_table_empty(session: Session) -> bool:
3636

3737
def load_daily_quotes(session: Session) -> None:
3838
"""This function loads the daily quotes to the db,
39-
if they weren't already loaden"""
39+
if they weren't already loaded"""
4040
if is_quotes_table_empty(session):
4141
add_quotes_to_db(session)

app/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
from app.config import PSQL_ENVIRONMENT
66
from app.database import models
77
from app.database.database import engine, get_db
8-
from app.dependencies import (
9-
MEDIA_PATH, STATIC_PATH, templates, logger)
10-
from app.internal.quotes import load_quotes, daily_quotes
8+
from app.dependencies import (logger, MEDIA_PATH, STATIC_PATH, templates)
9+
from app.internal.quotes import daily_quotes, load_quotes
1110
from app.routers import (
1211
agenda, dayview, email, event, invitation, profile, search, telegram,
1312
whatsapp
14-
)
13+
)
1514
from app.telegram.bot import telegram_bot
1615

1716

@@ -33,6 +32,7 @@ def create_tables(engine, psql_environment):
3332

3433
load_quotes.load_daily_quotes(next(get_db()))
3534

35+
app.logger = logger
3636

3737
app.include_router(profile.router)
3838
app.include_router(event.router)

app/routers/event.py

Lines changed: 86 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
from datetime import datetime as dt
1+
from datetime import datetime
22
from operator import attrgetter
33
from typing import Any, Dict, List, Optional
44

5-
from fastapi import APIRouter, Depends, Request
5+
from fastapi import APIRouter, Depends, HTTPException, Request
66
from sqlalchemy.exc import SQLAlchemyError
77
from sqlalchemy.orm import Session
8+
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
89
from starlette import status
910
from starlette.responses import RedirectResponse
1011
from starlette.status import HTTP_302_FOUND
1112

1213
from app.database.database import get_db
1314
from app.database.models import Event, User, UserEvent
14-
from app.dependencies import templates
15+
from app.dependencies import logger, templates
1516
from app.internal.event import validate_zoom_link
1617
from app.internal.utils import create_model
1718
from app.routers.user import create_user
@@ -34,10 +35,10 @@ async def create_new_event(request: Request, session=Depends(get_db)):
3435
data = await request.form()
3536
title = data['title']
3637
content = data['description']
37-
start = dt.strptime(data['start_date'] + ' ' + data['start_time'],
38-
'%Y-%m-%d %H:%M')
39-
end = dt.strptime(data['end_date'] + ' ' + data['end_time'],
40-
'%Y-%m-%d %H:%M')
38+
start = datetime.strptime(data['start_date'] + ' ' + data['start_time'],
39+
'%Y-%m-%d %H:%M')
40+
end = datetime.strptime(data['end_date'] + ' ' + data['end_time'],
41+
'%Y-%m-%d %H:%M')
4142
user = session.query(User).filter_by(id=1).first()
4243
user = user if user else create_user("u", "p", "e@mail.com", session)
4344
owner_id = user.id
@@ -50,30 +51,86 @@ async def create_new_event(request: Request, session=Depends(get_db)):
5051

5152
event = create_event(session, title, start, end, owner_id, content,
5253
location)
53-
return RedirectResponse(router.url_path_for('eventview', id=event.id),
54+
return RedirectResponse(router.url_path_for('eventview',
55+
event_id=event.id),
5456
status_code=HTTP_302_FOUND)
5557

5658

57-
@router.get("/view/{id}")
58-
async def eventview(request: Request, id: int):
59+
@router.get("/{event_id}")
60+
async def eventview(request: Request, event_id: int,
61+
db: Session = Depends(get_db)):
62+
try:
63+
event = get_event_by_id(db, event_id)
64+
except NoResultFound:
65+
raise HTTPException(status_code=404, detail="Event not found")
66+
except MultipleResultsFound:
67+
raise HTTPException(status_code=500, detail="Multiple events found")
68+
start_format = '%A, %d/%m/%Y %H:%M'
69+
end_format = ('%H:%M' if event.start.date() == event.end.date()
70+
else start_format)
5971
return templates.TemplateResponse("event/eventview.html",
60-
{"request": request, "event_id": id})
72+
{"request": request, "event": event,
73+
"start_format": start_format,
74+
"end_format": end_format})
75+
76+
77+
@router.delete("/{event_id}")
78+
def delete_event(request: Request, event_id: int,
79+
db: Session = Depends(get_db)):
80+
# TODO: Check if the user is the owner of the event.
81+
try:
82+
event = get_event_by_id(db, event_id)
83+
except NoResultFound:
84+
raise HTTPException(status_code=404, detail="Event not found")
85+
except MultipleResultsFound:
86+
raise HTTPException(status_code=500, detail="Multiple events found")
87+
88+
participants = get_participants_emails_by_event(db, event_id)
89+
90+
try:
91+
db.delete(event)
92+
db.query(UserEvent).filter_by(event_id=event_id).delete()
93+
db.commit()
94+
except (SQLAlchemyError, TypeError):
95+
return templates.TemplateResponse(
96+
"event/eventview.html", {"request": request, "event_id": event_id},
97+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
6198

99+
if participants and event.start > datetime.now():
100+
pass
101+
# TODO: Send them a cancellation notice
102+
# if the deletion is successful
103+
return RedirectResponse(
104+
url="/calendar", status_code=status.HTTP_200_OK)
62105

63-
def by_id(db: Session, event_id: int) -> Event:
64-
"""Select event by id"""
65106

66-
return db.query(Event).filter(Event.id == event_id).first()
107+
def get_event_by_id(db: Session, event_id: int) -> Event:
108+
"""Gets a single event by id"""
109+
if not isinstance(db, Session):
110+
raise AttributeError(
111+
f'Could not connect to database. '
112+
f'db instance type received: {type(db)}')
113+
try:
114+
event = db.query(Event).filter_by(id=event_id).one()
115+
except NoResultFound:
116+
raise NoResultFound(f"Event ID does not exist. ID: {event_id}")
117+
except MultipleResultsFound:
118+
error_message = (
119+
f'Multiple results found when getting event. Expected only one. '
120+
f'ID: {event_id}')
121+
logger.critical(error_message)
122+
raise MultipleResultsFound(error_message)
123+
return event
67124

68125

69-
def is_date_before(start_date: dt, end_date: dt) -> bool:
126+
def is_date_before(start_date: datetime, end_date: datetime) -> bool:
70127
"""Check if the start date is earlier than the end date"""
71128

72129
return start_date < end_date
73130

74131

75-
def is_it_possible_to_change_dates(
76-
db: Session, old_event: Event, event: Dict[str, Any]) -> bool:
132+
def is_it_possible_to_change_dates(old_event: Event,
133+
event: Dict[str, Any]) -> bool:
77134
return is_date_before(
78135
event.get('start', old_event.start),
79136
event.get('end', old_event.end))
@@ -94,9 +151,13 @@ def update_event(event_id: int, event: Dict, db: Session
94151
if not event_to_update:
95152
return None
96153
try:
97-
old_event = by_id(db=db, event_id=event_id)
98-
if old_event is None or not is_it_possible_to_change_dates(
99-
db, old_event, event_to_update):
154+
old_event = get_event_by_id(db, event_id)
155+
except NoResultFound:
156+
raise HTTPException(status_code=404, detail="Event not found")
157+
except MultipleResultsFound:
158+
raise HTTPException(status_code=500, detail="Multiple events found")
159+
try:
160+
if not is_it_possible_to_change_dates(old_event, event_to_update):
100161
return None
101162

102163
# Update database
@@ -107,7 +168,8 @@ def update_event(event_id: int, event: Dict, db: Session
107168
# TODO: Send emails to recipients.
108169
except (AttributeError, SQLAlchemyError, TypeError):
109170
return None
110-
return by_id(db=db, event_id=event_id)
171+
172+
return get_event_by_id(db=db, event_id=event_id)
111173

112174

113175
def create_event(db, title, start, end, owner_id, content=None, location=None):
@@ -141,38 +203,10 @@ def get_participants_emails_by_event(db: Session, event_id: int) -> List[str]:
141203
"""Returns a list of all the email address of the event invited users,
142204
by event id."""
143205

144-
return [email[0] for email in db.query(User.email).
206+
return (
207+
[email[0] for email in db.query(User.email).
145208
select_from(Event).
146209
join(UserEvent, UserEvent.event_id == Event.id).
147210
join(User, User.id == UserEvent.user_id).
148211
filter(Event.id == event_id).
149-
all()]
150-
151-
152-
@router.delete("/{event_id}")
153-
def delete_event(request: Request,
154-
event_id: int,
155-
db: Session = Depends(get_db)):
156-
157-
# TODO: Check if the user is the owner of the event.
158-
event = by_id(db, event_id)
159-
participants = get_participants_emails_by_event(db, event_id)
160-
try:
161-
# Delete event
162-
db.delete(event)
163-
164-
# Delete user_event
165-
db.query(UserEvent).filter(UserEvent.event_id == event_id).delete()
166-
167-
db.commit()
168-
169-
except (SQLAlchemyError, TypeError):
170-
return templates.TemplateResponse(
171-
"event/eventview.html", {"request": request, "event_id": event_id},
172-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
173-
if participants and event.start > dt.now():
174-
pass
175-
# TODO: Send them a cancellation notice
176-
# if the deletion is successful
177-
return RedirectResponse(
178-
url="/calendar", status_code=status.HTTP_200_OK)
212+
all()])
Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
1-
<div class="event_info_row title">
1+
<div class="event_info_row title" style="border-bottom: 4px solid {{event.color}}">
22
<div class="event_info_row_start">
3-
<h1>EVENT TITLE</h1>
3+
<h1>{{event.title}}</h1>
44
</div>
55
<div class="event_info_row_end">
6-
<span class="icon">AVAILABILITY</span>
7-
<span class="icon">PRIVACY</span>
6+
<!-- <span class="icon">AVAILABILITY</span>-->
7+
<!-- <span class="icon">PRIVACY</span>-->
88
</div>
99
</div>
1010
<div class="event_info_row">
1111
<span class="icon">ICON</span>
12-
<time datetime="DD/MM/YYYY HH:MI">DAY, DD/MM/YYYY HH:MI</time>
12+
<time datetime="{{event.start}}">{{event.start.strftime(start_format)}}</time>
1313
-
14-
<time datetime="DD/MM/YYYY HH:MI">HH:Mi</time>
14+
<time datetime="{{event.end}}">{{event.end.strftime(end_format)}}</time>
1515
</div>
1616

17-
<div class="event_info_row">
18-
<span class="icon">ICON</span>
19-
<span>Repeats every INTERVAL</span>
20-
</div>
17+
<!--<div class="event_info_row">-->
18+
<!-- <span class="icon">ICON</span>-->
19+
<!-- <span>Repeats every INTERVAL</span>-->
20+
<!--</div>-->
2121

2222
<div class="event_info_row">
2323
<span class="icon">ICON</span>
24-
<address>LOCATION / <a href="#">VC URL</a></address>
24+
<address>{{event.location}}</address>
25+
<!-- <address>LOCATION / <a href="#">VC URL</a></address>-->
2526
</div>
2627

2728
<p class="event_info_row">
28-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur quis ex ac molestie. Fusce libero
29-
ligula, dictum ac sollicitudin sed, consequat in nisi. Suspendisse feugiat diam quis efficitur aliquet. Duis purus
30-
mauris, luctus ultrices dictum id, fermentum et ex. Nunc in elementum mauris. Maecenas at tincidunt lorem. Sed quis
31-
ante commodo, tincidunt tortor at, tristique nisl. Donec at velit ultricies, viverra tellus at, ultrices ligula.
29+
{{event.content}}
3230
</p>

tests/client_fixture.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
from fastapi.testclient import TestClient
12
import pytest
3+
24
from app import main
35
from app.database.database import Base
46
from app.database.models import User
57
from app.main import app
68
from app.routers import agenda, event, invitation, profile
7-
from fastapi.testclient import TestClient
8-
99
from tests.conftest import get_test_db, test_engine
1010

1111

@@ -40,6 +40,11 @@ def home_test_client():
4040
yield from create_test_client(main.get_db)
4141

4242

43+
@pytest.fixture(scope="session")
44+
def event_test_client():
45+
yield from create_test_client(event.get_db)
46+
47+
4348
@pytest.fixture(scope="session")
4449
def profile_test_client():
4550
Base.metadata.create_all(bind=test_engine)
@@ -62,15 +67,3 @@ def get_test_placeholder_user():
6267
full_name='FakeName',
6368
telegram_id='666666'
6469
)
65-
66-
67-
@pytest.fixture(scope="session")
68-
def event_test_client():
69-
Base.metadata.create_all(bind=test_engine)
70-
app.dependency_overrides[event.get_db] = get_test_db
71-
72-
with TestClient(app) as client:
73-
yield client
74-
75-
app.dependency_overrides = {}
76-
Base.metadata.drop_all(bind=test_engine)

0 commit comments

Comments
 (0)