-
Notifications
You must be signed in to change notification settings - Fork 111
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
Finished Waves 1-6 #118
base: master
Are you sure you want to change the base?
Finished Waves 1-6 #118
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 |
---|---|---|
@@ -1,5 +1,37 @@ | ||
from app import db | ||
|
||
from flask import jsonify, abort, make_response | ||
|
||
class Goal(db.Model): | ||
|
||
goal_id = db.Column(db.Integer, primary_key=True) | ||
title = db.Column(db.String, nullable = False) | ||
tasks = db.relationship("Task", back_populates="goal", lazy=True) | ||
|
||
def to_json(self): | ||
|
||
return { | ||
"id": self.goal_id, | ||
"title": self.title | ||
} | ||
|
||
@classmethod | ||
def validate(cls, goal_id): | ||
try: | ||
goal_id = int(goal_id) | ||
except: | ||
abort(make_response(jsonify(f"{goal_id} is not a valid goal id."),400)) | ||
goal = cls.query.get(goal_id) | ||
if goal: | ||
return goal | ||
abort(make_response(jsonify(f"Goal with id of {goal_id} was not found"),404)) | ||
|
||
@classmethod | ||
def create(cls,request_body): | ||
new_goal = cls( | ||
title = request_body['title'] | ||
|
||
) | ||
return new_goal | ||
Comment on lines
+30
to
+34
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 think line 31 and the paren on line 33 needs to be tabbed over to the right one more time to match the opening bracket on line 30. You can also return the class without instantiating it and saving it to a variable like: return cls(
title = request_body['title']
) |
||
|
||
def update(self,request_body): | ||
self.title = request_body["title"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,56 @@ | ||
from app import db | ||
|
||
from flask import jsonify, abort, make_response | ||
|
||
class Task(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. Nicely done |
||
|
||
task_id = db.Column(db.Integer, primary_key=True) | ||
title = db.Column(db.String, nullable = False) | ||
description = db.Column(db.String) | ||
completed_at = db.Column(db.DateTime) | ||
goal_id = db.Column (db.Integer, db.ForeignKey('goal.goal_id'), nullable=True) | ||
goal = db.relationship("Goal", back_populates="tasks") | ||
|
||
def to_json(self): | ||
if not self.completed_at: | ||
is_complete = False | ||
else: | ||
is_complete = True | ||
Comment on lines
+14
to
+17
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 write this is within the task_dict after line 23, like: "is_complete": True if self.completed_at else False |
||
|
||
task_dict = { | ||
"id": self.task_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": is_complete | ||
} | ||
|
||
if self.goal_id: | ||
task_dict["goal_id"] = self.goal_id | ||
|
||
return task_dict | ||
|
||
|
||
|
||
@classmethod | ||
def validate(cls, task_id): | ||
try: | ||
task_id = int(task_id) | ||
except: | ||
abort(make_response(jsonify(f"{task_id} is not a valid task id."),400)) | ||
task = cls.query.get(task_id) | ||
if task: | ||
return task | ||
abort(make_response(jsonify(f"Task with id of {task_id} was not found"),404)) | ||
|
||
@classmethod | ||
def create(cls,request_body): | ||
new_task = cls( | ||
title = request_body['title'], | ||
description = request_body['description'], | ||
completed_at = request_body.get('completed_at', None) | ||
|
||
) | ||
Comment on lines
+46
to
+51
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. Lines 47 -51 needs to be indented one level deeper. |
||
return new_task | ||
|
||
def update(self,request_body): | ||
self.title = request_body["title"] | ||
self.description = request_body["description"] |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
from datetime import date | ||
from urllib import 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. You didn't actually end up using/needing this module so you can delete line 2 here |
||
from flask import Blueprint, jsonify, make_response, abort, request | ||
from app.models.goal import Goal | ||
from app.models.task import Task | ||
from app import db | ||
|
||
goal_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
|
||
@goal_bp.route("", methods = ["GET"]) | ||
def get_all_goal(): | ||
goal_response_body = [] | ||
|
||
if request.args.get("sort") == "asc": | ||
goals = Goal.query.order_by(Goal.title.asc()) | ||
elif request.args.get("sort") == "desc": | ||
goals = Goal.query.order_by(Goal.title.desc()) | ||
else: | ||
goals = Goal.query.all() | ||
|
||
for goal in goals: | ||
goal_response_body.append(goal.to_json()) | ||
Comment on lines
+21
to
+22
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 works well and is readable, but if you'd like to incorporate list comprehensions into your code, you could write it like this: goals_response = [goal.to_json() for goal in goals] |
||
|
||
return jsonify(goal_response_body), 200 | ||
|
||
@goal_bp.route("", methods = ["POST"]) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
print(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. Don't forget to remove debugging print statements when you're done with them. |
||
|
||
try: | ||
new_goal = Goal.create(request_body) | ||
except KeyError: | ||
return {"details": "Invalid data"}, 400 | ||
Comment on lines
+31
to
+34
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 error handling here |
||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
|
||
response_body = {} | ||
response_body["goal"] = new_goal.to_json() | ||
return jsonify(response_body), 201 | ||
Comment on lines
+39
to
+41
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. You can also create a dictionary in line on line 41 and directly return it like this (then you could delete lines 39-40): return make_response(jsonify({"goal": new_goal.to_json()}), 201) |
||
|
||
@goal_bp.route("/<id>", methods = ["GET"]) | ||
def get_one_goal(id): | ||
one_goal = Goal.validate(id) | ||
response_body = {} | ||
response_body["goal"] = one_goal.to_json() | ||
|
||
return jsonify(response_body), 200 | ||
Comment on lines
+46
to
+49
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. Here, too, you can also return the response dictionary in line on line 49 (then delete lines 46-47) return jsonify({"goal": one_goal.to_json()}), 200 |
||
|
||
@goal_bp.route("/<id>", methods = ["PUT"]) | ||
def update_goal(id): | ||
one_goal = Goal.validate(id) | ||
request_body = request.get_json() | ||
|
||
one_goal.update(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. The update() instance method for the Goal class is pretty simple since it only updates the title, but since we can't assume that the client will always pass us a correctly formatted request we should add some error handling for this method. For PUT and POST methods, we usually need data validation for the req body being sent in. Like you did in your POST req, you could add a try/except block around line 56 and if the request body does not have "title" then you could handle a KeyError like you did on line 34 and send back: return {"details": "Invalid data"}, 400 |
||
|
||
db.session.commit() | ||
response_body = {} | ||
response_body["goal"] = one_goal.to_json() | ||
|
||
return jsonify(response_body), 200 | ||
|
||
|
||
@goal_bp.route("/<id>", methods = ["DELETE"]) | ||
def delete_goal(id): | ||
one_goal = Goal.validate(id) | ||
db.session.delete(one_goal) | ||
db.session.commit() | ||
|
||
return jsonify({"details": f'Goal {one_goal.goal_id} "{one_goal.title}" successfully deleted'}) | ||
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 work consistently using the jsonify() method throughout your routes. |
||
|
||
@goal_bp.route("/<id>/tasks", methods = ["POST"]) | ||
def list_of_task_to_goal(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. You could rename this method to something like |
||
valid_goal = Goal.validate(id) | ||
request_body = request.get_json() | ||
|
||
for task_id in request_body["task_ids"]: | ||
Task.validate(task_id) | ||
task = Task.query.get(task_id) | ||
task.goal_id = valid_goal.goal_id | ||
|
||
db.session.commit() | ||
task_list = [] | ||
|
||
for task in valid_goal.tasks: | ||
task_list.append(task.task_id) | ||
|
||
response_body = {} | ||
response_body = { | ||
"id": valid_goal.goal_id, | ||
"task_ids": task_list} | ||
Comment on lines
+84
to
+92
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. It looks like you've created You can get the list of task_ids from the So if you deleted lines 84-92, you could achieve the same thing by doing this: return jsonify({"id": valid_goal.goal_id, "task_ids": request_body["task_ids"]}), 200 |
||
|
||
return jsonify(response_body), 200 | ||
|
||
@goal_bp.route("/<id>/tasks", methods = ["GET"]) | ||
def get_task_one_goal(id): | ||
valid_goal = Goal.validate(id) | ||
task_list = [] | ||
|
||
for task in valid_goal.tasks: | ||
task_list.append(task.to_json()) | ||
|
||
response_body = {} | ||
response_body = { | ||
"id": valid_goal.goal_id, | ||
"title": valid_goal.title, | ||
"tasks": task_list} | ||
Comment on lines
+106
to
+108
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. You do have part of this code in your to_json method for the Goal class so you could do something like (and remove lines 104-108: response_body = goal.to_json()
response_body["tasks"] = tasks
return jsonify(goal_response), 200 |
||
|
||
return jsonify(response_body), 200 | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
from datetime import date | ||
from urllib import 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. Delete unused imports |
||
from flask import Blueprint, jsonify, make_response, abort, request | ||
from app.models.task import Task | ||
from app import db | ||
|
||
|
||
task_bp = Blueprint("tasks", __name__, url_prefix="/tasks") | ||
|
||
|
||
@task_bp.route("", methods=["GET"]) | ||
def get_all_tasks(): | ||
task_response_body = [] | ||
# tasks = Task.query.all() | ||
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. Remove unused code so it doesn't accidentally get uncommented and execute when we don't expect it to. |
||
|
||
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() | ||
|
||
for task in tasks: | ||
task_response_body.append(task.to_json()) | ||
|
||
return jsonify(task_response_body), 200 | ||
|
||
@task_bp.route("", methods=["POST"]) | ||
def create_task(): | ||
request_body = request.get_json() | ||
print(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. Remove debugging print statements when you're done with them |
||
|
||
try: | ||
new_task = Task.create(request_body) | ||
except KeyError: | ||
return {"details": "Invalid data"}, 400 | ||
|
||
db.session.add(new_task) | ||
db.session.commit() | ||
|
||
response_body = {} | ||
response_body["task"] = new_task.to_json() | ||
return jsonify(response_body), 201 | ||
|
||
|
||
|
||
@task_bp.route("/<id>", methods = ["GET"]) | ||
def get_one_tasks(id): | ||
one_task = Task.validate(id) | ||
response_body = {} | ||
response_body["task"] = one_task.to_json() | ||
|
||
return jsonify(response_body), 200 | ||
Comment on lines
+49
to
+53
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. To make this a 2 line method, you could write the code like this too: one_task = Task.validate(id)
return jsonify({"task": one_task.to_json()}), 200 |
||
|
||
@task_bp.route("/<id>", methods = ["PUT"]) | ||
def update_task(id): | ||
one_task = Task.validate(id) | ||
request_body = request.get_json() | ||
|
||
one_task.update(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. We should add error handling here in case the client sends an incorrect request body so that we can handle a potential KeyError, like you did in your POST request on lines 33-36 |
||
|
||
db.session.commit() | ||
response_body = {} | ||
response_body["task"] = one_task.to_json() | ||
|
||
return jsonify(response_body), 200 | ||
|
||
@task_bp.route("/<id>", methods = ["DELETE"]) | ||
def delete_task(id): | ||
one_task = Task.validate(id) | ||
db.session.delete(one_task) | ||
db.session.commit() | ||
|
||
return jsonify({"details": f'Task {one_task.task_id} "{one_task.title}" successfully deleted'}) | ||
|
||
|
||
@task_bp.route("/<id>/mark_complete", methods = ["PATCH"]) | ||
def mark_complete(id): | ||
one_task = Task.validate(id) | ||
|
||
one_task.completed_at = date.today() | ||
|
||
db.session.commit() | ||
response_body = {} | ||
response_body["task"] = one_task.to_json() | ||
|
||
return jsonify(response_body), 200 | ||
|
||
@task_bp.route("/<id>/mark_incomplete", methods = ["PATCH"]) | ||
def mark_incomplete(id): | ||
one_task = Task.validate(id) | ||
one_task.completed_at = None | ||
|
||
db.session.commit() | ||
response_body = {} | ||
response_body["task"] = one_task.to_json() | ||
|
||
return jsonify(response_body), 200 | ||
|
||
|
||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Generic single-database configuration. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# A generic, single database configuration. | ||
|
||
[alembic] | ||
# template used to generate migration files | ||
# file_template = %%(rev)s_%%(slug)s | ||
|
||
# set to 'true' to run the environment during | ||
# the 'revision' command, regardless of autogenerate | ||
# revision_environment = false | ||
|
||
|
||
# Logging configuration | ||
[loggers] | ||
keys = root,sqlalchemy,alembic | ||
|
||
[handlers] | ||
keys = console | ||
|
||
[formatters] | ||
keys = generic | ||
|
||
[logger_root] | ||
level = WARN | ||
handlers = console | ||
qualname = | ||
|
||
[logger_sqlalchemy] | ||
level = WARN | ||
handlers = | ||
qualname = sqlalchemy.engine | ||
|
||
[logger_alembic] | ||
level = INFO | ||
handlers = | ||
qualname = alembic | ||
|
||
[handler_console] | ||
class = StreamHandler | ||
args = (sys.stderr,) | ||
level = NOTSET | ||
formatter = generic | ||
|
||
[formatter_generic] | ||
format = %(levelname)-5.5s [%(name)s] %(message)s | ||
datefmt = %H:%M:%S |
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.
👍