diff --git a/app/database/models.py b/app/database/models.py index 4dec81d0..92ef9746 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -1,11 +1,14 @@ from __future__ import annotations +import calendar + from datetime import datetime from typing import Any, Dict from sqlalchemy import ( Boolean, Column, + Date, DateTime, DDL, event, @@ -74,6 +77,14 @@ class User(Base): uselist=False, ) + weekly_tasks = relationship( + "WeeklyTask", + cascade="all, delete", + back_populates="owner", + ) + + tasks = relationship("Task", cascade="all, delete", back_populates="owner") + def __repr__(self): return f"" @@ -262,6 +273,57 @@ class OAuthCredentials(Base): owner = relationship("User", back_populates=__tablename__, uselist=False) +class Task(Base): + __tablename__ = "tasks" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String) + description = Column(String) + is_done = Column(Boolean, default=False) + is_important = Column(Boolean, nullable=False) + date = Column(Date, nullable=False) + time = Column(Time, nullable=False) + owner_id = Column(Integer, ForeignKey("users.id")) + + owner = relationship("User", back_populates="tasks") + + +class WeeklyTask(Base): + __tablename__ = "weekly_tasks" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String) + days = Column(String, nullable=False) + content = Column(String) + is_important = Column(Boolean, nullable=False) + task_time = Column(String, nullable=False) + + user_id = Column(Integer, ForeignKey("users.id")) + owner = relationship("User", back_populates=__tablename__) + + def set_days(self, days: str): + """Gets days represented by names + and sets them in the days field""" + atomic_days = [] + days_list = days.split(", ") + day_abbr = list(calendar.day_abbr) + for day in days_list: + day_num = day_abbr.index(day) + atomic_days.append(str(day_num)) + self.days = "".join(atomic_days) + + def get_days(self) -> str: + """return days represented by names""" + days = [] + atomic_days = self.days + day_abbr = list(calendar.day_abbr) + for day_num in atomic_days: + day_num = int(day_num) + day_name = day_abbr[day_num] + days.append(day_name) + return ", ".join(days) + + class SalarySettings(Base): # Code revision required after categories feature is added # Code revision required after holiday times feature is added diff --git a/app/internal/weekly_tasks.py b/app/internal/weekly_tasks.py new file mode 100644 index 00000000..f27fb5d5 --- /dev/null +++ b/app/internal/weekly_tasks.py @@ -0,0 +1,177 @@ +from datetime import date, datetime, time +from typing import Iterator, Optional + +from app.internal.security.schema import CurrentUser +from app.database.models import User, Task, WeeklyTask +from sqlalchemy.orm.session import Session + + +def check_inputs(days: str, task_time: time, title: str) -> bool: + """Checks inputs, used by the weekly_task_from_input function""" + return days and task_time and title + + +def weekly_task_from_input( + user: User, + title: Optional[str], + days: str, + content: Optional[str], + task_time: Optional[time], + is_important: bool, + weekly_task_id: int = 0, +) -> WeeklyTask: + """This function is being used to make a Weekly Task model + from the inputs. + + Args: + user (User): The user who wants to make or edit a Weekly Task. + title (str): Title of the Weekly Task. + days (str): Return days of the Weekly Task. + content (str): Content of the Weekly Task. + task_time (time): Return time of the Weekly Task. + is_important (bool): If the task is important. + weekly_task_id (int): The id of the weekly task, zero if not mentioned. + + Returns: + WeeklyTask: the model WeeklyTask which the function managed to make. + """ + if isinstance(user, CurrentUser): + user_id = user.user_id + else: + user_id = user.id + weekly_task = WeeklyTask( + title=title, + content=content, + is_important=is_important, + user_id=user_id, + ) + + if weekly_task_id != 0: + weekly_task.id = weekly_task_id + + inputs_ok = check_inputs(days, task_time, title) + if not inputs_ok: + return weekly_task + weekly_task.set_days(days) + weekly_task.task_time = task_time.strftime("%H:%M") + return weekly_task + + +def create_weekly_task( + weekly_task: WeeklyTask, + session: Session, +) -> bool: + """This function is being used to add a Weekly Task to the user. + + Args: + user (User): The user who wants to add the Weekly Task. + session (Session): The session to redirect to the database. + weekly_task (WeeklyTask): The Weekly Task that the user will add. + + Returns: + bool: Shows if the weekly_task has been added to the db. + """ + inputs_ok = check_inputs( + weekly_task.days, + weekly_task.task_time, + weekly_task.title, + ) + if not inputs_ok: + return False + session.add(weekly_task) + session.commit() + return True + + +def change_weekly_task( + user: User, + weekly_task: WeeklyTask, + session: Session, +) -> bool: + """This function is being used to edit a Weekly Task the user have. + + Args: + user (User): The user who wants to edit the Weekly Task. + session (Session): The session to redirect to the database. + weekly_task (WeeklyTask): The Weekly Task that the of the user, + with the edited values. + + Returns: + bool: Shows if the weekly_task has been edited in the db. + """ + inputs_ok = check_inputs( + weekly_task.days, + weekly_task.task_time, + weekly_task.title, + ) + if not inputs_ok: + return False + w_task_query = session.query(WeeklyTask) + old_weekly_task = w_task_query.filter_by(id=weekly_task.id).first() + + if weekly_task.user_id != user.id: + return False + + old_weekly_task.title = weekly_task.title + old_weekly_task.days = weekly_task.days + old_weekly_task.content = weekly_task.content + old_weekly_task.is_important = weekly_task.is_important + old_weekly_task.task_time = weekly_task.task_time + session.commit() + return True + + +def create_task(task: Task, user: User, session: Session) -> bool: + """Make a task, used by the generate_tasks function""" + user_tasks_query = session.query(Task).filter_by(owner_id=user.id) + task_by_time = user_tasks_query.filter_by(time=task.time) + task_by_date_time = task_by_time.filter_by(date=task.date) + task_by_title_and_time = task_by_date_time.filter_by(title=task.title) + task_exist = task_by_title_and_time.first() + if task_exist: + return False + session.add(task) + session.commit() + return True + + +def get_datetime(day: str, task_time: str) -> datetime: + """Getting the datetime of days in the current week, + used by the generate_tasks function""" + current_date = date.today() + current_week_num = current_date.strftime("%W") + current_year = current_date.strftime("%Y") + date_string = f"{day} {task_time} {current_week_num} {current_year}" + return datetime.strptime(date_string, "%a %H:%M %W %Y") + + +def generate_tasks(user: User, session: Session) -> Iterator[bool]: + """Generates tasks for the week + based on all the weekly tasks the user have""" + for weekly_task in user.weekly_tasks: + task_time = weekly_task.task_time + days = weekly_task.get_days() + days_list = days.split(", ") + for day in days_list: + date_time = get_datetime(day, task_time) + task = Task( + title=weekly_task.title, + description=weekly_task.content, + is_done=False, + is_important=weekly_task.is_important, + date=date_time.date(), + time=date_time.time(), + owner_id=user.id, + ) + yield create_task(task, user, session) + + +def remove_weekly_task(weekly_task_id: int, session: Session) -> bool: + """Removes a weekly task from the db based on the weekly task id""" + weekly_task_query = session.query(WeeklyTask) + weekly_task = weekly_task_query.filter_by(id=weekly_task_id).first() + if not weekly_task: + return False + session.query(WeeklyTask).filter_by(id=weekly_task_id).delete() + session.commit() + return True diff --git a/app/main.py b/app/main.py index 3560d142..9a0ee412 100644 --- a/app/main.py +++ b/app/main.py @@ -73,6 +73,7 @@ def create_tables(engine, psql_environment): search, telegram, user, + weekly_tasks, weekview, weight, whatsapp, @@ -123,6 +124,7 @@ async def swagger_ui_redirect(): salary.router, search.router, telegram.router, + weekly_tasks.router, user.router, weekview.router, weight.router, diff --git a/app/routers/weekly_tasks.py b/app/routers/weekly_tasks.py new file mode 100644 index 00000000..357db43c --- /dev/null +++ b/app/routers/weekly_tasks.py @@ -0,0 +1,256 @@ +import calendar +import datetime + +from typing import List, Tuple, Optional +from fastapi import APIRouter, Cookie, Depends, Request, Response, Form +from sqlalchemy.orm.session import Session +from starlette.responses import RedirectResponse, HTMLResponse +from starlette.status import HTTP_302_FOUND, HTTP_303_SEE_OTHER + +from app.database.models import WeeklyTask +from app.dependencies import get_db, templates +from app.internal.security.dependencies import ( + current_user, + schema, + is_logged_in, + current_user_from_db, +) +from app.internal.weekly_tasks import ( + remove_weekly_task, + weekly_task_from_input, + create_weekly_task, + change_weekly_task, +) + + +router = APIRouter( + prefix="/weekly-tasks", + tags=["weekly-tasks"], + responses={404: {"description": "Not found"}}, + dependencies=[Depends(get_db)], +) + + +def get_checked_days(days: str = "") -> List[Tuple[str, str, str]]: + """Produces the input checked_days for the template add_edit_weekly_task""" + days_list = days.split(", ") + checked_days = [] + for day_full_name in calendar.day_name: + day = day_full_name[:3] + day_lower = day.lower() + if day in days_list: + checked_days.append((day_full_name, day_lower, "checked")) + else: + checked_days.append((day_full_name, day_lower, "")) + return checked_days + + +def get_days_string( + sun: bool, + mon: bool, + tue: bool, + wed: bool, + thu: bool, + fri: bool, + sat: bool, +) -> str: + """Produces a string of all the days that were checked, + For use in the model weekly tasks.""" + days_dict = { + "Mon": mon, + "Tue": tue, + "Wed": wed, + "Thu": thu, + "Fri": fri, + "Sat": sat, + "Sun": sun, + } + days_list = [day for day, is_checked in days_dict.items() if is_checked] + days = ", ".join(days_list) + return days + + +@router.get("/", include_in_schema=False) +async def weekly_tasks_manager( + request: Request, + user: schema.CurrentUser = Depends(current_user_from_db), +): + + # TODO: Move the below function to a compatible location + # Need to run regularly whenever there are tasks on the week + # Or will run on the background after the user left the + # weekly-tasks manager page + # function: + # generate_tasks(user, session) # imported from app.internal.weekly_tasks + # session.close() + + return templates.TemplateResponse( + "weekly_tasks_manager.html", + {"request": request, "weekly_tasks": user.weekly_tasks}, + ) + + +@router.get("/add", dependencies=[Depends(is_logged_in)]) +def add_weekly_task(request: Request): + + checked_days = get_checked_days() + return templates.TemplateResponse( + "add_edit_weekly_task.html", + { + "request": request, + "weekly_task": None, + "mode": "add", + "checked_days": checked_days, + }, + ) + + +@router.delete("/", dependencies=[Depends(is_logged_in)]) +def delete_weekly_task(remove_id: int, session: Session = Depends(get_db)): + + remove_weekly_task(remove_id, session) + url = router.url_path_for("weekly_tasks_manager") + return RedirectResponse(url=url, status_code=HTTP_303_SEE_OTHER) + + +@router.get("/edit", dependencies=[Depends(is_logged_in)]) +def edit_weekly_task(edit_id: int, request: Request, session=Depends(get_db)): + + weekly_task = session.query(WeeklyTask).filter_by(id=edit_id).first() + checked_days = get_checked_days(weekly_task.get_days()) + return templates.TemplateResponse( + "add_edit_weekly_task.html", + { + "request": request, + "weekly_task": weekly_task, + "mode": "edit", + "checked_days": checked_days, + }, + ) + + +def set_cookies( + response: Response, + id: Optional[int], + title: Optional[str], + content: Optional[str], + days: str, + is_important: bool, +) -> Response: + """Sets the weekly task cookies + for the failed/add and failed/edit routers""" + if not id: + id = 0 + response.set_cookie(key="id", value=id) + response.set_cookie(key="title", value=title) + response.set_cookie(key="content", value=content) + response.set_cookie(key="days", value=days) + response.set_cookie(key="is_important", value=is_important) + return response + + +@router.post("/execute/{mode}") +def weekly_task_execute( + request: Request, + mode: str, + session: Session = Depends(get_db), + user: schema.CurrentUser = Depends(current_user_from_db), + title: str = Form(None), + sun: bool = Form(False), + mon: bool = Form(False), + tue: bool = Form(False), + wed: bool = Form(False), + thu: bool = Form(False), + fri: bool = Form(False), + sat: bool = Form(False), + content: str = Form(None), + is_important: bool = Form(False), + task_time: datetime.time = Form(None), + weekly_task_id: int = Form(...), +): + + request.cookies.clear() + + days = get_days_string(sun, mon, tue, wed, thu, fri, sat) + weekly_task = weekly_task_from_input( + user, + title, + days, + content, + task_time, + is_important, + weekly_task_id=weekly_task_id, + ) + + if mode == "add": + # creating the weekly task + created = create_weekly_task(weekly_task, session) + executed = created + + else: # mode == "edit": + # editing the weekly task + edited = change_weekly_task(user, weekly_task, session) + executed = edited + + if not executed: + url = router.url_path_for("weekly_task_failed", mode=mode) + html_respone = RedirectResponse( + url=url, + status_code=HTTP_303_SEE_OTHER, + ) + response = set_cookies( + html_respone, + weekly_task.id, + title, + content, + days, + weekly_task.is_important, + ) + return response + + url = router.url_path_for("weekly_tasks_manager") + return RedirectResponse(url=url, status_code=HTTP_302_FOUND) + + +@router.get("/failed/{mode}", response_class=HTMLResponse) +def weekly_task_failed( + mode: str, + request: Request, + user: schema.CurrentUser = Depends(current_user), + id: int = Cookie(0), + title: str = Cookie(None), + content: str = Cookie(None), + days: str = Cookie(...), + is_important: bool = Cookie(False), +): + task_time = None + if title == "None": + title = None + if content == "None": + content = None + weekly_task = weekly_task_from_input( + user, + title, + days, + content, + task_time, + is_important, + id, + ) + + if mode == "add": + fail_massage = "could not add The Weekly Task" + else: + fail_massage = "These changes could not be made to the Weekly Task" + checked_days = get_checked_days(days) + + return templates.TemplateResponse( + "add_edit_weekly_task.html", + { + "request": request, + "massage": fail_massage, + "weekly_task": weekly_task, + "mode": mode, + "checked_days": checked_days, + }, + ) diff --git a/app/templates/add_edit_weekly_task.html b/app/templates/add_edit_weekly_task.html new file mode 100644 index 00000000..f4dbb94f --- /dev/null +++ b/app/templates/add_edit_weekly_task.html @@ -0,0 +1,84 @@ +{% extends "base.html" %} + +{% block content %} + +
+ {% if mode == "edit" %} + +

