-
Notifications
You must be signed in to change notification settings - Fork 146
Lions - Jessica Ambriz-Madrigal #127
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: master
Are you sure you want to change the base?
Changes from all commits
50d14f0
a717374
76f4558
f2afdd9
382d76e
5c9f0f6
d132827
c83d4fe
2dcdcbc
f6a7c36
281cab7
ae1193d
1969afe
cb273e5
db6f6e5
3c7a72d
07f9391
02355f9
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 |
---|---|---|
|
@@ -138,4 +138,7 @@ dmypy.json | |
.pytype/ | ||
|
||
# Cython debug symbols | ||
cython_debug/ | ||
cython_debug/ | ||
|
||
# files | ||
REMEMEBER.txt |
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 |
---|---|---|
|
@@ -2,4 +2,36 @@ | |
|
||
|
||
class Task(db.Model): | ||
task_id = db.Column(db.Integer, primary_key=True) | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
title = db.Column(db.String) | ||
description = db.Column(db.String) | ||
completed_at = db.Column(db.DateTime, nullable=True) | ||
is_complete = db.Column(db.Boolean, default=False) | ||
goal_id = db.Column(db.Integer, db.ForeignKey('goal.goal_id'), nullable = True ) | ||
goals = db.relationship('Goal', back_populates='tasks') | ||
|
||
def to_dict(self): | ||
if self.completed_at is None: | ||
if self.goal_id is None: | ||
task_dict = { | ||
"id":self.id, | ||
"title":self.title, | ||
"description":self.description, | ||
"is_complete": False | ||
} | ||
else: | ||
task_dict = { | ||
"id":self.id, | ||
"goal_id": self.goal_id, | ||
"title":self.title, | ||
"description":self.description, | ||
"is_complete": False | ||
} | ||
else: | ||
task_dict = { | ||
"id":self.id, | ||
"title":self.title, | ||
"description":self.description, | ||
"is_complete": True | ||
} | ||
return task_dict | ||
Comment on lines
+14
to
+37
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 seems a little over-complicated. Could you do the basic dictionary in one block and then add key-value pairs based on an if-else? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,275 @@ | ||
from flask import Blueprint | ||
from flask import Blueprint,jsonify, abort, make_response, request | ||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from datetime import datetime | ||
import os | ||
import requests | ||
|
||
task_bp = Blueprint("task_bp",__name__, url_prefix="/tasks") | ||
|
||
def get_one_task_or_abort(task_id): | ||
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. Good helper function |
||
try: | ||
task_id = int(task_id) | ||
except ValueError: | ||
response_str = f"Invalid task_id: `{task_id}`.Id must be an integer." | ||
abort(make_response(jsonify({'message':response_str}), 400)) | ||
|
||
matching_task = Task.query.get(task_id) | ||
|
||
if not matching_task: | ||
response_str = f"Task with id {task_id} was not found in the database." | ||
abort(make_response(jsonify({'message':response_str}), 404)) | ||
return matching_task | ||
|
||
|
||
@task_bp.route("", methods=["POST"]) | ||
def add_task(): | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body or \ | ||
"description" not in request_body: | ||
return jsonify({"details": "Invalid data"}), 400 | ||
Comment on lines
+30
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. Good validation here. However I think you should also indicate what data is invalid. |
||
|
||
new_task = Task( | ||
title = request_body["title"], | ||
description = request_body["description"]) | ||
|
||
db.session.add(new_task) | ||
|
||
db.session.commit() | ||
|
||
return { | ||
"task":{ | ||
"id" : new_task.id, | ||
"title" : new_task.title, | ||
"description" : new_task.description, | ||
"is_complete" : new_task.is_complete | ||
} | ||
}, 201 | ||
Comment on lines
+42
to
+49
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 return value might be useful as a helper function for reuse. |
||
|
||
@task_bp.route("", methods=["GET"]) | ||
def get_all_tasks(): | ||
title_param = request.args.get("title") | ||
|
||
sort_param = request.args.get("sort") | ||
|
||
if not title_param: | ||
tasks = Task.query.all() | ||
|
||
if sort_param == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()).all() | ||
|
||
if sort_param == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()).all() | ||
Comment on lines
+57
to
+64
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 might cause |
||
|
||
response = [task.to_dict() for task in tasks] | ||
return jsonify(response), 200 | ||
|
||
|
||
@task_bp.route("/<task_id>", methods=["GET"]) | ||
def get_one_task(task_id): | ||
chosen_task = get_one_task_or_abort(task_id) | ||
return jsonify({"task":Task.to_dict(chosen_task)}), 200 | ||
|
||
@task_bp.route("/<task_id>", methods=["PUT"]) | ||
def update_one_task(task_id): | ||
chosen_task = get_one_task_or_abort(task_id) | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body or \ | ||
"description" not in request_body: | ||
return jsonify({"message": "Request must include title and description."}), 400 | ||
|
||
chosen_task.title = request_body["title"] | ||
chosen_task.description = request_body["description"] | ||
|
||
db.session.commit() | ||
|
||
return { | ||
"task":{ | ||
"id" : chosen_task.id, | ||
"title" : chosen_task.title, | ||
"description" : chosen_task.description, | ||
"is_complete" : chosen_task.is_complete | ||
} | ||
}, 200 | ||
|
||
@task_bp.route("/<task_id>", methods=["DELETE"]) | ||
def delete_one_task(task_id): | ||
chosen_task = get_one_task_or_abort(task_id) | ||
|
||
db.session.delete(chosen_task) | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"details": f'Task {task_id} "{chosen_task.title}" successfully deleted'}), 200 | ||
|
||
|
||
@task_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||
def update_is_complete(task_id): | ||
update_is_valid_id = get_one_task_or_abort(task_id) | ||
|
||
update_is_valid_id.completed_at = datetime.today() | ||
db.session.add(update_is_valid_id) | ||
db.session.commit() | ||
|
||
SLACK_PATH = "https://slack.com/api/chat.postMessage" | ||
SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN") | ||
|
||
headers = { | ||
"Authorization" : f"Bearer {SLACK_BOT_TOKEN}" | ||
} | ||
params = { | ||
"channel": "task-notifications", | ||
"text": f"Someone just completed the task {update_is_valid_id.title}" | ||
} | ||
|
||
requests.get(url=SLACK_PATH, headers=headers, params=params) | ||
Comment on lines
+117
to
+128
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. The Slack portion would make a good candidate for a helper function. |
||
|
||
|
||
return jsonify({"task":Task.to_dict(update_is_valid_id)}), 200 | ||
|
||
@task_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||
def update_incompete(task_id): | ||
task_incompete = get_one_task_or_abort(task_id) | ||
task_incompete.completed_at = None | ||
|
||
db.session.add(task_incompete) | ||
db.session.commit() | ||
|
||
return jsonify({"task":Task.to_dict(task_incompete)}), 200 | ||
|
||
|
||
# ################################################################### | ||
goal_bp = Blueprint("goal_bp",__name__, url_prefix="/goals") | ||
|
||
def get_one_goal_or_abort(id): | ||
try: | ||
id = int(id) | ||
except ValueError: | ||
response_str = f"Invalid id: `{id}`.Id must be an integer." | ||
abort(make_response(jsonify({'message':response_str}), 400)) | ||
|
||
matching_goal = Goal.query.get(id) | ||
|
||
if not matching_goal: | ||
response_str = f"Goal with id {id} was not found in the database." | ||
abort(make_response(jsonify({'message':response_str}), 404)) | ||
return matching_goal | ||
|
||
@goal_bp.route("", methods=["POST"]) | ||
def add_goal(): | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body: | ||
return jsonify({"details": "Invalid data"}), 400 | ||
Comment on lines
+165
to
+166
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. Again indicating what data is invalid would be useful. |
||
|
||
new_goal = Goal( | ||
title = request_body["title"]) | ||
|
||
db.session.add(new_goal) | ||
|
||
db.session.commit() | ||
|
||
return { | ||
"goal":{ | ||
"id" : new_goal.goal_id, | ||
"title" : new_goal.title | ||
} | ||
}, 201 | ||
|
||
|
||
@goal_bp.route("", methods=["GET"]) | ||
def get_all_goals(): | ||
|
||
goals = Goal.query.all() | ||
|
||
response = [Goal.to_dict(goal) for goal in goals] | ||
return jsonify(response), 200 | ||
|
||
|
||
@goal_bp.route("/<id>", methods=["GET"]) | ||
def get_one_goal(id): | ||
chosen_goal = get_one_goal_or_abort(id) | ||
goal_dict = { | ||
"goal":{ | ||
"id" : chosen_goal.goal_id, | ||
"title" : chosen_goal.title | ||
} | ||
} | ||
|
||
return jsonify(goal_dict), 200 | ||
|
||
@goal_bp.route("/<goal_id>", methods=["PUT"]) | ||
def update_one_task(goal_id): | ||
chosen_goal = get_one_goal_or_abort(goal_id) | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body: | ||
return jsonify({"message": "request must include title."}), 400 | ||
|
||
chosen_goal.title = request_body["title"] | ||
|
||
db.session.commit() | ||
|
||
return { | ||
"goal":{ | ||
"id" : chosen_goal.goal_id, | ||
"title" : chosen_goal.title | ||
} | ||
}, 200 | ||
|
||
@goal_bp.route("/<goal_id>", methods=["DELETE"]) | ||
def delete_one_goal(goal_id): | ||
chosen_goal = get_one_goal_or_abort(goal_id) | ||
|
||
db.session.delete(chosen_goal) | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"details": f'Goal {goal_id} "{chosen_goal.title}" successfully deleted'}), 200 | ||
|
||
# ################################################################### | ||
|
||
# POST | ||
@goal_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def post_task_ids_to_goal(goal_id): | ||
|
||
big_goal_id = get_one_goal_or_abort(goal_id) | ||
|
||
request_body = request.get_json() | ||
|
||
for task_id in request_body["task_ids"]: | ||
valid_task = get_one_task_or_abort(task_id) | ||
valid_task.goals = big_goal_id | ||
db.session.commit() | ||
|
||
big_goal_task_id_list = [] | ||
for task in big_goal_id.tasks: | ||
big_goal_task_id_list.append(task.id) | ||
|
||
new_goal_with_task = { | ||
"id" : big_goal_id.goal_id, | ||
"task_ids": big_goal_task_id_list | ||
} | ||
|
||
|
||
return jsonify(new_goal_with_task), 200 | ||
|
||
# # GET | ||
@goal_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def get_one_goal_with_tasks(goal_id): | ||
chosen_big_goal_id = get_one_goal_or_abort(goal_id) | ||
|
||
big_goal_task_id_list = [] | ||
for task in chosen_big_goal_id.tasks: | ||
big_goal_task_id_list.append(Task.to_dict(task)) | ||
|
||
goal_dict = { | ||
"id" : chosen_big_goal_id.goal_id, | ||
"title" : chosen_big_goal_id.title, | ||
"tasks": big_goal_task_id_list | ||
} | ||
|
||
return jsonify(goal_dict), 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.