-
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
Amethyst - Abby Goodman #129
base: main
Are you sure you want to change the base?
Changes from all commits
b45b8ae
57c5306
955f257
21836be
ecd3436
2102e20
3573193
115ffc1
7494df7
41f4466
1b08b34
fefdf05
ea1d5bd
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 |
---|---|---|
|
@@ -15,8 +15,10 @@ def create_app(test_config=None): | |
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False | ||
|
||
if test_config is None: | ||
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( | ||
"SQLALCHEMY_DATABASE_URI") | ||
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("RENDER_DB_URI") | ||
|
||
# app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( | ||
# "SQLALCHEMY_DATABASE_URI") | ||
else: | ||
app.config["TESTING"] = True | ||
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( | ||
|
@@ -30,5 +32,9 @@ def create_app(test_config=None): | |
migrate.init_app(app, 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. Great job! We need to pass our instance of our |
||
|
||
# Register Blueprints here | ||
from .routes import task_list_bp | ||
app.register_blueprint(task_list_bp) | ||
from .routes import goals_bp | ||
app.register_blueprint(goals_bp) | ||
Comment on lines
+35
to
+38
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 work registering these blueprints ✅ |
||
|
||
return app |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,26 @@ | |
|
||
|
||
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", back_populates="goal", lazy=True) | ||
Comment on lines
4
to
+7
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 completing Task List Abby! Your code looks clean and easily readable. I want to point out the good variable naming and your code is DRY. Great use of helper methods in your models! Excellent work using blue prints & creating RESTful CRUD routes for each model. |
||
|
||
|
||
|
||
|
||
@classmethod | ||
def from_dict(cls, goal_data): | ||
new_goal = Goal( | ||
title=goal_data["title"] | ||
) | ||
|
||
return new_goal | ||
Comment on lines
+13
to
+18
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 noticed you created this class method but didn't make use of it |
||
|
||
|
||
|
||
|
||
|
||
def to_dict_goal(self): | ||
return{ "goal": { | ||
"id":self.goal_id, | ||
"title":self.title}} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,41 @@ | |
|
||
|
||
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) #when refactoring change to varchar | ||
description = db.Column(db.String) # when refacoring change to varvhar | ||
completed_at = db.Column(db.DateTime, default=None, nullable=True) | ||
goal_id = db.Column(db.Integer, db.ForeignKey('goal.goal_id')) | ||
goal = db.relationship("Goal", back_populates="tasks") | ||
Comment on lines
4
to
+10
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 approach creating this relationship to your Goal model. Why not use |
||
|
||
|
||
@classmethod | ||
def from_dict(cls, task_data): | ||
new_task = Task( | ||
title=task_data["title"], | ||
description=task_data["description"], | ||
completed_at=task_data["completed_at"] | ||
) | ||
|
||
return new_task | ||
Comment on lines
+14
to
+21
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 noticed you created this class method but didn't make use of it |
||
|
||
|
||
|
||
|
||
|
||
def to_dict(self): | ||
return{"task": { | ||
"id":self.task_id, | ||
"title":self.title, | ||
"description":self.description, | ||
"is_complete": True if self.completed_at else False}} | ||
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. 👍🏾 |
||
# wave 1 | ||
#Our task list API should be able to work with an entity called Task. | ||
# Tasks are entities that describe a task a user wants to complete. They contain a: | ||
# title to name the task | ||
# description to hold details about the task | ||
# an optional datetime that the task is completed on | ||
# Our goal for this wave is to be able to create, read, update, and delete different tasks. | ||
# We will create RESTful routes for this different operations. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,245 @@ | ||
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, make_response, request, abort | ||
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. 👍🏾 These imports help a great deal when it comes to our request & response cycle |
||
from datetime import datetime | ||
import requests | ||
import json | ||
from dotenv import load_dotenv | ||
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 work! We need to import |
||
import os | ||
|
||
load_dotenv() | ||
task_list_bp = Blueprint("task_list", __name__, url_prefix="/tasks") | ||
goals_bp = Blueprint("goals_list", __name__, url_prefix="/goals") | ||
#validating the task_id | ||
def validate_task(task_id): | ||
try: | ||
task_id = int(task_id) | ||
except: | ||
abort(make_response({"message": f"Task {task_id} invalid"}, 400)) | ||
task = Task.query.get(task_id) | ||
if not task: | ||
abort(make_response({"details": "Invalid Data"}, 404)) | ||
return task | ||
Comment on lines
+15
to
+23
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 approach to handling an invalid task 😁 |
||
|
||
#validate goal_id | ||
def validate_goal(goal_id): | ||
try: | ||
goal_id = int(goal_id) | ||
except: | ||
abort(make_response({"message": f"Goal {goal_id} invalid"}, 400)) | ||
goal = Goal.query.get(goal_id) | ||
if not goal: | ||
abort(make_response({"details": "Invalid Data"}, 404)) | ||
return goal | ||
Comment on lines
+26
to
+34
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 approach to handling an invalid goal 😁 There's a bunch of repeated code here, any ideas of how to refactor? |
||
|
||
# create tasks | ||
@task_list_bp.route("", methods=["POST"]) | ||
def post_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( | ||
title = request_body["title"], | ||
description = request_body["description"], | ||
# completed_at = request_body["completed_at"] | ||
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. It’s good practice to avoid committing commented out code when submitting a PR. |
||
) | ||
db.session.add(new_task) | ||
db.session.commit() | ||
Comment on lines
+47
to
+48
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
|
||
return jsonify({ | ||
"task": { | ||
"id": new_task.task_id, | ||
"title": new_task.title, | ||
"is_complete": False, | ||
"description": new_task.description | ||
} | ||
}), 201 | ||
|
||
#create goals | ||
@goals_bp.route("", methods=["POST"]) | ||
def post_goal(): | ||
request_body = request.get_json() | ||
if "title" not in request_body: | ||
return make_response({"details": "Invalid data"}, 400) | ||
new_goal = Goal( | ||
title = request_body["title"] | ||
) | ||
db.session.add(new_goal) | ||
db.session.commit() | ||
return jsonify({ | ||
"goal": { | ||
"id": new_goal.goal_id, | ||
"title": new_goal.title} }), 201 | ||
# create nested post | ||
@goals_bp.route("/<goal_id>/tasks", methods=["POST"]) | ||
def create_task(goal_id): | ||
goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
result = {"id": goal.goal_id, | ||
"task_ids": request_body["task_ids"] | ||
} | ||
for task_id in request_body["task_ids"]: | ||
task = validate_task(task_id) | ||
goal.tasks.append(task) | ||
db.session.commit() | ||
return result | ||
|
||
# create nested get, singular goal, with its many tasks | ||
@goals_bp.route("<goal_id>/tasks", methods=["GET"]) | ||
def get_tasks_one_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
task_response = [] | ||
|
||
|
||
|
||
for task in goal.tasks: | ||
if not task.completed_at: | ||
task_response.append({ | ||
"id": task.task_id, | ||
"goal_id":goal.goal_id, | ||
"title":task.title, | ||
"description": task.description, | ||
"is_complete": False | ||
|
||
}) | ||
else: | ||
task_response.append({ | ||
"id": task.task_id, | ||
"goal_id": goal.goal_id, | ||
"title": task.title, | ||
"description": task.description, | ||
"completed_at":task.completed_at | ||
}) | ||
return jsonify({ | ||
"id": goal.goal_id, | ||
"title": goal.title, | ||
"tasks": task_response}) | ||
# #read all tasks | ||
@task_list_bp.route("", methods=["GET"]) | ||
def get_all_tasks(): | ||
task_response = [] | ||
sort_query = request.args.get("sort") | ||
if sort_query == 'asc': | ||
tasks = Task.query.order_by(Task.title).all() | ||
elif sort_query == 'desc': | ||
tasks = Task.query.order_by(Task.title.desc()).all() | ||
else: | ||
tasks = Task.query.all() | ||
for task in tasks: | ||
if not task.completed_at: | ||
task_response.append({"id":task.task_id, | ||
"title":task.title, | ||
"description": task.description, | ||
"is_complete": False | ||
|
||
}) | ||
else: | ||
task_response.append({ | ||
"id":task.task_id, | ||
"title":task.title, | ||
"description": task.description, | ||
"completed_at":task.completed_at | ||
}) | ||
return jsonify(task_response) | ||
|
||
#read all goals | ||
@goals_bp.route("", methods=["GET"]) | ||
def read_all_goals(): | ||
goal_response = [] | ||
goals = Goal.query.all() | ||
|
||
for goal in goals: | ||
if not goals: | ||
return jsonify(goal_response) | ||
else: | ||
goal_response.append({ | ||
"id":goal.goal_id, | ||
"title":goal.title}) | ||
|
||
return jsonify(goal_response) | ||
#read one task/ read if empty task. | ||
@task_list_bp.route("/<task_id>", methods=["GET"]) | ||
def get_one_task(task_id): | ||
task = validate_task(task_id) | ||
# goal = validate_goal(goal_id) | ||
|
||
if task.goal_id: | ||
return{"task": { | ||
"id":task.task_id, | ||
"goal_id": task.goal_id, | ||
"title":task.title, | ||
"description":task.description, | ||
"is_complete": True if task.completed_at else False}} | ||
else: | ||
return task.to_dict() | ||
#read one goal | ||
@goals_bp.route("/<goal_id>", methods=["GET"]) | ||
def get_one_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
|
||
|
||
return jsonify({"goal":{"id": goal.goal_id, | ||
"title":goal.title}}) | ||
# #update task | ||
@task_list_bp.route("/<task_id>", methods=["PUT"]) | ||
def update_task(task_id): | ||
|
||
task = validate_task(task_id) | ||
request_body = request.get_json() | ||
task.title = request_body["title"] | ||
task.description = request_body["description"] | ||
db.session.commit() | ||
return task.to_dict() | ||
|
||
#update goal | ||
@goals_bp.route("/<goal_id>", methods=["PUT"]) | ||
def update_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
request_body = request.get_json() | ||
goal.title = request_body["title"] | ||
db.session.commit | ||
return goal.to_dict_goal() | ||
|
||
#mark_complete endpoint with slack api implementation | ||
@task_list_bp.route("/<task_id>/mark_complete", methods=["PATCH"]) | ||
def update_task_to_complete(task_id): | ||
task = validate_task(task_id) | ||
task.completed_at = datetime.now() | ||
#slack implementation | ||
url = "https://slack.com/api/chat.postMessage" | ||
payload = json.dumps({ | ||
"channel": "C0581AUJACV", | ||
"text": (f"Someone just completed the task {task.title}") | ||
}) | ||
headers = { | ||
'Authorization': os.environ.get("SLACK_API_TOKEN"), | ||
'Content-Type': 'application/json' | ||
} | ||
response = requests.request("POST", url, headers=headers, data=payload) | ||
print(response.text) | ||
db.session.commit() | ||
return task.to_dict(), 200 | ||
Comment on lines
+205
to
+221
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 function is pretty packed, for readability I suggest using more spacing in between lines. Something like this:
|
||
|
||
#mark_incomplete endpoint | ||
@task_list_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"]) | ||
def update_task_to_incomplete(task_id): | ||
task = validate_task(task_id) | ||
task.completed_at = None | ||
db.session.commit() | ||
return task.to_dict() | ||
|
||
# delete task | ||
@task_list_bp.route("/<task_id>", methods=["DELETE"]) | ||
def delete_task(task_id): | ||
task = validate_task(task_id) | ||
db.session.delete(task) | ||
db.session.commit() | ||
return abort(make_response({"details":f"Task {task.task_id} \"{task.title}\" successfully deleted"})) | ||
|
||
#delete goal: | ||
@goals_bp.route("/<goal_id>", methods=["DELETE"]) | ||
def delete_goal(goal_id): | ||
goal = validate_goal(goal_id) | ||
db.session.delete(goal) | ||
db.session.commit() | ||
return abort(make_response({"details":f"Goal {goal.goal_id} \"{goal.title}\" successfully deleted"})) |
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.
👍🏾