diff --git a/app/routes/task_routes.py b/app/routes/task_routes.py index 3aae38d49..569567b9c 100644 --- a/app/routes/task_routes.py +++ b/app/routes/task_routes.py @@ -1 +1,119 @@ -from flask import Blueprint \ No newline at end of file +from flask import Blueprint, request, abort, make_response, Response +from .route_utilities import validate_model +from app.models.task import Task +from datetime import datetime +from ..db import db +import requests +import os + + +SLACK_API_URL = os.environ["SLACK_API_URL"] +SLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"] + +bp = Blueprint("tasks_bp", __name__, url_prefix="/tasks") + +@bp.get("") +def get_all_tasks(): + query = db.select(Task) + sort_method = request.args.get('sort') + + if sort_method and sort_method == "asc": + query = query.order_by(Task.title.asc()) + if sort_method and sort_method == "desc": + query = query.order_by(Task.title.desc()) + + tasks = db.session.scalars(query) + tasks_response = [task.to_dict() for task in tasks] + + return tasks_response + + +@bp.post("") +def create_task(): + request_body = request.get_json() + + try: + new_task = Task.from_dict(request_body) + + except KeyError as error: + response = {"details": f"Invalid data"} + abort(make_response(response, 400)) + + db.session.add(new_task) + db.session.commit() + + return {"task": new_task.to_dict()}, 201 + + +@bp.get("/") +def get_one_task(task_id): + task = validate_model(Task, task_id) + return {"task": task.to_dict()} + + +@bp.put("/") +def update_task(task_id): + task = validate_model(Task, task_id) + request_body = request.get_json() + + task.title = request_body["title"] + task.description = request_body["description"] + + db.session.add(task) + db.session.commit() + + return {"task": task.to_dict()} + + +@bp.delete("/") +def delete_task(task_id): + task = validate_model(Task, task_id) + db.session.delete(task) + db.session.commit() + + message = f"Task {task_id} \"{task.title}\" successfully deleted" + return {"details": message} + + +@bp.patch("//mark_complete") +def mark_task_complete(task_id): + task = validate_model(Task, task_id) + task.completed_at = datetime.now() + + db.session.add(task) + db.session.commit() + + #post_to_slack(task) + + return {"task": task.to_dict()} + + +@bp.patch("//mark_incomplete") +def mark_task_incomplete(task_id): + task = validate_model(Task, task_id) + task.completed_at = None + + db.session.add(task) + db.session.commit() + + #post_to_slack(task) + + return {"task": task.to_dict()} + + +def post_to_slack(task): + headers = { + "Authorization": f"Bearer {SLACK_BOT_TOKEN}", + } + if task.completed_at: + data = { + "channel": "instructors", + "text": f"Task {task.title} has been marked complete", + } + else: + data = { + "channel": "general", + "text": f"Task {task.title} has been marked incomplete", + } + + r = requests.post(SLACK_API_URL, headers=headers, data=data) \ No newline at end of file diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index dca626d78..527153e09 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -2,7 +2,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_no_saved_tasks(client): # Act response = client.get("/tasks") @@ -13,7 +13,7 @@ def test_get_tasks_no_saved_tasks(client): assert response_body == [] -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_one_saved_tasks(client, one_task): # Act response = client.get("/tasks") @@ -32,7 +32,7 @@ def test_get_tasks_one_saved_tasks(client, one_task): ] -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_task(client, one_task): # Act response = client.get("/tasks/1") @@ -51,7 +51,7 @@ def test_get_task(client, one_task): } -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_task_not_found(client): # Act response = client.get("/tasks/1") @@ -59,14 +59,11 @@ def test_get_task_not_found(client): # Assert assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Task 1 not found" - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_create_task(client): # Act response = client.post("/tasks", json={ @@ -93,7 +90,7 @@ def test_create_task(client): assert new_task.completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_update_task(client, one_task): # Act response = client.put("/tasks/1", json={ @@ -119,7 +116,7 @@ def test_update_task(client, one_task): assert task.completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_update_task_not_found(client): # Act response = client.put("/tasks/1", json={ @@ -130,14 +127,11 @@ def test_update_task_not_found(client): # Assert assert response.status_code == 404 - - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** + assert "message" in response_body + assert response_body["message"] == "Task 1 not found" -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_delete_task(client, one_task): # Act response = client.delete("/tasks/1") @@ -152,7 +146,7 @@ def test_delete_task(client, one_task): assert Task.query.get(1) == None -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_delete_task_not_found(client): # Act response = client.delete("/tasks/1") @@ -161,15 +155,12 @@ def test_delete_task_not_found(client): # Assert assert response.status_code == 404 - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - + assert "message" in response_body + assert response_body["message"] == "Task 1 not found" assert Task.query.all() == [] -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_create_task_must_contain_title(client): # Act response = client.post("/tasks", json={ @@ -186,7 +177,7 @@ def test_create_task_must_contain_title(client): assert Task.query.all() == [] -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_create_task_must_contain_description(client): # Act response = client.post("/tasks", json={ @@ -200,4 +191,4 @@ def test_create_task_must_contain_description(client): assert response_body == { "details": "Invalid data" } - assert Task.query.all() == [] + assert Task.query.all() == [] \ No newline at end of file diff --git a/tests/test_wave_02.py b/tests/test_wave_02.py index a087e0909..ea508eb03 100644 --- a/tests/test_wave_02.py +++ b/tests/test_wave_02.py @@ -1,7 +1,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_sorted_asc(client, three_tasks): # Act response = client.get("/tasks?sort=asc") @@ -29,7 +29,7 @@ def test_get_tasks_sorted_asc(client, three_tasks): ] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_sorted_desc(client, three_tasks): # Act response = client.get("/tasks?sort=desc") @@ -54,4 +54,4 @@ def test_get_tasks_sorted_desc(client, three_tasks): "id": 2, "is_complete": False, "title": "Answer forgotten email 📧"}, - ] + ] \ No newline at end of file diff --git a/tests/test_wave_03.py b/tests/test_wave_03.py index 32d379822..4919a93df 100644 --- a/tests/test_wave_03.py +++ b/tests/test_wave_03.py @@ -5,7 +5,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_mark_complete_on_incomplete_task(client, one_task): # Arrange """ @@ -42,7 +42,7 @@ def test_mark_complete_on_incomplete_task(client, one_task): assert Task.query.get(1).completed_at -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_mark_incomplete_on_complete_task(client, completed_task): # Act response = client.patch("/tasks/1/mark_incomplete") @@ -62,7 +62,7 @@ def test_mark_incomplete_on_complete_task(client, completed_task): assert Task.query.get(1).completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_mark_complete_on_completed_task(client, completed_task): # Arrange """ @@ -99,7 +99,7 @@ def test_mark_complete_on_completed_task(client, completed_task): assert Task.query.get(1).completed_at -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_mark_incomplete_on_incomplete_task(client, one_task): # Act response = client.patch("/tasks/1/mark_incomplete") @@ -119,7 +119,7 @@ def test_mark_incomplete_on_incomplete_task(client, one_task): assert Task.query.get(1).completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_mark_complete_missing_task(client): # Act response = client.patch("/tasks/1/mark_complete") @@ -127,14 +127,11 @@ def test_mark_complete_missing_task(client): # Assert assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Task 1 not found" - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_mark_incomplete_missing_task(client): # Act response = client.patch("/tasks/1/mark_incomplete") @@ -142,8 +139,64 @@ def test_mark_incomplete_missing_task(client): # Assert assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Task 1 not found" + + +# Let's add this test for creating tasks, now that +# the completion functionality has been implemented +# @pytest.mark.skip(reason="No way to test this feature yet") +def test_create_task_with_valid_completed_at(client): + # Act + response = client.post("/tasks", json={ + "title": "A Brand New Task", + "description": "Test Description", + "completed_at": datetime.utcnow() + }) + response_body = response.get_json() + + # Assert + assert response.status_code == 201 + assert "task" in response_body + assert response_body == { + "task": { + "id": 1, + "title": "A Brand New Task", + "description": "Test Description", + "is_complete": True + } + } + new_task = Task.query.get(1) + assert new_task + assert new_task.title == "A Brand New Task" + assert new_task.description == "Test Description" + assert new_task.completed_at - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** + +# Let's add this test for updating tasks, now that +# the completion functionality has been implemented +# @pytest.mark.skip(reason="No way to test this feature yet") +def test_update_task_with_completed_at_date(client, completed_task): + # Act + response = client.put("/tasks/1", json={ + "title": "Updated Task Title", + "description": "Updated Test Description", + "completed_at": datetime.utcnow() + }) + response_body = response.get_json() + + # Assert + assert response.status_code == 200 + assert "task" in response_body + assert response_body == { + "task": { + "id": 1, + "title": "Updated Task Title", + "description": "Updated Test Description", + "is_complete": True + } + } + task = Task.query.get(1) + assert task.title == "Updated Task Title" + assert task.description == "Updated Test Description" + assert task.completed_at \ No newline at end of file diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index aee7c52a1..d2db1900b 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -1,7 +1,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_goals_no_saved_goals(client): # Act response = client.get("/goals") @@ -12,7 +12,7 @@ def test_get_goals_no_saved_goals(client): assert response_body == [] -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_goals_one_saved_goal(client, one_goal): # Act response = client.get("/goals") @@ -29,7 +29,7 @@ def test_get_goals_one_saved_goal(client, one_goal): ] -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_goal(client, one_goal): # Act response = client.get("/goals/1") @@ -46,22 +46,19 @@ def test_get_goal(client, one_goal): } -@pytest.mark.skip(reason="test to be completed by student") +# @pytest.mark.skip(reason="test to be completed by student") def test_get_goal_not_found(client): - pass # Act response = client.get("/goals/1") response_body = response.get_json() - raise Exception("Complete test") # Assert - # ---- Complete Test ---- - # assertion 1 goes here - # assertion 2 goes here - # ---- Complete Test ---- + assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Goal 1 not found" -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_create_goal(client): # Act response = client.post("/goals", json={ @@ -80,34 +77,38 @@ def test_create_goal(client): } -@pytest.mark.skip(reason="test to be completed by student") +# @pytest.mark.skip(reason="test to be completed by student") def test_update_goal(client, one_goal): - raise Exception("Complete test") # Act - # ---- Complete Act Here ---- + response = client.put("/goals/1", json={ + "title": "Updated Goal Title" + }) + response_body = response.get_json() # Assert - # ---- Complete Assertions Here ---- - # assertion 1 goes here - # assertion 2 goes here - # assertion 3 goes here - # ---- Complete Assertions Here ---- + assert response.status_code == 200 + assert "goal" in response_body + assert response_body == { + "goal": { + "id": 1, + "title": "Updated Goal Title" + } + } -@pytest.mark.skip(reason="test to be completed by student") def test_update_goal_not_found(client): - raise Exception("Complete test") # Act - # ---- Complete Act Here ---- + response = client.put("/goals/1", json={ + "title": "Updated Goal Title" + }) + response_body = response.get_json() # Assert - # ---- Complete Assertions Here ---- - # assertion 1 goes here - # assertion 2 goes here - # ---- Complete Assertions Here ---- + assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Goal 1 not found" -@pytest.mark.skip(reason="No way to test this feature yet") def test_delete_goal(client, one_goal): # Act response = client.delete("/goals/1") @@ -124,27 +125,24 @@ def test_delete_goal(client, one_goal): response = client.get("/goals/1") assert response.status_code == 404 - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** + response_body = response.get_json() + assert "message" in response_body + assert response_body["message"] == "Goal 1 not found" -@pytest.mark.skip(reason="test to be completed by student") -def test_delete_goal_not_found(client): - raise Exception("Complete test") +# @pytest.mark.skip(reason="test to be completed by student") +def test_delete_goal_not_found(client): # Act - # ---- Complete Act Here ---- + response = client.delete("/goals/1") + response_body = response.get_json() # Assert - # ---- Complete Assertions Here ---- - # assertion 1 goes here - # assertion 2 goes here - # ---- Complete Assertions Here ---- + assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Goal 1 not found" -@pytest.mark.skip(reason="No way to test this feature yet") def test_create_goal_missing_title(client): # Act response = client.post("/goals", json={}) @@ -154,4 +152,4 @@ def test_create_goal_missing_title(client): assert response.status_code == 400 assert response_body == { "details": "Invalid data" - } + } \ No newline at end of file diff --git a/tests/test_wave_06.py b/tests/test_wave_06.py index 8afa4325e..0987c684a 100644 --- a/tests/test_wave_06.py +++ b/tests/test_wave_06.py @@ -2,7 +2,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_post_task_ids_to_goal(client, one_goal, three_tasks): # Act response = client.post("/goals/1/tasks", json={ @@ -23,7 +23,7 @@ def test_post_task_ids_to_goal(client, one_goal, three_tasks): assert len(Goal.query.get(1).tasks) == 3 -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_post_task_ids_to_goal_already_with_goals(client, one_task_belongs_to_one_goal, three_tasks): # Act response = client.post("/goals/1/tasks", json={ @@ -42,7 +42,7 @@ def test_post_task_ids_to_goal_already_with_goals(client, one_task_belongs_to_on assert len(Goal.query.get(1).tasks) == 2 -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal_no_goal(client): # Act response = client.get("/goals/1/tasks") @@ -50,14 +50,11 @@ def test_get_tasks_for_specific_goal_no_goal(client): # Assert assert response.status_code == 404 + assert "message" in response_body + assert response_body["message"] == "Goal 1 not found" - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal_no_tasks(client, one_goal): # Act response = client.get("/goals/1/tasks") @@ -74,7 +71,7 @@ def test_get_tasks_for_specific_goal_no_tasks(client, one_goal): } -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal(client, one_task_belongs_to_one_goal): # Act response = client.get("/goals/1/tasks") @@ -99,7 +96,7 @@ def test_get_tasks_for_specific_goal(client, one_task_belongs_to_one_goal): } -@pytest.mark.skip(reason="No way to test this feature yet") +# @pytest.mark.skip(reason="No way to test this feature yet") def test_get_task_includes_goal_id(client, one_task_belongs_to_one_goal): response = client.get("/tasks/1") response_body = response.get_json() @@ -115,4 +112,4 @@ def test_get_task_includes_goal_id(client, one_task_belongs_to_one_goal): "description": "Notice something new every day", "is_complete": False } - } + } \ No newline at end of file