Skip to content
Open
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
8 changes: 6 additions & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import os
from dotenv import load_dotenv


db = SQLAlchemy()
migrate = Migrate()
load_dotenv()
Expand All @@ -30,5 +29,10 @@ def create_app(test_config=None):
migrate.init_app(app, db)

# Register Blueprints here

from app.models.task import Task
from app.models.goal import Goal
Comment on lines +32 to +33
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You didn't need to import Task or Goal here since they are unused.

from .routes import tasks_bp, 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


class Goal(db.Model):
goal_id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.Text)
tasks = db.relationship("Task", backref="goals", lazy=True)
__tablename__ = "goals"

def to_json(self):
if self.tasks != []:
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 can be simplified as:

Suggested change
if self.tasks != []:
if self.tasks:

Since empty lists are falsey.

return {
"id": self.goal_id,
"title": self.title,
"tasks": self.tasks
}
else:
return {
"id": self.goal_id,
"title": self.title,
}
28 changes: 26 additions & 2 deletions app/models/task.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
from flask import current_app
from app import db


class Task(db.Model):
task_id = db.Column(db.Integer, primary_key=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("goals.goal_id"), nullable=True)
__tablename__ = "tasks"

def to_json(self):
is_complete = self.completed_at
if self.completed_at == None:
is_complete = False
else:
is_complete = True
Comment on lines +12 to +16
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 can be simplified as:

Suggested change
is_complete = self.completed_at
if self.completed_at == None:
is_complete = False
else:
is_complete = True
is_complete = self.completed_at != None

if self.goal_id != None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remember, None is falsey:

Suggested change
if self.goal_id != None:
if self.goal_id:

return {
"id": self.task_id,
"title": self.title,
"description": self.description,
"is_complete": is_complete,
"goal_id": self.goal_id
}
return {
"id": self.task_id,
"title": self.title,
"description": self.description,
"is_complete": is_complete
}
210 changes: 209 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,210 @@
from flask import Blueprint
from app import db
from app.models.task import Task
from app.models.goal import Goal
from flask import Blueprint, jsonify, request, make_response
from datetime import datetime
import os
import requests

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

@goals_bp.route("/<goal_id>/tasks", methods=["POST"], strict_slashes=False)
def assign_tasks_to_one_goal(goal_id):
request_body = request.get_json()

goal = Goal.query.get(goal_id)

task_ids = request_body["task_ids"]

for task_id in task_ids:
task = Task.query.get(task_id)
goal.tasks.append(task)

db.session.commit()

return make_response({
"id": goal.goal_id,
"task_ids": task_ids
}, 200)


@goals_bp.route("/<goal_id>/tasks", methods=["GET"], strict_slashes=False)
def get_tasks_from_goal_id(goal_id):
goal = Goal.query.get_or_404(goal_id)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice use of get_or_404!

tasks = goal.tasks

tasks_response = []

for task in tasks:
tasks_response.append(task.to_json())

goal_response = goal.to_json()
goal_response["tasks"] = tasks_response

return goal_response, 200
0
@goals_bp.route("", methods=["POST"], strict_slashes=False)
def create_goal():
request_body = request.get_json()

try:
new_goal = Goal(title=request_body["title"])

except KeyError:
return make_response({
"details": "Invalid data"
}, 400)

db.session.add(new_goal)
db.session.commit()

return {
"goal": new_goal.to_json()
}, 201

@goals_bp.route("/<goal_id>", methods=["GET"], strict_slashes=False)
def get_one_goal(goal_id):
goal = Goal.query.get_or_404(goal_id)

return {
"goal": goal.to_json()}, 200

@goals_bp.route("", methods=["GET"], strict_slashes=False)
def goals_index():
goals = Goal.query.all()
goals_response = []
for goal in goals:
goals_response.append(goal.to_json())

return jsonify(goals_response), 200

@goals_bp.route("/<goal_id>", methods=["PUT"], strict_slashes=False)
def update_goal(goal_id):
goal = Goal.query.get_or_404(goal_id)
form_data = request.get_json()

goal.title = form_data["title"]

db.session.commit()

return {
"goal": goal.to_json()
}, 200

@goals_bp.route("/<goal_id>", methods=["DELETE"], strict_slashes=False)
def delete_goal(goal_id):
goal = Goal.query.get_or_404(goal_id)

db.session.delete(goal)
db.session.commit()

return make_response({
"details": f'Goal {goal.goal_id} "{goal.title}" successfully deleted'
})


@tasks_bp.route("", methods=["GET"], strict_slashes=False)
def tasks_index():
sort_type = request.args.get("sort")

if sort_type == "desc":
tasks = Task.query.order_by(Task.title.desc())
elif sort_type == "asc":
tasks = Task.query.order_by(Task.title.asc())
else:
tasks = Task.query.all()

tasks_response = []

for task in tasks:
tasks_response.append(task.to_json())

return jsonify(tasks_response), 200

@tasks_bp.route("/<task_id>", methods=["GET"], strict_slashes=False)
def get_one_task(task_id):
task = Task.query.get_or_404(task_id)

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

@tasks_bp.route("", methods=["POST"], strict_slashes=False)
def create_task():
request_body = request.get_json()

try:
new_task = Task(title=request_body["title"],
description=request_body["description"],
completed_at=request_body["completed_at"])

except KeyError:
return make_response({
"details": "Invalid data"
}, 400)

db.session.add(new_task)
db.session.commit()

return {
"task": new_task.to_json()
}, 201

@tasks_bp.route("/<task_id>", methods=["PUT"], strict_slashes=False)
def update_task(task_id):
task = Task.query.get_or_404(task_id)
form_data = request.get_json()

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

db.session.commit()

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

@tasks_bp.route("/<task_id>", methods=["DELETE"], strict_slashes=False)
def delete_task(task_id):
task = Task.query.get_or_404(task_id)

db.session.delete(task)
db.session.commit()

return make_response({
"details": f'Task {task.task_id} "{task.title}" successfully deleted'
})

@tasks_bp.route("/<task_id>/mark_complete", methods=["PATCH"], strict_slashes=False)
def task_mark_complete(task_id):
task = Task.query.get_or_404(task_id)
current_datetime = datetime.utcnow()

task.completed_at = current_datetime

db.session.commit()

response = requests.get('https://slack.com/api/chat.postMessage',\
params={
"channel": "task-notifications",
"text": f"Someone just completed the task {task.title}"},
headers={'Authorization': os.environ['SLACK_TOKEN']})
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 should have been named SLACK_API_KEY (to match our .env in Learn) that's why the tests failed:

Suggested change
headers={'Authorization': os.environ['SLACK_TOKEN']})
headers={'Authorization': os.environ['SLACK_API_KEY']})


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


@tasks_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"], strict_slashes=False)
def task_mark_incomplete(task_id):
task = Task.query.get_or_404(task_id)

if task.completed_at != None:
task.completed_at = None

db.session.commit()

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