Edit Weekly Tasks

+ {% set weekly_task_id = weekly_task.id %} + + {% else %} + +

Add Weekly Tasks

+ {% set weekly_task_id = "0" %} + + {% endif %} +
+ + + {% if massage %} + + {% endif %} + + {% if weekly_task %} + {% if weekly_task.is_important %} + {% set is_important = "checked" %} + {% endif %} + + {% set title = weekly_task.title or "" %} + + {% set content = weekly_task.content or "" %} + {% endif %} + + + +
+ +
+ + +
+ + + +
+
    + {% for day_full_name, day, is_checked in checked_days %} + +
  • + + {{ day_full_name }} +
  • + + {% endfor %} +
+
+ +
+ + +
+ +
+ Description: + +
+ +
+ + +
+ + + + + + + +
+ + +{% endblock %} diff --git a/app/templates/weekly_tasks_manager.html b/app/templates/weekly_tasks_manager.html new file mode 100644 index 00000000..167fed54 --- /dev/null +++ b/app/templates/weekly_tasks_manager.html @@ -0,0 +1,98 @@ +{% extends "base.html" %} + +{% block content %} + +
+

Weekly Tasks

+
+ + +
+ + {% for weekly_task in weekly_tasks %} +
+
+ + + + Every: {{ weekly_task.get_days() }} + at: {{ weekly_task.task_time }} + + + + + + + + +
+ + + {% if weekly_task.content == None %} + {% set content = "" %} + {% else %} + {% set content = weekly_task.content %} + {% endif %} + + + +
+

