diff --git a/.gitignore b/.gitignore index 47d0085a..4b1dbcd8 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,5 @@ app/.vscode/ # PyCharm .idea + +junit/ diff --git a/app/routers/agenda.py b/app/routers/agenda.py index 35a032e0..b65c6b3b 100644 --- a/app/routers/agenda.py +++ b/app/routers/agenda.py @@ -17,7 +17,7 @@ def calc_dates_range_for_agenda( start: Optional[date], end: Optional[date], days: Optional[int], - ) -> Tuple[date, date]: +) -> Tuple[date, date]: """Create start and end dates according to the parameters in the page.""" if days is not None: start = date.today() @@ -35,17 +35,17 @@ def agenda( start_date: Optional[date] = None, end_date: Optional[date] = None, days: Optional[int] = None, - ) -> _TemplateResponse: +) -> _TemplateResponse: """Route for the agenda page, using dates range or exact amount of days.""" - user_id = 1 # there is no user session yet, so I use user id- 1. + user_id = 1 # there is no user session yet, so I use user id- 1. start_date, end_date = calc_dates_range_for_agenda( start_date, end_date, days - ) + ) events_objects = agenda_events.get_events_per_dates( db, user_id, start_date, end_date - ) + ) events = defaultdict(list) for event_obj in events_objects: event_duration = agenda_events.get_time_delta_string( diff --git a/app/routers/calendar_grid.py b/app/routers/calendar_grid.py index d8ceba23..dd3ef7f7 100644 --- a/app/routers/calendar_grid.py +++ b/app/routers/calendar_grid.py @@ -29,7 +29,7 @@ def __init__(self, date: datetime): self.date: datetime = date self.sday: str = self.date.strftime("%A") self.dailyevents: List[Tuple] = [] - self.events: List[Tuple] = [] + self.events: List[Tuple] = [] self.css: Dict[str, str] = { 'day_container': 'day', 'date': 'day-number', @@ -79,7 +79,7 @@ def __init__(self, date: datetime): super().__init__(date) self.css = { 'day_container': 'day ', - 'date': ' '.join(['day-number', 'text-gray']), + 'date': ' '.join(['day-number', 'text-gray']), 'daily_event': 'month-event', 'daily_event_front': ' '.join([ 'daily', @@ -100,7 +100,7 @@ class Today(Day): def __init__(self, date: datetime): super().__init__(date) self.css = { - 'day_container': ' '.join([ + 'day_container': ' '.join([ 'day', 'text-darkblue', 'background-yellow' @@ -134,7 +134,7 @@ def __init__(self, date: datetime): ]), 'date': 'day-number', 'daily_event': 'month-event', - 'daily_event_front': ' '.join([ + 'daily_event_front': ' '.join([ 'daily front', 'text-lightgray', 'background-red' @@ -196,8 +196,8 @@ def get_n_days(date: datetime, n: int) -> Iterator[Day]: def create_weeks( - days: Iterator[Day], - length: int = Week.WEEK_DAYS + days: Iterator[Day], + length: int = Week.WEEK_DAYS ) -> List[Week]: """Return lists of Weeks objects.""" ndays: List[Day] = list(days) diff --git a/app/routers/categories.py b/app/routers/categories.py index 322b02ca..0bc9eaf5 100644 --- a/app/routers/categories.py +++ b/app/routers/categories.py @@ -21,6 +21,15 @@ class CategoryModel(BaseModel): color: str user_id: int + class Config: + schema_extra = { + "example": { + "name": "Guitar lessons", + "color": "#aabbcc", + "user_id": 1, + } + } + # TODO(issue#29): get current user_id from session @router.get("/") diff --git a/app/routers/event.py b/app/routers/event.py index fd460583..85566593 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -40,17 +40,20 @@ async def create_new_event(request: Request, session=Depends(get_db)): end = datetime.strptime(data['end_date'] + ' ' + data['end_time'], '%Y-%m-%d %H:%M') user = session.query(User).filter_by(id=1).first() - user = user if user else create_user("u", "p", "e@mail.com", session) + user = user if user else create_user(username="u", password="p", + email="e@mail.com", language="", + session=session) owner_id = user.id location_type = data['location_type'] is_zoom = location_type == 'vc_url' location = data['location'] + category_id = data.get('category_id') if is_zoom: validate_zoom_link(location) event = create_event(session, title, start, end, owner_id, content, - location) + location, category_id=category_id) return RedirectResponse(router.url_path_for('eventview', event_id=event.id), status_code=status.HTTP_302_FOUND) @@ -74,7 +77,8 @@ async def eventview(request: Request, event_id: int, 'start': datetime, 'end': datetime, 'content': (str, type(None)), - 'location': (str, type(None)) + 'location': (str, type(None)), + 'category_id': (int, type(None)) } @@ -109,14 +113,13 @@ def by_id(db: Session, event_id: int) -> Event: def is_end_date_before_start_date( - start_date: datetime, end_date: datetime) -> bool: + start_date: datetime, end_date: datetime) -> bool: """Check if the start date is earlier than the end date""" return start_date > end_date -def check_change_dates_allowed( - old_event: Event, event: Dict[str, Any]): +def check_change_dates_allowed(old_event: Event, event: Dict[str, Any]): allowed = 1 try: start_date = event.get('start', old_event.start) @@ -127,8 +130,8 @@ def check_change_dates_allowed( allowed = 0 if allowed == 0: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Invalid times") + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid times") def is_fields_types_valid(to_check: Dict[str, Any], types: Dict[str, Any]): @@ -163,8 +166,8 @@ def _update_event(db: Session, event_id: int, event_to_update: Dict) -> Event: except (AttributeError, SQLAlchemyError) as e: logger.exception(str(e)) raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Internal server error") + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Internal server error") def update_event(event_id: int, event: Dict, db: Session @@ -181,7 +184,10 @@ def update_event(event_id: int, event: Dict, db: Session return event_updated -def create_event(db, title, start, end, owner_id, content=None, location=None): +def create_event(db: Session, title: str, start, end, owner_id: int, + content: str = None, + location: str = None, + category_id: int = None): """Creates an event and an association.""" event = create_model( @@ -192,6 +198,7 @@ def create_event(db, title, start, end, owner_id, content=None, location=None): content=content, owner_id=owner_id, location=location, + category_id=category_id, ) create_model( db, UserEvent, @@ -240,7 +247,6 @@ def _delete_event(db: Session, event: Event): @router.delete("/{event_id}") def delete_event(event_id: int, db: Session = Depends(get_db)): - # TODO: Check if the user is the owner of the event. event = by_id(db, event_id) participants = get_participants_emails_by_event(db, event_id) diff --git a/app/templates/agenda.html b/app/templates/agenda.html index 70563c60..bc1ac6a3 100644 --- a/app/templates/agenda.html +++ b/app/templates/agenda.html @@ -7,11 +7,11 @@ {% block content %}
diff --git a/schema.md b/schema.md index 852870e6..669162c6 100644 --- a/schema.md +++ b/schema.md @@ -31,7 +31,6 @@ │ ├── agenda_style.css │ ├── popover.js │ ├── style.css -│ ├── popover.js │ ├── templates │ ├── base.html │ ├── home.html diff --git a/tests/category_fixture.py b/tests/category_fixture.py index 08cc6b97..fb6c5b0c 100644 --- a/tests/category_fixture.py +++ b/tests/category_fixture.py @@ -5,9 +5,9 @@ @pytest.fixture -def category(session: Session, user: User) -> Category: +def category(session: Session, sender: User) -> Category: category = Category.create(session, name="Guitar Lesson", color="121212", - user_id=user.id) + user_id=sender.id) yield category session.delete(category) session.commit() diff --git a/tests/event_fixture.py b/tests/event_fixture.py index 83e1e759..fbf83ec0 100644 --- a/tests/event_fixture.py +++ b/tests/event_fixture.py @@ -3,14 +3,14 @@ import pytest from sqlalchemy.orm import Session -from app.database.models import Event, User +from app.database.models import Event, User, Category from app.routers.event import create_event today_date = datetime.today().replace(hour=0, minute=0, second=0) @pytest.fixture -def event(sender: User, session: Session) -> Event: +def event(sender: User, category: Category, session: Session) -> Event: return create_event( db=session, title='event', @@ -19,6 +19,7 @@ def event(sender: User, session: Session) -> Event: content='test event', owner_id=sender.id, location="Some random location", + category_id=category.id, ) diff --git a/tests/test_agenda_route.py b/tests/test_agenda_route.py index c877a4e8..1bd6682b 100644 --- a/tests/test_agenda_route.py +++ b/tests/test_agenda_route.py @@ -27,7 +27,7 @@ def test_agenda_page_no_arguments_when_today_events_exist( next_month_event, old_event ): resp = agenda_test_client.get(TestAgenda.AGENDA) - assert resp.status_code == status.HTTP_200_OK + assert resp.ok assert b"event 1" in resp.content assert b"event 2" in resp.content assert b"event 3" not in resp.content diff --git a/tests/test_categories.py b/tests/test_categories.py index c3c7e3d9..685ea934 100644 --- a/tests/test_categories.py +++ b/tests/test_categories.py @@ -27,8 +27,8 @@ def test_creating_new_category(client, user): set(response.json()['category'].items())) @staticmethod - def test_creating_not_unique_category_failed(client, user, category): - response = client.post("/categories/", json={"user_id": user.id, + def test_creating_not_unique_category_failed(client, sender, category): + response = client.post("/categories/", json={"user_id": sender.id, "name": "Guitar Lesson", "color": "121212"}) assert response.status_code == status.HTTP_400_BAD_REQUEST @@ -42,6 +42,13 @@ def test_create_event_with_category(category): assert event.category_id is not None assert event.category_id == category.id + @staticmethod + def test_update_event_with_category(today_event, category): + assert today_event.category_id is None + today_event.category_id = category.id + assert today_event.category_id is not None + assert today_event.category_id == category.id + @staticmethod def test_get_user_categories(client, category): response = client.get(f"/categories/?user_id={category.user_id}" @@ -52,7 +59,7 @@ def test_get_user_categories(client, category): ("name", "Guitar Lesson"), ("id", category.id)} @staticmethod - def test_get_category_by_name(client, user, category): + def test_get_category_by_name(client, sender, category): response = client.get(f"/categories/?user_id={category.user_id}" f"&name={category.name}") assert response.ok @@ -61,7 +68,7 @@ def test_get_category_by_name(client, user, category): ("name", "Guitar Lesson"), ("id", category.id)} @staticmethod - def test_get_category_by_color(client, user, category): + def test_get_category_by_color(client, sender, category): response = client.get(f"/categories/?user_id={category.user_id}&" f"color={category.color}") assert response.ok diff --git a/tests/test_event.py b/tests/test_event.py index aea49255..be9adb9d 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -57,7 +57,7 @@ def test_eventedit(event_test_client): def test_eventview_with_id(event_test_client, session, event): event_id = event.id event_details = [event.title, event.content, event.location, event.start, - event.end, event.color] + event.end, event.color, event.category_id] response = event_test_client.get(f"/event/{event_id}") assert response.ok assert b"View Event" in response.content @@ -67,6 +67,9 @@ def test_eventview_with_id(event_test_client, session, event): 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) assert response.ok @@ -75,7 +78,23 @@ def test_eventedit_post_correct(client, user): 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}} + + 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']) + + 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' @@ -83,12 +102,17 @@ def test_eventedit_post_wrong(client, user): @pytest.mark.parametrize("data", NONE_UPDATE_OPTIONS) def test_invalid_update(event, data, session): - assert update_event(event_id=event.id, - event=data, db=session) is None + """ + Test update existing event. + """ + assert update_event(event_id=event.id, event=data, db=session) is None @pytest.mark.parametrize("data", INVALID_FIELD_UPDATE) def test_invalid_fields(event, data, session): + """ + Test update existing event. + """ with pytest.raises(HTTPException): response = update_event(event_id=event.id, event=data, db=session) assert response.status_code == status.HTTP_400_BAD_REQUEST @@ -98,12 +122,15 @@ def test_not_check_change_dates_allowed(event): data = {"start": "20.01.2020"} with pytest.raises(HTTPException): assert ( - check_change_dates_allowed(event, data).status_code == - status.HTTP_400_BAD_REQUEST + check_change_dates_allowed(event, data).status_code == + status.HTTP_400_BAD_REQUEST ) def test_successful_update(event, session): + """ + Test update existing event successfully. + """ data = { "title": "successful", "start": datetime(2021, 1, 20), @@ -114,12 +141,27 @@ def test_successful_update(event, session): event_id=event.id, event=data, db=session).title +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, + } + updated_event = 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", } with pytest.raises(HTTPException): assert ( - update_event(event_id=event.id, event=data, db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + update_event(event_id=event.id, event=data, + db=None).status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR ) @@ -137,11 +179,11 @@ def test_db_close_update(session, event): data = {"title": "Problem connecting to db in func _update_event", } with pytest.raises(HTTPException): assert ( - _update_event( - event_id=event.id, - event_to_update=data, - db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + _update_event( + event_id=event.id, + event_to_update=data, + db=None).status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR ) @@ -153,16 +195,16 @@ def test_no_connection_to_db_in_delete(event): with pytest.raises(HTTPException): response = delete_event(event_id=1, db=None) assert ( - response.status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + response.status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR ) def test_no_connection_to_db_in_internal_deletion(event): with pytest.raises(HTTPException): assert ( - _delete_event(event=event, db=None).status_code == - status.HTTP_500_INTERNAL_SERVER_ERROR + _delete_event(event=event, db=None).status_code == + status.HTTP_500_INTERNAL_SERVER_ERROR )