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 Saharai Cante #66

Open
wants to merge 11 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 app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,9 @@ def create_app(test_config=None):
migrate.init_app(app, db)

# Register Blueprints here

from .routes import task_bp
from .routes import goal_bp
app.register_blueprint(task_bp)
app.register_blueprint(goal_bp)

Comment on lines +33 to +37

Choose a reason for hiding this comment

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

You can combine imports like so:

Suggested change
from .routes import task_bp
from .routes import goal_bp
app.register_blueprint(task_bp)
app.register_blueprint(goal_bp)
from .routes import task_bp, goal_bp
app.register_blueprint(task_bp)
app.register_blueprint(goal_bp)

return app
17 changes: 16 additions & 1 deletion app/models/goal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
from flask import current_app
from sqlalchemy.orm import relationship
from app import db
from app.models.task import Task
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Comment on lines +4 to +7

Choose a reason for hiding this comment

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

hm... Table, Column, etc. should all be coming from your db instance of sqlalchemy from __init__.py. So I don't think you need the sqlalchemy imports in this file as it's a little redundant.


Base = declarative_base()


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)

#helper function-returns goal attributes in corrected format
def goal_to_json(self):
goal_dict={"id": self.goal_id,
"title": self.title}
return goal_dict

37 changes: 36 additions & 1 deletion app/models/task.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
from flask import current_app
from sqlalchemy.orm import backref
from app import db


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)
description= db.Column(db.String)
completed_at=db.Column(db.DateTime, nullable=True, default=None)

#establishing one to many relationship with Goal
goal_id = db.Column(db.Integer,db.ForeignKey('goal.goal_id'), nullable=True)
goal = db.relationship("Goal",backref=db.backref('tasks'),lazy=True)

#returns True when there is a task completed
def is_complete(self):
if self.completed_at==None:
return False
else:
return True

#runs to determine which body to return based on goal bool status
def to_json(self):
if self.goal_id == None:
task_dict={
"id": self.task_id,
"title": self.title,
"description": self.description,
"is_complete": self.is_complete()
}
else:
task_dict={
"id": self.task_id,
"goal_id": self.goal_id,
"title": self.title,
"description": self.description,
"is_complete": self.is_complete()
}
return task_dict
Comment on lines +25 to +40

Choose a reason for hiding this comment

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

Nice! Great job Saharai. You could refactor this into a single dictionary like so:

Suggested change
if self.goal_id == None:
task_dict={
"id": self.task_id,
"title": self.title,
"description": self.description,
"is_complete": self.is_complete()
}
else:
task_dict={
"id": self.task_id,
"goal_id": self.goal_id,
"title": self.title,
"description": self.description,
"is_complete": self.is_complete()
}
return task_dict
task_dict={
"id": self.task_id,
"title": self.title,
"description": self.description,
"is_complete": self.is_complete()
}
if self.goal_id:
task_dict['goal_id'] = self.goal_id
return task_dict


293 changes: 292 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,293 @@
from flask import Blueprint
from flask import request,Blueprint,make_response,jsonify
from werkzeug.datastructures import Authorization

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



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

@task_bp.route("", methods=["GET"])
def get_tasks():

title_query = request.args.get("title")
if title_query:
tasks = Task.query.filter_by(title=title_query)

else:
tasks = Task.query.all()

tasks_response = []
for task in tasks:
tasks_response.append(task.to_json())

if "asc" in request.full_path:
sort_tasks=sorted(tasks_response, key = lambda i: i['title'])
return jsonify(sort_tasks)

elif "desc" in request.full_path:
sort_tasks=sorted(tasks_response, key = lambda i: i['title'], reverse=True)
return jsonify(sort_tasks)

else:
return jsonify(tasks_response)
Comment on lines +15 to +37

Choose a reason for hiding this comment

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

beautiful sort by title function!


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

request_body = request.get_json()

if all(keys in request_body for keys in ("title","description","completed_at"))== False:
return {
"details": "Invalid data"
},400
Comment on lines +44 to +47

Choose a reason for hiding this comment

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

Clever way of checking for values in the request body !

else:
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()

task = Task.query.get(new_task.task_id)
return {
"task": task.to_json()
}, 201


@task_bp.route("/<task_id>", methods=["GET"])
def get_task(task_id):
task = Task.query.get(task_id)

if task is None:
return make_response ("",404)
else:
return {"task":task.to_json()},200
Comment on lines +66 to +71

Choose a reason for hiding this comment

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

You can refine this function into a fewer lines by using Flask's own get_or_404() method

Suggested change
task = Task.query.get(task_id)
if task is None:
return make_response ("",404)
else:
return {"task":task.to_json()},200
task = Task.query.get_or_404(task_id)
return {"task":task.to_json()},200



