Conversation
| response = client.put("/goals/1", json={ | ||
| "title": "Updated Goal Title" | ||
| }) | ||
| goal = Goal.query.get(1) |
There was a problem hiding this comment.
❗️get is a legacy method that is deprecated and shouldn't be used (unless you're maintaining an older project that has this syntax).
Our tests should use syntax similar to what we have in our route_utilities functions: something like db.select with db.sessions.scalar
| @@ -1 +1,76 @@ | |||
| from flask import Blueprint No newline at end of file | |||
| from flask import Blueprint, make_response, request, Response, abort | |||
There was a problem hiding this comment.
make_response and abort are imported but not accessed and should be removed from the imports here to keep your project neat and free from unnecessary clutter.
| @@ -1 +1,66 @@ | |||
| from flask import Blueprint No newline at end of file | |||
| from flask import Blueprint, request, Response, session | |||
There was a problem hiding this comment.
session is imported but not accessed and should be removed.
| def to_dict(self): | ||
| return { | ||
| "id": self.id, | ||
| "title": self.title, | ||
| } | ||
|
|
||
| def to_dict_with_tasks(self): | ||
| return { | ||
| "id": self.id, | ||
| "task_ids": [task.id for task in self.tasks] | ||
| } |
There was a problem hiding this comment.
We can include the logic related to task_ids in to_dict by using conditional logic so that we don't need to create a second function.
| def to_dict(self): | |
| return { | |
| "id": self.id, | |
| "title": self.title, | |
| } | |
| def to_dict_with_tasks(self): | |
| return { | |
| "id": self.id, | |
| "task_ids": [task.id for task in self.tasks] | |
| } | |
| def to_dict(self): | |
| goal = { | |
| "id": self.id, | |
| "title": self.title, | |
| } | |
| if self.tasks: | |
| goal["task_ids"] = [task.id for task in self.tasks] | |
| return goal |
| # def to_dict_with_goal(self): | ||
| # task_dict = self.to_dict() | ||
| # task_dict["goal_id"] = self.goal_id | ||
| # return task_dict |
There was a problem hiding this comment.
Remove unnecessary commented out code to keep your codebase concise and to prevent bugs from being introduced.
| # def to_dict_with_goal(self): | |
| # task_dict = self.to_dict() | |
| # task_dict["goal_id"] = self.goal_id | |
| # return task_dict |
| tasks_response = [task.to_dict() for task in goal.tasks] | ||
|
|
||
|
|
||
| return { | ||
| "id": goal.id, | ||
| "title": goal.title, | ||
| "tasks": tasks_response | ||
| }, 200 No newline at end of file |
There was a problem hiding this comment.
I'd prefer this logic related to creating a dictionary that represents a goal and its tasks to be in the to_dict instance method for goal so that this route can be more single
responsibility and tidy.
Your to_dict could check to see that a goal has tasks and if it does, then it can handle creating a dictionary that has the key tasks and value as a list of dictionaries representing each task.
With that refactor, this route would only need to be:
| tasks_response = [task.to_dict() for task in goal.tasks] | |
| return { | |
| "id": goal.id, | |
| "title": goal.title, | |
| "tasks": tasks_response | |
| }, 200 | |
| return goal.to_dict() |
And no need to explicitly return 200 status code since Flask will do that for us.
| response_body = task.to_dict() | ||
| return response_body |
There was a problem hiding this comment.
You initialize the variable response_body but don't do anything with the variable besides return it on the next line. You can directly return the task dictionary, like:
| response_body = task.to_dict() | |
| return response_body | |
| return task.to_dict() |
| def mark_task_incomplete(id): | ||
| task = validate_model(Task, id) | ||
| task.completed_at = None | ||
| task.is_complete = False |
There was a problem hiding this comment.
We don't need to set is_complete because this is not an attribute on the model. is_complete is a key in the dictionary that represents a task and this value is computed from completed_at.
| task.is_complete = False |
| task = validate_model(Task, id) | ||
| task.completed_at = datetime.now() | ||
|
|
||
|
|
||
| db.session.commit() | ||
| return Response(status=204, mimetype="application/json") |
There was a problem hiding this comment.
Like I had mentioned earlier in our Slack conversation, this route needs to implement a call to Slack so that when a task is completed then a message is posted to Slack.
👀❗️ You can review the Wave 4 documentation here for details about this.
| # assertion 2 goes here | ||
| # assertion 3 goes here | ||
| assert response.status_code == 204 | ||
| assert goal.title == "Updated Goal Title" |
There was a problem hiding this comment.
We could also check that the response's has json format:
| assert goal.title == "Updated Goal Title" | |
| assert goal.title == "Updated Goal Title" | |
| assert response.mimetype == "application/json" | |
| SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN") | ||
| slack_message_text = f"Someone just completed the task {task.title}" | ||
| slack_channel = "task-notifications" | ||
| slack_url = "https://slack.com/api/chat.postMessage" | ||
| headers = { | ||
| "Authorization": f"Bearer {SLACK_BOT_TOKEN}", | ||
| "Content-Type": "application/json" | ||
| } | ||
| json = { | ||
| "channel": slack_channel, | ||
| "text": slack_message_text | ||
| } | ||
|
|
||
| response = requests.post(slack_url, headers=headers, json=json) | ||
| try: | ||
| requests.post(slack_url, headers=headers, json=json) | ||
| print(response.status_code, response.text) | ||
| except Exception as e: | ||
| print("Slack request failed:", e) |
There was a problem hiding this comment.
I'd prefer all the logic related to making a request to the Slack API to be in a separate function, maybe something called like make_slack_request. Doing so would keep this route more concise and concerned with fewer responsibilities.
There was a problem hiding this comment.
If you invoke a helper function for making a call to Slack, then your whole route would look like:
@task_list_bp.patch("/<id>/mark_complete")
def mark_task_complete(id):
task = validate_model(Task, id)
task.completed_at = datetime.now()
make_slack_request()
return Response(status=204, mimetype="application/json")| slack_channel = "task-notifications" | ||
| slack_url = "https://slack.com/api/chat.postMessage" |
There was a problem hiding this comment.
Nice job using variables to refer these string values so that they don't magically appear in your code.
Since they are constant variables, naming them with all caps would convey to other devs that these values should not be changed.
| slack_channel = "task-notifications" | |
| slack_url = "https://slack.com/api/chat.postMessage" | |
| SLACK_CHANNEL = "task-notifications" | |
| SLACK_URL = "https://slack.com/api/chat.postMessage" |
| response = requests.post(slack_url, headers=headers, json=json) | ||
| try: | ||
| requests.post(slack_url, headers=headers, json=json) | ||
| print(response.status_code, response.text) |
There was a problem hiding this comment.
We should remove debugging print statements when we no longer need them so they're not part of the codebase.
| print(response.status_code, response.text) |
No description provided.