Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,9 @@ def create_app(test_config=None):
migrate.init_app(app, db)

# Register Blueprints here
from .routes import tasks_bp
from .routes import goals_bp
app.register_blueprint(tasks_bp)
app.register_blueprint(goals_bp)

return app
17 changes: 16 additions & 1 deletion app/models/goal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
from flask import current_app
from app import db

# Our task list API should be able to work with an entity called Goal.

# ****Goals are entities that describe a task a user wants to complete.****

# They contain a title to name the goal.

# Our goal for this wave is to be able to create, read, update, and delete different goals.

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)
tasks = db.relationship("Task", backref='goal', lazy=True) #originally had task and not tasks

def now_json(self):
return{
"id": self.goal_id,
"title": self.title,
}
18 changes: 17 additions & 1 deletion app/models/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,20 @@


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'), nullable=True)

def to_json(self):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the custom method!


return {
"id": self.task_id,
"title": self.title,
"description": self.description,
"is_complete": self.completed_at != None
}



225 changes: 224 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,225 @@
from flask import Blueprint
from flask import Blueprint, request, make_response, jsonify
from app import db
from app.models.task import Task
from app.models.goal import Goal
from datetime import datetime
import datetime
from sqlalchemy import DateTime, desc
import requests
import os

tasks_bp = Blueprint("tasks", __name__, url_prefix="/tasks")
goals_bp = Blueprint("goals", __name__, url_prefix="/goals")

#wave4
def hi_slack_api(task):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love that this is in its own function! I think it make the code more readable!

key = os.environ.get("API_KEY")
url = "https://slack.com/api/chat.postMessage"
slack_str = f"Someone just completed the task {task.title}"
requests.post(url, headers={"Authorization": key}, params={"channel": "task-list-api", "text": slack_str})

#wave6
@goals_bp.route("/<goal_id>/tasks", methods=["POST"], strict_slashes= False)
def task_goal(goal_id):
request_body = request.get_json()
goal = Goal.query.get(goal_id)

if goal is None:
return make_response("", 404)

for task_id in request_body["task_ids"]:
task = Task.query.get(task_id)
task.goal_id = goal.goal_id

db.session.commit()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job remembering to commit here! Many folks forgot to since there isn't actually a test to verify the new tasks get saved in the db

return make_response({"id": goal.goal_id, "task_ids": request_body["task_ids"]}, 200)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change would allow the response to include ALL tasks associated with the goal, not just the new ones.
This isn't super intuitive so I'd definitely be down to talk through this more in our next 1:1

Suggested change
return make_response({"id": goal.goal_id, "task_ids": request_body["task_ids"]}, 200)
task_list = []
for task in goal.tasks:
task_list.append(task.task_id)
return make_response({"id": goal.goal_id, "task_ids": task_list}, 200)


@goals_bp.route("/<goal_id>/tasks", methods=["GET"], strict_slashes= False)
def get_goal_task(goal_id):
goal = Goal.query.get(goal_id)
if goal == None:
return make_response("", 404)

tasks = Task.query.filter_by(goal_id = goal_id)
task_list = []

for task in tasks:
task_list.append(helper_fun(task))

return make_response({"id": int(goal_id), "title": goal.title, "tasks": task_list }, 200)

def helper_fun(task_goal):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fun helper function! To improve the code further, this would be a very appropriate method to add to task.

return {
"id": task_goal.task_id,
"goal_id": task_goal.goal_id,
"title": task_goal.title,
"description": task_goal.description,
"is_complete": task_goal.completed_at != None
}

#wave 5
@goals_bp.route("", methods=["POST"], strict_slashes= False)
def create_goals():
request_body = request.get_json()

if "title" in request_body:
new_goal = Goal(title = request_body["title"])
db.session.add(new_goal)
db.session.commit()
return jsonify({"goal": new_goal.now_json()}), 201
else:
return jsonify({"details": "Invalid data"}), 400

@goals_bp.route("", methods=["GET"], strict_slashes= False)
def get_goals():
goals = Goal.query.order_by(Goal.title).all()
goals_response = []
for goal in goals:
goals_response.append(goal.now_json())
return jsonify(goals_response), 200

