-
Notifications
You must be signed in to change notification settings - Fork 146
Nested routes #137
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: master
Are you sure you want to change the base?
Nested routes #137
Changes from all commits
5c102ca
c696890
86c1c27
77d641e
b98d68a
688dd46
cce9342
1c134e7
4d0b632
08adb8a
b3a0ae6
715c469
39da99c
7766313
5635280
a9fdc69
4d3b714
a7ad15b
950f7c5
6709dcb
b415889
ab234b6
98cfdaa
2aa5222
beada76
a74795b
efff68e
eef38e5
4bfb38e
c3deccd
200a922
a7a5d8a
1651a08
20f5ecc
7ad720c
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,46 @@ | ||
from app import db | ||
import os | ||
|
||
|
||
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, default = None) | ||
is_complete = db.Column(db.Boolean, default = False) | ||
goal_id = db.Column(db.Integer, db.ForeignKey('goal.id'), nullable = True) | ||
goal = db.relationship("Goal", back_populates="tasks" ) | ||
#project instructions are recommending setting nullable to True. | ||
#where do we do this? | ||
|
||
def make_dict(self): | ||
"""given a task, return a dictionary | ||
with all the attibutes of that task.""" | ||
#if goal_id is empty, don't include it in dictionary. | ||
task_dict = { | ||
"id": self.task_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": self.is_complete | ||
} | ||
if self.goal_id: | ||
task_dict["goal_id"] = self.goal_id | ||
return task_dict | ||
|
||
@classmethod | ||
def from_dict(cls, data_dict): | ||
#make the following a helper function: | ||
if "completed_at" in data_dict: | ||
completed_at = data_dict["completed_at"] | ||
if not completed_at: | ||
is_complete = False | ||
else: | ||
is_complete = True | ||
else: is_complete = False | ||
#end helper function. | ||
|
||
new_object = cls( | ||
title = data_dict["title"], | ||
description = data_dict["description"], | ||
is_complete = is_complete) | ||
return new_object |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
from app.models.goal import Goal | ||
from app import db | ||
from app.routes.task import validate_task, Task | ||
|
||
goal_bp = Blueprint("goal_bp", __name__, url_prefix = "/goals") | ||
|
||
@goal_bp.route("", methods = ["POST"]) | ||
def post_new_goal(): | ||
request_body = request.get_json() | ||
if "title" not in request_body: | ||
response_str = "Invalid data" | ||
abort(make_response({"details":response_str}, 400)) | ||
|
||
new_goal = Goal.from_dict(request_body) | ||
db.session.add(new_goal) | ||
db.session.commit() | ||
goal_dict = new_goal.make_dict() | ||
response = {"goal": goal_dict} | ||
return make_response(response, 201) | ||
|
||
@goal_bp.route("", methods = ["GET"]) | ||
def get_all_goals(): | ||
goals = Goal.query.all() | ||
response = [] | ||
for goal in goals: | ||
goal_dict = goal.make_dict() | ||
response.append(goal_dict) | ||
return jsonify(response), 200 | ||
|
||
@goal_bp.route("/<goal_id>", methods = ["GET"]) | ||
def get_one_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
goal_dict = goal.make_dict() | ||
return make_response({"goal": goal_dict}, 200) | ||
|
||
@goal_bp.route("/<goal_id>", methods = ["PUT", "PATCH"]) | ||
def update_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
#This could be a helper function----# | ||
if "title" in request_body: | ||
goal.title = request_body["title"] | ||
else: | ||
response_str = f"Invalid data" | ||
abort(make_response({"details": response_str}, 400)) | ||
#end a helper function# | ||
db.session.commit() | ||
response = {"goal": goal.make_dict()} | ||
return make_response(response, 200) | ||
|
||
@goal_bp.route("/<goal_id>", methods = ["DELETE"]) | ||
def delete_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
db.session.delete(goal) | ||
db.session.commit() | ||
response_str = f'Goal {goal_id} "{goal.title}" successfully deleted' | ||
response_body = {"details": response_str} | ||
return make_response(response_body, 200) | ||
|
||
#nested routes: | ||
@goal_bp.route("/<goal_id>/tasks", methods = ["POST"]) | ||
def create_a_goal_with_tasks(goal_id): | ||
new_goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
task_id_list = request_body["task_ids"] | ||
#begin helper function | ||
#for those tasks, assign them to the goal, | ||
for task_id in task_id_list: | ||
task = validate_task(task_id) | ||
task.goal_id = goal_id | ||
#end helper function | ||
db.session.add(new_goal) | ||
db.session.commit() | ||
response_body = { | ||
"id": int(goal_id), #there should be a better way to deal with this. | ||
"task_ids": task_id_list} | ||
return make_response(response_body, 200) | ||
|
||
@goal_bp.route("/<goal_id>/tasks", methods = ["GET"]) | ||
def get_one_goal_with_tasks(goal_id): | ||
goal = validate_goal(goal_id) | ||
task_list = [] | ||
for task in goal.tasks: | ||
task_list.append(task.make_dict()) | ||
response = { | ||
"id": int(goal_id), #there should be a better way to do this; | ||
"title": goal.title, | ||
"tasks": task_list | ||
} | ||
return jsonify(response), 200 | ||
|
||
|
||
#ideally, combine this with validate task, passing in the class as well. | ||
def validate_goal(goal_id): | ||
try: | ||
goal_id = int(goal_id) | ||
except ValueError: | ||
response_str = f"Goal {goal_id} must be an integer" | ||
abort(make_response({"message": response_str}, 400)) | ||
goal = Goal.query.get(goal_id) | ||
if not goal: | ||
response_str = f"Goal {goal_id} not found" | ||
abort(make_response({"message": response_str}, 404)) | ||
return goal | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
from app.models.task import Task | ||
from app import db | ||
from sqlalchemy import desc, asc | ||
from datetime import date | ||
import os | ||
import requests | ||
|
||
#make a blueprint | ||
task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") | ||
|
||
#constants: (refactor to make this from the database later.) | ||
COL_NAMES = ["title", "description", "is_complete"] #later, add completed ad | ||
COL_DEFAULTS = [None, "", False] | ||
COL_NAME_DEFAULT_DICT = dict(zip(COL_NAMES, COL_DEFAULTS)) | ||
|
||
@task_bp.route("", methods = ["GET"]) | ||
def get_all_tasks(): | ||
#refactor to helper function | ||
sort_order = request.args.get("sort") | ||
if sort_order == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()).all() #Task.title is just a string?? | ||
elif sort_order == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()).all() #look up doc for asc. | ||
else: | ||
tasks = Task.query.all() | ||
#refactor to helper function end here | ||
response = [] | ||
for task in tasks: | ||
task_dict = task.make_dict() | ||
response.append(task_dict) | ||
return jsonify(response), 200 | ||
|
||
|
||
@task_bp.route("/<task_id>", methods = ["GET"]) | ||
def get_one_task(task_id): | ||
task = validate_task(task_id) | ||
task_dict = task.make_dict() | ||
return make_response({"task": task_dict}, 200) | ||
|
||
@task_bp.route("", methods = ["POST"]) | ||
def post_new_task(): | ||
request_body = request.get_json() | ||
#dict_of_field_vals = fill_empties_with_defaults(request_body) | ||
if "title" not in request_body or "description" not in request_body: | ||
response_str = "Invalid data" | ||
abort(make_response({"details":response_str}, 400)) | ||
if "completed_at" not in request_body: | ||
request_body["is_complete"] = False | ||
|
||
new_task = Task.from_dict(request_body) | ||
db.session.add(new_task) | ||
db.session.commit() | ||
task_dict = new_task.make_dict() | ||
response = {"task": task_dict} | ||
return make_response(response, 201) | ||
|
||
@task_bp.route("/<task_id>", methods = ["PUT", "PATCH"]) | ||
def update_task(task_id): | ||
task = validate_task(task_id) | ||
request_body = request.get_json() | ||
task = update_given_values(task, request_body) | ||
db.session.commit() | ||
response = {"task": task.make_dict()} #refactor this line and line 37 above to helper function? or method on class? | ||
return make_response(response, 200) | ||
|
||
#can the following be combined with the route above? | ||
@task_bp.route("/<task_id>/<complete_tag>", methods = ["PUT", "PATCH"]) | ||
def update_task_completion(task_id, complete_tag): | ||
task = validate_task(task_id) | ||
if complete_tag == "mark_complete": | ||
task.completed_at = date.today().strftime("%B %d, %Y") | ||
task.is_complete = True #not sure if this line is redundant | ||
|
||
#post a message to slack to say the task is complete. | ||
#----------Make the following into a helper function later-----# | ||
path = "https://slack.com/api/chat.postMessage" | ||
query_params = { | ||
"channel" : "task-notifications", | ||
"text": "Someone just completed the task " + task.title | ||
} | ||
headers = { | ||
"Authorization" : "Bearer " + os.environ.get("SLACK_API_KEY") | ||
} | ||
|
||
response = requests.post(path, params = query_params, headers = headers) | ||
#----------end helper function--------------# | ||
|
||
elif complete_tag == "mark_incomplete": | ||
task.completed_at = None | ||
task.is_complete = False #not sure if this line is redundant | ||
db.session.commit() | ||
response = {"task": task.make_dict()} | ||
return make_response(response, 200) | ||
|
||
|
||
@task_bp.route("/<task_id>", methods = ["DELETE"]) | ||
def delete_task(task_id): | ||
task = validate_task(task_id) | ||
db.session.delete(task) | ||
db.session.commit() | ||
response_body = {"details": f'Task {task_id} "{task.title}" successfully deleted'} | ||
return make_response(response_body, 200) | ||
|
||
#ideally, combine this with validate goal, passing in the class as well. | ||
def validate_task(task_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. Nice helper function! |
||
try: | ||
task_id = int(task_id) | ||
except ValueError: | ||
response_str = f"Task {task_id} must be an integer" | ||
abort(make_response({"message": response_str}, 400)) | ||
task = Task.query.get(task_id) | ||
if not task: | ||
response_str = f"Task {task_id} not found" | ||
abort(make_response({"message": response_str}, 404)) | ||
return task | ||
|
||
#can I make this a method for Tasks? | ||
def update_given_values(task, request_body): | ||
Comment on lines
+118
to
+119
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 indeed make this a method in the tasks class. It would make a great helper method. |
||
if "title" in request_body: | ||
task.title = request_body["title"] | ||
if "description" in request_body: | ||
task.description = request_body["description"] | ||
if "is_complete" in request_body: | ||
task.is_complete = request_body["is_complete"] | ||
#can add completed_at when you put that in. | ||
return task | ||
|
||
# I used this in my journal.py. This isn't want the tests are asking for, so I'll delete it. | ||
# def fill_empties_with_defaults(request_body): | ||
# """Go through entered fields. | ||
# If it has an entry, use that, if not, use the default.""" | ||
# task_dict = {} | ||
# for field, default in COL_NAME_DEFAULT_DICT.items(): | ||
|
||
# if field not in request_body: | ||
# task_dict[field] = default | ||
# else: | ||
# task_dict[field] = request_body[field] | ||
|
||
# return task_dict | ||
|
||
|
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.
The reason Learn is failing is that the Environment variable used by Learn is not named SLACK_API_KEY and so
os.environ.get("SLACK_API_KEY")
returnsNone
.