Skip to content
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

Sharks-Xiomara R. #93

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
11 changes: 11 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,15 @@ def create_app(test_config=None):

# Register Blueprints here

from .routes import task_bp
app.register_blueprint(task_bp)

from .goal_routes import goal_bp
app.register_blueprint(goal_bp)
Comment on lines +34 to +38

Choose a reason for hiding this comment

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

👍 great job separating these routes out into two different files! This is a common practice and makes it easier for teams to work on a project together without accidentally stepping on each other's toes.


# Register Models Here
# from app.models.task import Task

# from app.models.goal import Goal

return app
102 changes: 102 additions & 0 deletions app/goal_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from flask import Blueprint, jsonify, make_response, request
from requests import session

Choose a reason for hiding this comment

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

This session is not the same as db.session. They serve completely separate purposes, so we can get rid of this import completely

Suggested change
from requests import session

from app.helpers import validate_goal, validate_task
from app.models.goal import Goal
from app import db
from app.models.task import Task


goal_bp = Blueprint("goal", __name__, url_prefix="/goals")

# Create goal


@goal_bp.route("", methods=["POST"])

Choose a reason for hiding this comment

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

👍

def create_goal():
request_body = request.get_json()

if "title" not in request_body:
return {
"details": "Invalid data"
}, 400

new_goal = Goal.create(request_body)

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

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

# Get all goals


@goal_bp.route("", methods=["GET"])
def get_all_goals():
title_query = request.args.get("sort")

if title_query == "asc":
goals = Goal.query.order_by(Goal.title.asc())
elif title_query == "desc":
goals = Goal.query.order_by(Goal.title.desc())
else:
goals = Goal.query.all()

goals_response = []

for goal in goals:
goals_response.append(goal.to_json())

return jsonify(goals_response), 200

Choose a reason for hiding this comment

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

Try to stay consistent with your return statements here. Most of them are using make_response(jsonify()), so let's repeat that everywhere for predictability and consistency.

Suggested change
return jsonify(goals_response), 200
return make_response(jsonify(goals_response), 200)


# Get one goal


@goal_bp.route("/<id>", methods=["GET"])
def get_one_goal(id):
goal = validate_goal(id)
return jsonify({"goal": goal.to_json()}), 200

Choose a reason for hiding this comment

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

Suggested change
return jsonify({"goal": goal.to_json()}), 200
return make_response(jsonify({"goal": goal.to_json()}), 200)


# Update goal


@goal_bp.route("/<id>", methods=["PUT"])

Choose a reason for hiding this comment

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

👍

def update_goal(id):
goal = validate_goal(id)
request_body = request.get_json()

goal.update(request_body)
db.session.commit()

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


# Delete goal
@goal_bp.route("/<id>", methods=["DELETE"])

Choose a reason for hiding this comment

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

👍

def delete_goal(id):
goal = validate_goal(id)
db.session.delete(goal)
db.session.commit()

return make_response(jsonify({"details": f"Goal {id} \"{goal.title}\" successfully deleted"}), 200)

# Sending a List of Task IDs to a Goal


@goal_bp.route("/<id>/tasks", methods=["POST"])

Choose a reason for hiding this comment

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

👍

def add_tasks_to_one_goal(id):
goal = validate_goal(id)
request_body = request.get_json()

for id in request_body["id"]:

Choose a reason for hiding this comment

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

Here is where I think the last wave is failing. Double check what the correct key is in the tests! id is for the goal, not the list of tasks

new_task = Task.query.get(id)

Choose a reason for hiding this comment

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

I bet we could use the validate_task method here

Suggested change
new_task = Task.query.get(id)
new_task = validate_task(id)

new_task.id = id
db.session.commit()

list_of_task_ids = []
for task in goal.tasks:
list_of_task_ids.append(task.id)

return make_response(jsonify({"id": {new_task.id}, "task_ids": list_of_task_ids})), 200


# @goal_bp.route("/<goal_id>/tasks", methods=["POST"])

Choose a reason for hiding this comment

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

Suggested change
# @goal_bp.route("/<goal_id>/tasks", methods=["POST"])

30 changes: 30 additions & 0 deletions app/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from flask import make_response, abort

Choose a reason for hiding this comment

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

great job separating out helper functions!

from app.models.task import Task
from app.models.goal import Goal


def validate_task(id):
try:
id = int(id)
except:
return abort(make_response({"details": f"Task is invalid"}, 400))

task = Task.query.get(id)

if not task:
abort(make_response({"details": f"Task 1 not found"}, 404))

return task


def validate_goal(id):
try:
id = int(id)
except:
abort(make_response({"details": f"Goal is invalid"}, 400))

goal = Goal.query.get(id)

if not goal:
abort(make_response({"details": f"Goal 1 not found"}, 404))
return goal
22 changes: 21 additions & 1 deletion app/models/goal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@

from app import db


class Goal(db.Model):

Choose a reason for hiding this comment

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

👍

goal_id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String, nullable=False)
tasks = db.relationship("Task", back_populates="goals", lazy=True)

