From 9a128a4287bb13c4b92f43b655729a2c7d963837 Mon Sep 17 00:00:00 2001 From: imimouni Date: Fri, 12 Feb 2021 17:22:52 +0200 Subject: [PATCH 01/16] feat: event privacy + fix eventview error --- app/database/models.py | 1 + app/routers/event.py | 89 +++++++++++++++++-- .../partials/edit_event_details_tab.html | 5 +- tests/test_event.py | 42 +++++++-- 4 files changed, 119 insertions(+), 18 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index 9f0f7f1a..078713bc 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -59,6 +59,7 @@ class Event(Base): color = Column(String, nullable=True) emotion = Column(String, nullable=True) invitees = Column(String) + privacy = Column(String, default='Public', nullable=False) owner_id = Column(Integer, ForeignKey("users.id")) color = Column(String, nullable=True) diff --git a/app/routers/event.py b/app/routers/event.py index d0d4a06e..af6fdd29 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -4,8 +4,9 @@ from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound +from sqlalchemy.sql.elements import Null from starlette import status from starlette.responses import RedirectResponse @@ -16,11 +17,18 @@ raise_if_zoom_link_invalid, ) from app.internal.emotion import get_emotion +# TODO add this when the user system is merged (PR#195) +# from app.internal.security.dependancies import ( +# current_user, CurrentUser) from app.internal.utils import create_model from app.routers.user import create_user +from dictalchemy import asdict + TIME_FORMAT = '%Y-%m-%d %H:%M' +PRIVATE = 'Private' + UPDATE_EVENTS_FIELDS = { 'title': str, 'start': dt, @@ -63,6 +71,9 @@ async def create_new_event(request: Request, session=Depends(get_db)): is_zoom = location_type == 'vc_url' location = data['location'] category_id = data.get('category_id') + privacy = data['privacy'] + if privacy not in ['Public', 'Private', 'Hidden']: + privacy = 'Public' invited_emails = get_invited_emails(data['invited']) uninvited_contacts = get_uninvited_regular_emails(session, owner_id, @@ -71,30 +82,92 @@ async def create_new_event(request: Request, session=Depends(get_db)): if is_zoom: raise_if_zoom_link_invalid(location) - event = create_event(session, title, start, end, owner_id, content, + event = create_event(session, title, start, end, owner_id, privacy, content, location, invited_emails, category_id=category_id) messages = get_messages(session, event, uninvited_contacts) return RedirectResponse(router.url_path_for('eventview', event_id=event.id) - + f'messages={"---".join(messages)}', + + f'?messages={"---".join(messages)}', status_code=status.HTTP_302_FOUND) +def nonexisting_event(event_id: int) -> None: + error_message = f"Event ID does not exist. ID: {event_id}" + logger.exception(error_message) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=error_message) + + @router.get("/{event_id}") async def eventview(request: Request, event_id: int, db: Session = Depends(get_db)): event = by_id(db, event_id) + event_to_show = can_show_event(event, session) + if not event_to_show: + nonexisting_event(event.id) start_format = '%A, %d/%m/%Y %H:%M' end_format = ('%H:%M' if event.start.date() == event.end.date() else start_format) messages = request.query_params.get('messages', '').split("---") return templates.TemplateResponse("event/eventview.html", - {"request": request, "event": event, + {"request": request, "event": event_to_show, "start_format": start_format, "end_format": end_format, "messages": messages}) +class PrivateEvent: + """Represents a private event to show a non-owner of private event""" + def __init__(self, start, end, owner_id) -> None: + self.title = PRIVATE + self.start = start + self.end = end + self.privacy = PRIVATE + self.content = PRIVATE + self.owner_id = owner_id + self.location = PRIVATE + self.color = Null + self.invitees = PRIVATE + self.category_id = Null + self.emotion = Null + + +def check_event_owner( + event: Event, + session: Depends(get_db), + user: Optional[User]=None, + # TODO after user system is merged (PR#195): + # CurrentUser = Depends(current_user) +) -> bool: + if not user: + user = session.query(User).filter_by(id=1).first() + user = user if user else create_user(username="u", + password="p", + email="e@mail.com", + language_id=1, + session=session) + # TODO use current_user after user system merge + is_owner = event.owner_id == user.id + return is_owner + + +def can_show_event(event: Event, session: Depends(get_db), user: Optional[User]=None) -> Optional[Event]: + """Check the given events privacy and return + event/fixed event/ nothing (hidden) accordingly""" + if event.privacy == PRIVATE and not check_event_owner(event, session, user): + private_event = PrivateEvent( + start=event.start, + end=event.end, + owner_id=event.owner_id, + ) + return private_event + elif event.privacy == 'Hidden' and not check_event_owner(event, session, user): + return + elif event.privacy == 'Public' or check_event_owner(event, session, user): + return event + + def by_id(db: Session, event_id: int) -> Event: """Get a single event by id""" if not isinstance(db, Session): @@ -109,11 +182,7 @@ def by_id(db: Session, event_id: int) -> Event: try: event = db.query(Event).filter_by(id=event_id).one() except NoResultFound: - error_message = f"Event ID does not exist. ID: {event_id}" - logger.exception(error_message) - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=error_message) + nonexisting_event(event_id) except MultipleResultsFound: error_message = ( f'Multiple results found when getting event. Expected only one. ' @@ -196,6 +265,7 @@ def update_event(event_id: int, event: Dict, db: Session def create_event(db: Session, title: str, start, end, owner_id: int, + privacy: str = 'Public', content: str = None, location: str = None, invitees: List[str] = None, @@ -209,6 +279,7 @@ def create_event(db: Session, title: str, start, end, owner_id: int, title=title, start=start, end=end, + privacy=privacy, content=content, owner_id=owner_id, location=location, diff --git a/app/templates/event/partials/edit_event_details_tab.html b/app/templates/event/partials/edit_event_details_tab.html index d968bc16..5794b5a4 100644 --- a/app/templates/event/partials/edit_event_details_tab.html +++ b/app/templates/event/partials/edit_event_details_tab.html @@ -46,8 +46,9 @@ \ No newline at end of file diff --git a/tests/test_event.py b/tests/test_event.py index e8c4b588..45aa14d4 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -4,14 +4,15 @@ from fastapi.testclient import TestClient import pytest from sqlalchemy.orm import Session +from sqlalchemy.sql.elements import Null from starlette import status from app.database.models import Event from app.dependencies import get_db from app.main import app from app.routers.event import (_delete_event, _update_event, add_new_event, - by_id, check_change_dates_allowed, delete_event, - is_date_before, update_event) + by_id, check_change_dates_allowed, delete_event, eventview, + is_date_before, update_event, can_show_event) CORRECT_EVENT_FORM_DATA = { 'title': 'test title', @@ -24,7 +25,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'public', + 'privacy': 'Public', 'invited': 'a@a.com,b@b.com' } @@ -39,7 +40,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'public', + 'privacy': 'Public', 'invited': 'a@a.com,b@b.com' } @@ -54,7 +55,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'public', + 'privacy': 'Public', 'invited': 'a@a.com,b@b.com,ccc' } @@ -69,7 +70,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'public', + 'privacy': 'Public', 'invited': 'a@a.com,b@b.com' } @@ -84,7 +85,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'public', + 'privacy': 'Public', 'invited': 'a@a.com,b@b.com' } @@ -333,6 +334,33 @@ def test_deleting_an_event_does_not_exist(event_test_client, event): assert response.status_code == status.HTTP_404_NOT_FOUND +def test_can_show_event_public(event, session, sender, user): + assert can_show_event(event, session) == event + assert can_show_event(event, session, user) == event + + +def test_can_show_event_hidden(event, session, sender, user): + event.privacy = 'Hidden' + assert can_show_event(event, session, user) is None + assert can_show_event(event, session) == event + + +def test_can_show_event_private(event, session, sender, user): + event.privacy = 'Private' + private_event = can_show_event(event=event, session=session, user=user) + private_attributes = [ + private_event.title, private_event.location, + private_event.content, private_event.invitees + ] + null_attributes = [ + private_event.color, private_event.emotion, + private_event.category_id + ] + is_private_attributes = [attr == 'Private' for attr in private_attributes] + is_null_attributes = [attr is Null for attr in null_attributes] + assert all(is_private_attributes) and all(is_null_attributes) + + class TestApp: client = TestClient(app) date_test_data = [datetime.today() - timedelta(1), datetime.today()] From 13ac41f2766b2cc2dd3652a81cb540d04976f46e Mon Sep 17 00:00:00 2001 From: imimouni Date: Fri, 12 Feb 2021 17:45:11 +0200 Subject: [PATCH 02/16] fix: flake8 --- app/routers/event.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index e18ce39c..f22ebfa1 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -22,7 +22,6 @@ # current_user, CurrentUser) from app.internal.utils import create_model from app.routers.user import create_user -from dictalchemy import asdict TIME_FORMAT = '%Y-%m-%d %H:%M' @@ -83,7 +82,7 @@ async def create_new_event(request: Request, session=Depends(get_db)): if is_zoom: raise_if_zoom_link_invalid(location) - event = create_event(session, title, start, end, owner_id, privacy, + event = create_event(session, title, start, end, owner_id, privacy, content, location, invitees=invited_emails, category_id=category_id, availability=availability) @@ -114,7 +113,8 @@ async def eventview(request: Request, event_id: int, else start_format) messages = request.query_params.get('messages', '').split("---") return templates.TemplateResponse("event/eventview.html", - {"request": request, "event": event_to_show, + {"request": request, + "event": event_to_show, "start_format": start_format, "end_format": end_format, "messages": messages}) @@ -139,35 +139,40 @@ def __init__(self, start, end, owner_id) -> None: def check_event_owner( event: Event, session: Depends(get_db), - user: Optional[User]=None, + user: Optional[User] = None, # TODO after user system is merged (PR#195): # CurrentUser = Depends(current_user) ) -> bool: if not user: user = session.query(User).filter_by(id=1).first() user = user if user else create_user(username="u", - password="p", - email="e@mail.com", - language_id=1, - session=session) + password="p", + email="e@mail.com", + language_id=1, + session=session) # TODO use current_user after user system merge is_owner = event.owner_id == user.id return is_owner -def can_show_event(event: Event, session: Depends(get_db), user: Optional[User]=None) -> Optional[Event]: +def can_show_event( + event: Event, + session: Depends(get_db), + user: Optional[User]=None +) -> Optional[Event]: """Check the given events privacy and return event/fixed event/ nothing (hidden) accordingly""" - if event.privacy == PRIVATE and not check_event_owner(event, session, user): + is_owner = check_event_owner(event, session, user) + if event.privacy == PRIVATE and not is_owner: private_event = PrivateEvent( - start=event.start, - end=event.end, - owner_id=event.owner_id, + start = event.start, + end = event.end, + owner_id = event.owner_id, ) return private_event - elif event.privacy == 'Hidden' and not check_event_owner(event, session, user): - return - elif event.privacy == 'Public' or check_event_owner(event, session, user): + elif event.privacy == 'Hidden' and not is_owner: + return + elif event.privacy == 'Public' or is_owner: return event From 3c18874e24cf40bfd95ffc552bfd7a8b718b50b3 Mon Sep 17 00:00:00 2001 From: imimouni Date: Fri, 12 Feb 2021 17:48:13 +0200 Subject: [PATCH 03/16] fix: flake8 --- app/routers/event.py | 8 ++++---- tests/test_event.py | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index f22ebfa1..0c80ca03 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -158,16 +158,16 @@ def check_event_owner( def can_show_event( event: Event, session: Depends(get_db), - user: Optional[User]=None + user: Optional[User] = None ) -> Optional[Event]: """Check the given events privacy and return event/fixed event/ nothing (hidden) accordingly""" is_owner = check_event_owner(event, session, user) if event.privacy == PRIVATE and not is_owner: private_event = PrivateEvent( - start = event.start, - end = event.end, - owner_id = event.owner_id, + start=event.start, + end=event.end, + owner_id=event.owner_id, ) return private_event elif event.privacy == 'Hidden' and not is_owner: diff --git a/tests/test_event.py b/tests/test_event.py index caa5f55f..69de1a00 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -12,7 +12,8 @@ from app.main import app from app.routers.event import (_delete_event, _update_event, add_new_event, by_id, check_change_dates_allowed, delete_event, - is_date_before, update_event, create_event, can_show_event) + is_date_before, update_event, create_event, + can_show_event) CORRECT_EVENT_FORM_DATA = { From 5a342fccba5c4aff13d53ebfea6d70af57400b92 Mon Sep 17 00:00:00 2001 From: imimouni Date: Fri, 12 Feb 2021 18:01:25 +0200 Subject: [PATCH 04/16] fix: error in event.py refrencing session/db by wrong name --- app/routers/event.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 0c80ca03..9b6cdc57 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session, session +from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound from sqlalchemy.sql.elements import Null from starlette import status @@ -105,7 +105,7 @@ def nonexisting_event(event_id: int) -> None: async def eventview(request: Request, event_id: int, db: Session = Depends(get_db)): event = by_id(db, event_id) - event_to_show = can_show_event(event, session) + event_to_show = can_show_event(event, db) if not event_to_show: nonexisting_event(event.id) start_format = '%A, %d/%m/%Y %H:%M' From 73b0fcbced592e755fc864bef6714dce1b58acc6 Mon Sep 17 00:00:00 2001 From: imimouni Date: Tue, 16 Feb 2021 12:05:40 +0200 Subject: [PATCH 05/16] fix: flake8 --- app/internal/privacy.py | 4 - app/routers/event.py | 313 +++++++++++++++++++++------------------- tests/test_event.py | 219 ++++++++++++++-------------- 3 files changed, 267 insertions(+), 269 deletions(-) diff --git a/app/internal/privacy.py b/app/internal/privacy.py index 4ab6a772..12ced09a 100644 --- a/app/internal/privacy.py +++ b/app/internal/privacy.py @@ -23,7 +23,3 @@ def __init__(self, start, end, owner_id) -> None: self.invitees = PrivacyKinds.Private.name self.category_id = Null self.emotion = Null - -privacy_kinds = [kind.name for kind in PrivacyKinds] -print(privacy_kinds) -print(type(privacy_kinds[0])) \ No newline at end of file diff --git a/app/routers/event.py b/app/routers/event.py index 6b342b20..d8f01192 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -8,14 +8,15 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound -from sqlalchemy.sql.elements import Null from starlette import status from starlette.responses import RedirectResponse, Response from app.database.models import Comment, Event, User, UserEvent from app.dependencies import get_db, logger, templates from app.internal.event import ( - get_invited_emails, get_messages, get_uninvited_regular_emails, + get_invited_emails, + get_messages, + get_uninvited_regular_emails, raise_if_zoom_link_invalid, ) from app.internal import comment as cmt @@ -24,10 +25,8 @@ # from app.internal.security.dependancies import ( # current_user, CurrentUser) from app.internal.privacy import PrivateEvent, PrivacyKinds -from app.internal.utils import create_model -from app.routers.user import create_user from app.internal.utils import create_model, get_current_user - +from app.routers.user import create_user EVENT_DATA = Tuple[Event, List[Dict[str, str]], str, str] TIME_FORMAT = '%Y-%m-%d %H:%M' @@ -44,9 +43,11 @@ } router = APIRouter( - prefix="/event", - tags=["event"], - responses={404: {"description": "Not found"}}, + prefix='/event', + tags=['event'], + responses={404: { + 'description': 'Not found' + }}, ) @@ -59,12 +60,12 @@ class EventModel(BaseModel): location: str -@router.get("/") +@router.get('/') async def get_events(session=Depends(get_db)): return session.query(Event).all() -@router.post("/") +@router.post('/') async def create_event_api(event: EventModel, session=Depends(get_db)): create_event(db=session, title=event.title, @@ -76,24 +77,25 @@ async def create_event_api(event: EventModel, session=Depends(get_db)): return {'success': True} -@router.get("/edit", include_in_schema=False) -@router.get("/edit") +@router.get('/edit', include_in_schema=False) +@router.get('/edit') async def eventedit(request: Request) -> Response: - return templates.TemplateResponse("event/eventedit.html", - {"request": request, - "privacy": PrivacyKinds}) + return templates.TemplateResponse('event/eventedit.html', { + 'request': request, + 'privacy': PrivacyKinds + }) -@router.post("/edit", include_in_schema=False) -async def create_new_event(request: Request, - session=Depends(get_db)) -> Response: +@router.post('/edit', include_in_schema=False) +async def create_new_event( + request: Request, session=Depends(get_db) +) -> Response: data = await request.form() title = data['title'] content = data['description'] - start = dt.strptime(data['start_date'] + ' ' + - data['start_time'], TIME_FORMAT) - end = dt.strptime(data['end_date'] + ' ' + data['end_time'], - TIME_FORMAT) + start = dt.strptime(data['start_date'] + ' ' + data['start_time'], + TIME_FORMAT) + end = dt.strptime(data['end_date'] + ' ' + data['end_time'], TIME_FORMAT) owner_id = get_current_user(session).id availability = data.get('availability', 'True') == 'True' location = data['location'] @@ -105,48 +107,56 @@ async def create_new_event(request: Request, privacy = PrivacyKinds.Public.name invited_emails = get_invited_emails(data['invited']) - uninvited_contacts = get_uninvited_regular_emails(session, owner_id, - title, invited_emails) + uninvited_contacts = get_uninvited_regular_emails(session, owner_id, title, + invited_emails) if vc_link is not None: raise_if_zoom_link_invalid(vc_link) - event = create_event(db=session, title=title, start=start, end=end, - owner_id=owner_id, privacy=privacy, - content=content, location=location, + event = create_event(db=session, + title=title, + start=start, + end=end, + owner_id=owner_id, + privacy=privacy, + content=content, + location=location, invitees=invited_emails, category_id=category_id, availability=availability) messages = get_messages(session, event, uninvited_contacts) - return RedirectResponse(router.url_path_for('eventview', event_id=event.id) - + f'?messages={"---".join(messages)}', - status_code=status.HTTP_302_FOUND) + return RedirectResponse( + router.url_path_for('eventview', event_id=event.id) + + f'?messages={"---".join(messages)}', + status_code=status.HTTP_302_FOUND) def nonexisting_event(event_id: int) -> None: - error_message = f"Event ID does not exist. ID: {event_id}" + error_message = f'Event ID does not exist. ID: {event_id}' logger.exception(error_message) - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=error_message) + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=error_message) -@router.get("/{event_id}", include_in_schema=False) -async def eventview(request: Request, event_id: int, +@router.get('/{event_id}', include_in_schema=False) +async def eventview(request: Request, + event_id: int, db: Session = Depends(get_db)) -> Response: event, comments, end_format = get_event_data(db, event_id) event_to_show = can_show_event(event, db) if not event_to_show: nonexisting_event(event.id) - messages = request.query_params.get('messages', '').split("---") - return templates.TemplateResponse("event/eventview.html", - {"request": request, - "event": event_to_show, - "comments": comments, - "start_format": START_FORMAT, - "end_format": end_format, - "messages": messages}) + messages = request.query_params.get('messages', '').split('---') + return templates.TemplateResponse( + 'event/eventview.html', { + 'request': request, + 'event': event_to_show, + 'comments': comments, + 'start_format': START_FORMAT, + 'end_format': end_format, + 'messages': messages + }) def check_event_owner( @@ -158,9 +168,9 @@ def check_event_owner( ) -> bool: if not user: user = session.query(User).filter_by(id=1).first() - user = user if user else create_user(username="u", - password="p", - email="e@mail.com", + user = user if user else create_user(username='u', + password='p', + email='e@mail.com', language_id=1, session=session) # TODO use current_user after user system merge @@ -168,11 +178,9 @@ def check_event_owner( return is_owner -def can_show_event( - event: Event, - session: Depends(get_db), - user: Optional[User] = None -) -> Optional[Event]: +def can_show_event(event: Event, + session: Depends(get_db), + user: Optional[User] = None) -> Optional[Event]: """Check the given events privacy and return event/fixed event/ nothing (hidden) accordingly""" is_owner = check_event_owner(event, session, user) @@ -189,8 +197,9 @@ def can_show_event( return event -@router.post("/{event_id}/owner") -async def change_owner(request: Request, event_id: int, +@router.post('/{event_id}/owner') +async def change_owner(request: Request, + event_id: int, db: Session = Depends(get_db)): form = await request.form() if 'username' not in form: @@ -204,9 +213,8 @@ async def change_owner(request: Request, event_id: int, except AttributeError as e: error_message = f"Username does not exist. {form['username']}" logger.exception(str(e)) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_message) owner_to_update = {'owner_id': user_id} _update_event(db, event_id, owner_to_update) return RedirectResponse(router.url_path_for('eventview', @@ -217,13 +225,11 @@ async def change_owner(request: Request, event_id: int, def by_id(db: Session, event_id: int) -> Event: """Get a single event by id""" if not isinstance(db, Session): - error_message = ( - f'Could not connect to database. ' - f'db instance type received: {type(db)}') + error_message = (f'Could not connect to database. ' + f'db instance type received: {type(db)}') logger.critical(error_message) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_message) try: event = db.query(Event).filter_by(id=event_id).one() @@ -234,14 +240,12 @@ def by_id(db: Session, event_id: int) -> Event: f'Multiple results found when getting event. Expected only one. ' f'ID: {event_id}') logger.critical(error_message) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_message) return event -def is_end_date_before_start_date(start_date: dt, - end_date: dt) -> bool: +def is_end_date_before_start_date(start_date: dt, end_date: dt) -> bool: """Check if the start date is earlier than the end date""" return start_date > end_date @@ -256,9 +260,8 @@ def check_change_dates_allowed(old_event: Event, event: Dict[str, Any]): except TypeError: allowed = 0 if allowed == 0: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Invalid times") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, + detail='Invalid times') def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): @@ -267,16 +270,16 @@ def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): for field_name, field_type in to_check.items(): if types[field_name] and not isinstance(field_type, types[field_name]): errors.append( - f"{field_name} is '{type(field_type).__name__}' and" - + f"it should be from type '{types[field_name].__name__}'") + f"{field_name} is '{type(field_type).__name__}' and" + + f"it should be from type '{types[field_name].__name__}'") logger.warning(errors) if errors: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail=errors) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, + detail=errors) -def get_event_with_editable_fields_only(event: Dict[str, Any] - ) -> Dict[str, Any]: +def get_event_with_editable_fields_only( + event: Dict[str, Any]) -> Dict[str, Any]: """Remove all keys that are not allowed to update""" edit_event = {i: event[i] for i in UPDATE_EVENTS_FIELDS if i in event} @@ -296,13 +299,11 @@ def _update_event(db: Session, event_id: int, event_to_update: Dict) -> Event: return by_id(db, event_id) except (AttributeError, SQLAlchemyError) as e: logger.exception(str(e)) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Internal server error") + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail='Internal server error') -def update_event(event_id: int, event: Dict, db: Session - ) -> Optional[Event]: +def update_event(event_id: int, event: Dict, db: Session) -> Optional[Event]: # TODO Check if the user is the owner of the event. old_event = by_id(db, event_id) event_to_update = get_event_with_editable_fields_only(event) @@ -315,40 +316,45 @@ def update_event(event_id: int, event: Dict, db: Session return event_updated -def create_event(db: Session, title: str, start, end, owner_id: int, - privacy: str = 'Public', - content: Optional[str] = None, - location: Optional[str] = None, - vc_link: str = None, - color: Optional[str] = None, - invitees: List[str] = None, - category_id: Optional[int] = None, - availability: bool = True, - is_google_event: bool = False, - ): +def create_event( + db: Session, + title: str, + start, + end, + owner_id: int, + privacy: str = 'Public', + content: Optional[str] = None, + location: Optional[str] = None, + vc_link: str = None, + color: Optional[str] = None, + invitees: List[str] = None, + category_id: Optional[int] = None, + availability: bool = True, + is_google_event: bool = False, +): """Creates an event and an association.""" invitees_concatenated = ','.join(invitees or []) - event = create_model( - db, Event, - title=title, - start=start, - end=end, - privacy=privacy, - content=content, - owner_id=owner_id, - location=location, - vc_link=vc_link, - color=color, - emotion=get_emotion(title, content), - invitees=invitees_concatenated, - category_id=category_id, - availability=availability, - is_google_event=is_google_event - ) + event = create_model(db, + Event, + title=title, + start=start, + end=end, + privacy=privacy, + content=content, + owner_id=owner_id, + location=location, + vc_link=vc_link, + color=color, + emotion=get_emotion(title, content), + invitees=invitees_concatenated, + category_id=category_id, + availability=availability, + is_google_event=is_google_event) create_model( - db, UserEvent, + db, + UserEvent, user_id=owner_id, event_id=event.id, ) @@ -363,20 +369,19 @@ def sort_by_date(events: List[Event]) -> List[Event]: def get_attendees_email(session: Session, event: Event): - return ( - session.query(User.email).join(UserEvent) - .filter(UserEvent.events == event).all() - ) + return (session.query( + User.email).join(UserEvent).filter(UserEvent.events == event).all()) 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).select_from(Event).join( - UserEvent, UserEvent.event_id == Event.id).join( + return [ + email[0] for email in db.query(User.email).select_from(Event).join( + UserEvent, UserEvent.event_id == Event.id).join( User, User.id == UserEvent.user_id).filter( - Event.id == event_id).all()] + Event.id == event_id).all() + ] def _delete_event(db: Session, event: Event): @@ -391,14 +396,12 @@ def _delete_event(db: Session, event: Event): except (SQLAlchemyError, AttributeError) as e: logger.exception(str(e)) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Deletion failed") + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail='Deletion failed') -@router.delete("/{event_id}") -def delete_event(event_id: int, - db: Session = Depends(get_db)) -> Response: +@router.delete('/{event_id}') +def delete_event(event_id: int, db: Session = Depends(get_db)) -> Response: # 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) @@ -407,8 +410,7 @@ def delete_event(event_id: int, pass # TODO: Send them a cancellation notice # if the deletion is successful - return RedirectResponse( - url="/calendar", status_code=status.HTTP_200_OK) + return RedirectResponse(url='/calendar', status_code=status.HTTP_200_OK) def is_date_before(start_time: dt, end_time: dt) -> bool: @@ -429,20 +431,21 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: return None try: new_event = create_model(db, Event, **values) - create_model( - db, UserEvent, - user_id=values['owner_id'], - event_id=new_event.id - ) + create_model(db, + UserEvent, + user_id=values['owner_id'], + event_id=new_event.id) return new_event except (AssertionError, AttributeError, TypeError) as e: logger.exception(e) return None -@router.post("/{event_id}") -async def add_comment(request: Request, event_id: int, - session: Session = Depends(get_db)) -> Response: +@router.post('/{event_id}') +async def add_comment( + request: Request, event_id: int, + session: Session = Depends(get_db) +) -> Response: """Creates a comment instance in the DB. Redirects back to the event's comments tab upon creation.""" form = await request.form() @@ -476,30 +479,36 @@ def get_event_data(db: Session, event_id: int) -> EVENT_DATA: """ event = by_id(db, event_id) comments = json.loads(cmt.display_comments(db, event)) - end_format = ('%H:%M' if event.start.date() == event.end.date() - else START_FORMAT) + end_format = ('%H:%M' + if event.start.date() == event.end.date() else START_FORMAT) return event, comments, end_format -@router.get("/{event_id}/comments") -async def view_comments(request: Request, event_id: int, - db: Session = Depends(get_db)) -> Response: +@router.get('/{event_id}/comments') +async def view_comments( + request: Request, event_id: int, + db: Session = Depends(get_db) +) -> Response: """Renders event comment tab view. This essentially the same as `eventedit`, only with comments tab auto showed.""" event, comments, end_format = get_event_data(db, event_id) - return templates.TemplateResponse("event/eventview.html", - {"request": request, - "event": event, - "comments": comments, - 'comment': True, - "start_format": START_FORMAT, - "end_format": end_format}) - - -@router.post("/comments/delete") -async def delete_comment(request: Request, - db: Session = Depends(get_db)) -> Response: + return templates.TemplateResponse( + 'event/eventview.html', { + 'request': request, + 'event': event, + 'comments': comments, + 'comment': True, + 'start_format': START_FORMAT, + 'end_format': end_format + }) + + +@router.post('/comments/delete') +async def delete_comment( + request: Request, + db: Session = Depends(get_db) +) -> Response: """Deletes a comment instance from the db. Redirects back to the event's comments tab upon deletion. diff --git a/tests/test_event.py b/tests/test_event.py index 037b8922..c15c0e58 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -4,7 +4,6 @@ from fastapi import HTTPException from fastapi.testclient import TestClient -from sqlalchemy.orm import Session from sqlalchemy.sql.elements import Null from sqlalchemy.orm.session import Session from starlette import status @@ -14,13 +13,9 @@ from app.internal.privacy import PrivacyKinds from app.internal.utils import delete_instance from app.main import app -from app.routers.event import (_delete_event, _update_event, add_new_event, - by_id, check_change_dates_allowed, delete_event, - is_date_before, update_event, create_event, - can_show_event) +from app.routers.event import can_show_event from app.routers import event as evt - CORRECT_EVENT_FORM_DATA = { 'title': 'test title', 'start_date': '2021-01-28', @@ -97,47 +92,60 @@ } CORRECT_ADD_EVENT_DATA = { - "title": "test", - "start": "2021-02-13T09:03:49.560Z", - "end": "2021-02-13T09:03:49.560Z", - "content": "test", - "owner_id": 0, - "location": "test" + 'title': 'test', + 'start': '2021-02-13T09:03:49.560Z', + 'end': '2021-02-13T09:03:49.560Z', + 'content': 'test', + 'owner_id': 0, + 'location': 'test' } NONE_UPDATE_OPTIONS = [ - {}, {"test": "test"}, + {}, + { + 'test': 'test' + }, ] INVALID_FIELD_UPDATE = [ - {"start": "20.01.2020"}, - {"start": datetime(2020, 2, 2), "end": datetime(2020, 1, 1)}, - {"start": datetime(2030, 2, 2)}, {"end": datetime(1990, 1, 1)}, + { + 'start': '20.01.2020' + }, + { + 'start': datetime(2020, 2, 2), + 'end': datetime(2020, 1, 1) + }, + { + 'start': datetime(2030, 2, 2) + }, + { + 'end': datetime(1990, 1, 1) + }, ] def test_get_events(event_test_client, session, event): - response = event_test_client.get("/event/") + response = event_test_client.get('/event/') assert response.ok def test_create_event_api(event_test_client, session, event): - response = event_test_client.post("/event/", + response = event_test_client.post('/event/', data=json.dumps(CORRECT_ADD_EVENT_DATA)) assert response.ok def test_eventedit(event_test_client): - response = event_test_client.get("/event/edit") + response = event_test_client.get('/event/edit') assert response.ok - assert b"Edit Event" in response.content + assert b'Edit Event' in response.content def test_eventview_with_id(event_test_client, session, event): event_id = event.id - response = event_test_client.get(f"/event/{event_id}") + response = event_test_client.get(f'/event/{event_id}') assert response.ok - assert b"View Event" in response.content + assert b'View Event' in response.content def test_create_event_with_default_availability(client, user, session): @@ -176,7 +184,7 @@ def test_create_event_with_free_availability(client, user, session): def test_eventview_without_id(client): - response = client.get("/event/view") + response = client.get('/event/view') assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY @@ -192,7 +200,7 @@ def test_eventedit_missing_old_invites(client, user): data=different_invitees_event) assert response.ok assert response.status_code == status.HTTP_302_FOUND - for invitee in CORRECT_EVENT_FORM_DATA["invited"].split(","): + for invitee in CORRECT_EVENT_FORM_DATA['invited'].split(','): assert invitee in response.headers['location'] @@ -208,7 +216,7 @@ def test_eventedit_bad_emails(client, user): data=different_invitees_event) assert response.ok assert response.status_code == status.HTTP_302_FOUND - for invitee in CORRECT_EVENT_FORM_DATA["invited"].split(","): + for invitee in CORRECT_EVENT_FORM_DATA['invited'].split(','): assert invitee in response.headers['location'] assert 'ccc' not in response.headers['location'] @@ -256,20 +264,20 @@ def test_eventedit_with_pattern(client, user): data=WEEK_LATER_EVENT_FORM_DATA) assert response.ok assert response.status_code == status.HTTP_302_FOUND - assert ('Same event happened 1 weeks before too. ' in - response.headers['location'].replace('+', ' ')) + assert ('Same event happened 1 weeks before too. ' + in response.headers['location'].replace('+', ' ')) response = client.post(client.app.url_path_for('create_new_event'), data=TWO_WEEKS_LATER_EVENT_FORM_DATA) assert response.ok assert response.status_code == status.HTTP_302_FOUND - assert ('Same event happened 1 weeks before too. ' in - response.headers['location'].replace('+', ' ')) - assert ('Same event happened 2 weeks before too. ' in - response.headers['location'].replace('+', ' ')) + assert ('Same event happened 1 weeks before too. ' + in response.headers['location'].replace('+', ' ')) + assert ('Same event happened 2 weeks before too. ' + in response.headers['location'].replace('+', ' ')) -@pytest.mark.parametrize("data", NONE_UPDATE_OPTIONS) +@pytest.mark.parametrize('data', NONE_UPDATE_OPTIONS) def test_invalid_update(event, data, session): """ Test update existing event. @@ -277,7 +285,7 @@ def test_invalid_update(event, data, session): assert evt.update_event(event_id=event.id, event=data, db=session) is None -@pytest.mark.parametrize("data", INVALID_FIELD_UPDATE) +@pytest.mark.parametrize('data', INVALID_FIELD_UPDATE) def test_invalid_fields(event, data, session): """ Test update existing event. @@ -288,10 +296,10 @@ def test_invalid_fields(event, data, session): def test_not_check_change_dates_allowed(event): - data = {"start": "20.01.2020"} + data = {'start': '20.01.2020'} with pytest.raises(HTTPException): - assert (evt.check_change_dates_allowed(event, data).status_code == - status.HTTP_400_BAD_REQUEST) + assert (evt.check_change_dates_allowed( + event, data).status_code == status.HTTP_400_BAD_REQUEST) def test_update_event_availability(event, session): @@ -299,9 +307,7 @@ def test_update_event_availability(event, session): Test update event's availability. """ original_availability = event.availability - data = { - "availability": not original_availability - } + data = {'availability': not original_availability} assert original_availability is not evt.update_event( event_id=event.id, event=data, db=session).availability @@ -311,14 +317,14 @@ def test_successful_update(event, session): Test update existing event successfully. """ data = { - "title": "successful", - "start": datetime(2021, 1, 20), - "end": datetime(2021, 1, 21), - "availability": "False", + 'title': 'successful', + 'start': datetime(2021, 1, 20), + 'end': datetime(2021, 1, 21), + 'availability': 'False', } assert isinstance(evt.update_event(1, data, session), Event) updated_event = evt.update_event(event_id=event.id, event=data, db=session) - assert "successful" in updated_event.title + assert 'successful' in updated_event.title assert updated_event.availability is False @@ -327,46 +333,44 @@ def test_update_event_with_category(today_event, category, session): Test update category for an existing event successfully. """ data = { - "title": "successful", - "category_id": category.id, + 'title': 'successful', + 'category_id': category.id, } - updated_event = evt.update_event(event_id=today_event.id, event=data, + updated_event = evt.update_event(event_id=today_event.id, + event=data, db=session) - assert "successful" in updated_event.title + assert 'successful' in updated_event.title assert updated_event.category_id == category.id def test_update_db_close(event): - data = {"title": "Problem connecting to db in func update_event", } + data = { + 'title': 'Problem connecting to db in func update_event', + } with pytest.raises(HTTPException): - assert ( - evt.update_event(event_id=event.id, event=data, - db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR - ) + assert (evt.update_event( + event_id=event.id, event=data, + db=None).status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) def test_update_event_does_not_exist(event, session): - data = { - "content": "An update test for an event does not exist" - } + data = {'content': 'An update test for an event does not exist'} with pytest.raises(HTTPException): response = evt.update_event( event_id=status.HTTP_500_INTERNAL_SERVER_ERROR, - event=data, db=session) + event=data, + db=session) assert response.status_code == status.HTTP_404_NOT_FOUND def test_db_close_update(session, event): - data = {"title": "Problem connecting to db in func _update_event", } + data = { + 'title': 'Problem connecting to db in func _update_event', + } with pytest.raises(HTTPException): - assert ( - evt._update_event( - event_id=event.id, - event_to_update=data, - db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR - ) + assert (evt._update_event( + event_id=event.id, event_to_update=data, + db=None).status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) def test_repr(event): @@ -376,25 +380,21 @@ def test_repr(event): def test_no_connection_to_db_in_delete(event): with pytest.raises(HTTPException): response = evt.delete_event(event_id=1, db=None) - assert ( - response.status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR - ) + assert (response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) def test_no_connection_to_db_in_internal_deletion(event): with pytest.raises(HTTPException): - assert ( - evt._delete_event(event=event, db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR - ) + assert (evt._delete_event( + event=event, + db=None).status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) def test_successful_deletion(event_test_client, session, event): - response = event_test_client.delete("/event/1") + response = event_test_client.delete('/event/1') assert response.ok with pytest.raises(HTTPException): - assert "Event ID does not exist. ID: 1" in evt.by_id( + assert 'Event ID does not exist. ID: 1' in evt.by_id( db=session, event_id=1).content @@ -403,30 +403,29 @@ def test_change_owner(client, event_test_client, user, session, event): Test change owner of an event """ event_id = event.id - event_details = [event.title, event.content, event.location, event.start, - event.end, event.color, event.category_id] - response = event_test_client.post(f"/event/{event_id}/owner", - data=None) + event_details = [ + event.title, event.content, event.location, event.start, event.end, + event.color, event.category_id + ] + response = event_test_client.post(f'/event/{event_id}/owner', data=None) assert response.status_code == status.HTTP_302_FOUND assert response.ok - assert b"View Event" not in response.content + assert b'View Event' not in response.content for event_detail in event_details: assert str(event_detail).encode('utf-8') not in response.content, \ f'{event_detail} not in view event page' - data = {'username': "worng_username"} - response = event_test_client.post(f"/event/{event_id}/owner", - data=data) + data = {'username': 'worng_username'} + response = event_test_client.post(f'/event/{event_id}/owner', data=data) assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR assert b'Username does not exist.' in response.content data = {'username': user.username} - response = event_test_client.post(f"/event/{event_id}/owner", - data=data) + response = event_test_client.post(f'/event/{event_id}/owner', data=data) assert response.ok assert response.status_code == status.HTTP_302_FOUND def test_deleting_an_event_does_not_exist(event_test_client, event): - response = event_test_client.delete("/event/2") + response = event_test_client.delete('/event/2') assert response.status_code == status.HTTP_404_NOT_FOUND @@ -445,14 +444,15 @@ def test_can_show_event_private(event, session, user): event.privacy = PrivacyKinds.Private.name private_event = can_show_event(event=event, session=session, user=user) private_attributes = [ - private_event.title, private_event.location, - private_event.content, private_event.invitees + private_event.title, private_event.location, private_event.content, + private_event.invitees ] null_attributes = [ - private_event.color, private_event.emotion, - private_event.category_id + private_event.color, private_event.emotion, private_event.category_id + ] + is_private_attributes = [ + attr == event.privacy for attr in private_attributes ] - is_private_attributes = [attr == event.privacy for attr in private_attributes] is_null_attributes = [attr is Null for attr in null_attributes] assert all(is_private_attributes) and all(is_null_attributes) @@ -473,17 +473,13 @@ def test_add_comment(event_test_client: TestClient, session: Session, def test_get_event_data(session: Session, event: Event, comment: Comment) -> None: - data = ( - event, - [{ - 'id': 1, - 'avatar': 'profile.png', - 'username': 'test_username', - 'time': '01/01/2021 00:01', - 'content': 'test comment', - }], - '%H:%M' - ) + data = (event, [{ + 'id': 1, + 'avatar': 'profile.png', + 'username': 'test_username', + 'time': '01/01/2021 00:01', + 'content': 'test comment', + }], '%H:%M') assert evt.get_event_data(session, event.id) == data @@ -513,13 +509,13 @@ class TestApp: client = TestClient(app) date_test_data = [datetime.today() - timedelta(1), datetime.today()] event_test_data = { - 'title': "Test Title", - "location": "Fake City", + 'title': 'Test Title', + 'location': 'Fake City', 'vc_link': 'https://us02web.zoom.us/j/875384596', - "start": date_test_data[0], - "end": date_test_data[1], - "content": "Any Words", - "owner_id": 123, + 'start': date_test_data[0], + 'end': date_test_data[1], + 'content': 'Any Words', + 'owner_id': 123, 'invitees': 'user1, user2' } @@ -539,10 +535,7 @@ def check_is_date_before(): @staticmethod def test_bad_check_validation(): - assert not evt.is_date_before( - TestApp.date_test_data[0], - 'bad value' - ) + assert not evt.is_date_before(TestApp.date_test_data[0], 'bad value') @staticmethod def test_add_event(session: Session): From ffa1b984855f77710055c4e77065b59bc9d00a02 Mon Sep 17 00:00:00 2001 From: imimouni Date: Tue, 16 Feb 2021 12:31:17 +0200 Subject: [PATCH 06/16] fix: event_privacy privacykinds --- app/routers/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routers/event.py b/app/routers/event.py index d8f01192..638be399 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -322,7 +322,7 @@ def create_event( start, end, owner_id: int, - privacy: str = 'Public', + privacy: str = PrivacyKinds.Public.name, content: Optional[str] = None, location: Optional[str] = None, vc_link: str = None, From 4eed02c6af8125eb71d325ec8c02407c926f3e26 Mon Sep 17 00:00:00 2001 From: imimouni Date: Tue, 16 Feb 2021 12:36:27 +0200 Subject: [PATCH 07/16] fix: privacykinds in models.py --- app/database/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/database/models.py b/app/database/models.py index 0857a5cd..602f753e 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -14,6 +14,7 @@ from app.config import PSQL_ENVIRONMENT from app.dependencies import logger +from app.internal.privacy import PrivacyKinds import app.routers.salary.config as SalaryConfig Base = declarative_base() @@ -73,7 +74,7 @@ class Event(Base): vc_link = Column(String) color = Column(String, nullable=True) invitees = Column(String) - privacy = Column(String, default='Public', nullable=False) + privacy = Column(String, default=PrivacyKinds.Public.name, nullable=False) emotion = Column(String, nullable=True) availability = Column(Boolean, default=True, nullable=False) From f4a9f6bcbea5e6e21d80972c1ffa05fd65d83da2 Mon Sep 17 00:00:00 2001 From: imimouni Date: Wed, 17 Feb 2021 11:03:50 +0200 Subject: [PATCH 08/16] fix: format --- app/routers/event.py | 217 ++++++++++++++++++++----------------------- tests/test_event.py | 85 +++++++---------- 2 files changed, 138 insertions(+), 164 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 638be399..b832f122 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -107,23 +107,16 @@ async def create_new_event( privacy = PrivacyKinds.Public.name invited_emails = get_invited_emails(data['invited']) - uninvited_contacts = get_uninvited_regular_emails(session, owner_id, title, - invited_emails) + uninvited_contacts = get_uninvited_regular_emails(session, owner_id, + title, invited_emails) if vc_link is not None: raise_if_zoom_link_invalid(vc_link) - event = create_event(db=session, - title=title, - start=start, - end=end, - owner_id=owner_id, - privacy=privacy, - content=content, - location=location, - invitees=invited_emails, - category_id=category_id, - availability=availability) + event = create_event( + session, title, start, end, owner_id, privacy, content, + location, invited_emails, category_id, availability, + ) messages = get_messages(session, event, uninvited_contacts) return RedirectResponse( @@ -144,19 +137,17 @@ async def eventview(request: Request, event_id: int, db: Session = Depends(get_db)) -> Response: event, comments, end_format = get_event_data(db, event_id) - event_to_show = can_show_event(event, db) - if not event_to_show: + event_considering_privacy = event_to_show(event, db) + if not event_considering_privacy: nonexisting_event(event.id) messages = request.query_params.get('messages', '').split('---') - return templates.TemplateResponse( - 'event/eventview.html', { - 'request': request, - 'event': event_to_show, - 'comments': comments, - 'start_format': START_FORMAT, - 'end_format': end_format, - 'messages': messages - }) + return templates.TemplateResponse("event/eventview.html", + {"request": request, + "event": event_considering_privacy, + "comments": comments, + "start_format": START_FORMAT, + "end_format": end_format, + "messages": messages}) def check_event_owner( @@ -178,10 +169,10 @@ def check_event_owner( return is_owner -def can_show_event(event: Event, - session: Depends(get_db), - user: Optional[User] = None) -> Optional[Event]: - """Check the given events privacy and return +def event_to_show(event: Event, + session: Depends(get_db), + user: Optional[User] = None) -> Optional[Event]: + """Check the given event's privacy and return event/fixed event/ nothing (hidden) accordingly""" is_owner = check_event_owner(event, session, user) if event.privacy == PrivacyKinds.Private.name and not is_owner: @@ -225,11 +216,13 @@ async def change_owner(request: Request, def by_id(db: Session, event_id: int) -> Event: """Get a single event by id""" if not isinstance(db, Session): - error_message = (f'Could not connect to database. ' - f'db instance type received: {type(db)}') + error_message = ( + f'Could not connect to database. ' + f'db instance type received: {type(db)}') logger.critical(error_message) - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_message) try: event = db.query(Event).filter_by(id=event_id).one() @@ -240,8 +233,9 @@ def by_id(db: Session, event_id: int) -> Event: f'Multiple results found when getting event. Expected only one. ' f'ID: {event_id}') logger.critical(error_message) - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_message) return event @@ -260,8 +254,9 @@ def check_change_dates_allowed(old_event: Event, event: Dict[str, Any]): except TypeError: allowed = 0 if allowed == 0: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, - detail='Invalid times') + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid times") def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): @@ -270,12 +265,12 @@ def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): for field_name, field_type in to_check.items(): if types[field_name] and not isinstance(field_type, types[field_name]): errors.append( - f"{field_name} is '{type(field_type).__name__}' and" + - f"it should be from type '{types[field_name].__name__}'") + f"{field_name} is '{type(field_type).__name__}' and" + + f"it should be from type '{types[field_name].__name__}'") logger.warning(errors) if errors: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, - detail=errors) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail=errors) def get_event_with_editable_fields_only( @@ -299,9 +294,9 @@ def _update_event(db: Session, event_id: int, event_to_update: Dict) -> Event: return by_id(db, event_id) except (AttributeError, SQLAlchemyError) as e: logger.exception(str(e)) - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail='Internal server error') - + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal server error") def update_event(event_id: int, event: Dict, db: Session) -> Optional[Event]: # TODO Check if the user is the owner of the event. @@ -316,45 +311,40 @@ def update_event(event_id: int, event: Dict, db: Session) -> Optional[Event]: return event_updated -def create_event( - db: Session, - title: str, - start, - end, - owner_id: int, - privacy: str = PrivacyKinds.Public.name, - content: Optional[str] = None, - location: Optional[str] = None, - vc_link: str = None, - color: Optional[str] = None, - invitees: List[str] = None, - category_id: Optional[int] = None, - availability: bool = True, - is_google_event: bool = False, -): +def create_event(db: Session, title: str, start, end, owner_id: int, + privacy: str = PrivacyKinds.Public.name, + content: Optional[str] = None, + location: Optional[str] = None, + vc_link: str = None, + color: Optional[str] = None, + invitees: List[str] = None, + category_id: Optional[int] = None, + availability: bool = True, + is_google_event: bool = False, + ): """Creates an event and an association.""" invitees_concatenated = ','.join(invitees or []) - event = create_model(db, - Event, - title=title, - start=start, - end=end, - privacy=privacy, - content=content, - owner_id=owner_id, - location=location, - vc_link=vc_link, - color=color, - emotion=get_emotion(title, content), - invitees=invitees_concatenated, - category_id=category_id, - availability=availability, - is_google_event=is_google_event) + event = create_model( + db, Event, + title=title, + start=start, + end=end, + privacy=privacy, + content=content, + owner_id=owner_id, + location=location, + vc_link=vc_link, + color=color, + emotion=get_emotion(title, content), + invitees=invitees_concatenated, + category_id=category_id, + availability=availability, + is_google_event=is_google_event + ) create_model( - db, - UserEvent, + db, UserEvent, user_id=owner_id, event_id=event.id, ) @@ -369,19 +359,21 @@ def sort_by_date(events: List[Event]) -> List[Event]: def get_attendees_email(session: Session, event: Event): - return (session.query( - User.email).join(UserEvent).filter(UserEvent.events == event).all()) + return ( + session.query(User.email).join(UserEvent) + .filter(UserEvent.events == event).all() + ) 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).select_from(Event).join( - UserEvent, UserEvent.event_id == Event.id).join( - User, User.id == UserEvent.user_id).filter( - Event.id == event_id).all() - ] + return [email[0] for email in db.query(User.email). + select_from(Event). + join(UserEvent, UserEvent.event_id == Event.id). + join(User, User.id == UserEvent.user_id). + filter(Event.id == event_id). + all()] def _delete_event(db: Session, event: Event): @@ -396,8 +388,9 @@ def _delete_event(db: Session, event: Event): except (SQLAlchemyError, AttributeError) as e: logger.exception(str(e)) - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail='Deletion failed') + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Deletion failed") @router.delete('/{event_id}') @@ -410,7 +403,8 @@ def delete_event(event_id: int, db: Session = Depends(get_db)) -> Response: pass # TODO: Send them a cancellation notice # if the deletion is successful - return RedirectResponse(url='/calendar', status_code=status.HTTP_200_OK) + return RedirectResponse( + url="/calendar", status_code=status.HTTP_200_OK) def is_date_before(start_time: dt, end_time: dt) -> bool: @@ -431,10 +425,11 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: return None try: new_event = create_model(db, Event, **values) - create_model(db, - UserEvent, - user_id=values['owner_id'], - event_id=new_event.id) + create_model( + db, UserEvent, + user_id=values['owner_id'], + event_id=new_event.id + ) return new_event except (AssertionError, AttributeError, TypeError) as e: logger.exception(e) @@ -442,10 +437,8 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: @router.post('/{event_id}') -async def add_comment( - request: Request, event_id: int, - session: Session = Depends(get_db) -) -> Response: +async def add_comment(request: Request, event_id: int, + session: Session = Depends(get_db)) -> Response: """Creates a comment instance in the DB. Redirects back to the event's comments tab upon creation.""" form = await request.form() @@ -479,36 +472,30 @@ def get_event_data(db: Session, event_id: int) -> EVENT_DATA: """ event = by_id(db, event_id) comments = json.loads(cmt.display_comments(db, event)) - end_format = ('%H:%M' - if event.start.date() == event.end.date() else START_FORMAT) + end_format = ('%H:%M' if event.start.date() == event.end.date() + else START_FORMAT) return event, comments, end_format @router.get('/{event_id}/comments') -async def view_comments( - request: Request, event_id: int, - db: Session = Depends(get_db) -) -> Response: +async def view_comments(request: Request, event_id: int, + db: Session = Depends(get_db)) -> Response: """Renders event comment tab view. This essentially the same as `eventedit`, only with comments tab auto showed.""" event, comments, end_format = get_event_data(db, event_id) - return templates.TemplateResponse( - 'event/eventview.html', { - 'request': request, - 'event': event, - 'comments': comments, - 'comment': True, - 'start_format': START_FORMAT, - 'end_format': end_format - }) + return templates.TemplateResponse("event/eventview.html", + {"request": request, + "event": event, + "comments": comments, + 'comment': True, + "start_format": START_FORMAT, + "end_format": end_format}) @router.post('/comments/delete') -async def delete_comment( - request: Request, - db: Session = Depends(get_db) -) -> Response: +async def delete_comment(request: Request, + db: Session = Depends(get_db)) -> Response: """Deletes a comment instance from the db. Redirects back to the event's comments tab upon deletion. diff --git a/tests/test_event.py b/tests/test_event.py index c15c0e58..8d1d151f 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -13,8 +13,8 @@ from app.internal.privacy import PrivacyKinds from app.internal.utils import delete_instance from app.main import app -from app.routers.event import can_show_event from app.routers import event as evt +from app.routers.event import can_show_event, event_to_show CORRECT_EVENT_FORM_DATA = { 'title': 'test title', @@ -27,7 +27,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'Public', + 'privacy': PrivacyKinds.Public.name, 'invited': 'a@a.com,b@b.com' } @@ -42,7 +42,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'Public', + 'privacy': PrivacyKinds.Public.name, 'invited': 'a@a.com,b@b.com' } @@ -57,7 +57,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'Public', + 'privacy': PrivacyKinds.Public.name, 'invited': 'a@a.com,b@b.com,ccc' } @@ -72,7 +72,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'Public', + 'privacy': PrivacyKinds.Public.name, 'invited': 'a@a.com,b@b.com' } @@ -87,7 +87,7 @@ 'description': 'content', 'color': 'red', 'availability': 'busy', - 'privacy': 'Public', + 'privacy': PrivacyKinds.Public.name, 'invited': 'a@a.com,b@b.com' } @@ -101,26 +101,13 @@ } NONE_UPDATE_OPTIONS = [ - {}, - { - 'test': 'test' - }, + {}, {"test": "test"}, ] INVALID_FIELD_UPDATE = [ - { - 'start': '20.01.2020' - }, - { - 'start': datetime(2020, 2, 2), - 'end': datetime(2020, 1, 1) - }, - { - 'start': datetime(2030, 2, 2) - }, - { - 'end': datetime(1990, 1, 1) - }, + {"start": "20.01.2020"}, + {"start": datetime(2020, 2, 2), "end": datetime(2020, 1, 1)}, + {"start": datetime(2030, 2, 2)}, {"end": datetime(1990, 1, 1)}, ] @@ -344,13 +331,13 @@ def test_update_event_with_category(today_event, category, session): def test_update_db_close(event): - data = { - 'title': 'Problem connecting to db in func update_event', - } + data = {'title': 'Problem connecting to db in func update_event',} with pytest.raises(HTTPException): - assert (evt.update_event( - event_id=event.id, event=data, - db=None).status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) + assert ( + evt.update_event(event_id=event.id, event=data, + db=None).status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR + ) def test_update_event_does_not_exist(event, session): @@ -358,19 +345,20 @@ def test_update_event_does_not_exist(event, session): with pytest.raises(HTTPException): response = evt.update_event( event_id=status.HTTP_500_INTERNAL_SERVER_ERROR, - event=data, - db=session) + event=data, db=session) assert response.status_code == status.HTTP_404_NOT_FOUND def test_db_close_update(session, event): - data = { - 'title': 'Problem connecting to db in func _update_event', - } + data = {'title': 'Problem connecting to db in func _update_event',} with pytest.raises(HTTPException): - assert (evt._update_event( - event_id=event.id, event_to_update=data, - db=None).status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) + assert ( + evt._update_event( + event_id=event.id, + event_to_update=data, + db=None).status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR + ) def test_repr(event): @@ -385,9 +373,10 @@ def test_no_connection_to_db_in_delete(event): def test_no_connection_to_db_in_internal_deletion(event): with pytest.raises(HTTPException): - assert (evt._delete_event( - event=event, - db=None).status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) + assert ( + evt._delete_event(event=event, db=None).status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR + ) def test_successful_deletion(event_test_client, session, event): @@ -403,10 +392,8 @@ def test_change_owner(client, event_test_client, user, session, event): Test change owner of an event """ event_id = event.id - event_details = [ - event.title, event.content, event.location, event.start, event.end, - event.color, event.category_id - ] + event_details = [event.title, event.content, event.location, event.start, + event.end, event.color, event.category_id] response = event_test_client.post(f'/event/{event_id}/owner', data=None) assert response.status_code == status.HTTP_302_FOUND assert response.ok @@ -430,19 +417,19 @@ def test_deleting_an_event_does_not_exist(event_test_client, event): def test_can_show_event_public(event, session, user): - assert can_show_event(event, session) == event - assert can_show_event(event, session, user) == event + assert event_to_show(event, session) == event + assert event_to_show(event, session, user) == event def test_can_show_event_hidden(event, session, user): event.privacy = PrivacyKinds.Hidden.name - assert can_show_event(event, session, user) is None - assert can_show_event(event, session) == event + assert event_to_show(event, session, user) is None + assert event_to_show(event, session) == event def test_can_show_event_private(event, session, user): event.privacy = PrivacyKinds.Private.name - private_event = can_show_event(event=event, session=session, user=user) + private_event = event_to_show(event=event, session=session, user=user) private_attributes = [ private_event.title, private_event.location, private_event.content, private_event.invitees From a74ce1df45e455825202a230c750b836faea7104 Mon Sep 17 00:00:00 2001 From: imimouni Date: Wed, 17 Feb 2021 11:16:44 +0200 Subject: [PATCH 09/16] fix: flake8 --- app/routers/event.py | 1 + tests/test_event.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index b832f122..a1df89c5 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -298,6 +298,7 @@ def _update_event(db: Session, event_id: int, event_to_update: Dict) -> Event: status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error") + def update_event(event_id: int, event: Dict, db: Session) -> Optional[Event]: # TODO Check if the user is the owner of the event. old_event = by_id(db, event_id) diff --git a/tests/test_event.py b/tests/test_event.py index 8d1d151f..789971e4 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -331,7 +331,7 @@ def test_update_event_with_category(today_event, category, session): def test_update_db_close(event): - data = {'title': 'Problem connecting to db in func update_event',} + data = {'title': 'Problem connecting to db in func update_event', } with pytest.raises(HTTPException): assert ( evt.update_event(event_id=event.id, event=data, @@ -350,7 +350,7 @@ def test_update_event_does_not_exist(event, session): def test_db_close_update(session, event): - data = {'title': 'Problem connecting to db in func _update_event',} + data = {'title': 'Problem connecting to db in func _update_event', } with pytest.raises(HTTPException): assert ( evt._update_event( From dc942388f53defa129d3204a192d481a443afe1d Mon Sep 17 00:00:00 2001 From: imimouni Date: Wed, 17 Feb 2021 12:52:13 +0200 Subject: [PATCH 10/16] fix: flake8, import error, create event attributes, new pre-commit formatting --- app/routers/event.py | 415 ++++++++++++++++++++-------------- tests/test_event.py | 514 +++++++++++++++++++++++++------------------ 2 files changed, 546 insertions(+), 383 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index a1df89c5..3ef7c97d 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -21,6 +21,7 @@ ) from app.internal import comment as cmt from app.internal.emotion import get_emotion + # TODO add this when the user system is merged (PR#195) # from app.internal.security.dependancies import ( # current_user, CurrentUser) @@ -29,25 +30,23 @@ from app.routers.user import create_user EVENT_DATA = Tuple[Event, List[Dict[str, str]], str, str] -TIME_FORMAT = '%Y-%m-%d %H:%M' -START_FORMAT = '%A, %d/%m/%Y %H:%M' +TIME_FORMAT = "%Y-%m-%d %H:%M" +START_FORMAT = "%A, %d/%m/%Y %H:%M" UPDATE_EVENTS_FIELDS = { - 'title': str, - 'start': dt, - 'end': dt, - 'availability': bool, - 'content': (str, type(None)), - 'location': (str, type(None)), - 'vc_link': (str, type(None)), - 'category_id': (int, type(None)), + "title": str, + "start": dt, + "end": dt, + "availability": bool, + "content": (str, type(None)), + "location": (str, type(None)), + "vc_link": (str, type(None)), + "category_id": (int, type(None)), } router = APIRouter( - prefix='/event', - tags=['event'], - responses={404: { - 'description': 'Not found' - }}, + prefix="/event", + tags=["event"], + responses={404: {"description": "Not found"}}, ) @@ -60,94 +59,122 @@ class EventModel(BaseModel): location: str -@router.get('/') +@router.get("/") async def get_events(session=Depends(get_db)): return session.query(Event).all() -@router.post('/') +@router.post("/") async def create_event_api(event: EventModel, session=Depends(get_db)): - create_event(db=session, - title=event.title, - start=event.start, - end=event.start, - content=event.content, - owner_id=event.owner_id, - location=event.location) - return {'success': True} - - -@router.get('/edit', include_in_schema=False) -@router.get('/edit') + create_event( + db=session, + title=event.title, + start=event.start, + end=event.start, + content=event.content, + owner_id=event.owner_id, + location=event.location, + ) + return {"success": True} + + +@router.get("/edit", include_in_schema=False) +@router.get("/edit") async def eventedit(request: Request) -> Response: - return templates.TemplateResponse('event/eventedit.html', { - 'request': request, - 'privacy': PrivacyKinds - }) + return templates.TemplateResponse( + "event/eventedit.html", + {"request": request, "privacy": PrivacyKinds}, + ) -@router.post('/edit', include_in_schema=False) +@router.post("/edit", include_in_schema=False) async def create_new_event( - request: Request, session=Depends(get_db) + request: Request, + session=Depends(get_db), ) -> Response: data = await request.form() - title = data['title'] - content = data['description'] - start = dt.strptime(data['start_date'] + ' ' + data['start_time'], - TIME_FORMAT) - end = dt.strptime(data['end_date'] + ' ' + data['end_time'], TIME_FORMAT) + title = data["title"] + content = data["description"] + start = dt.strptime( + data["start_date"] + " " + data["start_time"], + TIME_FORMAT, + ) + end = dt.strptime(data["end_date"] + " " + data["end_time"], TIME_FORMAT) owner_id = get_current_user(session).id - availability = data.get('availability', 'True') == 'True' - location = data['location'] - vc_link = data['vc_link'] - category_id = data.get('category_id') - privacy = data['privacy'] + availability = data.get("availability", "True") == "True" + location = data["location"] + vc_link = data["vc_link"] + category_id = data.get("category_id") + privacy = data["privacy"] privacy_kinds = [kind.name for kind in PrivacyKinds] if privacy not in privacy_kinds: privacy = PrivacyKinds.Public.name - invited_emails = get_invited_emails(data['invited']) - uninvited_contacts = get_uninvited_regular_emails(session, owner_id, - title, invited_emails) + invited_emails = get_invited_emails(data["invited"]) + uninvited_contacts = get_uninvited_regular_emails( + session, + owner_id, + title, + invited_emails, + ) if vc_link is not None: raise_if_zoom_link_invalid(vc_link) event = create_event( - session, title, start, end, owner_id, privacy, content, - location, invited_emails, category_id, availability, + db=session, + title=title, + start=start, + end=end, + owner_id=owner_id, + privacy=privacy, + content=content, + location=location, + vc_link=vc_link, + invitees=invited_emails, + category_id=category_id, + availability=availability, ) messages = get_messages(session, event, uninvited_contacts) return RedirectResponse( - router.url_path_for('eventview', event_id=event.id) + - f'?messages={"---".join(messages)}', - status_code=status.HTTP_302_FOUND) + router.url_path_for("eventview", event_id=event.id) + + f'?messages={"---".join(messages)}', + status_code=status.HTTP_302_FOUND, + ) def nonexisting_event(event_id: int) -> None: - error_message = f'Event ID does not exist. ID: {event_id}' + error_message = f"Event ID does not exist. ID: {event_id}" logger.exception(error_message) - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, - detail=error_message) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=error_message, + ) -@router.get('/{event_id}', include_in_schema=False) -async def eventview(request: Request, - event_id: int, - db: Session = Depends(get_db)) -> Response: +@router.get("/{event_id}", include_in_schema=False) +async def eventview( + request: Request, + event_id: int, + db: Session = Depends(get_db), +) -> Response: event, comments, end_format = get_event_data(db, event_id) event_considering_privacy = event_to_show(event, db) if not event_considering_privacy: nonexisting_event(event.id) - messages = request.query_params.get('messages', '').split('---') - return templates.TemplateResponse("event/eventview.html", - {"request": request, - "event": event_considering_privacy, - "comments": comments, - "start_format": START_FORMAT, - "end_format": end_format, - "messages": messages}) + messages = request.query_params.get("messages", "").split("---") + return templates.TemplateResponse( + "event/eventview.html", + { + "request": request, + "event": event_considering_privacy, + "comments": comments, + "start_format": START_FORMAT, + "end_format": end_format, + "messages": messages, + }, + ) def check_event_owner( @@ -159,19 +186,27 @@ def check_event_owner( ) -> bool: if not user: user = session.query(User).filter_by(id=1).first() - user = user if user else create_user(username='u', - password='p', - email='e@mail.com', - language_id=1, - session=session) + user = ( + user + if user + else create_user( + username="u", + password="p", + email="e@mail.com", + language_id=1, + session=session, + ) + ) # TODO use current_user after user system merge is_owner = event.owner_id == user.id return is_owner -def event_to_show(event: Event, - session: Depends(get_db), - user: Optional[User] = None) -> Optional[Event]: +def event_to_show( + event: Event, + session: Depends(get_db), + user: Optional[User] = None, +) -> Optional[Event]: """Check the given event's privacy and return event/fixed event/ nothing (hidden) accordingly""" is_owner = check_event_owner(event, session, user) @@ -188,41 +223,49 @@ def event_to_show(event: Event, return event -@router.post('/{event_id}/owner') -async def change_owner(request: Request, - event_id: int, - db: Session = Depends(get_db)): +@router.post("/{event_id}/owner") +async def change_owner( + request: Request, + event_id: int, + db: Session = Depends(get_db), +): form = await request.form() - if 'username' not in form: - return RedirectResponse(router.url_path_for('eventview', - event_id=event_id), - status_code=status.HTTP_302_FOUND) - username = form['username'] + if "username" not in form: + return RedirectResponse( + router.url_path_for("eventview", event_id=event_id), + status_code=status.HTTP_302_FOUND, + ) + username = form["username"] user = db.query(User).filter_by(username=username).first() try: user_id = user.id except AttributeError as e: error_message = f"Username does not exist. {form['username']}" logger.exception(str(e)) - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) - owner_to_update = {'owner_id': user_id} + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_message, + ) + owner_to_update = {"owner_id": user_id} _update_event(db, event_id, owner_to_update) - return RedirectResponse(router.url_path_for('eventview', - event_id=event_id), - status_code=status.HTTP_302_FOUND) + return RedirectResponse( + router.url_path_for("eventview", event_id=event_id), + status_code=status.HTTP_302_FOUND, + ) def by_id(db: Session, event_id: int) -> Event: """Get a single event by id""" if not isinstance(db, Session): error_message = ( - f'Could not connect to database. ' - f'db instance type received: {type(db)}') + f"Could not connect to database. " + f"db instance type received: {type(db)}" + ) logger.critical(error_message) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + detail=error_message, + ) try: event = db.query(Event).filter_by(id=event_id).one() @@ -230,12 +273,14 @@ def by_id(db: Session, event_id: int) -> Event: nonexisting_event(event_id) except MultipleResultsFound: error_message = ( - f'Multiple results found when getting event. Expected only one. ' - f'ID: {event_id}') + f"Multiple results found when getting event. Expected only one. " + f"ID: {event_id}" + ) logger.critical(error_message) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=error_message) + detail=error_message, + ) return event @@ -247,8 +292,8 @@ def is_end_date_before_start_date(start_date: dt, end_date: dt) -> bool: def check_change_dates_allowed(old_event: Event, event: Dict[str, Any]): allowed = 1 try: - start_date = event.get('start', old_event.start) - end_date = event.get('end', old_event.end) + start_date = event.get("start", old_event.start) + end_date = event.get("end", old_event.end) if is_end_date_before_start_date(start_date, end_date): allowed = 0 except TypeError: @@ -256,7 +301,8 @@ def check_change_dates_allowed(old_event: Event, event: Dict[str, Any]): if allowed == 0: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="Invalid times") + detail="Invalid times", + ) def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): @@ -266,21 +312,25 @@ def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): if types[field_name] and not isinstance(field_type, types[field_name]): errors.append( f"{field_name} is '{type(field_type).__name__}' and" - + f"it should be from type '{types[field_name].__name__}'") + + f"it should be from type '{types[field_name].__name__}'", + ) logger.warning(errors) if errors: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail=errors) + status_code=status.HTTP_400_BAD_REQUEST, + detail=errors, + ) def get_event_with_editable_fields_only( - event: Dict[str, Any]) -> Dict[str, Any]: + event: Dict[str, Any], +) -> Dict[str, Any]: """Remove all keys that are not allowed to update""" edit_event = {i: event[i] for i in UPDATE_EVENTS_FIELDS if i in event} # Convert `availability` value into boolean. - if 'availability' in edit_event.keys(): - edit_event['availability'] = (edit_event['availability'] == 'True') + if "availability" in edit_event.keys(): + edit_event["availability"] = edit_event["availability"] == "True" return edit_event @@ -288,7 +338,9 @@ def _update_event(db: Session, event_id: int, event_to_update: Dict) -> Event: try: # Update database db.query(Event).filter(Event.id == event_id).update( - event_to_update, synchronize_session=False) + event_to_update, + synchronize_session=False, + ) db.commit() return by_id(db, event_id) @@ -296,7 +348,8 @@ def _update_event(db: Session, event_id: int, event_to_update: Dict) -> Event: logger.exception(str(e)) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Internal server error") + detail="Internal server error", + ) def update_event(event_id: int, event: Dict, db: Session) -> Optional[Event]: @@ -312,23 +365,29 @@ def update_event(event_id: int, event: Dict, db: Session) -> Optional[Event]: return event_updated -def create_event(db: Session, title: str, start, end, owner_id: int, - privacy: str = PrivacyKinds.Public.name, - content: Optional[str] = None, - location: Optional[str] = None, - vc_link: str = None, - color: Optional[str] = None, - invitees: List[str] = None, - category_id: Optional[int] = None, - availability: bool = True, - is_google_event: bool = False, - ): +def create_event( + db: Session, + title: str, + start, + end, + owner_id: int, + privacy: str = PrivacyKinds.Public.name, + content: Optional[str] = None, + location: Optional[str] = None, + vc_link: str = None, + color: Optional[str] = None, + invitees: List[str] = None, + category_id: Optional[int] = None, + availability: bool = True, + is_google_event: bool = False, +): """Creates an event and an association.""" - invitees_concatenated = ','.join(invitees or []) + invitees_concatenated = ",".join(invitees or []) event = create_model( - db, Event, + db, + Event, title=title, start=start, end=end, @@ -342,10 +401,11 @@ def create_event(db: Session, title: str, start, end, owner_id: int, invitees=invitees_concatenated, category_id=category_id, availability=availability, - is_google_event=is_google_event + is_google_event=is_google_event, ) create_model( - db, UserEvent, + db, + UserEvent, user_id=owner_id, event_id=event.id, ) @@ -356,25 +416,30 @@ def sort_by_date(events: List[Event]) -> List[Event]: """Sorts the events by the start of the event.""" temp = events.copy() - return sorted(temp, key=attrgetter('start')) + return sorted(temp, key=attrgetter("start")) def get_attendees_email(session: Session, event: Event): return ( - session.query(User.email).join(UserEvent) - .filter(UserEvent.events == event).all() + session.query(User.email) + .join(UserEvent) + .filter(UserEvent.events == event) + .all() ) 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). - select_from(Event). - join(UserEvent, UserEvent.event_id == Event.id). - join(User, User.id == UserEvent.user_id). - filter(Event.id == event_id). - all()] + by event id.""" + return [ + email[0] + for email in db.query(User.email) + .select_from(Event) + .join(UserEvent, UserEvent.event_id == Event.id) + .join(User, User.id == UserEvent.user_id) + .filter(Event.id == event_id) + .all() + ] def _delete_event(db: Session, event: Event): @@ -391,10 +456,11 @@ def _delete_event(db: Session, event: Event): logger.exception(str(e)) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Deletion failed") + detail="Deletion failed", + ) -@router.delete('/{event_id}') +@router.delete("/{event_id}") def delete_event(event_id: int, db: Session = Depends(get_db)) -> Response: # TODO: Check if the user is the owner of the event. event = by_id(db, event_id) @@ -404,8 +470,7 @@ def delete_event(event_id: int, db: Session = Depends(get_db)) -> Response: pass # TODO: Send them a cancellation notice # if the deletion is successful - return RedirectResponse( - url="/calendar", status_code=status.HTTP_200_OK) + return RedirectResponse(url="/calendar", status_code=status.HTTP_200_OK) def is_date_before(start_time: dt, end_time: dt) -> bool: @@ -422,14 +487,15 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: in the dict is not match to the fields in the DB return the Event Class item""" - if not is_date_before(values['start'], values['end']): + if not is_date_before(values["start"], values["end"]): return None try: new_event = create_model(db, Event, **values) create_model( - db, UserEvent, - user_id=values['owner_id'], - event_id=new_event.id + db, + UserEvent, + user_id=values["owner_id"], + event_id=new_event.id, ) return new_event except (AssertionError, AttributeError, TypeError) as e: @@ -437,20 +503,23 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: return None -@router.post('/{event_id}') -async def add_comment(request: Request, event_id: int, - session: Session = Depends(get_db)) -> Response: +@router.post("/{event_id}") +async def add_comment( + request: Request, + event_id: int, + session: Session = Depends(get_db), +) -> Response: """Creates a comment instance in the DB. Redirects back to the event's comments tab upon creation.""" form = await request.form() data = { - 'user_id': get_current_user(session).id, - 'event_id': event_id, - 'content': form['comment'], - 'time': dt.now(), + "user_id": get_current_user(session).id, + "event_id": event_id, + "content": form["comment"], + "time": dt.now(), } create_model(session, Comment, **data) - path = router.url_path_for('view_comments', event_id=event_id) + path = router.url_path_for("view_comments", event_id=event_id) return RedirectResponse(path, status_code=status.HTTP_303_SEE_OTHER) @@ -473,37 +542,47 @@ def get_event_data(db: Session, event_id: int) -> EVENT_DATA: """ event = by_id(db, event_id) comments = json.loads(cmt.display_comments(db, event)) - end_format = ('%H:%M' if event.start.date() == event.end.date() - else START_FORMAT) + end_format = ( + "%H:%M" if event.start.date() == event.end.date() else START_FORMAT + ) return event, comments, end_format -@router.get('/{event_id}/comments') -async def view_comments(request: Request, event_id: int, - db: Session = Depends(get_db)) -> Response: +@router.get("/{event_id}/comments") +async def view_comments( + request: Request, + event_id: int, + db: Session = Depends(get_db), +) -> Response: """Renders event comment tab view. This essentially the same as `eventedit`, only with comments tab auto showed.""" event, comments, end_format = get_event_data(db, event_id) - return templates.TemplateResponse("event/eventview.html", - {"request": request, - "event": event, - "comments": comments, - 'comment': True, - "start_format": START_FORMAT, - "end_format": end_format}) - - -@router.post('/comments/delete') -async def delete_comment(request: Request, - db: Session = Depends(get_db)) -> Response: + return templates.TemplateResponse( + "event/eventview.html", + { + "request": request, + "event": event, + "comments": comments, + "comment": True, + "start_format": START_FORMAT, + "end_format": end_format, + }, + ) + + +@router.post("/comments/delete") +async def delete_comment( + request: Request, + db: Session = Depends(get_db), +) -> Response: """Deletes a comment instance from the db. Redirects back to the event's comments tab upon deletion. """ form = await request.form() - comment_id = form['comment_id'] - event_id = form['event_id'] + comment_id = form["comment_id"] + event_id = form["event_id"] cmt.delete_comment(db, comment_id) - path = router.url_path_for('view_comments', event_id=event_id) + path = router.url_path_for("view_comments", event_id=event_id) return RedirectResponse(path, status_code=303) diff --git a/tests/test_event.py b/tests/test_event.py index 789971e4..b1a0367b 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -14,125 +14,129 @@ from app.internal.utils import delete_instance from app.main import app from app.routers import event as evt -from app.routers.event import can_show_event, event_to_show +from app.routers.event import event_to_show CORRECT_EVENT_FORM_DATA = { - 'title': 'test title', - 'start_date': '2021-01-28', - 'start_time': '12:59', - 'end_date': '2021-01-28', - 'end_time': '15:01', - 'location': 'fake city', - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'description': 'content', - 'color': 'red', - 'availability': 'busy', - 'privacy': PrivacyKinds.Public.name, - 'invited': 'a@a.com,b@b.com' + "title": "test title", + "start_date": "2021-01-28", + "start_time": "12:59", + "end_date": "2021-01-28", + "end_time": "15:01", + "location": "fake city", + "vc_link": "https://us02web.zoom.us/j/875384596", + "description": "content", + "color": "red", + "availability": "busy", + "privacy": PrivacyKinds.Public.name, + "invited": "a@a.com,b@b.com", } WRONG_EVENT_FORM_DATA = { - 'title': 'test title', - 'start_date': '2021-01-28', - 'start_time': '15:59', - 'end_date': '2021-01-27', - 'end_time': '15:01', - 'location': 'fake city', - 'vc_link': 'not a zoom link', - 'description': 'content', - 'color': 'red', - 'availability': 'busy', - 'privacy': PrivacyKinds.Public.name, - 'invited': 'a@a.com,b@b.com' + "title": "test title", + "start_date": "2021-01-28", + "start_time": "15:59", + "end_date": "2021-01-27", + "end_time": "15:01", + "location": "fake city", + "vc_link": "not a zoom link", + "description": "content", + "color": "red", + "availability": "busy", + "privacy": PrivacyKinds.Public.name, + "invited": "a@a.com,b@b.com", } BAD_EMAILS_FORM_DATA = { - 'title': 'test title', - 'start_date': '2021-01-28', - 'start_time': '15:59', - 'end_date': '2021-01-27', - 'end_time': '15:01', - 'location': 'fake city', - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'description': 'content', - 'color': 'red', - 'availability': 'busy', - 'privacy': PrivacyKinds.Public.name, - 'invited': 'a@a.com,b@b.com,ccc' + "title": "test title", + "start_date": "2021-01-28", + "start_time": "15:59", + "end_date": "2021-01-27", + "end_time": "15:01", + "location": "fake city", + "vc_link": "https://us02web.zoom.us/j/875384596", + "description": "content", + "color": "red", + "availability": "busy", + "privacy": PrivacyKinds.Public.name, + "invited": "a@a.com,b@b.com,ccc", } WEEK_LATER_EVENT_FORM_DATA = { - 'title': 'test title', - 'start_date': '2021-02-04', - 'start_time': '12:59', - 'end_date': '2021-02-04', - 'end_time': '15:01', - 'location': 'fake city', - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'description': 'content', - 'color': 'red', - 'availability': 'busy', - 'privacy': PrivacyKinds.Public.name, - 'invited': 'a@a.com,b@b.com' + "title": "test title", + "start_date": "2021-02-04", + "start_time": "12:59", + "end_date": "2021-02-04", + "end_time": "15:01", + "location": "fake city", + "vc_link": "https://us02web.zoom.us/j/875384596", + "description": "content", + "color": "red", + "availability": "busy", + "privacy": PrivacyKinds.Public.name, + "invited": "a@a.com,b@b.com", } TWO_WEEKS_LATER_EVENT_FORM_DATA = { - 'title': 'test title', - 'start_date': '2021-02-11', - 'start_time': '12:59', - 'end_date': '2021-02-11', - 'end_time': '15:01', - 'location': 'fake city', - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'description': 'content', - 'color': 'red', - 'availability': 'busy', - 'privacy': PrivacyKinds.Public.name, - 'invited': 'a@a.com,b@b.com' + "title": "test title", + "start_date": "2021-02-11", + "start_time": "12:59", + "end_date": "2021-02-11", + "end_time": "15:01", + "location": "fake city", + "vc_link": "https://us02web.zoom.us/j/875384596", + "description": "content", + "color": "red", + "availability": "busy", + "privacy": PrivacyKinds.Public.name, + "invited": "a@a.com,b@b.com", } CORRECT_ADD_EVENT_DATA = { - 'title': 'test', - 'start': '2021-02-13T09:03:49.560Z', - 'end': '2021-02-13T09:03:49.560Z', - 'content': 'test', - 'owner_id': 0, - 'location': 'test' + "title": "test", + "start": "2021-02-13T09:03:49.560Z", + "end": "2021-02-13T09:03:49.560Z", + "content": "test", + "owner_id": 0, + "location": "test", } NONE_UPDATE_OPTIONS = [ - {}, {"test": "test"}, + {}, + {"test": "test"}, ] INVALID_FIELD_UPDATE = [ {"start": "20.01.2020"}, {"start": datetime(2020, 2, 2), "end": datetime(2020, 1, 1)}, - {"start": datetime(2030, 2, 2)}, {"end": datetime(1990, 1, 1)}, + {"start": datetime(2030, 2, 2)}, + {"end": datetime(1990, 1, 1)}, ] def test_get_events(event_test_client, session, event): - response = event_test_client.get('/event/') + response = event_test_client.get("/event/") assert response.ok def test_create_event_api(event_test_client, session, event): - response = event_test_client.post('/event/', - data=json.dumps(CORRECT_ADD_EVENT_DATA)) + response = event_test_client.post( + "/event/", + data=json.dumps(CORRECT_ADD_EVENT_DATA), + ) assert response.ok def test_eventedit(event_test_client): - response = event_test_client.get('/event/edit') + response = event_test_client.get("/event/edit") assert response.ok - assert b'Edit Event' in response.content + assert b"Edit Event" in response.content def test_eventview_with_id(event_test_client, session, event): event_id = event.id - response = event_test_client.get(f'/event/{event_id}') + response = event_test_client.get(f"/event/{event_id}") assert response.ok - assert b'View Event' in response.content + assert b"View Event" in response.content def test_create_event_with_default_availability(client, user, session): @@ -140,12 +144,12 @@ def test_create_event_with_default_availability(client, user, session): Test create event with default availability. (busy) """ data = { - 'title': 'test title', - 'start': datetime.strptime('2021-01-01 15:59', '%Y-%m-%d %H:%M'), - 'end': datetime.strptime('2021-01-02 15:01', '%Y-%m-%d %H:%M'), - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'content': 'content', - 'owner_id': user.id, + "title": "test title", + "start": datetime.strptime("2021-01-01 15:59", "%Y-%m-%d %H:%M"), + "end": datetime.strptime("2021-01-02 15:01", "%Y-%m-%d %H:%M"), + "vc_link": "https://us02web.zoom.us/j/875384596", + "content": "content", + "owner_id": user.id, } event = evt.create_event(session, **data) @@ -157,13 +161,13 @@ def test_create_event_with_free_availability(client, user, session): Test create event with free availability. """ data = { - 'title': 'test title', - 'start': datetime.strptime('2021-01-01 15:59', '%Y-%m-%d %H:%M'), - 'end': datetime.strptime('2021-01-02 15:01', '%Y-%m-%d %H:%M'), - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'content': 'content', - 'owner_id': user.id, - 'availability': False, + "title": "test title", + "start": datetime.strptime("2021-01-01 15:59", "%Y-%m-%d %H:%M"), + "end": datetime.strptime("2021-01-02 15:01", "%Y-%m-%d %H:%M"), + "vc_link": "https://us02web.zoom.us/j/875384596", + "content": "content", + "owner_id": user.id, + "availability": False, } event = evt.create_event(session, **data) @@ -171,100 +175,127 @@ def test_create_event_with_free_availability(client, user, session): def test_eventview_without_id(client): - response = client.get('/event/view') + response = client.get("/event/view") assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY def test_eventedit_missing_old_invites(client, user): - response = client.post(client.app.url_path_for('create_new_event'), - data=CORRECT_EVENT_FORM_DATA) + response = client.post( + client.app.url_path_for("create_new_event"), + data=CORRECT_EVENT_FORM_DATA, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND different_invitees_event = CORRECT_EVENT_FORM_DATA.copy() - different_invitees_event['invited'] = 'c@c.com,d@d.com' - response = client.post(client.app.url_path_for('create_new_event'), - data=different_invitees_event) + different_invitees_event["invited"] = "c@c.com,d@d.com" + response = client.post( + client.app.url_path_for("create_new_event"), + data=different_invitees_event, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND - for invitee in CORRECT_EVENT_FORM_DATA['invited'].split(','): - assert invitee in response.headers['location'] + for invitee in CORRECT_EVENT_FORM_DATA["invited"].split(","): + assert invitee in response.headers["location"] def test_eventedit_bad_emails(client, user): - response = client.post(client.app.url_path_for('create_new_event'), - data=BAD_EMAILS_FORM_DATA) + response = client.post( + client.app.url_path_for("create_new_event"), + data=BAD_EMAILS_FORM_DATA, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND different_invitees_event = CORRECT_EVENT_FORM_DATA.copy() - different_invitees_event['invited'] = 'c@c.com,d@d.com' - response = client.post(client.app.url_path_for('create_new_event'), - data=different_invitees_event) + different_invitees_event["invited"] = "c@c.com,d@d.com" + response = client.post( + client.app.url_path_for("create_new_event"), + data=different_invitees_event, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND - for invitee in CORRECT_EVENT_FORM_DATA['invited'].split(','): - assert invitee in response.headers['location'] - assert 'ccc' not in response.headers['location'] + for invitee in CORRECT_EVENT_FORM_DATA["invited"].split(","): + assert invitee in response.headers["location"] + assert "ccc" not in response.headers["location"] def test_eventedit_post_correct(client, user): """ Test create new event successfully. """ - response = client.post(client.app.url_path_for('create_new_event'), - data=CORRECT_EVENT_FORM_DATA) + response = client.post( + client.app.url_path_for("create_new_event"), + data=CORRECT_EVENT_FORM_DATA, + ) assert response.ok - assert (client.app.url_path_for('eventview', event_id=1).strip('1') - in response.headers['location']) + assert ( + client.app.url_path_for("eventview", event_id=1).strip("1") + in response.headers["location"] + ) def test_create_event_with_category(client, user, category, session): """ Test create event with category successfully. """ - data = {**CORRECT_EVENT_FORM_DATA, **{'category_id': category.id}} + data = {**CORRECT_EVENT_FORM_DATA, **{"category_id": category.id}} - response = client.post(client.app.url_path_for('create_new_event'), - data=data) + response = client.post( + client.app.url_path_for("create_new_event"), + data=data, + ) assert response.ok - assert (client.app.url_path_for('eventview', event_id=1).strip('1') - in response.headers['location']) + assert ( + client.app.url_path_for("eventview", event_id=1).strip("1") + in response.headers["location"] + ) def test_eventedit_post_wrong(client, user): """ Test create new event unsuccessfully. """ - response = client.post(client.app.url_path_for('create_new_event'), - data=WRONG_EVENT_FORM_DATA) - assert response.json()['detail'] == 'VC type with no valid zoom link' + response = client.post( + client.app.url_path_for("create_new_event"), + data=WRONG_EVENT_FORM_DATA, + ) + assert response.json()["detail"] == "VC type with no valid zoom link" def test_eventedit_with_pattern(client, user): - response = client.post(client.app.url_path_for('create_new_event'), - data=CORRECT_EVENT_FORM_DATA) + response = client.post( + client.app.url_path_for("create_new_event"), + data=CORRECT_EVENT_FORM_DATA, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND - response = client.post(client.app.url_path_for('create_new_event'), - data=WEEK_LATER_EVENT_FORM_DATA) + response = client.post( + client.app.url_path_for("create_new_event"), + data=WEEK_LATER_EVENT_FORM_DATA, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND - assert ('Same event happened 1 weeks before too. ' - in response.headers['location'].replace('+', ' ')) - - response = client.post(client.app.url_path_for('create_new_event'), - data=TWO_WEEKS_LATER_EVENT_FORM_DATA) + assert "Same event happened 1 weeks before too. " in response.headers[ + "location" + ].replace("+", " ") + + response = client.post( + client.app.url_path_for("create_new_event"), + data=TWO_WEEKS_LATER_EVENT_FORM_DATA, + ) assert response.ok assert response.status_code == status.HTTP_302_FOUND - assert ('Same event happened 1 weeks before too. ' - in response.headers['location'].replace('+', ' ')) - assert ('Same event happened 2 weeks before too. ' - in response.headers['location'].replace('+', ' ')) + assert "Same event happened 1 weeks before too. " in response.headers[ + "location" + ].replace("+", " ") + assert "Same event happened 2 weeks before too. " in response.headers[ + "location" + ].replace("+", " ") -@pytest.mark.parametrize('data', NONE_UPDATE_OPTIONS) +@pytest.mark.parametrize("data", NONE_UPDATE_OPTIONS) def test_invalid_update(event, data, session): """ Test update existing event. @@ -272,7 +303,7 @@ def test_invalid_update(event, data, session): assert evt.update_event(event_id=event.id, event=data, db=session) is None -@pytest.mark.parametrize('data', INVALID_FIELD_UPDATE) +@pytest.mark.parametrize("data", INVALID_FIELD_UPDATE) def test_invalid_fields(event, data, session): """ Test update existing event. @@ -283,10 +314,12 @@ def test_invalid_fields(event, data, session): def test_not_check_change_dates_allowed(event): - data = {'start': '20.01.2020'} + data = {"start": "20.01.2020"} with pytest.raises(HTTPException): - assert (evt.check_change_dates_allowed( - event, data).status_code == status.HTTP_400_BAD_REQUEST) + assert ( + evt.check_change_dates_allowed(event, data).status_code + == status.HTTP_400_BAD_REQUEST + ) def test_update_event_availability(event, session): @@ -294,9 +327,15 @@ def test_update_event_availability(event, session): Test update event's availability. """ original_availability = event.availability - data = {'availability': not original_availability} - assert original_availability is not evt.update_event( - event_id=event.id, event=data, db=session).availability + data = {"availability": not original_availability} + assert ( + original_availability + is not evt.update_event( + event_id=event.id, + event=data, + db=session, + ).availability + ) def test_successful_update(event, session): @@ -304,14 +343,14 @@ def test_successful_update(event, session): Test update existing event successfully. """ data = { - 'title': 'successful', - 'start': datetime(2021, 1, 20), - 'end': datetime(2021, 1, 21), - 'availability': 'False', + "title": "successful", + "start": datetime(2021, 1, 20), + "end": datetime(2021, 1, 21), + "availability": "False", } assert isinstance(evt.update_event(1, data, session), Event) updated_event = evt.update_event(event_id=event.id, event=data, db=session) - assert 'successful' in updated_event.title + assert "successful" in updated_event.title assert updated_event.availability is False @@ -320,71 +359,85 @@ def test_update_event_with_category(today_event, category, session): Test update category for an existing event successfully. """ data = { - 'title': 'successful', - 'category_id': category.id, + "title": "successful", + "category_id": category.id, } - updated_event = evt.update_event(event_id=today_event.id, - event=data, - db=session) - assert 'successful' in updated_event.title + updated_event = evt.update_event( + event_id=today_event.id, + event=data, + db=session, + ) + assert "successful" in updated_event.title assert updated_event.category_id == category.id def test_update_db_close(event): - data = {'title': 'Problem connecting to db in func update_event', } + data = { + "title": "Problem connecting to db in func update_event", + } with pytest.raises(HTTPException): assert ( - evt.update_event(event_id=event.id, event=data, - db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + evt.update_event( + event_id=event.id, + event=data, + db=None, + ).status_code + == status.HTTP_500_INTERNAL_SERVER_ERROR ) def test_update_event_does_not_exist(event, session): - data = {'content': 'An update test for an event does not exist'} + data = {"content": "An update test for an event does not exist"} with pytest.raises(HTTPException): response = evt.update_event( event_id=status.HTTP_500_INTERNAL_SERVER_ERROR, - event=data, db=session) + event=data, + db=session, + ) assert response.status_code == status.HTTP_404_NOT_FOUND def test_db_close_update(session, event): - data = {'title': 'Problem connecting to db in func _update_event', } + data = { + "title": "Problem connecting to db in func _update_event", + } with pytest.raises(HTTPException): assert ( evt._update_event( event_id=event.id, event_to_update=data, - db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + db=None, + ).status_code + == status.HTTP_500_INTERNAL_SERVER_ERROR ) def test_repr(event): - assert event.__repr__() == f'' + assert event.__repr__() == f"" def test_no_connection_to_db_in_delete(event): with pytest.raises(HTTPException): response = evt.delete_event(event_id=1, db=None) - assert (response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR) + assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR def test_no_connection_to_db_in_internal_deletion(event): with pytest.raises(HTTPException): assert ( - evt._delete_event(event=event, db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + evt._delete_event(event=event, db=None).status_code + == status.HTTP_500_INTERNAL_SERVER_ERROR ) def test_successful_deletion(event_test_client, session, event): - response = event_test_client.delete('/event/1') + response = event_test_client.delete("/event/1") assert response.ok with pytest.raises(HTTPException): - assert 'Event ID does not exist. ID: 1' in evt.by_id( - db=session, event_id=1).content + assert ( + "Event ID does not exist. ID: 1" + in evt.by_id(db=session, event_id=1).content + ) def test_change_owner(client, event_test_client, user, session, event): @@ -392,27 +445,35 @@ def test_change_owner(client, event_test_client, user, session, event): Test change owner of an event """ event_id = event.id - event_details = [event.title, event.content, event.location, event.start, - event.end, event.color, event.category_id] - response = event_test_client.post(f'/event/{event_id}/owner', data=None) + event_details = [ + event.title, + event.content, + event.location, + event.start, + event.end, + event.color, + event.category_id, + ] + response = event_test_client.post(f"/event/{event_id}/owner", data=None) assert response.status_code == status.HTTP_302_FOUND assert response.ok - assert b'View Event' not in response.content + assert b"View Event" not in response.content for event_detail in event_details: - assert str(event_detail).encode('utf-8') not in response.content, \ - f'{event_detail} not in view event page' - data = {'username': 'worng_username'} - response = event_test_client.post(f'/event/{event_id}/owner', data=data) + assert ( + str(event_detail).encode("utf-8") not in response.content + ), f"{event_detail} not in view event page" + data = {"username": "worng_username"} + response = event_test_client.post(f"/event/{event_id}/owner", data=data) assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR - assert b'Username does not exist.' in response.content - data = {'username': user.username} - response = event_test_client.post(f'/event/{event_id}/owner', data=data) + assert b"Username does not exist." in response.content + data = {"username": user.username} + response = event_test_client.post(f"/event/{event_id}/owner", data=data) assert response.ok assert response.status_code == status.HTTP_302_FOUND def test_deleting_an_event_does_not_exist(event_test_client, event): - response = event_test_client.delete('/event/2') + response = event_test_client.delete("/event/2") assert response.status_code == status.HTTP_404_NOT_FOUND @@ -431,11 +492,15 @@ def test_can_show_event_private(event, session, user): event.privacy = PrivacyKinds.Private.name private_event = event_to_show(event=event, session=session, user=user) private_attributes = [ - private_event.title, private_event.location, private_event.content, - private_event.invitees + private_event.title, + private_event.location, + private_event.content, + private_event.invitees, ] null_attributes = [ - private_event.color, private_event.emotion, private_event.category_id + private_event.color, + private_event.emotion, + private_event.category_id, ] is_private_attributes = [ attr == event.privacy for attr in private_attributes @@ -444,12 +509,15 @@ def test_can_show_event_private(event, session, user): assert all(is_private_attributes) and all(is_null_attributes) -def test_add_comment(event_test_client: TestClient, session: Session, - event: Event) -> None: +def test_add_comment( + event_test_client: TestClient, + session: Session, + event: Event, +) -> None: assert session.query(Comment).first() is None - content = 'test comment' - path = evt.router.url_path_for('add_comment', event_id=event.id) - data = {'comment': content} + content = "test comment" + path = evt.router.url_path_for("add_comment", event_id=event.id) + data = {"comment": content} response = event_test_client.post(path, data=data, allow_redirects=True) assert response.ok assert content in response.text @@ -458,37 +526,53 @@ def test_add_comment(event_test_client: TestClient, session: Session, delete_instance(session, comment) -def test_get_event_data(session: Session, event: Event, - comment: Comment) -> None: - data = (event, [{ - 'id': 1, - 'avatar': 'profile.png', - 'username': 'test_username', - 'time': '01/01/2021 00:01', - 'content': 'test comment', - }], '%H:%M') +def test_get_event_data( + session: Session, + event: Event, + comment: Comment, +) -> None: + data = ( + event, + [ + { + "id": 1, + "avatar": "profile.png", + "username": "test_username", + "time": "01/01/2021 00:01", + "content": "test comment", + }, + ], + "%H:%M", + ) assert evt.get_event_data(session, event.id) == data -def test_view_comments(event_test_client: TestClient, event: Event, - comment: Comment) -> None: - path = evt.router.url_path_for('view_comments', event_id=event.id) +def test_view_comments( + event_test_client: TestClient, + event: Event, + comment: Comment, +) -> None: + path = evt.router.url_path_for("view_comments", event_id=event.id) response = event_test_client.get(path) assert response.ok assert comment.content in response.text -def test_delete_comment(event_test_client: TestClient, session: Session, - event: Event, comment: Comment) -> None: +def test_delete_comment( + event_test_client: TestClient, + session: Session, + event: Event, + comment: Comment, +) -> None: assert session.query(Comment).first() - path = evt.router.url_path_for('delete_comment') + path = evt.router.url_path_for("delete_comment") data = { - 'event_id': event.id, - 'comment_id': comment.id, + "event_id": event.id, + "comment_id": comment.id, } response = event_test_client.post(path, data=data, allow_redirects=True) assert response.ok - assert 'Post Comment' in response.text + assert "Post Comment" in response.text assert session.query(Comment).first() is None @@ -496,14 +580,14 @@ class TestApp: client = TestClient(app) date_test_data = [datetime.today() - timedelta(1), datetime.today()] event_test_data = { - 'title': 'Test Title', - 'location': 'Fake City', - 'vc_link': 'https://us02web.zoom.us/j/875384596', - 'start': date_test_data[0], - 'end': date_test_data[1], - 'content': 'Any Words', - 'owner_id': 123, - 'invitees': 'user1, user2' + "title": "Test Title", + "location": "Fake City", + "vc_link": "https://us02web.zoom.us/j/875384596", + "start": date_test_data[0], + "end": date_test_data[1], + "content": "Any Words", + "owner_id": 123, + "invitees": "user1, user2", } @staticmethod @@ -522,7 +606,7 @@ def check_is_date_before(): @staticmethod def test_bad_check_validation(): - assert not evt.is_date_before(TestApp.date_test_data[0], 'bad value') + assert not evt.is_date_before(TestApp.date_test_data[0], "bad value") @staticmethod def test_add_event(session: Session): @@ -531,12 +615,12 @@ def test_add_event(session: Session): @staticmethod def test_add_bad_event(session: Session): bad_event_test_data = TestApp.event_test_data - bad_event_test_data['no_colume'] = 'some data' + bad_event_test_data["no_colume"] = "some data" assert evt.add_new_event(bad_event_test_data, session) is None @staticmethod def test_add_bad_times_to_event(session: Session): bad_event_test_data = TestApp.event_test_data - bad_event_test_data['start'] = TestApp.date_test_data[1] - bad_event_test_data['end'] = TestApp.date_test_data[0] + bad_event_test_data["start"] = TestApp.date_test_data[1] + bad_event_test_data["end"] = TestApp.date_test_data[0] assert evt.add_new_event(bad_event_test_data, session) is None From e16e824328b99960975b0a1bf84f391599a9e525 Mon Sep 17 00:00:00 2001 From: imimouni Date: Sat, 20 Feb 2021 00:53:37 +0200 Subject: [PATCH 11/16] fix: per requested - use copy of Event instead of PrivateEvent object --- app/internal/privacy.py | 18 ---- app/routers/event.py | 47 ++++------ .../partials/edit_event_details_tab.html | 87 ------------------- .../event/edit_event_details_tab.html | 6 +- tests/test_event.py | 2 +- 5 files changed, 23 insertions(+), 137 deletions(-) delete mode 100644 app/templates/event/partials/edit_event_details_tab.html diff --git a/app/internal/privacy.py b/app/internal/privacy.py index 12ced09a..c90fbf87 100644 --- a/app/internal/privacy.py +++ b/app/internal/privacy.py @@ -1,25 +1,7 @@ import enum -from sqlalchemy.sql.elements import Null - class PrivacyKinds(enum.Enum): Public = 1 Private = 2 Hidden = 3 - - -class PrivateEvent: - """Represents a private event to show a non-owner of private event""" - def __init__(self, start, end, owner_id) -> None: - self.title = PrivacyKinds.Private.name - self.start = start - self.end = end - self.privacy = PrivacyKinds.Private.name - self.content = PrivacyKinds.Private.name - self.owner_id = owner_id - self.location = PrivacyKinds.Private.name - self.color = Null - self.invitees = PrivacyKinds.Private.name - self.category_id = Null - self.emotion = Null diff --git a/app/routers/event.py b/app/routers/event.py index fc5a0020..62a3d7f7 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -8,6 +8,7 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound +from sqlalchemy.sql.elements import Null from starlette import status from starlette.responses import RedirectResponse, Response from starlette.templating import _TemplateResponse @@ -22,13 +23,8 @@ ) from app.internal import comment as cmt from app.internal.emotion import get_emotion - -# TODO add this when the user system is merged (PR#195) -# from app.internal.security.dependancies import ( -# current_user, CurrentUser) -from app.internal.privacy import PrivateEvent, PrivacyKinds +from app.internal.privacy import PrivacyKinds from app.internal.utils import create_model, get_current_user -from app.routers.user import create_user EVENT_DATA = Tuple[Event, List[Dict[str, str]], str] TIME_FORMAT = "%Y-%m-%d %H:%M" @@ -87,7 +83,7 @@ async def create_event_api(event: EventModel, session=Depends(get_db)): @router.get("/edit") async def eventedit(request: Request) -> Response: return templates.TemplateResponse( - "event/eventedit.html", + "eventedit.html", {"request": request, "privacy": PrivacyKinds}, ) @@ -112,6 +108,7 @@ async def create_new_event( vc_link = data["vc_link"] category_id = data.get("category_id") + privacy = data["privacy"] privacy_kinds = [kind.name for kind in PrivacyKinds] if privacy not in privacy_kinds: privacy = PrivacyKinds.Public.name @@ -194,23 +191,10 @@ def check_event_owner( event: Event, session: Depends(get_db), user: Optional[User] = None, - # TODO after user system is merged (PR#195): - # CurrentUser = Depends(current_user) ) -> bool: - if not user: - user = session.query(User).filter_by(id=1).first() - user = ( - user - if user - else create_user( - username="u", - password="p", - email="e@mail.com", - language_id=1, - session=session, - ) - ) # TODO use current_user after user system merge + if not user: + user = get_current_user(session) is_owner = event.owner_id == user.id return is_owner @@ -221,14 +205,21 @@ def event_to_show( user: Optional[User] = None, ) -> Optional[Event]: """Check the given event's privacy and return - event/fixed event/ nothing (hidden) accordingly""" + event/fixed private event/ nothing (hidden) accordingly""" is_owner = check_event_owner(event, session, user) if event.privacy == PrivacyKinds.Private.name and not is_owner: - private_event = PrivateEvent( - start=event.start, - end=event.end, - owner_id=event.owner_id, - ) + event_dict = event.__dict__.copy() + if event_dict.get("_sa_instance_state", None): + event_dict.pop("_sa_instance_state") + event_dict.pop("id") + private_event = Event(**event_dict) + private_event.title = PrivacyKinds.Private.name + private_event.content = PrivacyKinds.Private.name + private_event.location = PrivacyKinds.Private.name + private_event.color = Null + private_event.invitees = PrivacyKinds.Private.name + private_event.category_id = Null + private_event.emotion = Null return private_event elif event.privacy == PrivacyKinds.Hidden.name and not is_owner: return diff --git a/app/templates/event/partials/edit_event_details_tab.html b/app/templates/event/partials/edit_event_details_tab.html deleted file mode 100644 index 9e4f7665..00000000 --- a/app/templates/event/partials/edit_event_details_tab.html +++ /dev/null @@ -1,87 +0,0 @@ -
- - -
- -
- - -
-
-
-
- - - -
-
- - - -
-
- - - -
-
-
- - - - -
-
- - - - -
-
- - - -
-
- -
-
- - -
-
-
- - -
-
- - - - - - - -
-
diff --git a/app/templates/partials/calendar/event/edit_event_details_tab.html b/app/templates/partials/calendar/event/edit_event_details_tab.html index 4e27dadf..2a331382 100644 --- a/app/templates/partials/calendar/event/edit_event_details_tab.html +++ b/app/templates/partials/calendar/event/edit_event_details_tab.html @@ -67,9 +67,9 @@
- diff --git a/tests/test_event.py b/tests/test_event.py index de556ffd..81f0aae2 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -540,7 +540,7 @@ def test_can_show_event_hidden(event, session, user): def test_can_show_event_private(event, session, user): event.privacy = PrivacyKinds.Private.name - private_event = event_to_show(event=event, session=session, user=user) + private_event = event_to_show(event, session, user) private_attributes = [ private_event.title, private_event.location, From 9cb0c065291f26f32f6c540c9a4e909ba08dc37f Mon Sep 17 00:00:00 2001 From: imimouni Date: Sat, 20 Feb 2021 01:39:08 +0200 Subject: [PATCH 12/16] fix: pytest order of attributes create_event --- app/routers/event.py | 8 +-- app/templates/profile.html | 6 +- tests/test_emotion.py | 128 +++++++++++++++++++++++++------------ 3 files changed, 95 insertions(+), 47 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 62a3d7f7..b1b83bce 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -113,7 +113,7 @@ async def create_new_event( if privacy not in privacy_kinds: privacy = PrivacyKinds.Public.name is_google_event = data.get("is_google_event", "True") == "True" - + print(is_google_event) invited_emails = get_invited_emails(data["invited"]) uninvited_contacts = get_uninvited_regular_emails( session, @@ -130,9 +130,8 @@ async def create_new_event( title=title, start=start, end=end, - owner_id=owner_id, - privacy=privacy, all_day=all_day, + owner_id=owner_id, content=content, location=location, vc_link=vc_link, @@ -140,6 +139,7 @@ async def create_new_event( category_id=category_id, availability=availability, is_google_event=is_google_event, + privacy=privacy, ) messages = get_messages(session, event, uninvited_contacts) @@ -377,7 +377,6 @@ def create_event( start, end, owner_id: int, - privacy: str = PrivacyKinds.Public.name, all_day: bool = False, content: Optional[str] = None, location: Optional[str] = None, @@ -387,6 +386,7 @@ def create_event( category_id: Optional[int] = None, availability: bool = True, is_google_event: bool = False, + privacy: str = PrivacyKinds.Public.name, ): """Creates an event and an association.""" diff --git a/app/templates/profile.html b/app/templates/profile.html index 929cc53c..f7aed062 100644 --- a/app/templates/profile.html +++ b/app/templates/profile.html @@ -177,8 +177,8 @@ -{% include "partials/calendar/event/text_editor_partial_body.html" %} {% endblock content %} \ No newline at end of file +{% include "partials/calendar/event/text_editor_partial_body.html" %} {% endblock content %} diff --git a/tests/test_emotion.py b/tests/test_emotion.py index f16c88d8..644dfe14 100644 --- a/tests/test_emotion.py +++ b/tests/test_emotion.py @@ -7,7 +7,8 @@ is_emotion_above_significance, get_dominant_emotion, get_emotion, - get_html_emoticon) + get_html_emoticon, +) from app.routers.event import create_event @@ -20,47 +21,71 @@ emotion_tests = [ - (HAPPY_MESSAGE, HAPPY_MESSAGE, Emoticon('Happy', 1.0, '😃')), - (SAD_MESSAGE, SAD_MESSAGE, Emoticon('Sad', 1.0, '🙁')), - (ANGRY_MESSAGE, ANGRY_MESSAGE, Emoticon('Angry', 1.0, '😠')), - (FEAR_MESSAGE, FEAR_MESSAGE, Emoticon('Fear', 1.0, '😱')), - (SURPRISE_MESSAGE, SURPRISE_MESSAGE, - Emoticon('Surprise', 1.0, '😮')), - (SURPRISE_MESSAGE, None, Emoticon('Surprise', 1.0, '😮')), - (SURPRISE_MESSAGE, "", Emoticon('Surprise', 1.0, '😮')), - (HAPPY_MESSAGE, SAD_MESSAGE, Emoticon('Happy', 0.6, '😃')), + (HAPPY_MESSAGE, HAPPY_MESSAGE, Emoticon("Happy", 1.0, "😃")), + (SAD_MESSAGE, SAD_MESSAGE, Emoticon("Sad", 1.0, "🙁")), + (ANGRY_MESSAGE, ANGRY_MESSAGE, Emoticon("Angry", 1.0, "😠")), + (FEAR_MESSAGE, FEAR_MESSAGE, Emoticon("Fear", 1.0, "😱")), + ( + SURPRISE_MESSAGE, + SURPRISE_MESSAGE, + Emoticon("Surprise", 1.0, "😮"), + ), + (SURPRISE_MESSAGE, None, Emoticon("Surprise", 1.0, "😮")), + (SURPRISE_MESSAGE, "", Emoticon("Surprise", 1.0, "😮")), + (HAPPY_MESSAGE, SAD_MESSAGE, Emoticon("Happy", 0.6, "😃")), ] emotion_significance_tests = [ - (Emoticon('Happy', 1.0, '😃'), None, True), - (Emoticon('Happy', 0.6, '😃'), None, True), - (Emoticon('Happy', 0.4, '😃'), None, False), - (Emoticon('Happy', 0.4, '😃'), 0.3, True), - (Emoticon('Happy', 0.7, '😃'), 0.8, False) + (Emoticon("Happy", 1.0, "😃"), None, True), + (Emoticon("Happy", 0.6, "😃"), None, True), + (Emoticon("Happy", 0.4, "😃"), None, False), + (Emoticon("Happy", 0.4, "😃"), 0.3, True), + (Emoticon("Happy", 0.7, "😃"), 0.8, False), ] get_html_emoticon_tests = [ - (Emoticon('Happy', 1.0, '😃'), '😃'), - (Emoticon('Happy', 1.0, None), None) + (Emoticon("Happy", 1.0, "😃"), "😃"), + (Emoticon("Happy", 1.0, None), None), ] get_emotion_tests = [ - Emoticon(HAPPY_MESSAGE, HAPPY_MESSAGE, '😃'), - Emoticon(SAD_MESSAGE, SAD_MESSAGE, '🙁'), - Emoticon(HAPPY_MESSAGE, SAD_MESSAGE, '😃'), - Emoticon(" ", " ", None) + Emoticon(HAPPY_MESSAGE, HAPPY_MESSAGE, "😃"), + Emoticon(SAD_MESSAGE, SAD_MESSAGE, "🙁"), + Emoticon(HAPPY_MESSAGE, SAD_MESSAGE, "😃"), + Emoticon(" ", " ", None), ] create_event_tests = [ - (HAPPY_MESSAGE, datetime.datetime(2019, 5, 21, 0, 0), - datetime.datetime(2019, 5, 22, 0, 0), False, 1, HAPPY_MESSAGE, "location", - "😃"), - (SAD_MESSAGE, datetime.datetime(2019, 5, 21, 0, 0), - datetime.datetime(2019, 5, 22, 0, 0), False, 1, HAPPY_MESSAGE, "location", - "🙁"), - (" ", datetime.datetime(2019, 5, 21, 0, 0), - datetime.datetime(2019, 5, 22, 0, 0), False, 1, " ", "location", - None) + ( + HAPPY_MESSAGE, + datetime.datetime(2019, 5, 21, 0, 0), + datetime.datetime(2019, 5, 22, 0, 0), + False, + 1, + HAPPY_MESSAGE, + "location", + "😃", + ), + ( + SAD_MESSAGE, + datetime.datetime(2019, 5, 21, 0, 0), + datetime.datetime(2019, 5, 22, 0, 0), + False, + 1, + HAPPY_MESSAGE, + "location", + "🙁", + ), + ( + " ", + datetime.datetime(2019, 5, 21, 0, 0), + datetime.datetime(2019, 5, 22, 0, 0), + False, + 1, + " ", + "location", + None, + ), ] @@ -69,14 +94,18 @@ def test_dominant_emotion(title, content, result): assert get_dominant_emotion(title, content) == result -@pytest.mark.parametrize("dominant_emotion, significance, result", - emotion_significance_tests) +@pytest.mark.parametrize( + "dominant_emotion, significance, result", + emotion_significance_tests, +) def test_is_emotion_above_significance(dominant_emotion, significance, result): if significance is None: assert is_emotion_above_significance(dominant_emotion) == result else: - assert is_emotion_above_significance(dominant_emotion, - significance) == result + assert ( + is_emotion_above_significance(dominant_emotion, significance) + == result + ) @pytest.mark.parametrize("dominant_emotion, result", get_html_emoticon_tests) @@ -89,10 +118,29 @@ def test_get_emotion(title, content, result): assert get_emotion(title, content) == result -@pytest.mark.parametrize("title, start, end, all_day, owner_id, content, " + - "location, result", create_event_tests) -def test_create_event(title, start, end, all_day, - owner_id, content, location, result, session): - event = create_event(session, title, start, - end, all_day, owner_id, content, location) +@pytest.mark.parametrize( + "title, start, end, all_day, owner_id, content, " + "location, result", + create_event_tests, +) +def test_create_event( + title, + start, + end, + all_day, + owner_id, + content, + location, + result, + session, +): + event = create_event( + session, + title, + start, + end, + owner_id, + all_day, + content, + location, + ) assert event.emotion == result From d1b41d916a85ab00832ed6d46dbec499707054ac Mon Sep 17 00:00:00 2001 From: imimouni Date: Sat, 20 Feb 2021 10:17:41 +0200 Subject: [PATCH 13/16] fix: remove print --- app/routers/event.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/routers/event.py b/app/routers/event.py index b1b83bce..9a5117fc 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -113,7 +113,6 @@ async def create_new_event( if privacy not in privacy_kinds: privacy = PrivacyKinds.Public.name is_google_event = data.get("is_google_event", "True") == "True" - print(is_google_event) invited_emails = get_invited_emails(data["invited"]) uninvited_contacts = get_uninvited_regular_emails( session, From 86b12ce0d5c68532a9d50dee23f4707d6d58fcf2 Mon Sep 17 00:00:00 2001 From: imimouni <71105447+imimouni@users.noreply.github.com> Date: Sat, 20 Feb 2021 13:50:31 +0200 Subject: [PATCH 14/16] Update event.py nonexisting_event -> raise_for_non_existing_event --- app/routers/event.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 9a5117fc..e6468f67 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -149,7 +149,7 @@ async def create_new_event( ) -def nonexisting_event(event_id: int) -> None: +def raise_for_nonexisting_event(event_id: int) -> None: error_message = f"Event ID does not exist. ID: {event_id}" logger.exception(error_message) raise HTTPException( @@ -171,7 +171,7 @@ async def eventview( end_format = "" event_considering_privacy = event_to_show(event, db) if not event_considering_privacy: - nonexisting_event(event.id) + raise_for_nonexisting_event(event.id) messages = request.query_params.get("messages", "").split("---") return templates.TemplateResponse( "eventview.html", @@ -273,7 +273,7 @@ def by_id(db: Session, event_id: int) -> Event: try: event = db.query(Event).filter_by(id=event_id).one() except NoResultFound: - nonexisting_event(event_id) + raise_for_nonexisting_event(event_id) except MultipleResultsFound: error_message = ( f"Multiple results found when getting event. Expected only one. " From 8a97b192a7470f229e70486ab6a3f0c82d27156e Mon Sep 17 00:00:00 2001 From: imimouni Date: Sat, 20 Feb 2021 14:12:08 +0200 Subject: [PATCH 15/16] fix: merge-calendar privacy privacykinds --- .../profile_card/modals/calendar_privacy_modal.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/partials/user_profile/sidebar_left/profile_card/modals/calendar_privacy_modal.html b/app/templates/partials/user_profile/sidebar_left/profile_card/modals/calendar_privacy_modal.html index b56b1572..0a2a7e7c 100644 --- a/app/templates/partials/user_profile/sidebar_left/profile_card/modals/calendar_privacy_modal.html +++ b/app/templates/partials/user_profile/sidebar_left/profile_card/modals/calendar_privacy_modal.html @@ -7,10 +7,10 @@ - \ No newline at end of file + From 29c517358a03b6d995fe6476672d0a9dc65b212c Mon Sep 17 00:00:00 2001 From: imimouni Date: Sat, 20 Feb 2021 14:17:10 +0200 Subject: [PATCH 16/16] fix indentation event.py --- app/routers/event.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 6d582a32..a8e290bc 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -83,14 +83,20 @@ async def create_event_api(event: EventModel, session=Depends(get_db)): @router.get("/edit", include_in_schema=False) @router.get("/edit") -async def eventedit(request: Request, - db_session: Session = Depends(get_db)) -> Response: - user_id = 1 # until issue#29 will get current user_id from session +async def eventedit( + request: Request, + db_session: Session = Depends(get_db), +) -> Response: + user_id = 1 # until issue#29 will get current user_id from session categories_list = get_user_categories(db_session, user_id) - return templates.TemplateResponse("eventedit.html", - {"request": request, - "categories_list": categories_list, - "privacy": PrivacyKinds}) + return templates.TemplateResponse( + "eventedit.html", + { + "request": request, + "categories_list": categories_list, + "privacy": PrivacyKinds, + }, + ) @router.post("/edit", include_in_schema=False)