-
Notifications
You must be signed in to change notification settings - Fork 128
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
Raina Campbell Completed Task List #125
base: main
Are you sure you want to change the base?
Changes from all commits
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,117 @@ | ||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from .task_routes import validate_model | ||
from flask import Blueprint, jsonify, request, make_response, abort | ||
from datetime import datetime | ||
import os | ||
import requests | ||
goal_bp = Blueprint("goal_bp", __name__, url_prefix="/goals") | ||
|
||
# ----------------------------- ROUTES FOR GOAL MODEL ------------------------- | ||
|
||
# create a new goal | ||
@goal_bp.route("",methods=["POST"]) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
|
||
try: | ||
new_goal = Goal.from_dict(request_body) | ||
except: | ||
return {"details": "Invalid data"}, 400 | ||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
|
||
return { | ||
"goal":{ | ||
"id": new_goal.goal_id, | ||
"title": new_goal.title | ||
} | ||
}, 201 | ||
|
||
# One (goal) to many (tasks) relationship | ||
@goal_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def update_goal_id_for_task(goal_id): | ||
goal_from_id = validate_model(Goal, goal_id) | ||
|
||
request_body = request.get_json() | ||
|
||
for i in request_body["task_ids"]: | ||
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. Love this! |
||
task_to_update = validate_model(Task, i) | ||
task_to_update.goal = goal_from_id | ||
db.session.commit() | ||
|
||
return make_response(jsonify({ | ||
"id": int(goal_id), | ||
"task_ids": request_body["task_ids"] | ||
})), 200 | ||
|
||
|
||
@goal_bp.route("", methods=["GET"]) | ||
def read_all_goals(): | ||
goals = Goal.query.all() | ||
goals_response = [] | ||
|
||
for goal in goals: | ||
goals_response.append(goal.to_dict()) | ||
Comment on lines
+56
to
+57
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. How would your code look different if you were to implement this into a class function? |
||
|
||
return jsonify(goals_response), 200 | ||
|
||
|
||
@goal_bp.route("/<goal_id>", methods=["GET"]) | ||
def read_one_task(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
return { | ||
"goal":{ | ||
"id": goal.goal_id, | ||
"title": goal.title | ||
} | ||
}, 200 | ||
|
||
@goal_bp.route("/<goal_id>", methods=["PUT"]) | ||
def update_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
request_body = request.get_json() | ||
|
||
goal.title = request_body["title"] | ||
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. Right now, if a user sends a request without the key title your server would crash. There's a couple of ways to handle this, you could call the |
||
|
||
db.session.commit() | ||
|
||
return {"goal": goal.to_dict()} | ||
|
||
|
||
|
||
@goal_bp.route("/<goal_id>", methods=["DELETE"]) | ||
def delete_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
db.session.delete(goal) | ||
db.session.commit() | ||
|
||
return make_response({"details":f'Goal {goal.goal_id} "{goal.title}" successfully deleted'}, 200) | ||
|
||
|
||
# get tasks for a speciic goal | ||
@goal_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def get_tasks_for_specific_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
tasks_response = [] | ||
|
||
for task in goal.tasks: | ||
tasks_response.append({ | ||
"id": task.task_id, | ||
"goal_id": task.goal_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": (task.completed_at != None) | ||
}) | ||
|
||
return { | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
"tasks": tasks_response | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,28 @@ | ||
from app import db | ||
|
||
|
||
# create Task model | ||
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) | ||
goal_id = db.Column(db.Integer, db.ForeignKey('goal.goal_id'), nullable = True) | ||
goal = db.relationship("Goal", back_populates = "tasks") | ||
|
||
def to_dict(self): | ||
task_as_dict = {} | ||
task_as_dict["id"] = self.task_id | ||
task_as_dict["title"] = self.title | ||
task_as_dict["description"] = self.description | ||
task_as_dict["is_complete"] = (self.completed_at != None) | ||
|
||
return task_as_dict | ||
|
||
@classmethod | ||
def from_dict(cls, task_data): | ||
new_task = Task( | ||
title=task_data["title"], | ||
description=task_data["description"] | ||
) | ||
|
||
return new_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. ⭐️ |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
import requests | ||
from datetime import * | ||
import os | ||
|
||
|
||
# register task bp | ||
task_bp = Blueprint("task_bp", __name__, url_prefix="/tasks") | ||
SLACK_BOT_TOKEN = os.environ.get('SLACK_BOT_TOKEN') | ||
|
||
# validate task by id! | ||
def validate_model(cls, model_id): | ||
try: | ||
model_id = int(model_id) | ||
except: | ||
abort(make_response({"message":f"{cls.__name__} {model_id} invalid"}, 400)) | ||
|
||
model = cls.query.get(model_id) | ||
|
||
if not model: | ||
abort(make_response({"message":f"{cls.__name__} {model_id} not found"}, 404)) | ||
|
||
return model | ||
|
||
# create task as a post request | ||
@task_bp.route("",methods=["POST"]) | ||
def create_task(): | ||
request_body = request.get_json() | ||
|
||
#try except | ||
try: | ||
task = Task.from_dict(request_body) | ||
except: | ||
return {"details": "Invalid data"}, 400 | ||
|
||
db.session.add(task) | ||
db.session.commit() | ||
|
||
return {"task":{ | ||
"id": task.task_id, | ||
"description": task.description, | ||
"is_complete": (task.completed_at != None), | ||
"title": task.title}}, 201 | ||
|
||
|
||
# read all tasks | ||
@task_bp.route("",methods=["GET"]) | ||
def read_all_tasks(): | ||
tasks = Task.query.all() | ||
|
||
# ascending sort | ||
sorting_query = request.args.get("sort") | ||
if sorting_query == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()).all() | ||
elif sorting_query == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()).all() | ||
else: | ||
tasks = Task.query.all() | ||
Comment on lines
+56
to
+61
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 on making the database do the heavy lifting when it comes to sorting the tasks! |
||
|
||
tasks_response = [] | ||
for task in tasks: | ||
tasks_response.append(task.to_dict()) | ||
|
||
return jsonify(tasks_response), 200 | ||
|
||
|
||
# read one task | ||
@task_bp.route("/<task_id>", methods=["GET"]) | ||
def read_one_task(task_id): | ||
task = validate_model(Task, task_id) | ||
if task.goal_id: | ||
return { | ||
"task":{ | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"goal_id": task.goal_id, | ||
"is_complete": (task.completed_at != None) | ||
} | ||
} | ||
else: | ||
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 way you could implement this by changing your |
||
return { | ||
"task":{ | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": (task.completed_at != None) | ||
} | ||
} | ||
|
||
|
||
# if task.goal_id != None: | ||
# result["task"]["goal_id"] = task.goal_id | ||
# return result, 200 | ||
|
||
|
||
# update a task | ||
@task_bp.route("/<task_id>", methods=["PUT"]) | ||
def update_task(task_id): | ||
task_to_update = validate_model(Task, task_id) | ||
|
||
request_body = request.get_json() | ||
|
||
task_to_update.title = request_body["title"] | ||
task_to_update.description = request_body["description"] | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"task":task_to_update.to_dict()}), 200 | ||
|
||
|
||
# patch request to mark complete on incompleted task | ||
@task_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||
def update_task_to_completed(task_id): | ||
task = validate_model(Task, task_id) | ||
|
||
if task.completed_at is None: | ||
task.completed_at = datetime.utcnow() | ||
|
||
slack_message = f"Someone just completed the task {task.title}" | ||
# add header | ||
header = {"Authorization": f"Bearer {SLACK_BOT_TOKEN}"} | ||
# add response | ||
json_body = { | ||
"channel": "C057EA9H4G7", | ||
"text": slack_message | ||
} | ||
response = requests.post("https://slack.com/api/chat.postMessage", headers=header, json=json_body) | ||
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. Our code could possibly fail while trying to commit the change to our database and therefore sending the message to Slack could be a false positive. We should send alerts like these at the very end of our logic just in case something goes wrong. |
||
|
||
# if the status code from response does not indicate successful action | ||
# if response.status_code != 200: | ||
# # send failure message | ||
# abort(make_response({"details": "Failed to send message to Slack"}, 500)) | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"task": task.to_dict()}), 200 | ||
|
||
@task_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||
def update_task_incomplete(task_id): | ||
task = validate_model(Task, task_id) | ||
|
||
if task.completed_at != None: | ||
task.completed_at = None | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"task":task.to_dict()}), 200 | ||
|
||
# delete a task | ||
@task_bp.route("/<task_id>", methods=["DELETE"]) | ||
def delete_task(task_id): | ||
|
||
task = validate_model(Task, task_id) | ||
|
||
db.session.delete(task) | ||
db.session.commit() | ||
|
||
return abort(make_response({"details":f"Task {task_id} \"{task.title}\" successfully deleted"}, 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. Well done on this project, I didn't have much to comment on and that is a good thing! Keep up the good work! Really looking forward to what you create in the frontend! Please feel free to reach out if you have any questions about the feedback that I left! ✨💫🤭 |
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.
Like we validated models, could we also make a general function that validates request bodies? Here's an example:
We can pass in the
request_body
and a list of strings that arekeys
and then check to see if those keys are present.