-
Notifications
You must be signed in to change notification settings - Fork 71
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
Scissors - Melissa Nguyen #62
base: master
Are you sure you want to change the base?
Changes from all commits
707ed5d
b72744d
b5be11c
8e75757
e68b079
9a6833f
6328a77
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 @@ | ||
web: gunicorn 'app:create_app()' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,275 @@ | ||
from flask import Blueprint | ||
import requests | ||
import os | ||
from flask import request, Blueprint, make_response, jsonify | ||
|
||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from datetime import datetime | ||
|
||
|
||
tasks_bp = Blueprint("tasks", __name__, url_prefix= "/tasks") | ||
|
||
@tasks_bp.route("", methods=["POST", "GET"]) | ||
def create_task(): | ||
|
||
if request.method == "POST": | ||
request_body = request.get_json() | ||
if "title" not in request_body or "description" not in request_body or "completed_at" not in request_body: | ||
return jsonify({"details": "Invalid data"}), 400 | ||
else: | ||
new_task = Task(title=request_body["title"], description=request_body["description"], completed_at=request_body["completed_at"]) | ||
|
||
db.session.add(new_task) | ||
db.session.commit() | ||
|
||
return make_response({ | ||
"task": { | ||
"id": new_task.task_id, | ||
"title": new_task.title, | ||
"description": new_task.description, | ||
"is_complete": new_task.task_completed() | ||
} | ||
Comment on lines
+27
to
+32
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. Notice that this code is repeated many places throughout your |
||
}, 201) | ||
|
||
elif request.method == "GET": | ||
task_query = request.args.get("sort") | ||
if task_query == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()).all() | ||
elif task_query == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()).all() | ||
else: | ||
tasks = Task.query.all() | ||
|
||
tasks_response = [] | ||
for task in tasks: | ||
tasks_response.append({ | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed() | ||
}) | ||
return jsonify(tasks_response), 200 | ||
|
||
@tasks_bp.route("/<task_id>", methods=["GET", "PUT", "DELETE"]) | ||
def specific_task(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task == None: | ||
return make_response(), 404 | ||
|
||
elif request.method == "GET": | ||
|
||
if task.goal == None: | ||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed() | ||
} | ||
}), 200 | ||
|
||
else: | ||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"goal_id": task.goal_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed() | ||
} | ||
}), 200 | ||
|
||
elif request.method == "PUT": | ||
form_data = request.get_json() | ||
|
||
task.title = form_data["title"] | ||
task.description = form_data["description"] | ||
task.completed_at = form_data["completed_at"] | ||
db.session.commit() | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed() | ||
} | ||
}), 200 | ||
|
||
elif request.method == "DELETE": | ||
db.session.delete(task) | ||
db.session.commit() | ||
return jsonify({"details": f'Task {task.task_id} "{task.title}" successfully deleted'}), 200 | ||
|
||
def slack_message_bot(task): | ||
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 work making a helper function. |
||
|
||
params = { | ||
"channel": "task-notifications", | ||
"text": f"Someone just completed the task {task.task_id}" | ||
} | ||
|
||
headers = { | ||
"Authorization": f"Bearer {os.environ.get('SLACK_API_KEY')}" | ||
} | ||
|
||
requests.post("https://slack.com/api/chat.postMessage", data=params, headers=headers) | ||
|
||
|
||
@tasks_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||
def mark_complete(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task == None: | ||
return make_response(), 404 | ||
|
||
task.completed_at = datetime.utcnow() | ||
db.session.commit() | ||
|
||
slack_message_bot(task) | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed() | ||
} | ||
}), 200 | ||
|
||
@tasks_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||
def mark_incomplete(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task == None: | ||
return make_response(), 404 | ||
Comment on lines
+145
to
+146
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. You have this code 3 places. Consider making a helper function, or even using Also, this is a very minor note, but I just learned this cohort that the python-y way to say |
||
|
||
task.completed_at = None | ||
db.session.commit() | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed() | ||
} | ||
}), 200 | ||
Comment on lines
+151
to
+158
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. Sometimes your code uses |
||
|
||
goals_bp = Blueprint("goals", __name__, url_prefix= "/goals") | ||
@goals_bp.route("", methods=["POST", "GET"]) | ||
def create_goal(): | ||
if request.method == "POST": | ||
request_body = request.get_json() | ||
if "title" not in request_body: | ||
return jsonify({"details": "Invalid data"}), 400 | ||
else: | ||
new_goal = Goal(title=request_body["title"]) | ||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
Comment on lines
+167
to
+171
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. Minor note: this does not need to be nested in an |
||
|
||
return make_response({ | ||
"goal": { | ||
"id": new_goal.goal_id, | ||
"title": new_goal.title, | ||
} | ||
}, 201) | ||
|
||
elif request.method == "GET": | ||
goals = Goal.query.all() | ||
goals_response = [] | ||
|
||
for goal in goals: | ||
goals_response.append({ | ||
"id": goal.goal_id, | ||
"title": goal.title | ||
}) | ||
Comment on lines
+173
to
+188
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. Just as with the task response body, note that the goal response body is used a few times. Consider moving this logic to an instance method on the goal model that returns the needed dictionary, and then calling it where needed with something like |
||
|
||
return jsonify(goals_response), 200 | ||
|
||
@goals_bp.route("/<goal_id>", methods=["GET", "PUT", "DELETE"]) | ||
def specific_goal(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
|
||
if goal == None: | ||
return make_response(), 404 | ||
|
||
elif request.method == "GET": | ||
return jsonify({ | ||
"goal": { | ||
"id": goal.goal_id, | ||
"title": goal.title | ||
} | ||
}), 200 | ||
|
||
elif request.method == "PUT": | ||
form_data = request.get_json() | ||
|
||
goal.title = form_data["title"] | ||
db.session.commit() | ||
|
||
return jsonify({ | ||
"goal": { | ||
"id": goal.goal_id, | ||
"title": goal.title | ||
} | ||
}), 200 | ||
|
||
|
||
elif request.method == "DELETE": | ||
db.session.delete(goal) | ||
db.session.commit() | ||
return jsonify({"details": f'Goal {goal.goal_id} "{goal.title}" successfully deleted'}), 200 | ||
|
||
@goals_bp.route("<goal_id>/tasks", methods=["POST", "GET"]) | ||
def goal_to_task(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
|
||
if goal is None: | ||
return make_response("", 404) | ||
|
||
if request.method == "POST": | ||
request_body = request.get_json() | ||
|
||
for task_id in request_body["task_ids"]: | ||
task = Task.query.get(task_id) | ||
task.goal_id = goal.goal_id | ||
Comment on lines
+236
to
+238
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. Nice work writing this logic to make the relationship between goals and tasks. |
||
|
||
return jsonify({ | ||
"id": goal.goal_id, | ||
"task_ids": request_body["task_ids"] | ||
}), 200 | ||
|
||
|
||
elif request.method == "GET": | ||
tasks = Task.query.filter_by(goal_id=goal.goal_id) | ||
|
||
connected_tasks = [] | ||
for task in tasks: | ||
connected_tasks.append({ | ||
"id": task.task_id, | ||
"goal_id": task.goal_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.task_completed(), | ||
}) | ||
|
||
return jsonify({ | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
"tasks": connected_tasks | ||
}), 200 | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Generic single-database configuration. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# A generic, single database configuration. | ||
|
||
[alembic] | ||
# template used to generate migration files | ||
# file_template = %%(rev)s_%%(slug)s | ||
|
||
# set to 'true' to run the environment during | ||
# the 'revision' command, regardless of autogenerate | ||
# revision_environment = false | ||
|
||
|
||
# Logging configuration | ||
[loggers] | ||
keys = root,sqlalchemy,alembic | ||
|
||
[handlers] | ||
keys = console | ||
|
||
[formatters] | ||
keys = generic | ||
|
||
[logger_root] | ||
level = WARN | ||
handlers = console | ||
qualname = | ||
|
||
[logger_sqlalchemy] | ||
level = WARN | ||
handlers = | ||
qualname = sqlalchemy.engine | ||
|
||
[logger_alembic] | ||
level = INFO | ||
handlers = | ||
qualname = alembic | ||
|
||
[handler_console] | ||
class = StreamHandler | ||
args = (sys.stderr,) | ||
level = NOTSET | ||
formatter = generic | ||
|
||
[formatter_generic] | ||
format = %(levelname)-5.5s [%(name)s] %(message)s | ||
datefmt = %H:%M:%S |
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 work using this helper function.