-
Notifications
You must be signed in to change notification settings - Fork 97
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
Cedar - Maria O. #91
base: master
Are you sure you want to change the base?
Cedar - Maria O. #91
Changes from all commits
785f939
515e1d7
50697c6
6ed2e44
15fc2d8
7911477
56868ec
30469bc
0871ef7
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,6 +1,38 @@ | ||
from flask import current_app | ||
from app import db | ||
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) | ||
# is_complete = db.Column(db.Boolean) | ||
completed_at = db.Column(db.DateTime(timezone=True), nullable=True) | ||
# relationship | ||
goal_id = db.Column(db.Integer, db.ForeignKey('goal.goal_id'), nullable=True) | ||
|
||
def to_dict(self): | ||
return { | ||
"id": self.task_id, | ||
"title": self.title, | ||
"description": self.description, | ||
# "is_complete": True if self.completed_at == True else False | ||
"is_complete": bool(self.completed_at) | ||
} | ||
Comment on lines
+15
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. Consider how you could refactor this to include the goal_id when
|
||
|
||
def to_dict_for_goal(self): | ||
return { | ||
"id": self.task_id, | ||
"goal_id": self.goal_id, | ||
"title": self.title, | ||
"description": self.description, | ||
"is_complete": bool(self.completed_at) | ||
} | ||
|
||
def is_related_to_goal(self): | ||
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 is related to goal! Note how the suggested refactor above negates the need for this check. |
||
if self.goal_id: | ||
related_to_goal = True | ||
else: | ||
related_to_goal = False | ||
return related_to_goal |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
from flask import Blueprint, request, make_response, jsonify, abort | ||
from flask.wrappers import Response | ||
from app.models.goal import Goal | ||
from app.models.task import Task | ||
from app import db | ||
from datetime import date, datetime | ||
|
||
goals_bp = Blueprint('goals', __name__, url_prefix='/goals') | ||
|
||
# # Helper functions | ||
def valid_int(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. Note that you have this function in goal_routes and task_routes. Consider making a separate file with shared helper functions and importing them where you need them to DRY up your code. |
||
try: | ||
number = int(id) | ||
return number | ||
except: | ||
response_body = 'Invalid Data' | ||
abort(make_response(response_body,400)) | ||
|
||
def get_goal_from_id(goal_id): | ||
id = valid_int(goal_id) | ||
selected_goal = Goal.query.filter_by(goal_id=goal_id).one_or_none() | ||
# Goal not found | ||
if selected_goal is None: | ||
abort(make_response("Not Found", 404)) | ||
return selected_goal | ||
|
||
def valid_goal(request_body): | ||
if "title" not in request_body: | ||
abort(make_response({"details": "Invalid data"}, 400)) | ||
# # | ||
|
||
# Create a goal | ||
@goals_bp.route("", methods=["POST"], strict_slashes=False) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
valid_goal(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. Great use of a helper function. You might consider also including the creation of the goal in |
||
new_goal = Goal(title=request_body["title"]) | ||
db.session.add(new_goal) | ||
db.session.commit() | ||
response = {"goal": new_goal.to_dict()} | ||
return make_response(response, 201) | ||
|
||
# Get all goals | ||
@goals_bp.route("", methods=["GET"], strict_slashes=False) | ||
def get_all_goals(): | ||
response_list = [] | ||
goal_objects = Goal.query.all() | ||
for goal in goal_objects: | ||
response_list.append(goal.to_dict()) | ||
Comment on lines
+48
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. This could be a nice place to use a list comprehension. |
||
return make_response(jsonify(response_list), 200) | ||
|
||
# Get one goal | ||
@goals_bp.route("/<goal_id>", methods=["GET"], strict_slashes=False) | ||
def get_goal(goal_id): | ||
selected_goal = get_goal_from_id(goal_id) | ||
response_body = {"goal": selected_goal.to_dict()} | ||
return make_response(response_body, 200) | ||
|
||
# Update goal | ||
@goals_bp.route("/<goal_id>", methods=["PUT"], strict_slashes=False) | ||
def update_goal(goal_id): | ||
selected_goal = get_goal_from_id(goal_id) | ||
request_body = request.get_json() | ||
if "title" in request_body: | ||
selected_goal.title = request_body["title"] | ||
db.session.commit() | ||
response_body = {"goal": selected_goal.to_dict()} | ||
return make_response(response_body, 200) | ||
|
||
# Delete goal | ||
@goals_bp.route("/<goal_id>", methods=["DELETE"], strict_slashes=False) | ||
def delete_goal(goal_id): | ||
selected_goal = get_goal_from_id(goal_id) | ||
db.session.delete(selected_goal) | ||
db.session.commit() | ||
response_body = {'details': f'Goal {goal_id} "{selected_goal.title}" successfully deleted'} | ||
return make_response(response_body, 200) | ||
|
||
# Post list of task_ids to a goal | ||
@goals_bp.route("/<goal_id>/tasks", methods=["POST"], strict_slashes=False) | ||
def post_tasks_to_goal(goal_id): | ||
selected_goal = get_goal_from_id(goal_id) | ||
selected_goal = Goal.query.get(goal_id) | ||
request_body = request.get_json() | ||
task_ids_list = request_body["task_ids"] | ||
|
||
for task_id in task_ids_list: | ||
task = Task.query.get(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. We should probably double check that the task ids in the request body actually correspond to existing tasks. We can do that with a Also note that |
||
selected_goal.tasks.append(task) | ||
|
||
db.session.commit() | ||
response_body = selected_goal.add_tasks_to_goal_dict() | ||
return make_response(response_body, 200) | ||
|
||
# Get tasks from goal | ||
@goals_bp.route("/<goal_id>/tasks", methods=["GET"], strict_slashes=False) | ||
def get_tasks_from_goal(goal_id): | ||
selected_goal = get_goal_from_id(goal_id) | ||
selected_goal = Goal.query.get(goal_id) | ||
|
||
|
||
|
||
response_body = selected_goal.get_tasks_from_goal_dict() | ||
|
||
return make_response(response_body, 200) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
from flask import Blueprint, request, make_response, jsonify, abort | ||
from app.models.task import Task | ||
from app import db | ||
from datetime import date, datetime | ||
|
||
tasks_bp = Blueprint('tasks', __name__, url_prefix='/tasks') | ||
|
||
# # Helper functions | ||
def valid_int(id): | ||
try: | ||
number = int(id) | ||
return number | ||
except: | ||
response_body = 'Invalid Data' | ||
abort(make_response(response_body,400)) | ||
|
||
def get_task_from_id(task_id): | ||
id = valid_int(task_id) | ||
selected_task = Task.query.filter_by(task_id=task_id).one_or_none() | ||
# Task not found | ||
if selected_task is None: | ||
abort(make_response("Not Found", 404)) | ||
return selected_task | ||
|
||
def valid_task(request_body): | ||
if "title" not in request_body or "description" not in request_body or "completed_at" not in request_body: | ||
abort(make_response({"details": "Invalid data"}, 400)) | ||
# # | ||
|
||
# Create one task with error handlers | ||
@tasks_bp.route("", methods=["POST"], strict_slashes=False) | ||
def create_task(): | ||
request_body = request.get_json() | ||
valid_task(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 like with goal, consider using |
||
new_task = Task( | ||
title=request_body["title"], | ||
description=request_body["description"], | ||
completed_at=request_body["completed_at"] | ||
) | ||
db.session.add(new_task) | ||
db.session.commit() | ||
response = {"task": new_task.to_dict()} | ||
return make_response(response, 201) | ||
|
||
# Get all tasks | ||
@tasks_bp.route("", methods=["GET"], strict_slashes=False) | ||
def get_all_tasks(): | ||
sort_tasks = request.args.get("sort") | ||
response_list = [] | ||
# Sort task: by title, ascending | ||
if sort_tasks == "asc": | ||
task_objects = Task.query.order_by(Task.title.asc()) | ||
# Sort task: by title descending | ||
elif sort_tasks == "desc": | ||
task_objects = Task.query.order_by(Task.title.desc()) | ||
else: | ||
task_objects = Task.query.all() | ||
for task in task_objects: | ||
response_list.append(task.to_dict()) | ||
return make_response(jsonify(response_list), 200) | ||
|
||
# Get one task | ||
@tasks_bp.route("/<task_id>", methods=["GET"], strict_slashes=False) | ||
def get_task(task_id): | ||
selected_task = get_task_from_id(task_id) | ||
if selected_task.is_related_to_goal(): | ||
response_body = {"task": selected_task.to_dict_for_goal()} | ||
else: | ||
response_body = {"task": selected_task.to_dict()} | ||
return make_response(response_body, 200) | ||
|
||
# Update task | ||
@tasks_bp.route("/<task_id>", methods=["PUT"], strict_slashes=False) | ||
def update_task(task_id): | ||
selected_task = get_task_from_id(task_id) | ||
request_body = request.get_json() | ||
if "title" in request_body: | ||
selected_task.title = request_body["title"] | ||
if "description" in request_body: | ||
selected_task.description = request_body["description"] | ||
db.session.commit() | ||
response_body = {"task": selected_task.to_dict()} | ||
return make_response(response_body, 200) | ||
|
||
# Mark task complete | ||
@tasks_bp.route("/<task_id>/mark_complete", methods=["PATCH"], strict_slashes=False) | ||
def mark_task_complete(task_id): | ||
selected_task = get_task_from_id(task_id) | ||
# selected_task.is_complete = True | ||
selected_task.completed_at = datetime.now() | ||
db.session.commit() | ||
response_body = {"task": selected_task.to_dict()} | ||
return make_response(response_body, 200) | ||
|
||
# Mark task incomplete | ||
@tasks_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"], strict_slashes=False) | ||
def mark_task_incomplete(task_id): | ||
selected_task = get_task_from_id(task_id) | ||
selected_task.completed_at = None | ||
db.session.commit() | ||
response_body = {"task": selected_task.to_dict()} | ||
return make_response(response_body, 200) | ||
|
||
# Delete task | ||
@tasks_bp.route("/<task_id>", methods=["DELETE"], strict_slashes=False) | ||
def delete_task(task_id): | ||
selected_task = get_task_from_id(task_id) | ||
db.session.delete(selected_task) | ||
db.session.commit() | ||
response_body = {'details': f'Task {task_id} "{selected_task.title}" successfully deleted'} | ||
return make_response(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.
Good work making these instance methods