-
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
Amethyst - Hannah Watkins #132
base: main
Are you sure you want to change the base?
Changes from all commits
ee10423
fcbfa13
4553537
eb33fc2
f6af1c4
6bb5571
8fc3155
b0a3327
f843d3d
3a390dc
69a0bca
323aa4a
4070498
82e5587
c6302f7
520f562
877bebc
424ac44
1e56dd3
b3f7133
fab2dc0
47c2dc6
b208f6f
c2ac9d3
c55a1d6
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 |
---|---|---|
@@ -1,5 +1,12 @@ | ||
from app import db | ||
|
||
|
||
class Goal(db.Model): | ||
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 job completing Task List Hannah! Your code looks clean and easily readable. I want to point out the good variable naming and your code is DRY. Great use of helper methods in your models! Excellent work using blue prints & creating RESTful CRUD routes for each model. |
||
goal_id = db.Column(db.Integer, primary_key=True) | ||
title = db.Column(db.String(56)) | ||
tasks = db.relationship('Task', backref='goal', lazy=True) | ||
|
||
def to_dict(self): | ||
return{ | ||
"id": self.goal_id, | ||
"title": self.title, | ||
} | ||
Comment on lines
+8
to
+12
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 job creating this reusable helper function |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,22 @@ | ||
from app import db | ||
|
||
|
||
# create task model, define 'Task' using SQLAlchemy | ||
class Task(db.Model): | ||
task_id = db.Column(db.Integer, primary_key=True) | ||
title = db.Column(db.String(56)) | ||
description = db.Column(db.String(200)) | ||
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) | ||
Comment on lines
+9
to
+10
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 approach creating this relationship to your Goal model. |
||
def to_dict(self): | ||
task_dict = { | ||
"id": self.task_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": True if self.completed_at else False | ||
} | ||
|
||
if self.goal_id: | ||
task_dict["goal_id"] = self.goal_id | ||
|
||
return task_dict |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,306 @@ | ||
from flask import Blueprint | ||
from flask import Blueprint, jsonify, request | ||
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. 👍🏾 These imports help a great deal when it comes to our request & response cycle |
||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from datetime import datetime | ||
import requests | ||
|
||
tasks_bp = Blueprint("tasks", __name__, url_prefix=("/tasks")) | ||
goals_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
|
||
# create route (get) to get all tasks | ||
@tasks_bp.route("", methods=["GET"]) | ||
def get_all_tasks(): | ||
tasks_response = [] | ||
sort = request.args.get("sort") | ||
|
||
if sort == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()).all() | ||
elif sort == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()).all() | ||
else: | ||
Comment on lines
+17
to
+21
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 job with this query param logic |
||
tasks = Task.query.all() | ||
|
||
for task in tasks: | ||
tasks_response.append({ | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.completed_at != None | ||
}) | ||
|
||
return jsonify(tasks_response) | ||
|
||
|
||
# create a task | ||
@tasks_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 | ||
|
||
new_task = Task( | ||
title = request_body.get("title"), | ||
description = request_body.get("description"), | ||
completed_at = request_body.get("completed_at") | ||
) | ||
|
||
db.session.add(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. Great job using
|
||
db.session.commit() | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": new_task.task_id, | ||
"title": new_task.title, | ||
"description": new_task.description, | ||
"is_complete": new_task.completed_at != None | ||
} | ||
}), 201 | ||
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. Usually a resource creation relates to a 201 response code 👍🏾 |
||
|
||
|
||
# get a specific task | ||
@tasks_bp.route("/<task_id>", methods=["GET"]) | ||
def get_specific_task(task_id): | ||
task = Task.query.get_or_404(task_id) | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.desciption, | ||
"is_complete": task.completed_at != None | ||
} | ||
}) | ||
|
||
|
||
|
||
# update a task | ||
@tasks_bp.route("/<task_id>", methods=["PUT"]) | ||
def update_task(task_id): | ||
task = Task.query.get(task_id) | ||
request_body = request.get_json() | ||
|
||
if task is None: | ||
return jsonify({"details": f"Task {task_id} was not found."}), 404 | ||
|
||
task.title = request_body.get("title", task.title) | ||
task.description = request_body.get("description", task.description) | ||
|
||
db.session.commit() | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.completed_at != None | ||
} | ||
}) | ||
|
||
|
||
# detele a task & return 404 + message if not found | ||
@tasks_bp.route("/<task_id>", methods=["DELETE"]) | ||
def delete_task(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task is None: | ||
return jsonify({"details": f"Task {task_id} was not found."}), 404 | ||
|
||
db.session.delete(task) | ||
db.session.commit() | ||
|
||
return jsonify({ | ||
"details": f'Task {task_id} "{task.title}" successfully deleted' | ||
}) | ||
|
||
|
||
# Get task not found | ||
@tasks_bp.route("/<int:task_id>", methods=["GET"]) | ||
def get_task_not_found(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task is None: | ||
return jsonify({"details": f"Task {task_id} was not found."}), 404 | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": task.completed_at != None | ||
} | ||
}) | ||
|
||
# mark task complete | ||
@tasks_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||
def mark_complete(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task is None: | ||
return jsonify({"details": f"Task {task_id} was not found."}), 404 | ||
|
||
task.completed_at = datetime.now() | ||
db.session.add(task) | ||
db.session.commit() | ||
|
||
message = f"Task {task.title} is complete." | ||
send_slack_request("study-sesh", message) | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": True | ||
} | ||
}) | ||
|
||
|
||
@tasks_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||
def mark_incomplete(task_id): | ||
task = Task.query.get(task_id) | ||
|
||
if task is None: | ||
return jsonify({"details": f"Task {task_id} was not found."}), 404 | ||
|
||
task.completed_at = None | ||
db.session.add(task) | ||
db.session.commit() | ||
|
||
return jsonify({ | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": False | ||
} | ||
}) | ||
|
||
|
||
# create function that sends request to slack API | ||
def send_slack_request(channel, message): | ||
path = "https://slack.com/api/chat.postMessage" | ||
headers = { | ||
"Type": "application/json", | ||
"Authorization": "xoxb-4715007748918-5273625376965-Fd54AOr5GENxXWt10KbNsgpE" | ||
} | ||
payload = { | ||
"channel": channel, | ||
"text": message | ||
} | ||
response = requests.post(path, headers=headers, json=payload) | ||
response.raise_for_status() | ||
|
||
|
||
# get a goal | ||
@goals_bp.route("", methods=["GET"]) | ||
def get_goals(): | ||
goals = Goal.query.all() | ||
goals_dict = [goal.to_dict() for goal in goals] | ||
return jsonify(goals_dict) | ||
|
||
|
||
# create a new goal | ||
@goals_bp.route("", methods=["POST"]) | ||
def create_goal(): | ||
request_data = request.get_json() | ||
if "title" not in request_data: | ||
return jsonify({"details": "Invalid data"}), 400 | ||
|
||
title = request_data["title"] | ||
goal = Goal(title=title) | ||
db.session.add(goal) | ||
db.session.commit() | ||
|
||
return jsonify({"goal": goal.to_dict()}), 201 | ||
|
||
|
||
# update existing goal | ||
@goals_bp.route("/<goal_id>", methods=["PUT"]) | ||
def update_goal(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
|
||
if goal is None: | ||
return jsonify({"details": f"Goal {goal_id} was not found."}), 404 | ||
|
||
request_data = request.get_json() | ||
goal.title = request_data.get("title", goal.title) | ||
db.session.commit() | ||
|
||
return jsonify({"goal": goal.to_dict()}) | ||
|
||
|
||
# get specific goal/ account for 404 | ||
@goals_bp.route("/<goal_id>", methods=["GET"]) | ||
def get_goal(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
|
||
if goal is None: | ||
return jsonify({"details": f"Goal {goal_id} was not found."}), 404 | ||
|
||
return jsonify({"goal": goal.to_dict()}) | ||
|
||
|
||
# delete a goal | ||
@goals_bp.route("/<goal_id>", methods=["DELETE"]) | ||
def delete_goal(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
|
||
if goal is None: | ||
return jsonify({"details": f"Goal {goal_id} was not found."}), 404 | ||
|
||
db.session.delete(goal) | ||
db.session.commit() | ||
|
||
return jsonify({ | ||
"details": f'Goal {goal.goal_id} "{goal.title}" successfully deleted' | ||
}), 200 | ||
|
||
|
||
# Add a task to a goal | ||
@goals_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def goal_tasks_post(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
|
||
if not goal: | ||
return {"message": f"Goal {goal_id} not found"}, 404 | ||
|
||
request_body = request.get_json() | ||
|
||
answer = { | ||
"id": goal.goal_id, | ||
"task_ids": request_body["task_ids"], | ||
} | ||
|
||
for task_id in request_body["task_ids"]: | ||
task = Task.query.get(task_id) | ||
if not task: | ||
return { | ||
"message": | ||
f"Task {task_id} not found" | ||
}, 404 | ||
|
||
goal.tasks.append(task) | ||
|
||
db.session.commit() | ||
|
||
return answer, 200 | ||
|
||
# get task for specific goal | ||
@goals_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def get_task_for_goal(goal_id): | ||
goal = Goal.query.get(goal_id) | ||
if not goal: | ||
return {"message": f"Goal not found"}, 404 | ||
|
||
answer = { | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
"tasks": [], | ||
} | ||
|
||
for task in goal.tasks: | ||
answer["tasks"].append(task.to_dict()) | ||
|
||
return answer, 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.
Great work registering these blueprints ✅