Skip to content

Task List Project #14

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

Open
wants to merge 7 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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,8 @@ dmypy.json
.pytype/

# Cython debug symbols
cython_debug/
cython_debug/

#SLACK TOKEN
TOKEN_SLACK
xoxb-708663070965-2182160263012-6dhG9fHP6cx1d1Wlla10RjEz
23 changes: 9 additions & 14 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,29 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import os
from dotenv import load_dotenv


db = SQLAlchemy()
migrate = Migrate()
load_dotenv()


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

if test_config is None:
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
"SQLALCHEMY_DATABASE_URI")
else:
app.config["TESTING"] = True
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
"SQLALCHEMY_TEST_DATABASE_URI")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/task_list_api_development'

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_list_api_bp
app.register_blueprint(task_list_api_bp)

# Register Blueprints here
from .routes import goals_bp
app.register_blueprint(goals_bp)

return app
Comment on lines 11 to 29
Copy link

@tgoslee tgoslee Jun 23, 2021

Choose a reason for hiding this comment

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

this should go back to how you previously had it

if test_config is None: app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_DATABASE_URI") else: app.config["TESTING"] = True app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_TEST_DATABASE_URI")

20 changes: 20 additions & 0 deletions app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# from flask import Flask
# from flask_sqlalchemy import SQLAlchemy
# from flask_migrate import Migrate

# db = SQLAlchemy()
# migrate = Migrate()


# def create_app(test_config=None):
# app = Flask(__name__)

# app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/task_list_api_development'

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

# from app.models.task import Task

# return app
Comment on lines +1 to +20
Copy link

Choose a reason for hiding this comment

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

remove commented code

4 changes: 3 additions & 1 deletion app/models/goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@


class Goal(db.Model):
goal_id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True)
Copy link

Choose a reason for hiding this comment

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

good change!

title = db.Column(db.String)
tasks = db.relationship('Task', backref='goal', lazy=True)
6 changes: 5 additions & 1 deletion app/models/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@


class Task(db.Model):
task_id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
Copy link

Choose a reason for hiding this comment

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

good change!

title = db.Column(db.String)
description = db.Column(db.String)
completed_at = db.Column(db.DateTime, nullable=True, default=None)
goal_id = db.Column(db.Integer, db.ForeignKey('goal.id'), nullable=True)
284 changes: 283 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,284 @@
from flask import Blueprint
from flask import Blueprint, request, jsonify, make_response
from app import db
from app.models.task import Task
from app.models.goal import Goal
from datetime import datetime
import os
import requests
from dotenv import load_dotenv

load_dotenv()

task_list_api_bp = Blueprint("task_list_api", __name__, url_prefix="/tasks")
goals_bp = Blueprint("goals", __name__, url_prefix="/goals")


@task_list_api_bp.route("", methods=["GET", "POST"])
def tasks():
Copy link

Choose a reason for hiding this comment

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

Because GET and POST are not using the same code you could consider created two different methods to handle GET and POST. For example, get_tasks and create_task

if request.method == "GET":
tasks_sort = request.args.get("sort")
tasks = Task.query.all()

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

tasks_response = []
# is_complete = task.completed_at
Copy link

Choose a reason for hiding this comment

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

remove this commented code


for task in tasks:
is_complete = task.completed_at
if task.completed_at != None:
is_complete = True
elif tasks_sort and task.completed_at is None:
is_complete = False

tasks_response.append({
"id": task.id,
"title": task.title,
"description": task.description,
"is_complete": is_complete
})

return jsonify(tasks_response)

elif request.method == "POST":

request_body = request.get_json(force=True)

if 'title' not in request_body or 'description' not in request_body or 'completed_at' not in request_body:
return {"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 {
"task": {
"id": new_task.id,
"title": new_task.title,
"description": new_task.description,
"is_complete": True if new_task.completed_at else False
}
}, 201


@task_list_api_bp.route("/<task_id>", methods=["GET", "DELETE", "PUT"])
def handle_task(task_id):
task = Task.query.get(task_id)
if task is None:
return make_response(f"{task_id} doesnt exist", 404)

# is_complete = task.completed_at
# if task.completed_at != None:
# is_complete == True
# print(is_complete)

