-
Notifications
You must be signed in to change notification settings - Fork 128
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
Sapphire - Linh H. #127
base: main
Are you sure you want to change the base?
Sapphire - Linh H. #127
Changes from all commits
43ee5fd
fb42416
702295d
c6a044d
a122ccf
e651e96
32f1fb1
7750431
75f10d9
6f32476
8e701a7
0f1c425
099312b
caad9c0
38252f1
d1d62c1
b9d3db3
00f641c
c3789b2
cf6e15d
8b7c0a1
426efd3
701210a
affd1ef
01515da
8a53918
a92765c
5a22ed3
b99a25e
ae01536
f369be7
104044c
6047864
c996f27
29ad072
3658a19
1a1390a
e244465
57b686b
6b8422f
bb94cd1
7a323ea
24b4b16
baf6450
5ba683b
1ffd7fa
3b0c98b
eb2ca32
378dad6
011b73e
ec1e8cf
b8d0921
463a3a8
614f249
b5c1aea
951ce91
a2ab691
0a8d0c3
f927beb
096f9ab
a34eab2
e404149
af29edb
4491c07
b328ed8
3a8b9b3
525863f
5d6068e
e0a3f39
8141fb7
2521be9
ebffe07
a158945
fa30d39
3f56a5c
6b9a1af
f7d2a08
ab28522
2b6f24f
0d263ce
494b364
c2a4485
a5972f2
c253929
c27a1bc
304e025
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,93 @@ | ||
from app import db | ||
from app.models.goal import Goal | ||
from app.models.task import Task | ||
from app.helper_functions import validate_model | ||
from flask import Blueprint, jsonify, make_response, request | ||
from sqlalchemy import asc, desc | ||
|
||
|
||
goals_bp = Blueprint("goal_bp", __name__, url_prefix="/goals") | ||
|
||
|
||
@goals_bp.route("", methods=["POST"]) | ||
def create_goal(): | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body: | ||
return make_response({"details": "Invalid data"}, 400) | ||
|
||
new_goal = Goal.from_dict(request_body) | ||
|
||
db.session.add(new_goal) | ||
db.session.commit() | ||
Comment on lines
+21
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. Great job using
By referencing |
||
|
||
return make_response({"goal": new_goal.to_dict()}, 201) | ||
|
||
|
||
@goals_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def create_tasks_under_one_goal(goal_id): | ||
request_body = request.get_json() | ||
goal = validate_model(Goal, goal_id) | ||
|
||
task_list = [] | ||
for task_id in request_body["task_ids"]: | ||
task = validate_model(Task, task_id) | ||
task.goal = goal | ||
task_list.append(task_id) | ||
|
||
db.session.commit() | ||
|
||
return make_response({"id": goal.goal_id, "task_ids": task_list}, 200) | ||
|
||
|
||
@goals_bp.route("", methods=["GET"]) | ||
def read_all_goals(): | ||
sort_query = request.args.get("sort") | ||
if sort_query: | ||
if sort_query == "asc": | ||
goals = Goal.query.order_by(Goal.title.asc()).all() | ||
elif sort_query == "desc": | ||
goals = Goal.query.order_by(Goal.title.desc()).all() | ||
else: | ||
goals = Goal.query.all() | ||
|
||
all_goals = [goal.to_dict() for goal in goals] | ||
|
||
return jsonify(all_goals), 200 | ||
|
||
|
||
@goals_bp.route("/<goal_id>", methods=["GET"]) | ||
def read_one_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
return make_response({"goal": goal.to_dict()}, 200) | ||
|
||
|
||
@goals_bp.route("/<goal_id>/tasks", methods=["GET"]) | ||
def read_tasks_under_one_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
goal_task_list = [task.to_dict() for task in goal.tasks] | ||
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 listcomp! 👾 |
||
|
||
return make_response({"id": goal.goal_id, "title": goal.title, "tasks": goal_task_list}, 200) | ||
|
||
|
||
@goals_bp.route("/<goal_id>", methods=["PUT"]) | ||
def update_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
request_body = request.get_json() | ||
goal.title = request_body["title"] | ||
|
||
db.session.commit() | ||
|
||
return make_response({"goal": goal.to_dict()}, 200) | ||
|
||
|
||
@goals_bp.route("/<goal_id>", methods=["DELETE"]) | ||
def delete_goal(goal_id): | ||
goal = validate_model(Goal, goal_id) | ||
|
||
db.session.delete(goal) | ||
db.session.commit() | ||
|
||
return make_response({"details":f"Goal {goal.goal_id} \"{goal.title}\" successfully deleted"}), 200 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from app import db | ||
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. I love everything about this helper file! Great job pulling out |
||
from flask import make_response, abort | ||
import os, requests | ||
|
||
|
||
def validate_model(cls, model_id): | ||
try: | ||
model_id = int(model_id) | ||
except: | ||
abort(make_response({"message": f"{cls.__name__} {model_id} is invalid"}, 400)) | ||
|
||
model = cls.query.get(model_id) | ||
|
||
if not model: | ||
abort(make_response({"message": f"{cls.__name__} {model_id} not found"}, 404)) | ||
|
||
return model | ||
|
||
def slack_bot_message(message): | ||
slack_api_key = os.environ.get("SLACK_BOT_TOKEN") | ||
slack_url = "https://slack.com/api/chat.postMessage" | ||
header = {"Authorization": slack_api_key} | ||
|
||
slack_query_params = { | ||
"channel": "task-notifications", | ||
"text": message | ||
} | ||
print(slack_api_key) | ||
requests.post(url=slack_url, data=slack_query_params, headers=header) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,29 @@ | |
|
||
class Goal(db.Model): | ||
goal_id = db.Column(db.Integer, primary_key=True) | ||
title = db.Column(db.String) | ||
tasks = db.relationship("Task", back_populates="goal", lazy=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. Beautiful sync to the Task model! Good job remembering to include our |
||
|
||
def to_dict(self): | ||
return dict( | ||
id=self.goal_id, | ||
title=self.title | ||
) | ||
|
||
@classmethod | ||
def from_dict(cls, goal_data): | ||
new_goal = Goal(title=goal_data["title"]) | ||
return new_goal | ||
|
||
|
||
# _______________ | ||
# | | | ||
# | Thank | | ||
# | you for | | ||
# | reviewing | | ||
# | my code! | | ||
# |_____________| | ||
# || | ||
# (\__/)|| | ||
# (•ㅅ•) || | ||
# / >|| | ||
Comment on lines
+21
to
+31
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. Wait, this is so cute 😭 TY for showing up!! |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,43 @@ | |
|
||
|
||
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) | ||
completed_at = db.Column(db.DateTime, nullable=True) | ||
is_complete = db.Column(db.Boolean) | ||
goal_id = db.Column(db.Integer, db.ForeignKey("goal.goal_id"), nullable=True) | ||
goal = db.relationship("Goal", back_populates="tasks") | ||
Comment on lines
+10
to
+11
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. Awesome! Beautiful sync up! Fun fact: the default value for nullable is |
||
|
||
def to_dict(self): | ||
if self.completed_at: | ||
return dict( | ||
id=self.task_id, | ||
title=self.title, | ||
description=self.description, | ||
is_complete=True | ||
) | ||
|
||
if self.goal_id and not self.completed_at: | ||
return dict( | ||
id=self.task_id, | ||
goal_id=self.goal_id, | ||
title=self.title, | ||
description=self.description, | ||
is_complete=False | ||
) | ||
else: | ||
return dict( | ||
id=self.task_id, | ||
title=self.title, | ||
description=self.description, | ||
is_complete=False | ||
) | ||
Comment on lines
+13
to
+36
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 a great place to DRY our code! Remember that the pipe operator
|
||
|
||
@classmethod | ||
def from_dict(cls, task_data): | ||
new_task = Task( | ||
title=task_data["title"], | ||
description=task_data["description"] | ||
) | ||
return new_task | ||
Comment on lines
+38
to
+44
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. Awesome |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from app import db | ||
from app.models.task import Task | ||
from app.helper_functions import validate_model, slack_bot_message | ||
from flask import Blueprint, jsonify, make_response, request, abort | ||
from sqlalchemy import asc, desc | ||
from datetime import datetime | ||
|
||
|
||
tasks_bp = Blueprint("tasks_bp", __name__, url_prefix="/tasks") | ||
|
||
|
||
@tasks_bp.route("", methods=["POST"]) | ||
def create_task(): | ||
request_body = request.get_json() | ||
|
||
if "title" not in request_body or "description" not in request_body: | ||
return make_response({"details": "Invalid data"}, 400) | ||
|
||
new_task = Task.from_dict(request_body) | ||
|
||
db.session.add(new_task) | ||
db.session.commit() | ||
|
||
return make_response({"task": new_task.to_dict()}, 201) | ||
|
||
|
||
@tasks_bp.route("", methods=["GET"]) | ||
def read_all_tasks(): | ||
sort_query = request.args.get("sort") | ||
if sort_query: | ||
if sort_query == "asc": | ||
tasks = Task.query.order_by(Task.title.asc()).all() | ||
elif sort_query == "desc": | ||
tasks = Task.query.order_by(Task.title.desc()).all() | ||
else: | ||
tasks = Task.query.all() | ||
|
||
all_tasks = [task.to_dict() for task in tasks] | ||
|
||
return jsonify(all_tasks), 200 | ||
|
||
|
||
@tasks_bp.route("/<task_id>", methods=["GET"]) | ||
def read_one_task(task_id): | ||
task = validate_model(Task, task_id) | ||
|
||
return make_response({"task": task.to_dict()}, 200) | ||
|
||
|
||
@tasks_bp.route("/<task_id>", methods=["PUT"]) | ||
def update_task(task_id): | ||
task = validate_model(Task, task_id) | ||
request_body = request.get_json() | ||
|
||
task.title = request_body["title"] | ||
task.description = request_body["description"] | ||
|
||
db.session.commit() | ||
|
||
return make_response({"task": task.to_dict()}, 200) | ||
|
||
|
||
@tasks_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||
def mark_task_complete(task_id): | ||
task = validate_model(Task, task_id) | ||
|
||
task.completed_at = datetime.utcnow() | ||
db.session.commit() | ||
|
||
slack_bot_message(f"Someone just completed the task {task.title}") | ||
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. So clean! So clear! Spectacular! 🌟 |
||
|
||
return make_response({"task": task.to_dict()}, 200) | ||
|
||
|
||
@tasks_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||
def mark_task_incomplete(task_id): | ||
task = validate_model(Task, task_id) | ||
|
||
task.completed_at = None | ||
db.session.commit() | ||
|
||
return make_response({"task": task.to_dict()}, 200) | ||
|
||
|
||
@tasks_bp.route("/<task_id>", methods=["DELETE"]) | ||
def delete_task(task_id): | ||
task = validate_model(Task, task_id) | ||
|
||
db.session.delete(task) | ||
db.session.commit() | ||
|
||
return make_response({"details": f"Task {task.task_id} \"{task.title}\" successfully deleted"}), 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.
Beautiful imports in your app init! 🎉