-
Notifications
You must be signed in to change notification settings - Fork 111
Whales - Alexa Coffman #107
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?
Changes from all commits
44f1def
0cbb311
e7feabb
8de8960
4150fb8
c73290a
1d027f2
0e9fabf
03f2610
185595b
f2d05de
278593c
ee148b3
190b1d1
c709e41
c93bcac
0cb2473
418921f
2b4fef2
623514d
0ca6a41
f9b0fea
7d16549
5ce2e7d
27a54a3
ecdb5ac
8cbfd02
9229915
dfa21d1
c7f0225
511cf14
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
web: gunicorn 'app:create_app()' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
from flask import Blueprint, jsonify, request | ||
from app import db | ||
from app.models.goal import Goal | ||
from sqlalchemy import asc | ||
from app.task_routes import validate_task | ||
|
||
goals_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
|
||
def validate_goal(goal_id): | ||
try: | ||
goal_id = int(goal_id) | ||
except ValueError: | ||
return jsonify({"msg" : f"'{goal_id}' is invalid"}), 400 | ||
|
||
goal = Goal.query.get(goal_id) | ||
if not goal: | ||
return jsonify({"message" : f"Could not find '{goal_id}'"}), 404 | ||
return goal | ||
|
||
def format_goal(goal): | ||
return { | ||
"goal": { | ||
"id" : goal.goal_id, | ||
"title" : goal.title | ||
} | ||
} | ||
|
||
@goals_bp.route('', methods=['POST']) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body: | ||
return { | ||
"details" : "Invalid data" | ||
}, 400 | ||
|
||
new_goal = Goal( | ||
title = request_body['title'] | ||
) | ||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
|
||
return format_goal(new_goal), 201 | ||
|
||
@goals_bp.route('', methods=['GET']) | ||
def get_goals(): | ||
goals = Goal.query.order_by(asc(Goal.title)).all() | ||
goals_response = [] | ||
|
||
for goal in goals: | ||
goals_response.append( | ||
{ | ||
"id" : goal.goal_id, | ||
"title" : goal.title | ||
} | ||
) | ||
return jsonify(goals_response), 200 | ||
|
||
@goals_bp.route('/<goal_id>', methods=['GET']) | ||
def get_one_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
|
||
if isinstance(goal, Goal): | ||
return format_goal(goal), 200 | ||
return goal | ||
|
||
@goals_bp.route('/<goal_id>', methods=['PUT']) | ||
def update_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
|
||
if isinstance(goal, Goal): | ||
goal.title = request_body["title"] | ||
db.session.commit() | ||
|
||
return format_goal(goal), 200 | ||
return goal | ||
|
||
@goals_bp.route('/<goal_id>', methods=['DELETE']) | ||
def delete_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
if isinstance(goal, Goal): | ||
db.session.delete(goal) | ||
db.session.commit() | ||
return { | ||
"details" : f'Goal {goal_id} "{goal.title}" successfully deleted' | ||
} | ||
return goal | ||
|
||
@goals_bp.route('/<goal_id>/tasks', methods=['POST']) | ||
def assign_task_to_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
|
||
if isinstance(goal, Goal): | ||
tasks = [] | ||
for task_id in request_body['task_ids']: | ||
task = validate_task(task_id) | ||
task.goal_id = goal_id | ||
tasks.append(task.task_id) | ||
db.session.commit() | ||
|
||
return { | ||
"id" : goal.goal_id, | ||
"task_ids" : tasks | ||
} | ||
return goal | ||
|
||
@goals_bp.route('/<goal_id>/tasks', methods=['GET']) | ||
def get_tasks_of_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
|
||
if isinstance(goal, Goal): | ||
tasks = [] | ||
for task in goal.tasks: | ||
tasks.append({ | ||
"id" : task.task_id, | ||
"goal_id" : task.goal_id, | ||
"title" : task.title, | ||
"description" : task.description, | ||
"is_complete" : bool(task.completed_at) | ||
}) | ||
|
||
return { | ||
"id" : goal.goal_id, | ||
"title" : goal.title, | ||
"tasks" : tasks | ||
} | ||
return goal |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
from flask import Blueprint, jsonify, request | ||
from app import db | ||
from app.models.task import Task | ||
from sqlalchemy import desc, asc | ||
from datetime import datetime | ||
import requests | ||
import os | ||
|
||
tasks_bp = Blueprint("tasks", __name__, url_prefix="/tasks") | ||
|
||
|
||
def validate_task(task_id): | ||
try: | ||
task_id = int(task_id) | ||
except ValueError: | ||
return jsonify({"msg" : f"'{task_id}' is invalid"}), 400 | ||
|
||
task = Task.query.get(task_id) | ||
if not task: | ||
return jsonify({"message" : f"Could not find '{task_id}'"}), 404 | ||
return task | ||
|
||
def format_task(task): | ||
return { | ||
"task" : | ||
{ | ||
"is_complete" : bool(task.completed_at), | ||
"description" : task.description, | ||
"title" : task.title, | ||
"id" : task.task_id, | ||
"goal_id" : task.goal_id | ||
} | ||
} | ||
|
||
def push_complete_to_slack(task): | ||
bot_token = os.environ.get("SLACK_BOT_TOKEN") | ||
params = { | ||
"channel" : "task-notifications", | ||
"text" : f"Someone just completed the task {task.title}" | ||
} | ||
headers = {"authorization" : "Bearer " + bot_token} | ||
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 reason your tests fail on Learn is because you try to use the token without first verifying that it is not A check at the beginning of this that does an early return if there's no token is probably the correct way to solve this (since if Slack/not configured is down you'd like your API to still work). |
||
requests.post("https://slack.com/api/chat.postMessage", params=params, headers=headers) | ||
|
||
@tasks_bp.route('', methods=['POST']) | ||
def create_task(): | ||
request_body = request.get_json() | ||
|
||
if "description" not in request_body or "title" not in request_body: | ||
return { | ||
"details" : "Invalid data" | ||
}, 400 | ||
|
||
new_task = Task( | ||
description = request_body['description'], | ||
title = request_body['title'] | ||
) | ||
|
||
if "completed_at" in request_body: | ||
new_task.completed_at = request_body['completed_at'] | ||
|
||
db.session.add(new_task) | ||
db.session.commit() | ||
|
||
return format_task(new_task), 201 | ||
|
||
@tasks_bp.route('', methods=['GET']) | ||
def get_tasks(): | ||
sort_query = request.args.get("sort") | ||
if sort_query == "desc": | ||
tasks = Task.query.order_by(desc(Task.title)).all() | ||
else: | ||
tasks = Task.query.order_by(asc(Task.title)).all() | ||
|
||
tasks_response = [] | ||
|
||
for task in tasks: | ||
tasks_response.append( | ||
{ | ||
"is_complete" : bool(task.completed_at), | ||
"description" : task.description, | ||
"title" : task.title, | ||
"id" : task.task_id | ||
} | ||
) | ||
|
||
return jsonify(tasks_response), 200 | ||
|
||
@tasks_bp.route('/<task_id>', methods=['GET']) | ||
def get_one_task(task_id): | ||
task = validate_task(task_id) | ||
|
||
if isinstance(task, Task): | ||
return format_task(task), 200 | ||
return task | ||
|
||
@tasks_bp.route('/<task_id>', methods=['PUT']) | ||
def update_task(task_id): | ||
task = validate_task(task_id) | ||
request_body = request.get_json() | ||
|
||
if isinstance(task, Task): | ||
task.title = request_body["title"] | ||
task.description = request_body["description"] | ||
db.session.commit() | ||
|
||
if "completed_at" in request_body: | ||
task.completed_at = request_body['completed_at'] | ||
|
||
return format_task(task), 200 | ||
return task | ||
|
||
@tasks_bp.route('/<task_id>', methods=['DELETE']) | ||
def delete_task(task_id): | ||
task = validate_task(task_id) | ||
if isinstance(task, Task): | ||
db.session.delete(task) | ||
db.session.commit() | ||
return { | ||
"details" : f'Task {task_id} "{task.title}" successfully deleted' | ||
} | ||
return task | ||
|
||
@tasks_bp.route('/<task_id>/mark_complete', methods=['PATCH']) | ||
def mark_complete(task_id): | ||
task = validate_task(task_id) | ||
|
||
if isinstance(task, Task): | ||
|
||
task.completed_at = datetime.utcnow() | ||
db.session.commit() | ||
push_complete_to_slack(task) | ||
return format_task(task), 200 | ||
return task | ||
|
||
@tasks_bp.route('/<task_id>/mark_incomplete', methods=['PATCH']) | ||
def mark_incomplete(task_id): | ||
task = validate_task(task_id) | ||
|
||
if isinstance(task, Task): | ||
task.completed_at = None | ||
db.session.commit() | ||
return format_task(task), 200 | ||
return task | ||
|
||
|
||
|
||
|
||
|
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.
Nice job including the invalid
goal_id
! 😃