-
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
Zoisite - Jasmin W. #114
base: main
Are you sure you want to change the base?
Zoisite - Jasmin W. #114
Changes from all commits
e3a6c47
b73462d
09e7eef
3980ba4
8c45d94
5238ec2
fbe251b
2ef6422
609d288
d0c6b09
8b5a787
8f03afa
0f63cf3
608f8b9
9d2130c
ec911c9
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,28 @@ | ||
from flask import abort, make_response | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
|
||
|
||
def validate_id(id): | ||
try: | ||
id = int(id) | ||
except: | ||
abort(make_response({"message":f"this is not a valid id: {id}"}, 400)) | ||
|
||
task = Task.query.get(id) | ||
if not task: | ||
abort(make_response({"message":f"id {id} not found!"}, 404)) | ||
return task | ||
|
||
def validate_goal(id): | ||
try: | ||
id = int(id) | ||
except: | ||
abort(make_response({"message":f"this is not a valid id: {id}"}, 400)) | ||
|
||
goal = Goal.query.get(id) | ||
if not goal: | ||
abort(make_response({"message":f"id {id} not found!"}, 404)) | ||
return goal | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,26 @@ | |
|
||
|
||
class Goal(db.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, nullable=False) | ||
tasks = db.relationship("Task", back_populates="goal", lazy=True) | ||
|
||
|
||
def to_dict(self): | ||
return{ | ||
"id":self.goal_id, | ||
"title":self.title | ||
} | ||
|
||
#create function for title | ||
@classmethod | ||
def create(cls, request_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. Just for consistency's sake, it's a good idea to use a method name that is closer to from_dict to mirror our to_dict method! |
||
return cls( | ||
title = request_body["title"] | ||
) | ||
|
||
|
||
#update function for title | ||
def update(self, request_body): | ||
self.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. I love this method! I'm excited to see how you use it! |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,48 @@ | ||
from app import db | ||
from datetime import datetime | ||
|
||
|
||
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, nullable=False) | ||
description = db.Column(db.String, nullable=False) | ||
completed_at = db.Column(db.DateTime, nullable=True, default=None) | ||
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. In this situation, the default for nullable is True, so we do not have to specify that particular constraint! Similarly, when nullable is set to True, the default for default is None, so that also doesn't need to be specified! |
||
goal = db.relationship("Goal", back_populates="tasks") | ||
goals_id = db.Column(db.Integer, db.ForeignKey("goal.goal_id")) | ||
|
||
def to_dict(self): | ||
is_complete=True if self.completed_at else False | ||
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 with this specification! |
||
|
||
task_dict = { | ||
"id":self.task_id, | ||
"title":self.title, | ||
"description":self.description, | ||
"is_complete":is_complete | ||
} | ||
if self.goals_id: | ||
task_dict["goal_id"] = self.goals_id | ||
|
||
return task_dict | ||
|
||
#create function for title & description | ||
@classmethod | ||
def create(cls, request_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. Same idea as before, I would change this method name to something like from_dict to keep things consistent! |
||
return cls( | ||
title = request_body["title"], | ||
description = request_body["description"] | ||
) | ||
|
||
#update function for title & description | ||
def update(self, request_body): | ||
self.title=request_body["title"] | ||
self.description=request_body["description"] | ||
|
||
|
||
#patch function for is_completed | ||
def patch_complete(self): | ||
self.completed_at=datetime.utcnow() | ||
|
||
#patch function for is_completed when incomplete | ||
def patch_incomplete(self): | ||
self.completed_at=None | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,234 @@ | ||
from flask import Blueprint | ||
from flask import Blueprint, jsonify, make_response, 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. When we have multiple models, our number of routes really starts to add up, so don't be afraid to separate them into multiple files! |
||
import os, requests | ||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from app.helper import validate_id, validate_goal | ||
|
||
tasks_bp = Blueprint("tasks", __name__, url_prefix="/tasks") | ||
goals_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
|
||
|
||
#get all tasks-"/tasks"-GET(read) | ||
@tasks_bp.route("", methods=["GET"]) | ||
def get_all_tasks(): | ||
if request.args.get("sort") == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()) | ||
elif request.args.get("sort") == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()) | ||
else: | ||
tasks = Task.query.all() | ||
tasks_response = [] | ||
for task in tasks: | ||
tasks_response.append(task.to_dict()) | ||
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 be a good candidate for a list comprehension! |
||
return jsonify(tasks_response), 200 | ||
|
||
|
||
#get one tasks-"/tasks/1"-GET(read) | ||
@tasks_bp.route("/<id>", methods=["GET"]) | ||
def get_task(id): | ||
task = validate_id(id) | ||
return jsonify({"task":task.to_dict()}), 200 | ||
|
||
|
||
#create task-"/tasks"-POST(create) | ||
@tasks_bp.route("", methods=["POST"]) | ||
def create_task(): | ||
request_body = request.get_json() | ||
try: | ||
new_task = Task.create(request_body) | ||
except KeyError: | ||
return make_response({"details": "Invalid data"}), 400 | ||
|
||
db.session.add(new_task) | ||
db.session.commit() | ||
return jsonify({"task":new_task.to_dict()}), 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. Great job with the error handling here! We never know if the user's request body contains everything we are asking for! |
||
|
||
|
||
#update task-"tasks/1"-PUT(update) | ||
@tasks_bp.route("/<id>", methods=["PUT"]) | ||
def update_task(id): | ||
task = validate_id(id) | ||
request_body = request.get_json() | ||
task.update(request_body) | ||
db.session.commit() | ||
return jsonify({"task":task.to_dict()}), 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. I love your use of the update method you created in the task model! That being said, this would be another good candidate for some error handling! |
||
|
||
|
||
#delete task-"tasks/1"-DELETE(delete) | ||
@tasks_bp.route("/<id>", methods=["DELETE"]) | ||
def delete_task(id): | ||
task = validate_id(id) | ||
db.session.delete(task) | ||
db.session.commit() | ||
return jsonify({"details": f'Task {id} "{task.to_dict()["title"]}" successfully deleted'}), 200 | ||
|
||
|
||
#patch task-"tasks/1/mark_complete"-PATCH(update) | ||
@tasks_bp.route("/<id>/mark_complete", methods=["PATCH"]) | ||
def mark_complete(id): | ||
task = validate_id(id) | ||
request_body = request.get_json() | ||
task.patch_complete() | ||
db.session.commit() | ||
|
||
SLACK_API_URL = "https://slack.com/api/chat.postMessage" | ||
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. As a constant, it might be a good idea to move this variable outside all your routes! |
||
if "SLACKBOT_TOKEN" is None: | ||
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. "SLACKBOT_TOKEN" is currently being read as a string, so this conditional will never be triggered. The more appropriate approach would be to use the os.environ.get("SLACKBOT_TOKEN"). |
||
return jsonify({'error': 'Slackbot token not set'}), 500 | ||
headers = {"Authorization": os.environ.get("SLACKBOT_TOKEN")} | ||
params = { | ||
'channel': 'task-notification', | ||
'text': f"Someone just completed the task {task.title}", | ||
} | ||
response = requests.post(SLACK_API_URL, headers=headers, json=params) | ||
if not response.ok: | ||
return jsonify({'error': 'Failed to send Slack message'}), 500 | ||
|
||
return jsonify({"task":task.to_dict()}), 200 | ||
|
||
|
||
#patch task-"tasks/1/mark_incomplete"-PATCH(update) | ||
@tasks_bp.route("/<id>/mark_incomplete", methods=["PATCH"]) | ||
def mark_incomplete(id): | ||
task = validate_id(id) | ||
request_body = request.get_json() | ||
task.patch_incomplete() | ||
db.session.commit() | ||
return jsonify({"task":task.to_dict()}), 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. Another great candidate for error handling! |
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
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. Don't forget to remove some of this extraneous space before submitting a pull request! |
||
#==========================WAVE 5 GOALS BELOW========================== | ||
|
||
|
||
|
||
|
||
|
||
#get all goals-"/tasks"-GET(read) | ||
@goals_bp.route("", methods=["GET"]) | ||
def get_all_goals(): | ||
goals = Goal.query.all() | ||
goals_response = [] | ||
for goal in goals: | ||
goals_response.append(goal.to_dict()) | ||
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. Don't forget we can use list comprehensions here! |
||
return jsonify(goals_response), 200 | ||
|
||
|
||
#get one goals-"/goals/1"-GET(read) | ||
@goals_bp.route("/<id>", methods=["GET"]) | ||
def get_goal(id): | ||
goal = validate_goal(id) | ||
return jsonify({"goal":goal.to_dict()}), 200 | ||
|
||
|
||
#create goal-"/goals"-POST(create) | ||
@goals_bp.route("", methods=["POST"]) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
try: | ||
new_goal = Goal.create(request_body) | ||
except KeyError: | ||
return make_response({"details": "Invalid data"}), 400 | ||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
return jsonify({"goal":new_goal.to_dict()}), 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. Great job with error handling again! |
||
|
||
|
||
#update goal-"goals/1"-PUT(update) | ||
@goals_bp.route("/<id>", methods=["PUT"]) | ||
def update_goal(id): | ||
goal = validate_goal(id) | ||
request_body = request.get_json() | ||
goal.update(request_body) | ||
db.session.commit() | ||
return jsonify({"goal":goal.to_dict()}), 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. Don't forget your error handling here! |
||
|
||
|
||
#delete goal-"goals/1"-DELETE(delete) | ||
@goals_bp.route("/<id>", methods=["DELETE"]) | ||
def delete_goal(id): | ||
goal = validate_goal(id) | ||
db.session.delete(goal) | ||
db.session.commit() | ||
return jsonify({"details": f'Goal {id} "{goal.to_dict()["title"]}" successfully deleted'}), 200 | ||
|
||
|
||
#patch goal-"goals/1/mark_complete"-PATCH(update) | ||
@goals_bp.route("/<id>/mark_complete", methods=["PATCH"]) | ||
def mark_complete(id): | ||
goal = validate_id(id) | ||
request_body = request.get_json() | ||
goal.patch_complete() | ||
db.session.commit() | ||
return jsonify({"goal":goal.to_dict()}), 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. And more error handling here! |
||
|
||
|
||
#patch goal-"goals/1/mark_incomplete"-PATCH(update) | ||
@goals_bp.route("/<id>/mark_incomplete", methods=["PATCH"]) | ||
def mark_incomplete(id): | ||
goal = validate_id(id) | ||
request_body = request.get_json() | ||
goal.patch_incomplete() | ||
db.session.commit() | ||
return jsonify({"goal":goal.to_dict()}), 200 | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
# #=================wave6================== | ||
|
||
#add tasks to goal-"/goal_id/tasks"-POST(create) | ||
@goals_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def add_tasks_to_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
|
||
for task_id in request_body["task_ids"]: | ||
task = validate_id(task_id) | ||
goal.tasks.append(task) | ||
|
||
db.session.commit() | ||
|
||
task_ids = [task.task_id for task in goal.tasks] | ||
|
||
response_body = { | ||
"id": goal.goal_id, | ||
"task_ids": task_ids | ||
} | ||
return jsonify(response_body), 200 | ||
|
||
#get tasks from goal-"/goal_id/tasks"-GET(read) | ||
@goals_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def get_tasks_from_goal_id(goal_id): | ||
goal = validate_goal(goal_id) | ||
task_list= [task.to_dict() for task in goal.tasks] | ||
|
||
response_body = { | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
"tasks": task_list | ||
} | ||
|
||
return jsonify(response_body), 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.
Keep in mind that these are two very similar methods! I wonder if there is a way to combine the two into one! Great job separating it into its own helper file though!