def to_json(self):
return {
"id": self.id,
"title": self.title
}

def update(self, request_body):
self.title = request_body["title"]

@classmethod
def create(cls, request_body):
new_task = cls(
title=request_body["title"]
)

return new_task
43 changes: 42 additions & 1 deletion app/models/task.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@

from email.policy import default
from unittest.mock import DEFAULT
Comment on lines +2 to +3

Choose a reason for hiding this comment

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

hmm where did these come from? Double check where your default attribute comes from in SQLAlchemy. It is part of the db import itself. We don't need to directly import anything else.

Suggested change
from email.policy import default
from unittest.mock import DEFAULT

from app import db
from .goal import Goal

Choose a reason for hiding this comment

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

We don't need to import the other model here since we aren't creating an instances of Goal inside Task; we refer to "Goal" as a string, not an actual instance of the model.

Suggested change
from .goal import Goal

from sqlalchemy.orm import relationship

# DEFINING A MODEL
'''
Models in our Flask code will create a
direct connection between the data modeled in our
database. We will create a class for each model.
The class will define the state and behavior of our model.
'''


class Task(db.Model):

Choose a reason for hiding this comment

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

👍

task_id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String)
description = db.Column(db.String)
completed_at = db.Column(db.DateTime, default=None)
goal_id = db.Column(db.Integer, db.ForeignKey("goal.id"), nullable=True)
goals = db.relationship("Goal", back_populates="tasks")

def to_json(self):
return {
"id": self.id,
"title": self.title,
"description": self.description,
"is_complete": False if not self.completed_at else True
}

def update(self, request_body):
self.title = request_body["title"]
self.description = request_body["description"]
# self.completed_at = request_body["completed_at"]

@classmethod
def create(cls, request_body):
new_task = cls(
title=request_body['title'],
description=request_body['description'],
completed_at=request_body.get("completed_at", None)
)

return new_task
136 changes: 135 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1 +1,135 @@
from flask import Blueprint
from flask import Blueprint, jsonify, abort, make_response, request
from requests import session

Choose a reason for hiding this comment

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

Suggested change
from requests import session

from .helpers import validate_task
from app import db
from .models.task import Task
from datetime import datetime
import requests
import os


# TASK BLUEPRINT
'''
Blueprints are a Flask class that provides a pattern
for grouping related routes (endpoints)
'''

task_bp = Blueprint("task", __name__, url_prefix="/tasks")


# Create a Task: Valid Task With null completed_at

@task_bp.route("", methods=["POST"])

Choose a reason for hiding this comment

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

👍

def create_task():
request_body = request.get_json()

if "title" not in request_body:
return {
"details": "Invalid data"
}, 400
if "description" not in request_body:
return {
"details": "Invalid data"
}, 400

new_task = Task.create(request_body)

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

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


# Get Tasks

@task_bp.route("", methods=["POST", "GET"])

Choose a reason for hiding this comment

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

👍

def get_all_tasks():
title_query = request.args.get("sort")

# description_query=request.args.get("description")

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

# tasks_response = [task.to_dict() for task in tasks]

tasks_response = []

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

return jsonify(tasks_response), 200


# Get One Task: One Saved Task
@task_bp.route("/<id>", methods=["GET"])

Choose a reason for hiding this comment

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

👍

def get_one_task(id):
task = validate_task(id)
return jsonify({"task": task.to_json()}), 200


# Update Task
@task_bp.route("/<id>", methods=["PUT"])
def update_task(id):
task = validate_task(id)
request_body = request.get_json()

task.update(request_body)
db.session.commit()

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


# Delete Task: Deleting a Task
@task_bp.route("/<id>", methods=["DELETE"])
def delete_task(id):
task = validate_task(id)
db.session.delete(task)
db.session.commit()

return make_response(jsonify({"details": f"Task {id} \"{task.title}\" successfully deleted"}))

# Mark Complete


@task_bp.route("/<id>/mark_complete", methods=["PATCH"])

Choose a reason for hiding this comment

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

👍

def mark_complete(id):
task = validate_task(id)

task.completed_at = datetime.utcnow()

db.session.commit()
post_message_to_slack(task)

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

# Slack


def post_message_to_slack(task):
send_msg_path = "https://slack.com/api/chat.postMessage"
confirm_message = f"You completed the task {task.title}!"
query_params = {
"channel": "task-notifications",
"text": confirm_message
}
headers = {
"Authorization": os.environ.get("slack_token")
}
requests.post(send_msg_path, params=query_params, headers=headers)
Comment on lines +113 to +123

Choose a reason for hiding this comment

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

Let's move this to the helpers.py file!



# Mark Incomplete
@task_bp.route("/<id>/mark_incomplete", methods=["PATCH"])

Choose a reason for hiding this comment

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

👍

def mark_incomplete(id):
task = validate_task(id)

task.completed_at = None

db.session.commit()

return make_response(jsonify({"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 @@
Single-database configuration for Flask.
Loading