-
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 mac #78
base: master
Are you sure you want to change the base?
cedar mac #78
Changes from all commits
92ca7b0
949b63e
74abeb0
6eedc80
0bffd65
f2538d7
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 |
---|---|---|
@@ -1,2 +1,182 @@ | ||
from flask import Blueprint | ||
""" | ||
Something went very, very wrong and I had to scrap my original repository, | ||
start over and copy paste my code in. Hence, the lack of commits. | ||
I was beginning the deployment stage and accidently deleted the origin remote. | ||
This lead me down a very dark path, trying different git commands that I didnt fully understand. | ||
It was chaos. | ||
|
||
Anyways, | ||
I didn't have time to do docstrings. | ||
Also, I experimented with doubling up the route decorators since the functions were | ||
pretty much the same for Task & Goals. | ||
Not sure if this is a no no in real life, or if there is a better way to do it. Let me know! | ||
Comment on lines
+11
to
+12
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 looks good to me! Nice use of |
||
""" | ||
|
||
|
||
from flask import Blueprint, jsonify, request, abort, g | ||
from app import db | ||
from app.models.task import Task | ||
from app.models.goal import Goal | ||
from datetime import datetime | ||
import requests | ||
import os | ||
|
||
tasks_bp = Blueprint("tasks", __name__, url_prefix="/tasks") | ||
goals_bp = Blueprint("goals", __name__, url_prefix="/goals") | ||
|
||
|
||
def slack_bot(text): | ||
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 work creating a helper function. You might consider refactoring as an instance method on |
||
|
||
SLACK_KEY = os.environ.get("SLACK_KEY") | ||
req_body = {"channel": "task-notifications", "text": text} | ||
headers = {"Authorization": f"Bearer {SLACK_KEY}"} | ||
path = "https://slack.com/api/chat.postMessage" | ||
|
||
requests.post(path, json=req_body, headers=headers) | ||
|
||
|
||
def validate(model, id): | ||
|
||
try: | ||
id = int(id) | ||
except ValueError: | ||
abort(400) | ||
return model.query.get_or_404(id) | ||
|
||
|
||
def sort(model): | ||
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 work breaking this out into a helper function |
||
|
||
sort = request.args.get("sort") | ||
if sort == "asc": | ||
model = model.query.order_by(model.title.asc()) | ||
elif sort == "desc": | ||
model = model.query.order_by(model.title.desc()) | ||
|
||
return model | ||
|
||
|
||
@goals_bp.before_request | ||
@tasks_bp.before_request | ||
def get_model(): | ||
|
||
bps = {"tasks": (Task, "task"), "goals": (Goal, "goal")} | ||
g.model, g.name = bps[request.blueprint] | ||
|
||
|
||
@goals_bp.route("", methods=["GET"]) | ||
@tasks_bp.route("", methods=["GET"]) | ||
def get_all(): | ||
|
||
model = g.model | ||
if "sort" in request.args: | ||
models = sort(model) | ||
else: | ||
models = model.query.all() | ||
|
||
return jsonify([model.to_dict() for model in models]) | ||
|
||
|
||
@goals_bp.route("/<id>", methods=["GET"]) | ||
@tasks_bp.route("/<id>", methods=["GET"]) | ||
def get_one(id): | ||
|
||
model, name = g.model, g.name | ||
model = validate(model, id) | ||
|
||
return {f"{name}": model.to_dict()} | ||
|
||
|
||
@goals_bp.route("", methods=["POST"]) | ||
@tasks_bp.route("", methods=["POST"]) | ||
def create(): | ||
|
||
model, name = g.model, g.name | ||
request_body = request.get_json() | ||
|
||
try: | ||
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 works, and is very concise! You might consider refactor to check for the specific missing column information to provide these details to the user. |
||
if model == Task: | ||
new_entry = model( | ||
title=request_body["title"], | ||
description=request_body["description"], | ||
completed_at=request_body["completed_at"], | ||
) | ||
elif model == Goal: | ||
new_entry = model(title=request_body["title"]) | ||
|
||
except: | ||
return {"details": "Invalid data"}, 400 | ||
|
||
db.session.add(new_entry) | ||
db.session.commit() | ||
|
||
return {f"{name}": new_entry.to_dict()}, 201 | ||
|
||
|
||
@goals_bp.route("/<id>", methods=["PUT"]) | ||
@tasks_bp.route("/<id>", methods=["PUT"]) | ||
def update_one(id): | ||
model, name = g.model, g.name | ||
model = validate(model, id) | ||
request_body = request.get_json() | ||
|
||
model.update(request_body) | ||
|
||
db.session.commit() | ||
return {f"{name}": model.to_dict()} | ||
|
||
|
||
@goals_bp.route("/<id>", methods=["DELETE"]) | ||
@tasks_bp.route("/<id>", methods=["DELETE"]) | ||
def delete_one(id): | ||
|
||
model, name = g.model, g.name | ||
model = validate(model, id) | ||
|
||
db.session.delete(model) | ||
db.session.commit() | ||
|
||
return { | ||
"details": f'{name.capitalize()} {model.id} "{model.title}" successfully deleted' | ||
} | ||
|
||
|
||
@tasks_bp.route("/<id>/mark_complete", methods=["PATCH"]) | ||
def mark_task_complete(id): | ||
|
||
task = validate(Task, id) | ||
text = f"Someone just completed the task {task.title}" | ||
slack_bot(text) | ||
|
||
task.completed_at = datetime.now() | ||
db.session.commit() | ||
|
||
return {"task": task.to_dict()} | ||
|
||
|
||
@tasks_bp.route("/<id>/mark_incomplete", methods=["PATCH"]) | ||
def mark_task_incomplete(id): | ||
|
||
task = validate(Task, id) | ||
task.completed_at = None | ||
db.session.commit() | ||
|
||
return {"task": task.to_dict()} | ||
|
||
|
||
@goals_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def add_tasks_to_goal(goal_id): | ||
goal = validate(Goal, goal_id) | ||
request_body = request.get_json() | ||
|
||
try: | ||
goal.tasks = [validate(Task, task_id) for task_id in request_body["task_ids"]] | ||
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 use of |
||
except: | ||
return {"details": "Invalid data"}, 400 | ||
|
||
return {"id": goal.id, "task_ids": [task.id for task in goal.tasks]} | ||
|
||
|
||
@goals_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def get_tasks_by_goal(goal_id): | ||
goal = validate(Goal, goal_id) | ||
return goal.to_dict(has_tasks=True) | ||
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. Cool use of a keyword argument |
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 work creating this DRY, flexible helper method!