+ {{ weekly_task.title }} +

+

+ {{ content }} +

+
+ important: {{ weekly_task.is_important }} +
+ + +
+ {% endfor %} + +
+ + + +
+ Add Weekly Task +
+ + + + + +{% endblock %} diff --git a/tests/client_fixture.py b/tests/client_fixture.py index 5f5f8971..7627e34a 100644 --- a/tests/client_fixture.py +++ b/tests/client_fixture.py @@ -16,6 +16,8 @@ invitation, meds, profile, + weekview, + weekly_tasks, weight, ) from app.routers.salary import routes as salary @@ -24,6 +26,17 @@ main.app.include_router(security_testing_routes.router) +REGISTER_DETAIL = { + "username": "correct_user", + "full_name": "full_name", + "password": "correct_password", + "confirm_password": "correct_password", + "email": "example@email.com", + "description": "", +} + +LOGIN_DATA = {"username": "correct_user", "password": "correct_password"} + def get_test_placeholder_user() -> User: return User( @@ -52,6 +65,26 @@ def create_test_client(get_db_function) -> Generator[Session, None, None]: Base.metadata.drop_all(bind=test_engine) +def create_logged_test_client( + get_db_function, +) -> Generator[Session, None, None]: + Base.metadata.create_all(bind=test_engine) + main.app.dependency_overrides[get_db_function] = get_test_db + + with TestClient(main.app) as client: + client.post(client.app.url_path_for("register"), data=REGISTER_DETAIL) + client.post(client.app.url_path_for("login"), data=LOGIN_DATA) + yield client + + main.app.dependency_overrides = {} + Base.metadata.drop_all(bind=test_engine) + + +@pytest.fixture(scope="session") +def weekview_client() -> Generator[TestClient, None, None]: + yield from create_test_client(weekview.get_db) + + @pytest.fixture(scope="session") def agenda_test_client() -> Generator[TestClient, None, None]: yield from create_test_client(agenda.get_db) @@ -102,6 +135,11 @@ def profile_test_client() -> Generator[Session, None, None]: Base.metadata.drop_all(bind=test_engine) +@pytest.fixture(scope="function") +def weekly_tasks_test_client(): + yield from create_logged_test_client(weekly_tasks.get_db) + + @pytest.fixture(scope="session") def audio_test_client() -> Iterator[TestClient]: yield from create_test_client(audio.get_db) diff --git a/tests/conftest.py b/tests/conftest.py index 1d8a21d2..500ede4a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,7 @@ 'tests.invitation_fixture', 'tests.association_fixture', 'tests.client_fixture', + 'tests.weekly_tasks_fixture', 'tests.asyncio_fixture', 'tests.logger_fixture', 'tests.category_fixture', diff --git a/tests/test_weekly_tasks_internal.py b/tests/test_weekly_tasks_internal.py new file mode 100644 index 00000000..beebe616 --- /dev/null +++ b/tests/test_weekly_tasks_internal.py @@ -0,0 +1,224 @@ +from datetime import datetime + +from app.database.models import Task +from app.internal.weekly_tasks import ( + check_inputs, + generate_tasks, + create_task, + remove_weekly_task, + weekly_task_from_input, + create_weekly_task, + change_weekly_task, +) + + +def test_weekly_tasks_check_inputs(weekly_task_time): + ok = check_inputs(days="", task_time=None, title="the title") + assert not ok + ok = check_inputs( + days="Sun", + task_time=weekly_task_time, + title="the title", + ) + assert ok + + +def test_weekly_tasks_create_task(user, session): + date_time = datetime(2021, 1, 21, 3, 19) + task = Task( + title="task1", + description="my description", + is_done=False, + is_important=True, + date=date_time.date(), + time=date_time.time(), + owner_id=user.id, + ) + created = create_task(task, user, session) + assert created + created = create_task(task, user, session) + assert not created + created_task = user.tasks[0] + assert created_task.date == date_time.date() + assert created_task.time == date_time.time() + + +def test_create_weekly_task(user, session, weekly_task): + # creating the weekly task + weekly_task.user_id = user.id + created = create_weekly_task(weekly_task, session) + assert created + # checks if weekly task been added to the db + user_w_t = user.weekly_tasks[0] + assert user_w_t + assert user_w_t.content == weekly_task.content + assert user_w_t.task_time == weekly_task.task_time + + # adding without a title + weekly_task.title = None + created = create_weekly_task(weekly_task, session) + assert not created + + # the user's weekly tasks should remain in quantity 1 + user_w_tasks = user.weekly_tasks + assert len(user_w_tasks) == 1 + + +def test_change_weekly_task( + user, + session, + weekly_task, + weekly_task2, + weekly_task3, +): + # creating weekly task for edit testing + weekly_task.user_id = user.id + created = create_weekly_task(weekly_task, session) + assert created + + # creating another weekly task for edit testing + weekly_task2.user_id = user.id + created = create_weekly_task(weekly_task2, session) + assert created + user_w_t = user.weekly_tasks + assert len(user_w_t) == 2 + + # get weekly task id for edit + user_w_t = user.weekly_tasks[0] + edit_id = user_w_t.id + + # seting the weekly task for editing + weekly_task.id = edit_id + weekly_task.title = "new title" + weekly_task.content = "new content" + + changed = change_weekly_task(user, weekly_task, session) + assert changed + changed_user_w_t = user.weekly_tasks[0] + assert changed_user_w_t.title == weekly_task.title + assert changed_user_w_t.content == weekly_task.content + + # editing without a title + weekly_task3.user_id = user.id + weekly_task3.id = edit_id + weekly_task3.title = None + changed = change_weekly_task(user, weekly_task3, session) + assert not changed + edited_w_t = user.weekly_tasks[0] + assert edited_w_t.title != weekly_task3.title + + +def test_weekly_task_change_permission( + user, + user2, + session, + weekly_task, + weekly_task2, +): + # creating the weekly task + weekly_task.user_id = user.id + created = create_weekly_task(weekly_task, session) + assert created + user_w_task = user.weekly_tasks[0] + assert user_w_task + + # another user trying to change the weekly task + weekly_task2.id = user_w_task.id + weekly_task2.user_id = user2.id + changed = change_weekly_task(user2, weekly_task, session) + assert not changed + user_w_t = user.weekly_tasks[0] + assert user_w_t.title != weekly_task2.title + assert user_w_t.content != weekly_task2.content + assert user_w_t.is_important != weekly_task2.is_important + + +def test_weekly_task_from_input(user, weekly_task, weekly_task_time): + # all inputs are ok + w_t = weekly_task_from_input( + user, + weekly_task.title, + weekly_task.get_days(), + weekly_task.content, + weekly_task_time, + weekly_task.is_important, + weekly_task_id=1, + ) + assert w_t + # all the weekly task data should be saved + assert w_t.title == weekly_task.title + assert w_t.days == weekly_task.days + assert w_t.content == weekly_task.content + assert w_t.is_important == weekly_task.is_important + assert w_t.task_time == weekly_task.task_time + + # not all inputs are ok + w_t = weekly_task_from_input( + user, + "", + weekly_task.days, + weekly_task.content, + weekly_task_time, + weekly_task.is_important, + ) + assert w_t + # As much data as possible is saved, except for time and days + assert w_t.content == weekly_task.content + assert w_t.days != weekly_task.days + assert w_t.task_time != weekly_task.task_time + + +def test_remove_weekly_task(user, session, weekly_task): + weekly_task.user_id = user.id + created = create_weekly_task(weekly_task, session) + assert created + + # Checks if the weekly task exists in the db + user_w_task = user.weekly_tasks[0] + assert user_w_task + # geting the id of the weekly task for removal + weekly_task_id = user_w_task.id + removed = remove_weekly_task(weekly_task_id, session) + assert removed + # Checks if the weekly task exists in the db after removal + assert not user.weekly_tasks + + # Trying to remove a weekly task that does not exists + removed = remove_weekly_task(weekly_task_id, session) + assert not removed + + +def test_weekly_tasks_generate_tasks(user, session, weekly_task): + weekly_task.user_id = user.id + created = create_weekly_task(weekly_task, session) + assert created + + # Activates the generator + task_gen = generate_tasks(user, session) + tasks_added = list(task_gen) + + # the number of days defined in the weekly task is 3 + assert tasks_added.count(True) == 3 + + tasks = user.tasks + # The number of tasks a user has should be + # the number of days defined in the weekly task + assert len(tasks) == 3 + # The Tasks should be defined according to the weekly task + for task in tasks: + assert weekly_task.title == task.title + assert weekly_task.content == task.description + assert weekly_task.is_important == task.is_important + time_string = task.time.strftime("%H:%M") + assert weekly_task.task_time == time_string + day = task.date.strftime("%a") + assert day in weekly_task.get_days() + + # another activation at the same day + # shouldn't affect the db + # Only after a week new tasks should be added + task_gen = generate_tasks(user, session) + tasks_added = list(task_gen) + assert tasks_added.count(True) == 0 + tasks = user.tasks + assert len(tasks) == 3 diff --git a/tests/test_weekly_tasks_route.py b/tests/test_weekly_tasks_route.py new file mode 100644 index 00000000..4854d34f --- /dev/null +++ b/tests/test_weekly_tasks_route.py @@ -0,0 +1,188 @@ +from app.database.models import WeeklyTask + + +def test_weekly_tasks_manager(weekly_tasks_test_client): + # Get weekly-tasks page + data = weekly_tasks_test_client.get("/weekly-tasks").content + + # Verify that it is the page and there are no tasks + assert b"Weekly Tasks" in data + assert b"Test Task 1" not in data + + +def test_weekly_tasks_add( + weekly_tasks_test_client, + weekly_task, + input_weekly_task, +): + # Get add page + data = weekly_tasks_test_client.get("/weekly-tasks/add").content + # Verify that it is the add page + assert b"Add Weekly Tasks" in data + + # sets successful weekly task input + # Sets the input to add mode + input_weekly_task["mode"] = "add" + input_weekly_task["weekly_task_id"] = "0" + weekly_tasks_test_client.post( + "/weekly-tasks/execute/add", + data=input_weekly_task, + ).content + + # Get weekly-tasks manager page + data = weekly_tasks_test_client.get("/weekly-tasks").content + # Checks that weekly task details are displayed + assert b"Weekly Tasks" in data + assert weekly_task.title.encode() in data + assert weekly_task.get_days().encode() in data + assert weekly_task.content.encode() in data + assert weekly_task.task_time.encode() in data + + +def test_weekly_tasks_failed_add(weekly_tasks_test_client, weekly_task): + # set weekly task data for failed attempt + weekly_task_data = { + "title": weekly_task.title, + "mode": "add", + "weekly_task_id": "0", + } + data = weekly_tasks_test_client.post( + "/weekly-tasks/execute/add", + data=weekly_task_data, + ).content + # on failed attempt redirects to failed/add page + data = weekly_tasks_test_client.get("/weekly-tasks/failed/add").content + assert b"Add Weekly Tasks" in data + assert b"could not add The Weekly Task" in data + assert weekly_task.title.encode() in data + + +def test_weekly_tasks_edit( + weekly_tasks_test_client, + weekly_task, + session, + input_weekly_task, +): + # adding weekly task to db + input_weekly_task["mode"] = "add" + input_weekly_task["weekly_task_id"] = "0" + weekly_tasks_test_client.post( + "/weekly-tasks/execute/add", + data=input_weekly_task, + ).content + # Get weekly tasks id from db for edit + w_task_query = session.query(WeeklyTask) + w_task_by_title = w_task_query.filter_by(title=weekly_task.title) + weekly_task_from_db = w_task_by_title.first() + w_task_id = weekly_task_from_db.id + + data = weekly_tasks_test_client.get( + f"/weekly-tasks/edit?edit_id={w_task_id}", + ).content + + # Checks that all weekly task details are displayed + assert b"Edit Weekly Tasks" in data + assert weekly_task.title.encode() in data + assert weekly_task.content.encode() in data + assert weekly_task.task_time.encode() in data + + # Sets the input to edit mode + input_weekly_task["mode"] = "edit" + # Sets the id for edit + input_weekly_task["weekly_task_id"] = w_task_id + + # sets successful weekly task input + input_weekly_task["title"] = "Test Task 2" + input_weekly_task["fri"] = True + input_weekly_task["thu"] = True + input_weekly_task["sun"] = False + + weekly_tasks_test_client.post( + "/weekly-tasks/execute/edit", + data=input_weekly_task, + ).content + + # Get weekly-tasks manager page + data = weekly_tasks_test_client.get("/weekly-tasks").content + # Checks that all weekly task details are displayed correctly + assert b"Weekly Tasks" in data + assert weekly_task.title.encode() not in data + assert b"Test Task 2" in data + assert weekly_task.get_days().encode() not in data + assert b"Mon, Thu, Fri, Sat" in data + assert weekly_task.content.encode() in data + assert weekly_task.task_time.encode() in data + + +def test_weekly_tasks_failed_edit( + weekly_tasks_test_client, + weekly_task, + session, + input_weekly_task, +): + # adding weekly task to db + input_weekly_task["mode"] = "add" + input_weekly_task["weekly_task_id"] = "0" + data = weekly_tasks_test_client.post( + "/weekly-tasks/execute/add", + data=input_weekly_task, + ).content + print(data) + # Get weekly tasks id from db for edit + w_task_query = session.query(WeeklyTask) + w_task_by_title = w_task_query.filter_by(title=weekly_task.title) + weekly_task_from_db = w_task_by_title.first() + w_task_id = weekly_task_from_db.id + + # Sets the input to edit mode + input_weekly_task["mode"] = "edit" + # Sets the id for edit + input_weekly_task["weekly_task_id"] = w_task_id + + # set weekly task input for failed attempt + input_weekly_task["title"] = "" + weekly_tasks_test_client.post( + "/weekly-tasks/execute/edit", + data=input_weekly_task, + ).content + + # on failed attempt redirects to failed/edit page + data = weekly_tasks_test_client.get("/weekly-tasks/failed/edit").content + + assert b"Edit Weekly Tasks" in data + assert b"These changes could not be made to the Weekly Task" in data + assert weekly_task.title.encode() not in data + assert weekly_task.content.encode() in data + + +def test_delete_weekly_tasks( + weekly_tasks_test_client, + session, + input_weekly_task, + weekly_task, +): + # adds weekly_task + input_weekly_task["mode"] = "add" + input_weekly_task["weekly_task_id"] = "0" + data = weekly_tasks_test_client.post( + "/weekly-tasks/execute/add", + data=input_weekly_task, + ).content + + # get weekly task id for removal + w_task_query = session.query(WeeklyTask) + w_task_by_title = w_task_query.filter_by(title=weekly_task.title) + weekly_task_from_db = w_task_by_title.first() + w_task_id = weekly_task_from_db.id + + # removes weekly_task + weekly_tasks_test_client.delete(f"/weekly-tasks/?remove_id={w_task_id}") + + # Checking if removed successfully + w_task_query = session.query(WeeklyTask) + w_task_by_title = w_task_query.filter_by(title=weekly_task.title) + weekly_task_from_db = w_task_by_title.first() + data = weekly_tasks_test_client.get("/weekly-tasks").content + assert b"Weekly Tasks" in data + assert weekly_task_from_db is None + assert weekly_task.title.encode() not in data diff --git a/tests/test_weekview.py b/tests/test_weekview.py index 7980b38b..7c0cde8e 100644 --- a/tests/test_weekview.py +++ b/tests/test_weekview.py @@ -9,48 +9,55 @@ def create_weekview_event(events, session, user): for event in events: create_event( db=session, - title='test', + title="test", start=event.start, end=event.end, owner_id=user.id, - color=event.color + color=event.color, ) def test_get_week_dates(weekdays, sunday): week_dates = list(get_week_dates(sunday)) for i in range(6): - assert week_dates[i].strftime('%A') == weekdays[i] + assert week_dates[i].strftime("%A") == weekdays[i] -def test_weekview_day_names(session, user, client, weekdays): - response = client.get("/week/2021-1-3") - soup = BeautifulSoup(response.content, 'html.parser') - day_divs = soup.find_all("div", {"class": 'day-name'}) +def test_weekview_day_names(session, user, weekview_client, weekdays): + response = weekview_client.get("/week/2021-1-3") + soup = BeautifulSoup(response.content, "html.parser") + day_divs = soup.find_all("div", {"class": "day-name"}) for i in range(6): + print(i) + print(str(day_divs[i])) assert weekdays[i][:3].upper() in str(day_divs[i]) -def test_weekview_day_dates(session, user, client, sunday): - response = client.get("/week/2021-1-3") - soup = BeautifulSoup(response.content, 'html.parser') - day_divs = soup.find_all("span", {"class": 'date-nums'}) +def test_weekview_day_dates(session, user, weekview_client, sunday): + response = weekview_client.get("/week/2021-1-3") + soup = BeautifulSoup(response.content, "html.parser") + day_divs = soup.find_all("span", {"class": "date-nums"}) week_dates = list(get_week_dates(sunday)) for i in range(6): - time_str = f'{week_dates[i].day} / {week_dates[i].month}' + time_str = f"{week_dates[i].day} / {week_dates[i].month}" assert time_str in day_divs[i] @pytest.mark.parametrize( "date,event", - [("2021-1-31", 'event1'), - ("2021-1-31", 'event2'), - ("2021-2-3", 'event3')] + [("2021-1-31", "event1"), ("2021-1-31", "event2"), ("2021-2-3", "event3")], ) def test_weekview_html_events( - event1, event2, event3, session, user, client, date, event + event1, + event2, + event3, + session, + user, + weekview_client, + date, + event, ): create_weekview_event([event1, event2, event3], session=session, user=user) - response = client.get(f"/week/{date}") - soup = BeautifulSoup(response.content, 'html.parser') + response = weekview_client.get(f"/week/{date}") + soup = BeautifulSoup(response.content, "html.parser") assert event in str(soup.find("div", {"id": event})) diff --git a/tests/user_fixture.py b/tests/user_fixture.py index b50fb900..a8b30056 100644 --- a/tests/user_fixture.py +++ b/tests/user_fixture.py @@ -22,6 +22,19 @@ def user(session: Session) -> Generator[User, None, None]: delete_instance(session, mock_user) +@pytest.fixture +def user2(session: Session) -> Generator[User, None, None]: + mock_user = create_model( + session, User, + username='test_username2', + password='test_password2', + email='test2.email@gmail.com', + language_id=1, + ) + yield mock_user + delete_instance(session, mock_user) + + @pytest.fixture def sender(session: Session) -> Generator[User, None, None]: mock_user = create_model( diff --git a/tests/weekly_tasks_fixture.py b/tests/weekly_tasks_fixture.py new file mode 100644 index 00000000..66b03405 --- /dev/null +++ b/tests/weekly_tasks_fixture.py @@ -0,0 +1,63 @@ +import pytest +from datetime import datetime + +from app.database.models import WeeklyTask + + +@pytest.fixture +def weekly_task(): + w_t = WeeklyTask( + title="Test Task 1", + content="my content", + is_important=True, + task_time="11:00", + ) + w_t.set_days("Mon, Sat, Sun") + return w_t + + +@pytest.fixture +def weekly_task2(): + w_t = WeeklyTask( + title="Test Task 2", + content="my content2", + is_important=False, + task_time="12:00", + ) + w_t.set_days("Sat, Sun") + return w_t + + +@pytest.fixture +def weekly_task3(): + w_t = WeeklyTask( + title="Test Task 2", + days="Sat", + content="my content3", + is_important=False, + task_time="12:00", + ) + w_t.set_days("Sat") + return w_t + + +@pytest.fixture +def input_weekly_task(): + return { + "title": "Test Task 1", + "sun": True, + "mon": True, + "sat": True, + "content": "my content", + "is_important": True, + "task_time": "11:00", + } + + +@pytest.fixture +def weekly_task_time(weekly_task): + date_time_string = f"2021-01-28 {weekly_task.task_time}" + date_time_format = "%Y-%m-%d %H:%M" + date_time = datetime.strptime(date_time_string, date_time_format) + task_time = date_time.time() + return task_time