if request.method == "GET":
select_task = {
"task": {
"id": task.id,
"title": task.title,
"description": task.description,
"is_complete": task.completed_at
}
Comment on lines +84 to +89
Copy link

@tgoslee tgoslee Jun 24, 2021

Choose a reason for hiding this comment

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

consider creating a helper function to add this. This makes your code more readable.

}
return jsonify(select_task), 200

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

elif request.method == "PUT":
form_data = request.get_json()
task.title = form_data["title"]
task.description = form_data["description"]
completed_at = form_data["completed_at"]

db.session.commit()

return {
"task": {
"id": task.id,
"title": "Updated Task Title",
"description": "Updated Test Description",
"is_complete": True if task.completed_at else False
}
}, 200


def post_slack(message_slack):

TOKEN_SLACK = os.environ.get(
"SLACK_BOT_TOKEN")
slack_path = "https://slack.com/api/chat.postMessage"
query_params = {
'channel': 'task-notifications',
'text': message_slack
}
headers = {'Authorization': f"Bearer {TOKEN_SLACK}"}
requests.post(slack_path, params=query_params, headers=headers)


@task_list_api_bp.route("/<task_id>/mark_complete", methods=["PATCH"])
def mark_complete(task_id):
task = Task.query.get(task_id)

if task is None:
return make_response(f"{task_id} doesnt exist", 404)
task.completed_at = datetime.utcnow()

db.session.commit()

if request.method == "PATCH":
message_slack = f"Someone just completed the task: {task.title}"
post_slack(message_slack)
return {
"task": {
"id": task.id,
"title": task.title,
"description": task.description,
"is_complete": True if task.completed_at else False
}
}, 200


@task_list_api_bp.route("/<task_id>/mark_incomplete", methods=["PATCH"])
def mark_incomplete(task_id):
task = Task.query.get(task_id)

if task is None:
return make_response(f"{task_id} doesnt exist", 404)

task.completed_at = None
db.session.commit()

if request.method == "PATCH":
return {
"task": {
"id": task.id,
"title": task.title,
"description": task.description,
"is_complete": True if task.completed_at else False
}
}, 200


@goals_bp.route("", methods=["GET", "POST"])
def handle_goals():
Copy link

Choose a reason for hiding this comment

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

Consider also breaking up this GET and POST for goals into two helper functions get_goals and create_goal

if request.method == "GET":
goals = Goal.query.all()

goals_response = []

for goal in goals:
print(goal.title)
print(goal.id)
goals_response.append({
"id": goal.id,
"title": goal.title
})

return jsonify(goals_response)

elif request.method == "POST":

request_body = request.get_json(force=True)
if "title" not in request_body:
return {"details": "Invalid data"}, 400
new_goal = Goal(title=request_body["title"])

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

return make_response(
{
"goal": {
"id": new_goal.id,
"title": new_goal.title
}
}, 201
)


@goals_bp.route("/<goal_id>", methods=["GET", "DELETE", "PUT"])
def handle_goal(goal_id):
goal = Goal.query.get(goal_id)
if goal is None:
return make_response(f"{goal_id} doesnt exist", 404)

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

elif request.method == "DELETE":
db.session.delete(goal)
db.session.commit()
return make_response({"details": f"Goal {goal.id} \"{goal.title}\" successfully deleted"})

elif request.method == "PUT":
form_data = request.get_json()
goal.title = form_data["title"]

db.session.commit()

return {
"goal": {
"id": goal.id,
"title": "Updated Goal Title",
}
}, 200


@goals_bp.route("/<goal_id>/tasks", methods=["POST", "GET"])
def handle_goal_tasks(goal_id):
goal = Goal.query.get(goal_id)
if goal is None:
return make_response(f"{goal_id} doesnt exist", 404)

if request.method == "GET":
tasks = Task.query.filter(Task.goal_id == goal_id)
all_goal_tasks = []

for task in tasks:
all_goal_tasks.append({
"id": task.id,
"goal_id": goal.id,
"title": task.title,
"description": task.description,
"is_complete": True if task.completed_at else False
})

return make_response(
{
"id": goal.id,
"title": goal.title,
"tasks": all_goal_tasks
}, 200)

elif request.method == "POST":
form_data = request.get_json()
task_ids = form_data['task_ids']

for id in task_ids:
task = Task.query.get(id)
task.goal_id = goal_id

db.session.commit()

return make_response(
{
"id": goal.id,
"task_ids": task_ids
}, 200)
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.
Loading