-
Notifications
You must be signed in to change notification settings - Fork 127
Zoisite Luwam Ghebremicael #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d3b6a25
3b04e8e
f00cd2d
ddb0972
25df3ee
763f291
36e673e
762bea8
3d2c06a
3891dd7
7c4d4e3
a669c46
a0d7368
2d53b43
70e6fa7
e8c5c77
bc2f340
1aee551
8d19974
b1fb43e
9d50edf
00c3a4f
68bf2b1
80324b3
c66a0b8
3dd77c8
11d2442
df2b6bb
254a86a
b7af79c
00a0c2d
7287e00
d747a54
e4216fc
a342052
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
release: flask db upgrade | ||
web: gunicorn "app:create_app()" |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overall, really nice use of descriptive names and spacing to make the code easy to read! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import os | ||
import requests | ||
from flask import Blueprint, jsonify, make_response, request | ||
from app import db | ||
from app.models.goal import Goal | ||
from app.models.task import Task | ||
from app.helper import validate_goal, validate_task | ||
|
||
#CREATE BP/ENDPOINT | ||
goals_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
|
||
# GET all goals - GET[READ] - /goals | ||
@goals_bp.route("", methods =["GET"]) | ||
def get_all_goals(): | ||
# if request.args.get("sort") == "asc": | ||
# goals = Goal.query.order_by(Goal.title.asc()) | ||
# elif request.args.get("sort") == "desc": | ||
# goals = Goal.query.order_by(Goal.title.desc()) | ||
# else: | ||
Comment on lines
+15
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want to remove commented code like this before opening PRs and rely on our repo's commit history if we need to look up past code. |
||
goals = Goal.query.all() | ||
goals_response = [] | ||
for goal in goals: | ||
goals_response.append(goal.to_json()) | ||
Comment on lines
+21
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be a great place for a list comprehension: goals_response = [goal.to_json() for goal in goals] Are there other places in the project that we fill a list where we could use list comprehensions? |
||
|
||
return jsonify(goals_response), 200 | ||
|
||
# GET one goal - /goals/<id> - [READ] | ||
@goals_bp.route("/<id>", methods=["GET"]) | ||
def get_one_goal(id): | ||
goal = validate_goal(id) | ||
return jsonify({"goal":goal.to_json()}), 200 | ||
|
||
#POST - /goals - [CREATE] | ||
@goals_bp.route("", methods= ["POST"]) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
new_goal = Goal.create_dict(request_body) | ||
db.session.add(new_goal) | ||
db.session.commit() | ||
return make_response({"goal":new_goal.to_json()}), 201 | ||
|
||
#UPDATE one goal- PUT /goals/<id> [UPDATE] | ||
@goals_bp.route("/<id>",methods=["PUT"]) | ||
def update_goal(id): | ||
goal = validate_goal(id) | ||
request_body = request.get_json() | ||
goal.update_dict(request_body) | ||
db.session.commit() | ||
|
||
return jsonify({"goal":goal.to_json()}), 200 | ||
|
||
#DELETE one goal -DELETE /goals/<id> [DELETE] | ||
@goals_bp.route("/<id>", methods=["DELETE"]) | ||
def delete_goal(id): | ||
goal_to_delete = validate_goal(id) | ||
|
||
db.session.delete(goal_to_delete) | ||
db.session.commit() | ||
|
||
message = {"details": f'Goal 1 "{goal_to_delete.title}" successfully deleted'} | ||
return make_response(message, 200) | ||
|
||
#POST tasks ids to goal /goals/1/tasks [CREATE] | ||
@goals_bp.route("/<id>/tasks", methods=["POST"]) | ||
def post_task_ids_to_goal(id): | ||
goal = validate_goal(id) | ||
request_body = request.get_json() | ||
|
||
validated_task = [] | ||
[validated_task.append(validate_task(task_id)) for task_id in request_body["task_ids"]] | ||
Comment on lines
+69
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line might not be doing what you intend, line 70 fills validated_task = [validate_task(task_id) for task_id in request_body["task_ids"]] This line is a bit long, so we could create a variable to get the task_ids = request_body["task_ids"]
validated_task = [validate_task(task_id) for task_id in task_ids] This feedback around creating a list that we don't end up using by wrapping the line in square brackets applies to line 75 below as well. How might we re-write that line? |
||
# for task_id in request_body["task_ids"]: | ||
# task = validate_task(task_id) | ||
# validated_task.append(task) | ||
|
||
[goal.tasks.append(task) for task in validated_task if task not in goal.tasks] | ||
# for task in validated_task: | ||
# if task not in goal.tasks: | ||
# goal.tasks.append(task) | ||
|
||
db.session.commit() | ||
|
||
|
||
return make_response({"id" : goal.goal_id, "task_ids":request_body["task_ids"]}), 200 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some lines across the files that are a bit long for best practices, how could we split them up? |
||
|
||
# GET one goal - /goals/<id> - [READ] | ||
@goals_bp.route("/<id>/tasks", methods=["GET"]) | ||
def get_tasks_in_one_goal(id): | ||
goal = validate_goal(id) | ||
goal_with_tasks = [Task.to_json(task) for task in goal.tasks] | ||
return jsonify({"id":goal.goal_id, "title":goal.title, "tasks":goal_with_tasks}), 200 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from flask import abort, make_response | ||
from app.models.goal import Goal | ||
from app.models.task import Task | ||
|
||
def validate_task(id): | ||
try: | ||
id = int(id) | ||
except: | ||
abort(make_response({"message": f"Task {id} is invalid"}, 400)) | ||
|
||
task = Task.query.get(id) | ||
|
||
if not task: | ||
abort(make_response({"message": f"Task {id} not found"}, 404)) | ||
|
||
return task | ||
|
||
|
||
def validate_goal(id): | ||
try: | ||
id = int(id) | ||
except: | ||
abort(make_response({"message": f"Goal {id} is invalid"}, 400)) | ||
|
||
goal = Goal.query.get(id) | ||
|
||
if not goal: | ||
abort(make_response({"message": f"Goal {id} not found"}, 404)) | ||
|
||
return goal | ||
Comment on lines
+5
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a lot of similar code between these functions, how could we D.R.Y. up our code? |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,53 @@ | ||
from app import db | ||
from flask import abort, make_response, jsonify | ||
|
||
|
||
# Define the Goal class: it represents a goal in the database | ||
class Goal(db.Model): | ||
# Define the goal_id column as the primary key for the Goal table | ||
goal_id = db.Column(db.Integer, primary_key=True) | ||
|
||
# Define the title column as a string that cannot be null | ||
title = db.Column(db.String, nullable = False) | ||
|
||
|
||
# Establish a one-to-many relationship with the Task model(many tasks) | ||
# The `tasks` attribute holds a list of Task objects related to this Goal | ||
# `back_populates` refers to the "goal" relationship defined in the Task model | ||
tasks = db.relationship("Task", back_populates="goal", lazy=True) | ||
|
||
|
||
# Convert a Goal object to a JSON-serializable dictionary | ||
def to_json(self): | ||
return{ | ||
"id": self.goal_id, | ||
"title": self.title | ||
} | ||
|
||
# Update the Goal object's attributes based on the provided request body | ||
def update_dict(self, request_body): | ||
self.title = request_body["title"] | ||
|
||
|
||
# @classmethod decorator indicates that create_dict is a class method associated | ||
# with the class itself rather than any particular instance of the class | ||
|
||
# cls parameter: a reference to the class (Goal in this case) and is used | ||
# to call the class’s constructor (cls(...)), allowing the method to create a new instance of the class. | ||
|
||
# create_dict method is designed to create a new Goal object using data provided in a dictionary (response_body). | ||
@classmethod | ||
# When the create_dict method creates a new Goal object, it does so by calling the class constructor (cls(...)) | ||
def create_dict(cls, response_body): | ||
try: | ||
# Create a new Goal object by calling the class constructor (cls(...)) | ||
# Extract the "title" value from response_body and pass it to the Goal constructor | ||
# to set the title attribute of the new Goal instance. | ||
new_goal = cls( | ||
title = response_body["title"] | ||
) | ||
except KeyError: | ||
# If the "title" key is missing from the response body, return a 400 error | ||
abort(make_response(jsonify({"details": "Invalid data"}), 400)) | ||
return new_goal | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,49 @@ | ||
from app import db | ||
|
||
from flask import abort, make_response, jsonify | ||
from datetime import datetime | ||
|
||
class Task(db.Model): | ||
task_id = db.Column(db.Integer, primary_key=True) | ||
task_id = db.Column(db.Integer, primary_key=True, autoincrement = True) | ||
title = db.Column(db.String, nullable = False) | ||
description = db.Column(db.String, nullable = False ) | ||
completed_at = db.Column(db.DateTime, default = None, nullable = True) | ||
#child - many to one (the task are the children/ many children(tasks) to one parent(goal)) | ||
goal_id = db.Column(db.Integer, db.ForeignKey("goal.goal_id"), nullable=True) | ||
goal = db.relationship("Goal", back_populates="tasks") | ||
|
||
|
||
def to_json(self): | ||
is_complete = True if self.completed_at else False; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great choice to derive the value of |
||
|
||
task_return = { | ||
"id": self.task_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": is_complete | ||
} | ||
|
||
if self.goal_id: | ||
task_return["goal_id"] = self.goal_id | ||
|
||
return task_return | ||
|
||
def update_dict(self, request_body): | ||
self.title = request_body["title"] | ||
self.description = request_body["description"] | ||
|
||
def patch_complete(self, request_body): | ||
self.completed_at = datetime.utcnow() | ||
|
||
def patch_incomplete(self,request_body): | ||
self.completed_at = None | ||
|
||
@classmethod | ||
def create_dict(cls, response_body): | ||
try: | ||
new_task = cls( | ||
title = response_body["title"], | ||
description = response_body["description"] | ||
) | ||
except KeyError: | ||
abort(make_response(jsonify({"details": "Invalid data"}), 400)) | ||
return new_task |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import os, requests | ||
from flask import Blueprint, jsonify, make_response, request | ||
from app import db | ||
from app.models.task import Task | ||
from app.helper import validate_task | ||
|
||
#CREATE BP/ENDPOINT | ||
tasks_bp = Blueprint("tasks", __name__, url_prefix= "/tasks") | ||
|
||
# GET all tasks - /tasks [READ] | ||
@tasks_bp.route("", methods =["GET"]) | ||
def get_all_tasks(): | ||
if request.args.get("sort") == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()) | ||
elif request.args.get("sort") == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()) | ||
else: | ||
tasks = Task.query.all() | ||
tasks_response = [] | ||
for task in tasks: | ||
tasks_response.append(task.to_json()) | ||
|
||
return jsonify(tasks_response), 200 | ||
|
||
# GET one task - /tasks/<id> [READ] | ||
@tasks_bp.route("/<id>", methods=["GET"]) | ||
def get_one_task(id): | ||
task = validate_task(id) | ||
return jsonify({"task":task.to_json()}), 200 | ||
|
||
#POST - /tasks [CREATE] | ||
@tasks_bp.route("", methods= ["POST"]) | ||
def create_task(): | ||
request_body = request.get_json() | ||
new_task = Task.create_dict(request_body) | ||
|
||
db.session.add(new_task) | ||
db.session.commit() | ||
|
||
return make_response({"task":new_task.to_json()}), 201 | ||
|
||
#PUT one task - /tasks/<id> [UPDATE] | ||
@tasks_bp.route("/<id>",methods=["PUT"]) | ||
def update_task(id): | ||
task = validate_task(id) | ||
request_body = request.get_json() | ||
task.update_dict(request_body) | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"task":task.to_json()}), 200 | ||
|
||
#DELETE one task - /tasks/<id> [DELETE] | ||
@tasks_bp.route("/<id>", methods=["DELETE"]) | ||
def delete_task(id): | ||
task_to_delete = validate_task(id) | ||
|
||
db.session.delete(task_to_delete) | ||
db.session.commit() | ||
|
||
message = {"details": f'Task 1 "{task_to_delete.title}" successfully deleted'} | ||
return make_response(message, 200) | ||
|
||
#PATCH /tasks/1/mark_incomplete [UPDATE] | ||
@tasks_bp.route("/<id>/mark_incomplete", methods = ["PATCH"]) | ||
def mark_incompleted(id): | ||
task_to_incomplete = validate_task(id) | ||
request_body = request.get_json() | ||
task_to_incomplete.patch_incomplete(request_body) | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"task":task_to_incomplete.to_json()}), 200 | ||
|
||
# PATCH tasks/<id>/mark_complete [UPDATE] | ||
@tasks_bp.route("/<id>/mark_complete", methods=["PATCH"]) | ||
def mark_completed(id): | ||
task_to_complete = validate_task(id) | ||
request_body = request.get_json() | ||
task_to_complete.patch_complete(request_body) | ||
|
||
db.session.commit() | ||
|
||
PATH = "https://slack.com/api/chat.postMessage" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
query_params = { | ||
"channel": "api-test-channel", | ||
"text": f'Task {task_to_complete.title} has been completed!' | ||
} | ||
|
||
the_headers = {"Authorization": os.environ.get("SLACK_API_KEY")} | ||
|
||
requests.post(PATH, params=query_params, headers=the_headers) | ||
|
||
return jsonify({"task":task_to_complete.to_json()}), 200 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Generic single-database configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice choice to split up the routes into files that are more specific to the resources they work with!
Based on the paths to the files, it looks like the route files are directly in the
app
folder. Since we have more than one route file, I'd suggest creating aroutes
directory to organize those route files in.