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

ROCK - BRITTANY C. #54

Open
wants to merge 9 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
12 changes: 10 additions & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import pytest
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import os
from dotenv import load_dotenv
import datetime
Comment on lines 6 to +7

Choose a reason for hiding this comment

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

since you aren't using datetime in this file, we don't need to import it. also, are you calling load_dotenv anywhere? were your environment variables able to load without it?

Suggested change
from dotenv import load_dotenv
import datetime
from dotenv import load_dotenv
load_dotenv()



db = SQLAlchemy()
Expand All @@ -12,6 +14,7 @@

def create_app(test_config=None):
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

if test_config is None:
Expand All @@ -21,14 +24,19 @@ def create_app(test_config=None):
app.config["TESTING"] = True
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
"SQLALCHEMY_TEST_DATABASE_URI")
app.config['JSON_SORT_KEYS']=False

db.init_app(app)
migrate.init_app(app, db)

# Import models here for Alembic setup
from app.models.task import Task
from app.models.goal import Goal

db.init_app(app)
migrate.init_app(app, db)
from .routes import task_bp, goal_bp

# Register Blueprints here
app.register_blueprint(task_bp)
app.register_blueprint(goal_bp)

return app
29 changes: 28 additions & 1 deletion app/models/goal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
from types import new_class

Choose a reason for hiding this comment

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

is this being used?

Suggested change
from types import new_class

from flask import current_app
from app import db
from app.models.task import Task

Choose a reason for hiding this comment

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

Suggested change
from app.models.task import Task

# Parent Class


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', backref='goal', lazy=True)

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

def goal_json_object_with_tasks(self):

Choose a reason for hiding this comment

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

👍 great helper method!

new_list = []
for task in self.tasks:
new_list.append({
"id": task.id,
"goal_id": task.goal_id,
"title": task.title,
"description": task.description,
"is_complete": task.completed_at_helper()
})
return {
"id": self.goal_id,
"title": self.title,
"tasks": new_list
}
30 changes: 28 additions & 2 deletions app/models/task.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
from flask import current_app
from app import db

from datetime import datetime

Choose a reason for hiding this comment

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

Suggested change
from datetime import datetime

# this is telling flask about my database task table

class Task(db.Model):
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)

goal_id = db.Column(db.Integer, db.ForeignKey('goal.goal_id'), nullable=True)
def completed_at_helper(self):
if self.completed_at == None:
complete = False
else:
complete = True
return complete

def json_object(self):

new_reponse = {
"id": self.id,
"title": self.title,
"description": self.description,
"is_complete": self.completed_at_helper()
}
if self.goal_id is not None:
new_reponse["goal_id"] = self.goal_id
return new_reponse
# Use this helper function any time the return is expected to be complete ^^^

205 changes: 204 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,205 @@
from flask import Blueprint
from app import db
from app.models.task import Task
from app.models.goal import Goal
from flask import request
from flask import request, Blueprint, make_response
from flask import jsonify
Comment on lines +4 to +6

Choose a reason for hiding this comment

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

we can put these all together:

Suggested change
from flask import request
from flask import request, Blueprint, make_response
from flask import jsonify
from flask import request, Blueprint, make_response, jsonify

from datetime import datetime
from sqlalchemy import asc, desc
import requests
import json
import os
goal_bp = Blueprint("goals", __name__, url_prefix="/goals")
task_bp = Blueprint("tasks", __name__, url_prefix="/tasks")

#This is is a new endpoint.
@task_bp.route("/<task_id>", methods=["GET", "PUT", "DELETE"], strict_slashes=False)
def get_single_task(task_id):
task = Task.query.get(task_id)

if task is None:
return jsonify(None), 404
Comment on lines +18 to +21

Choose a reason for hiding this comment

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

we can use a method that'll do both these things for us:

Suggested change
task = Task.query.get(task_id)
if task is None:
return jsonify(None), 404
task = Task.query.get_or_404(task_id)


complete = task.completed_at_helper() # Helper function to return boolean

if request.method == "GET":
return jsonify({"task": task.json_object()}), 200

elif request.method == "PUT":
form_data = request.get_json()

task.title = form_data["title"]
task.description = form_data["description"]
task.is_complete = form_data["completed_at"]

db.session.commit()
return jsonify({"task": task.json_object()}),200

elif request.method == "DELETE":
db.session.delete(task)
db.session.commit()
return jsonify({"details": f'Task {task.id} "{task.title}" successfully deleted'}), 200

@task_bp.route("", methods=["GET", "POST"])
def handle_tasks():
if request.method == "GET":

request_value = request.args.get("sort")

if request_value == None:
tasks = Task.query.all()

