-
Notifications
You must be signed in to change notification settings - Fork 52
Feat: Event View Backend #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
yammesicka
merged 27 commits into
PythonFreeCourse:develop
from
OdeYec:event_view_backend
Feb 1, 2021
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
835faf7
Moved the route to top of file
OdeYec 3141482
refractor!: by_id to get_event_by_id
OdeYec 973415c
fix: changed delete_event to match new get_event_by_id
OdeYec 70704de
feat: improved eventview's get endpoint
OdeYec 60bdf78
refractor: moved logger from main to dependencies
OdeYec 6d0182c
refractor: event_test_client to use create_test_client
OdeYec ec1ebb7
ui: changed static placeholders to actual event data
OdeYec 9b8200c
fix: added exception handling
OdeYec d1e786d
fix: changed error code in test
OdeYec 4b1a735
fix: changed update_event to fit new get_event_by_id
OdeYec 7487a91
refactor: lint fix
OdeYec f4f8e8e
refactor: minor fixes
OdeYec f42f0c5
fix: split exception catching to raise more accurate HTTP Exceptions
OdeYec d47b925
refractor: lint fixes
OdeYec 50442a2
refractor: lint fixes
OdeYec 76983d4
Merge branch 'develop' into event_view_backend
yammesicka 7cf4660
refractor: fixed conflicts
OdeYec e91025d
Merge remote-tracking branch 'origin/event_view_backend' into event_v…
OdeYec 4558f11
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
OdeYec 2ab3c3d
changed eventview route to /{event_id}/
OdeYec 5d9ce54
changed eventview route to /{event_id}/
OdeYec 8e4a25c
fix: added back some exception handling to delete_event
OdeYec 3a07c2d
refractor: lint will be the death of me
OdeYec 3689be9
refractor: lint fixt
OdeYec 0e43d44
fix: added back some exception handling to delete_event
OdeYec d87244a
refractor: lint will be the death of me
OdeYec d78df37
Merge remote-tracking branch 'origin/event_view_backend' into event_v…
OdeYec File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,18 @@ | ||
from datetime import datetime as dt | ||
from datetime import datetime | ||
from operator import attrgetter | ||
from typing import Any, Dict, List, Optional | ||
|
||
from fastapi import APIRouter, Depends, Request | ||
from fastapi import APIRouter, Depends, HTTPException, Request | ||
from sqlalchemy.exc import SQLAlchemyError | ||
from sqlalchemy.orm import Session | ||
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound | ||
from starlette import status | ||
from starlette.responses import RedirectResponse | ||
from starlette.status import HTTP_302_FOUND | ||
|
||
from app.database.database import get_db | ||
from app.database.models import Event, User, UserEvent | ||
from app.dependencies import templates | ||
from app.dependencies import logger, templates | ||
from app.internal.event import validate_zoom_link | ||
from app.internal.utils import create_model | ||
from app.routers.user import create_user | ||
|
@@ -34,10 +35,10 @@ async def create_new_event(request: Request, session=Depends(get_db)): | |
data = await request.form() | ||
title = data['title'] | ||
content = data['description'] | ||
start = dt.strptime(data['start_date'] + ' ' + data['start_time'], | ||
'%Y-%m-%d %H:%M') | ||
end = dt.strptime(data['end_date'] + ' ' + data['end_time'], | ||
'%Y-%m-%d %H:%M') | ||
start = datetime.strptime(data['start_date'] + ' ' + data['start_time'], | ||
'%Y-%m-%d %H:%M') | ||
end = datetime.strptime(data['end_date'] + ' ' + data['end_time'], | ||
'%Y-%m-%d %H:%M') | ||
user = session.query(User).filter_by(id=1).first() | ||
user = user if user else create_user("u", "p", "[email protected]", session) | ||
owner_id = user.id | ||
|
@@ -50,30 +51,86 @@ async def create_new_event(request: Request, session=Depends(get_db)): | |
|
||
event = create_event(session, title, start, end, owner_id, content, | ||
location) | ||
return RedirectResponse(router.url_path_for('eventview', id=event.id), | ||
return RedirectResponse(router.url_path_for('eventview', | ||
event_id=event.id), | ||
status_code=HTTP_302_FOUND) | ||
|
||
|
||
@router.get("/view/{id}") | ||
async def eventview(request: Request, id: int): | ||
@router.get("/{event_id}") | ||
async def eventview(request: Request, event_id: int, | ||
db: Session = Depends(get_db)): | ||
try: | ||
event = get_event_by_id(db, event_id) | ||
except NoResultFound: | ||
raise HTTPException(status_code=404, detail="Event not found") | ||
except MultipleResultsFound: | ||
raise HTTPException(status_code=500, detail="Multiple events found") | ||
start_format = '%A, %d/%m/%Y %H:%M' | ||
yammesicka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end_format = ('%H:%M' if event.start.date() == event.end.date() | ||
else start_format) | ||
return templates.TemplateResponse("event/eventview.html", | ||
{"request": request, "event_id": id}) | ||
{"request": request, "event": event, | ||
"start_format": start_format, | ||
"end_format": end_format}) | ||
|
||
|
||
@router.delete("/{event_id}") | ||
def delete_event(request: Request, event_id: int, | ||
db: Session = Depends(get_db)): | ||
# TODO: Check if the user is the owner of the event. | ||
try: | ||
event = get_event_by_id(db, event_id) | ||
except NoResultFound: | ||
raise HTTPException(status_code=404, detail="Event not found") | ||
except MultipleResultsFound: | ||
raise HTTPException(status_code=500, detail="Multiple events found") | ||
|
||
participants = get_participants_emails_by_event(db, event_id) | ||
|
||
try: | ||
db.delete(event) | ||
db.query(UserEvent).filter_by(event_id=event_id).delete() | ||
db.commit() | ||
except (SQLAlchemyError, TypeError): | ||
return templates.TemplateResponse( | ||
"event/eventview.html", {"request": request, "event_id": event_id}, | ||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) | ||
|
||
if participants and event.start > datetime.now(): | ||
pass | ||
# TODO: Send them a cancellation notice | ||
# if the deletion is successful | ||
return RedirectResponse( | ||
url="/calendar", status_code=status.HTTP_200_OK) | ||
|
||
def by_id(db: Session, event_id: int) -> Event: | ||
"""Select event by id""" | ||
|
||
return db.query(Event).filter(Event.id == event_id).first() | ||
def get_event_by_id(db: Session, event_id: int) -> Event: | ||
"""Gets a single event by id""" | ||
if not isinstance(db, Session): | ||
raise AttributeError( | ||
f'Could not connect to database. ' | ||
f'db instance type received: {type(db)}') | ||
try: | ||
event = db.query(Event).filter_by(id=event_id).one() | ||
except NoResultFound: | ||
raise NoResultFound(f"Event ID does not exist. ID: {event_id}") | ||
except MultipleResultsFound: | ||
error_message = ( | ||
f'Multiple results found when getting event. Expected only one. ' | ||
f'ID: {event_id}') | ||
logger.critical(error_message) | ||
raise MultipleResultsFound(error_message) | ||
return event | ||
|
||
|
||
def is_date_before(start_date: dt, end_date: dt) -> bool: | ||
def is_date_before(start_date: datetime, end_date: datetime) -> bool: | ||
"""Check if the start date is earlier than the end date""" | ||
|
||
return start_date < end_date | ||
|
||
|
||
def is_it_possible_to_change_dates( | ||
db: Session, old_event: Event, event: Dict[str, Any]) -> bool: | ||
def is_it_possible_to_change_dates(old_event: Event, | ||
event: Dict[str, Any]) -> bool: | ||
return is_date_before( | ||
event.get('start', old_event.start), | ||
event.get('end', old_event.end)) | ||
|
@@ -94,9 +151,13 @@ def update_event(event_id: int, event: Dict, db: Session | |
if not event_to_update: | ||
return None | ||
try: | ||
old_event = by_id(db=db, event_id=event_id) | ||
if old_event is None or not is_it_possible_to_change_dates( | ||
db, old_event, event_to_update): | ||
old_event = get_event_by_id(db, event_id) | ||
except NoResultFound: | ||
raise HTTPException(status_code=404, detail="Event not found") | ||
except MultipleResultsFound: | ||
raise HTTPException(status_code=500, detail="Multiple events found") | ||
try: | ||
if not is_it_possible_to_change_dates(old_event, event_to_update): | ||
return None | ||
|
||
# Update database | ||
|
@@ -107,7 +168,8 @@ def update_event(event_id: int, event: Dict, db: Session | |
# TODO: Send emails to recipients. | ||
except (AttributeError, SQLAlchemyError, TypeError): | ||
return None | ||
return by_id(db=db, event_id=event_id) | ||
|
||
return get_event_by_id(db=db, event_id=event_id) | ||
|
||
|
||
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]: | |
"""Returns a list of all the email address of the event invited users, | ||
by event id.""" | ||
|
||
return [email[0] for email in db.query(User.email). | ||
return ( | ||
[email[0] for email in db.query(User.email). | ||
select_from(Event). | ||
yammesicka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
join(UserEvent, UserEvent.event_id == Event.id). | ||
join(User, User.id == UserEvent.user_id). | ||
filter(Event.id == event_id). | ||
all()] | ||
|
||
|
||
@router.delete("/{event_id}") | ||
def delete_event(request: Request, | ||
event_id: int, | ||
db: Session = Depends(get_db)): | ||
|
||
# TODO: Check if the user is the owner of the event. | ||
event = by_id(db, event_id) | ||
participants = get_participants_emails_by_event(db, event_id) | ||
try: | ||
# Delete event | ||
db.delete(event) | ||
|
||
# Delete user_event | ||
db.query(UserEvent).filter(UserEvent.event_id == event_id).delete() | ||
|
||
db.commit() | ||
|
||
except (SQLAlchemyError, TypeError): | ||
return templates.TemplateResponse( | ||
"event/eventview.html", {"request": request, "event_id": event_id}, | ||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) | ||
if participants and event.start > dt.now(): | ||
pass | ||
# TODO: Send them a cancellation notice | ||
# if the deletion is successful | ||
return RedirectResponse( | ||
url="/calendar", status_code=status.HTTP_200_OK) | ||
all()]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,30 @@ | ||
<div class="event_info_row title"> | ||
<div class="event_info_row title" style="border-bottom: 4px solid {{event.color}}"> | ||
<div class="event_info_row_start"> | ||
<h1>EVENT TITLE</h1> | ||
<h1>{{event.title}}</h1> | ||
</div> | ||
<div class="event_info_row_end"> | ||
<span class="icon">AVAILABILITY</span> | ||
<span class="icon">PRIVACY</span> | ||
<!-- <span class="icon">AVAILABILITY</span>--> | ||
<!-- <span class="icon">PRIVACY</span>--> | ||
</div> | ||
</div> | ||
<div class="event_info_row"> | ||
<span class="icon">ICON</span> | ||
<time datetime="DD/MM/YYYY HH:MI">DAY, DD/MM/YYYY HH:MI</time> | ||
<time datetime="{{event.start}}">{{event.start.strftime(start_format)}}</time> | ||
- | ||
<time datetime="DD/MM/YYYY HH:MI">HH:Mi</time> | ||
<time datetime="{{event.end}}">{{event.end.strftime(end_format)}}</time> | ||
</div> | ||
|
||
<div class="event_info_row"> | ||
<span class="icon">ICON</span> | ||
<span>Repeats every INTERVAL</span> | ||
</div> | ||
<!--<div class="event_info_row">--> | ||
<!-- <span class="icon">ICON</span>--> | ||
<!-- <span>Repeats every INTERVAL</span>--> | ||
<!--</div>--> | ||
|
||
OdeYec marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<div class="event_info_row"> | ||
<span class="icon">ICON</span> | ||
<address>LOCATION / <a href="#">VC URL</a></address> | ||
<address>{{event.location}}</address> | ||
<!-- <address>LOCATION / <a href="#">VC URL</a></address>--> | ||
</div> | ||
|
||
<p class="event_info_row"> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur quis ex ac molestie. Fusce libero | ||
ligula, dictum ac sollicitudin sed, consequat in nisi. Suspendisse feugiat diam quis efficitur aliquet. Duis purus | ||
mauris, luctus ultrices dictum id, fermentum et ex. Nunc in elementum mauris. Maecenas at tincidunt lorem. Sed quis | ||
ante commodo, tincidunt tortor at, tristique nisl. Donec at velit ultricies, viverra tellus at, ultrices ligula. | ||
{{event.content}} | ||
</p> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.