@task_bp.route("/<task_id>", methods=["PUT"])
def put_task(task_id):
task = Task.query.get(task_id)

if task is None:
return make_response ("",404)
else:
form_data = request.get_json()

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

db.session.commit()

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

@task_bp.route("/<task_id>", methods=["DELETE"])
def delete_task(task_id):
task = Task.query.get(task_id)

if task is None:
return make_response ("",404)
else:

db.session.delete(task)

db.session.commit()


return make_response({
"details": f'Task {task.task_id} \"{task.title}\" successfully deleted'
}, 200)

@task_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 ("",404)

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

return {
"task": task.to_json()
}, 200
Comment on lines +111 to +121

Choose a reason for hiding this comment

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

Nice this function is really clean!



@task_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 ("",404)

task.completed_at=datetime.utcnow()
db.session.commit()


url = f"https://slack.com/api/chat.postMessage?channel=task-notifications&text=Someone just completed the task {task.title}"



token=os.environ.get("bot_user_token")


headers_dict={"Authorization":token}
response = requests.request("POST", url, headers=headers_dict)
return {
"task": {
"id": task.task_id,
"title": task.title,
"description":task.description,
"is_complete": task.is_complete()
}
}, 200
Comment on lines +134 to +150

Choose a reason for hiding this comment

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

Great job in getting your slack bot to work. A future refactor can include putting this logic into a helper function.




from app.models.goal import Goal
goal_bp = Blueprint("goal", __name__, url_prefix="/goals")

@goal_bp.route("", methods=["GET"])
def get_goals():

title_query = request.args.get("title")
if title_query:
goals = Goal.query.filter_by(title=title_query)

else:
goals = Goal.query.all()

goals_response = []
for goal in goals:
goals_response.append(goal.goal_to_json())

if "asc" in request.full_path:

sort_goals=sorted(goals_response, key = lambda i: i['title'])

return jsonify(sort_goals)

elif "desc" in request.full_path:
sort_goals=sorted(goals_response, key = lambda i: i['title'], reverse=True)
Comment on lines +173 to +178

Choose a reason for hiding this comment

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

LOVING the use of sorted() and the lambda function 🔥

return jsonify(sort_goals)

else:
return jsonify(goals_response)

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

request_body = request.get_json()

if "title" not in request_body:
return {
"details": "Invalid data"
},400
else:
new_goal = Goal(title=request_body["title"])

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

get_goal = Goal.query.get(new_goal.goal_id)

return {
"goal": get_goal.goal_to_json()
}, 201


@goal_bp.route("/<goal_id>", methods=["GET"])
def get_goal(goal_id):
goal = Goal.query.get(goal_id)

if goal is None:
return make_response ("",404)
else:
return {
"goal": goal.goal_to_json()
}, 200

@goal_bp.route("/<goal_id>", methods=["PUT"])
def put_goal(goal_id):
goal = Goal.query.get(goal_id)

if goal is None:
return make_response ("",404)
else:
form_data = request.get_json()

goal.title=form_data["title"]


#db.session.add(new_data) #adding data to db(a record/row) i.e git add
db.session.commit()
#new_task = Task.query.get(new_data.task_id)
return {
"goal": goal.goal_to_json()
}, 200


@goal_bp.route("/<goal_id>", methods=["DELETE"])
def delete_goal(goal_id):
goal = Goal.query.get(goal_id)

if goal is None:
return make_response ("",404)
else:
db.session.delete(goal)

db.session.commit()

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

@goal_bp.route("/<goal_id>/tasks", methods=["POST"])
def post_task_id_to_goal(goal_id):
request_body=request.get_json() #information passed in given in json
goal=Goal.query.get(goal_id) #grab my goal from db and bring it back


if not goal:
return make_response("",404)
else:
for task_id in request_body["task_ids"]:
task=Task.query.get(task_id)
task.goal_id=goal_id
db.session.commit()

return {
"id": goal.goal_id,
"task_ids": request_body["task_ids"]
},200


@goal_bp.route("/<goal_id>/tasks", methods=["GET"])
def get_tasks_for_goal(goal_id):

goal=Goal.query.get(goal_id)
task = Task.query.get(goal_id)


if not goal:
return make_response("",404)
else:

outer_dict=goal.goal_to_json()
if task==None:
outer_dict['tasks']=[]
else:
inner_dict=task.to_json()

inner_dict["goal_id"]=goal.goal_id
outer_dict['tasks']=[inner_dict]
db.session.commit()
return outer_dict,200
Comment on lines +275 to +292

Choose a reason for hiding this comment

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

Great job! This was a very clean way of getting the tasks for a specific goal_id


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