@goals_bp.route("/<goal_id>", methods=["GET"], strict_slashes= False)
def get_goal_by_id(goal_id):
goal = Goal.query.get(goal_id)
if goal is None:
return jsonify(None), 404
else:
return make_response({"goal": goal.now_json()}, 200)

@goals_bp.route("/<goal_id>", methods=["PUT"], strict_slashes= False)
def update_goal(goal_id):
goal = Goal.query.get(goal_id)
if goal is None:
return jsonify(None), 404

form_data = request.get_json()

goal.title = form_data["title"]

db.session.commit()

return jsonify({"goal":goal.now_json()}), 200

@goals_bp.route("/<goal_id>", methods=["DELETE"], strict_slashes= False)
def abandon_goals(goal_id):
goal = Goal.query.get(goal_id)
if goal is None:
return jsonify(None), 404
else:
db.session.delete(goal)
db.session.commit()
return jsonify({"details": f"Goal {goal.goal_id} \"{goal.title}\" successfully deleted"}), 200
#end wave 5

@tasks_bp.route("", methods=["GET", "POST"], strict_slashes= False)
def deal_tasks():
if request.method == "GET":
sort_query = request.args.get("sort")#<- handles second wave 2
if sort_query == "desc":
tasks = Task.query.order_by(Task.title.desc()).all()
else:
tasks = Task.query.order_by(Task.title).all() #<- handles first wave 2
tasks_response = []
for task in tasks:
tasks_response.append(task.to_json())
return jsonify(tasks_response)

elif request.method == "POST":
request_body = request.get_json()

if "title" in request_body and "description" in request_body and "completed_at" in request_body:
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()
return make_response({"task": new_task.to_json()}, 201)
else:
return jsonify({"details": "Invalid data"}), 400

#also handles wave 6 last test
@tasks_bp.route("/<task_id>", methods=["GET"], strict_slashes= False)
def get_task_by_id(task_id):
task = Task.query.get(task_id)
if task is None:
return jsonify(None), 404
elif task.goal_id:
return make_response({"task": helper_fun(task)}, 200)
else:
return make_response({"task": task.to_json()}, 200)

@tasks_bp.route("/<task_id>", methods=["DELETE"], strict_slashes= False)
def delete_task(task_id):
print("in delete task") #what is this left over from, testing maybe? get rid of this you silly
task = Task.query.get(task_id)
if task is None:
return jsonify(None), 404
else:
db.session.delete(task)
db.session.commit()
return make_response({"details": f'Task {task.task_id} "{task.title}" successfully deleted'}, 200)


@tasks_bp.route("/<task_id>", methods=["PUT"], strict_slashes= False)
def update_task(task_id):
task = Task.query.get(task_id)
if task is None:
return jsonify(None), 404

form_data = request.get_json()

task.title = form_data["title"]
task.description = form_data["description"]
task.completed_at = form_data["completed_at"]

db.session.commit()

return jsonify({"task":task.to_json()}), 200

#wave 3
@tasks_bp.route("/<task_id>/mark_complete", methods=["Patch"])
def mark_complete(task_id):
task= Task.query.get(task_id)
if not task:
return "", 404

if task.completed_at:
task.completed_at = datetime.datetime.now()
else:
task.completed_at = datetime.datetime.now()

db.session.add(task)
db.session.commit()

hi_slack_api(task) #wave 4

if task.completed_at:
return jsonify({
"task": task.to_json()
}), 200

@tasks_bp.route("/<task_id>/mark_incomplete", methods=["Patch"])
def mark_incomplete(task_id):
task= Task.query.get(task_id)
if not task:
return "", 404

if task.completed_at:
task.completed_at = None
return {
"task": task.to_json()
}, 200
else:
task.completed_at = None
return {
"task": task.to_json()
}, 200








1 change: 1 addition & 0 deletions migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
45 changes: 45 additions & 0 deletions migrations/alembic.ini
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
Loading