-
Notifications
You must be signed in to change notification settings - Fork 146
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
Lions - Task List - Nina-Tuyen Tran #132
base: master
Are you sure you want to change the base?
Changes from all commits
1ad7560
61d7e7f
118fa91
a160f1c
96613d7
eb851b4
dd3b22f
8729de5
b4f8fdc
81bda6c
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 |
---|---|---|
@@ -0,0 +1,114 @@ | ||
from app import db | ||
from app.models.goal import Goal | ||
from app.models.task import Task | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
import requests | ||
from app.helper_routes import get_one_obj_or_abort | ||
|
||
goal_bp = Blueprint("goal_bp", __name__, url_prefix="/goals") | ||
#--------------------------------POST------------------------------- | ||
@goal_bp.route("", methods=["POST"]) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body: | ||
return jsonify({"details": "Invalid data"}), 400 | ||
|
||
new_goal = Goal(title = request_body["title"]) | ||
|
||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
|
||
|
||
return jsonify({"goal": new_goal.to_dict()}), 201 | ||
|
||
@goal_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def send_a_task_to_goal(goal_id): | ||
request_body = request.get_json() | ||
|
||
chosen_goal = get_one_obj_or_abort(Goal, goal_id) | ||
list_of_chosen_tasks = [get_one_obj_or_abort(Task, task_id) for task_id in request_body["task_ids"]] | ||
# for task_id in request_body["task_ids"]: | ||
# chosen_task = get_one_obj_or_abort(Task, task_id) | ||
# list_of_chosen_tasks.append(chosen_task) | ||
|
||
chosen_goal.tasks = list_of_chosen_tasks | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"id": int(goal_id), "task_ids": request_body["task_ids"]}), 200 | ||
|
||
|
||
#--------------------------------GET------------------------------- | ||
@goal_bp.route("", methods=["GET"]) | ||
def get_all_saved_goals(): | ||
goals = Goal.query.all() | ||
|
||
response = [] | ||
for goal in goals: | ||
goal_dict = { | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
} | ||
response.append(goal_dict) | ||
|
||
return jsonify(response), 200 | ||
|
||
@goal_bp.route("/<goal_id>", methods=["GET"]) | ||
def get_one_saved_goal(goal_id): | ||
chosen_goal = get_one_obj_or_abort(Goal, goal_id) | ||
goal = {"id": chosen_goal.goal_id, "title": chosen_goal.title} | ||
return jsonify({"goal": goal}), 200 | ||
|
||
|
||
@goal_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def get_tasks_of_one_goal(goal_id): | ||
chosen_goal = get_one_obj_or_abort(Goal, goal_id) | ||
#tasks = Task.query.filter_by(goal_id=goal_id) | ||
list_of_tasks = [task.to_dict() for task in chosen_goal.tasks] | ||
#list_of_tasks = [task.to_dict() for task in tasks] | ||
# list_of_tasks = [] | ||
# for task in tasks: | ||
# task_dict = { | ||
# "id": task.id, | ||
# "title": task.title, | ||
# "description": task.description, | ||
# "goal_id": int(goal_id), | ||
# "is_complete": task.is_complete | ||
|
||
# } | ||
# list_of_tasks.append(task_dict) | ||
|
||
chosen_goal_dict = { | ||
"id":chosen_goal.goal_id, | ||
"title":chosen_goal.title, | ||
"tasks": list_of_tasks | ||
} | ||
Comment on lines
+83
to
+87
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. Turning a goal into a dict would be a great helper method in the model like the to_dict function you already have. |
||
|
||
return jsonify(chosen_goal_dict), 200 | ||
|
||
#--------------------------------PUT------------------------------- | ||
@goal_bp.route("/<goal_id>", methods=["PUT"]) | ||
def update_goal(goal_id): | ||
|
||
chosen_goal = get_one_obj_or_abort(Goal, goal_id) | ||
request_body = request.get_json() | ||
|
||
chosen_goal.title = request_body["title"] | ||
db.session.commit() | ||
|
||
return jsonify({"goal": chosen_goal.to_dict()}), 200 | ||
|
||
#---------------------------------------DELETE------------------------------------------------ | ||
@goal_bp.route("/<goal_id>", methods=["DELETE"]) | ||
def delete_one_task(goal_id): | ||
chosen_goal = get_one_obj_or_abort(Goal, goal_id) | ||
|
||
db.session.delete(chosen_goal) | ||
db.session.commit() | ||
|
||
return jsonify({"details": f'Goal {goal_id} "{chosen_goal.title}" successfully deleted'}), 200 | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from flask import jsonify, abort, make_response | ||
|
||
def get_one_obj_or_abort(cls, obj_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. Great helper function |
||
try: | ||
obj_id = int(obj_id) | ||
except ValueError: | ||
response_str = f"Invalid ID. Please submit an integer for the ID." | ||
abort(make_response(jsonify({"Message":response_str}), 400)) | ||
|
||
matching_obj = cls.query.get(obj_id) | ||
|
||
if not matching_obj: | ||
response_str = f"{cls.__name__} with id #{obj_id} was not found in the database." | ||
abort(make_response(jsonify({"Message": response_str}), 404)) | ||
|
||
return matching_obj |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,34 @@ | ||
from app import db | ||
|
||
|
||
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) | ||
description = db.Column(db.String) | ||
completed_at = db.Column(db.DateTime, nullable=True) | ||
is_complete = db.Column(db.Boolean, nullable=True) | ||
Comment on lines
+7
to
+8
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. I don't think you need two fields for this, one would do and it makes things more complicated to keep them in sync. That said it doesn't hurt much. |
||
goal_id = db.Column(db.Integer, db.ForeignKey("goal.goal_id"), nullable=True) | ||
goal = db.relationship("Goal", back_populates = "tasks") | ||
|
||
|
||
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. Another helpful method might be a class method which takes a dictionary as an argument and creates an instance of the model. |
||
def to_dict(self): | ||
task_dict = { | ||
"id": self.task_id, | ||
"goal_id": self.goal_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": self.is_complete | ||
} | ||
|
||
if self.is_complete is None: | ||
task_dict["is_complete"] = False | ||
else: | ||
task_dict["is_complete"] = True | ||
|
||
if self.goal_id is None: | ||
task_dict.pop("goal_id") | ||
|
||
return task_dict | ||
|
||
|
||
|
||
|
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,190 @@ | ||||||||||||||||||||||||||||||||||
from app import db | ||||||||||||||||||||||||||||||||||
from app.models.task import Task | ||||||||||||||||||||||||||||||||||
from flask import Blueprint, jsonify, abort, make_response, request | ||||||||||||||||||||||||||||||||||
import requests | ||||||||||||||||||||||||||||||||||
from datetime import datetime | ||||||||||||||||||||||||||||||||||
import os | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
task_bp = Blueprint("task_bp", __name__, url_prefix="/tasks") | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
#---------------------------------------GET------------------------------------------------ | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
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. Great 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=["GET"]) | ||||||||||||||||||||||||||||||||||
def read_all_tasks(): | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
sort_param = request.args.get("sort") | ||||||||||||||||||||||||||||||||||
if sort_param == "asc": | ||||||||||||||||||||||||||||||||||
tasks = Task.query.order_by(Task.title.asc()).all() | ||||||||||||||||||||||||||||||||||
elif sort_param is None: | ||||||||||||||||||||||||||||||||||
tasks = Task.query.all() | ||||||||||||||||||||||||||||||||||
elif sort_param == "desc": | ||||||||||||||||||||||||||||||||||
tasks = Task.query.order_by(Task.title.desc()).all() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
response = [] | ||||||||||||||||||||||||||||||||||
for task in tasks: | ||||||||||||||||||||||||||||||||||
task_dict = { | ||||||||||||||||||||||||||||||||||
"id": task.task_id, | ||||||||||||||||||||||||||||||||||
"title": task.title, | ||||||||||||||||||||||||||||||||||
"description": task.description, | ||||||||||||||||||||||||||||||||||
"is_complete": False | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
response.append(task_dict) | ||||||||||||||||||||||||||||||||||
return jsonify(response), 200 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
name_param = request.args.get("title") | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if name_param is None: | ||||||||||||||||||||||||||||||||||
tasks = Task.query.all() | ||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||
tasks = Task.query.filter_by(title=name_param) | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
response = [] | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
for task in tasks: | ||||||||||||||||||||||||||||||||||
if task.completed_at is None: | ||||||||||||||||||||||||||||||||||
task_dict = { | ||||||||||||||||||||||||||||||||||
"id": task.task_id, | ||||||||||||||||||||||||||||||||||
"title": task.title, | ||||||||||||||||||||||||||||||||||
"description": task.description, | ||||||||||||||||||||||||||||||||||
"is_complete": False | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
response.append(task_dict) | ||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||
task_dict = { | ||||||||||||||||||||||||||||||||||
"id": task.task_id, | ||||||||||||||||||||||||||||||||||
"title": task.title, | ||||||||||||||||||||||||||||||||||
"description": task.description, | ||||||||||||||||||||||||||||||||||
"completed at": task.completed_at, | ||||||||||||||||||||||||||||||||||
"is_complete": True} | ||||||||||||||||||||||||||||||||||
Comment on lines
+62
to
+75
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 could be compressed a bit you could just make a local variable set to be equal to |
||||||||||||||||||||||||||||||||||
response.append(task_dict) | ||||||||||||||||||||||||||||||||||
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) | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
# if chosen_task.completed_at is None: | ||||||||||||||||||||||||||||||||||
# task = { | ||||||||||||||||||||||||||||||||||
# "id": chosen_task.task_id, | ||||||||||||||||||||||||||||||||||
# "title": chosen_task.title, | ||||||||||||||||||||||||||||||||||
# "description": chosen_task.description, | ||||||||||||||||||||||||||||||||||
# "is_complete": False | ||||||||||||||||||||||||||||||||||
# } | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
# else: | ||||||||||||||||||||||||||||||||||
# task = { | ||||||||||||||||||||||||||||||||||
# "id": chosen_task.task_id, | ||||||||||||||||||||||||||||||||||
# "title": chosen_task.title, | ||||||||||||||||||||||||||||||||||
# "description": chosen_task.description, | ||||||||||||||||||||||||||||||||||
# "completed at": chosen_task.completed_at, | ||||||||||||||||||||||||||||||||||
# "is_complete": True} | ||||||||||||||||||||||||||||||||||
Comment on lines
+82
to
+97
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.
Suggested change
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return jsonify({"task": chosen_task.to_dict()}), 200 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
#-------------------------------------POST------------------------------------------------ | ||||||||||||||||||||||||||||||||||
@task_bp.route("", methods=["POST"]) | ||||||||||||||||||||||||||||||||||
def create_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
+109
to
+111
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 that you're doing data validation here, but I suggest telling the end user what is invalid about the data. |
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
new_task = Task(title = request_body["title"], description = request_body["description"]) | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
db.session.add(new_task) | ||||||||||||||||||||||||||||||||||
db.session.commit() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return jsonify({"task": new_task.to_dict()}), 201 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
#---------------------------------------UPDATE------------------------------------------------ | ||||||||||||||||||||||||||||||||||
@task_bp.route("/<task_id>", methods=["PUT"]) | ||||||||||||||||||||||||||||||||||
def update_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"}) | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
chosen_task.title = request_body["title"] | ||||||||||||||||||||||||||||||||||
chosen_task.description = request_body["description"] | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
db.session.commit() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return jsonify({"task": chosen_task.to_dict()}), 200 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
#---------------------------------------DELETE------------------------------------------------ | ||||||||||||||||||||||||||||||||||
@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 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
#---------------------------------------PATCH------------------------------------------------ | ||||||||||||||||||||||||||||||||||
@task_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||||||||||||||||||||||||||||||||||
def mark_complete_on_incompleted_task(task_id): | ||||||||||||||||||||||||||||||||||
chosen_task = get_one_task_or_abort(task_id) | ||||||||||||||||||||||||||||||||||
today = datetime.now() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if chosen_task.completed_at is None: | ||||||||||||||||||||||||||||||||||
chosen_task.completed_at = today | ||||||||||||||||||||||||||||||||||
chosen_task.is_complete = True | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||
chosen_task.completed_at = today | ||||||||||||||||||||||||||||||||||
chosen_task.is_complete = True | ||||||||||||||||||||||||||||||||||
db.session.commit() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
header = os.environ.get("api_slack") | ||||||||||||||||||||||||||||||||||
url = "http://slack.com/api/chat.postMessage" | ||||||||||||||||||||||||||||||||||
response_str = f"Someone just completed the task {chosen_task.title}" | ||||||||||||||||||||||||||||||||||
data = {"channel":"task-notifications", "text": response_str} | ||||||||||||||||||||||||||||||||||
r = requests.post(url, params=data, headers={"Authorization":header}) | ||||||||||||||||||||||||||||||||||
Comment on lines
+165
to
+169
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. Interacting with Slack would make a good candidate for a helper function. |
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return jsonify({"task": chosen_task.to_dict()}), 200 | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
@task_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||||||||||||||||||||||||||||||||||
def mark_incomplete_on_completed_task(task_id): | ||||||||||||||||||||||||||||||||||
chosen_task = get_one_task_or_abort(task_id) | ||||||||||||||||||||||||||||||||||
today = datetime.now() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if chosen_task.completed_at: | ||||||||||||||||||||||||||||||||||
chosen_task.completed_at = None | ||||||||||||||||||||||||||||||||||
chosen_task.is_complete = None | ||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||
chosen_task.completed_at = None | ||||||||||||||||||||||||||||||||||
chosen_task.is_complete = None | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
db.session.commit() | ||||||||||||||||||||||||||||||||||
return jsonify({"task": chosen_task.to_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.
Nice that you're doing input validation here, but it would be good to let the user know what data was invalid.