-
Notifications
You must be signed in to change notification settings - Fork 127
Amethyst - Elaine W. #116
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: main
Are you sure you want to change the base?
Amethyst - Elaine W. #116
Changes from all commits
22923b5
4707ed3
b36dba2
2d6a371
ae1103c
dd35dec
33001e2
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,15 @@ | ||
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 Elaine! 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) | ||
goal_id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
title = db.Column(db.String) | ||
tasks = db.relationship("Task", back_populates="goal", lazy='select') | ||
|
||
# @classmethod | ||
# # in class methods, cls must come first. it's a reference to the class itself | ||
# def from_dict(cls, task_data): | ||
# new_goal = goal( | ||
# title=goal_data["title"] | ||
# ) | ||
|
||
# return new_goal |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,29 @@ | ||
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) | ||
goal_id = db.Column(db.Integer, db.ForeignKey('goal.goal_id')) | ||
goal = db.relationship('Goal', back_populates='tasks') | ||
|
||
Comment on lines
+7
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. Why not use lazy here? |
||
@classmethod | ||
def from_dict(cls, task_data): | ||
new_task = Task(title=task_data["title"], | ||
description=task_data["description"], | ||
completed_at=None) | ||
return new_task | ||
|
||
def to_dict(self): | ||
task_as_dict = { | ||
"id": self.task_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": bool(self.completed_at) | ||
} | ||
|
||
if self.goal_id: | ||
task_as_dict["goal_id"] = self.goal_id | ||
|
||
return task_as_dict | ||
Comment on lines
+11
to
+29
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 +1,280 @@ | ||
from flask import Blueprint | ||
from flask import Blueprint, jsonify, request, abort, make_response | ||
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 sqlalchemy import desc | ||
from datetime import datetime | ||
import requests | ||
import os | ||
|
||
task_bp = Blueprint("tasks", __name__, url_prefix="/tasks") | ||
goal_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
Comment on lines
+10
to
+11
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. 👍🏾 |
||
|
||
def validate_model(cls, model_id): | ||
try: | ||
model_id = int(model_id) | ||
except: | ||
abort(make_response({"message":f"{model_id} invalid type ({type(model_id)})"}, 400)) | ||
# abort(make_response({"message":f"Invalid data"}, 400)) | ||
|
||
model = cls.query.get(model_id) | ||
|
||
if not model: | ||
abort(make_response({"message":f"{cls.__name__.lower()} {model_id} not found"}, 404)) | ||
|
||
return model | ||
|
||
Comment on lines
+13
to
+26
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. 😁 |
||
|
||
# Create a Task: Valid Task with 'null' 'completed at' | ||
@task_bp.route("", methods=["POST"]) | ||
def handle_tasks(): | ||
# handle the HTTP request body - pasrses JSON body into a Python dict | ||
request_body = request.get_json() | ||
|
||
# create a new task instance from the request | ||
if len(request_body) < 2: | ||
return {"details": "Invalid data"}, 400 | ||
else: | ||
new_task = Task.from_dict(request_body) | ||
|
||
# database collects new changes - adding new_task as a record | ||
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
|
||
# database saves and commits the new changes | ||
db.session.commit() | ||
|
||
return { | ||
"task": | ||
{ | ||
"id": new_task.task_id, | ||
"title": new_task.title, | ||
"description": new_task.description, | ||
"is_complete": bool(new_task.completed_at) | ||
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. 😁 |
||
} | ||
}, 201 | ||
|
||
|
||
# Get Saved Tasks from the database | ||
@task_bp.route("", methods=["GET"]) | ||
def read_all_tasks(): | ||
task_response = [] | ||
|
||
sort_query = request.args.get("sort") | ||
|
||
if sort_query == "asc": | ||
tasks = Task.query.order_by(Task.title).all() | ||
elif sort_query == "desc": | ||
tasks = Task.query.order_by(desc(Task.title)).all() | ||
else: | ||
Comment on lines
+61
to
+67
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 handling this query param logic ⭐️ |
||
tasks = Task.query.all() | ||
|
||
if tasks: | ||
for task in tasks: | ||
task_response.append(task.to_dict()) | ||
return jsonify(task_response) | ||
|
||
|
||
@task_bp.route("/<task_id>", methods=["GET"]) | ||
def read_one_task(task_id): | ||
task = validate_model(Task, task_id) | ||
if not task.goal_id: | ||
return { | ||
"task": | ||
{ | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": bool(task.completed_at) | ||
} | ||
} | ||
else: | ||
return { | ||
"task": | ||
{ | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": bool(task.completed_at), | ||
"goal_id": task.goal_id | ||
} | ||
} | ||
|
||
|
||
|
||
@task_bp.route("/<task_id>", methods=["PUT"]) | ||
def update_task(task_id): | ||
task = validate_model(Task, task_id) | ||
request_body = request.get_json() | ||
|
||
task.title = request_body["title"] | ||
task.description = request_body["description"] | ||
|
||
db.session.commit() | ||
|
||
return { | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": bool(task.completed_at) | ||
}} | ||
|
||
@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 make_response({"details": f'Task {task.task_id} "{task.title}" successfully deleted'}) | ||
|
||
|
||
|
||
|
||
def handle_slack_api(task_title): | ||
url = "https://slack.com/api/chat.postMessage?channel=task-notifications&text=beep%20boop&pretty=1" | ||
|
||
payload = { | ||
"channel": "C0574HS4KL2", | ||
"text": task_title | ||
} | ||
|
||
auth_key = {"Authorization": f"Bearer {os.environ.get('AUTHORIZATION')}"} | ||
|
||
response = requests.post(url, headers=auth_key, data=payload) | ||
|
||
print(response.text) | ||
|
||
|
||
|
||
|
||
@task_bp.route("/<task_id>/<task_status>", methods=["PATCH"]) | ||
def update_completed_task(task_id, task_status): | ||
task = validate_model(Task, task_id) | ||
if task: | ||
if task_status == "mark_complete": | ||
task.completed_at = datetime.now() | ||
# call to API helper fuction here | ||
handle_slack_api(task.title) | ||
elif task_status == "mark_incomplete": | ||
task.completed_at = None | ||
|
||
db.session.commit() | ||
|
||
return { | ||
"task": { | ||
"id": task.task_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"is_complete": bool(task.completed_at) | ||
}} | ||
|
||
# Wave 5: Creating a Second Model Goal | ||
# Make a POST request | ||
# Create a Valid Goal | ||
@goal_bp.route("", methods=["POST"]) | ||
def handle_goals(): | ||
request_body = request.get_json() | ||
|
||
if len(request_body) < 1: | ||
return {"details": "Invalid data"}, 400 | ||
else: | ||
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 | ||
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 👍🏾 |
||
|
||
@goal_bp.route("", methods=["GET"]) | ||
def read_all_goals(): | ||
goal_response = [] | ||
|
||
goals = Goal.query.all() | ||
|
||
if goals: | ||
for goal in goals: | ||
goal_response.append({ | ||
"id": goal.goal_id, | ||
"title": goal.title | ||
}) | ||
return jsonify(goal_response), 200 | ||
|
||
@goal_bp.route("/<goal_id>", methods=["GET"]) | ||
def read_goal(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"] | ||
|
||
db.session.add(goal) | ||
db.session.commit() | ||
|
||
return { | ||
"goal": { | ||
"id": goal.goal_id, | ||
"title": goal.title | ||
}}, 200 | ||
|
||
|
||
@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'}) | ||
|
||
|
||
@goal_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def create_tasks_for_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
request_body = request.get_json() | ||
|
||
for task in request_body["task_ids"]: | ||
task = validate_model(Task, task) | ||
goal.tasks.append(task) | ||
|
||
|
||
db.session.commit() | ||
return { | ||
"id": goal.goal_id, | ||
"task_ids": request_body["task_ids"] | ||
} | ||
|
||
|
||
|
||
@goal_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def read_tasks_from_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
goals_tasks = { | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
"tasks": [] | ||
} | ||
|
||
for task in goal.tasks: | ||
|
||
goals_tasks["tasks"].append(task.to_dict()) | ||
|
||
return goals_tasks, 200 |
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 ✅