if request_value == "asc":
tasks = Task.query.order_by(Task.title.asc())


if request_value == "desc":
tasks = Task.query.order_by(Task.title.desc())
task_response = []

for task in tasks:

task_response.append(task.json_object())

return jsonify(task_response), 200

elif request.method == "POST":
request_body = request.get_json()

if "completed_at" not in request_body or "description" not in request_body or "title" not in request_body:
return jsonify({
"details": "Invalid data"
}), 400

new_task = Task(title=request_body["title"],
description=request_body["description"],
completed_at=request_body["completed_at"])

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

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

db.session.add(new_task)
db.session.commit()
Comment on lines +83 to +84

Choose a reason for hiding this comment

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

anything after a return statement will never get hit (but I think this was just duplicate code since you have it on lines 78 and 79)

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


def slack_bot(task):

Choose a reason for hiding this comment

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

👍

url = "https://slack.com/api/chat.postMessage?channel=task-notifications"
slack_token = os.environ.get("SLACK")

payload = {"text": f"Someone just completed the task: '{task.title}'!"}
headers = {"Authorization": f"Bearer {slack_token}"}

return requests.request("PATCH", url, headers=headers, data=payload)


@task_bp.route("/<task_id>/mark_complete", methods=["PATCH"], strict_slashes=False)
def mark_complete(task_id):

if request.method == "PATCH":

task = Task.query.get(task_id)

if task is None:
return jsonify(None), 404
Comment on lines +101 to +104

Choose a reason for hiding this comment

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

here we could use get_or_404() method, too!


task.completed_at = datetime.now()


db.session.commit()

# call slackbot
slack_bot(task)


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


@task_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"], strict_slashes=False)
def mark_incomplete(task_id):

if request.method == "PATCH":

task = Task.query.get(task_id)

if task is None:
return jsonify(None), 404

task.completed_at = None

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


Comment on lines +120 to +132

Choose a reason for hiding this comment

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

careful with adding too many blank lines! it can make code harder to read

Suggested change
if request.method == "PATCH":
task = Task.query.get(task_id)
if task is None:
return jsonify(None), 404
task.completed_at = None
return jsonify({"task":task.json_object()}),200
if request.method == "PATCH":
task = Task.query.get(task_id)
if task is None:
return jsonify(None), 404
task.completed_at = None
return jsonify({"task":task.json_object()}),200

@goal_bp.route("/<goal_id>", methods=["GET", "PUT", "DELETE"], strict_slashes=False)
def get_goal(goal_id):

goal = Goal.query.get(goal_id)

if goal is None:
return jsonify(None), 404
Comment on lines +136 to +139

Choose a reason for hiding this comment

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

here we could use get_or_404() method, too!


if request.method == "GET":
return jsonify({"goal": goal.goal_json_object()}), 200

elif request.method == "PUT":
form_data = request.get_json()
#Go and get the data from the request
goal.title = form_data["title"]

db.session.commit()
return jsonify({"goal": goal.goal_json_object()}),200

elif request.method == "DELETE":
db.session.delete(goal)
db.session.commit()

return jsonify({"details": f'Goal {goal.goal_id} "{goal.title}" successfully deleted'}), 200

@goal_bp.route("", methods=["POST", "GET"], strict_slashes=False)
def one_goal():
if request.method == "GET":
goals = Goal.query.all()

goal_response = []

for goal in goals:
goal_response.append(goal.goal_json_object())

return jsonify(goal_response), 200

if request.method == "POST":
request_body = request.get_json()

if request_body == {}:
return jsonify({"details": f'Invalid data'}), 400

new_goal = Goal(title=request_body["title"])

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

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

#New endpoint to look up goals and task
@goal_bp.route("/<goal_id>/tasks", methods=["POST"], strict_slashes=False)
def goals_task(goal_id):

form_data = request.get_json()

for task_id in form_data["task_ids"]:

task = Task.query.get(task_id)

task.goal_id = goal_id

db.session.commit()

return make_response({"id": int(goal_id),"task_ids":form_data["task_ids"]}), 200
Comment on lines +185 to +197

Choose a reason for hiding this comment

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

try not to use so many blank lines


@goal_bp.route("/<goal_id>/tasks", methods=["GET"], strict_slashes=False)
def goals_task_get(goal_id):
if request.method == "GET":
goal = Goal.query.get(goal_id)
if goal is None:
return make_response("", 404)
Comment on lines +202 to +204

Choose a reason for hiding this comment

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

here we could use get_or_404() method, too!

return jsonify(goal.goal_json_object_with_tasks())
1 change: 1 addition & 0 deletions migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
45 changes: 45 additions & 0 deletions migrations/alembic.ini
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
Loading