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

C16 - Spruce - Vange Spracklin #23

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5a3dd7d
finished set up
reidhowdy Dec 20, 2021
006318b
"moved imports to correct init file"
reidhowdy Dec 20, 2021
c5ea866
wrote some notes about relationships and endpoints
reidhowdy Dec 21, 2021
5ee0705
Model relationships and first card route.
cohC16 Dec 21, 2021
421539a
Dealing with commitment issues
cohC16 Dec 21, 2021
c8ba410
Get all route
cohC16 Dec 21, 2021
d4fc90c
Added routes & moved some - see comments
reidhowdy Dec 21, 2021
1201f7d
Create board function now adds board id
cohC16 Dec 22, 2021
caf9742
adds routes for GET all boards and GET one board by id
HouseOfVange Dec 22, 2021
cf9d94b
Decorator for board validation implemented
cohC16 Dec 22, 2021
6a9fbb6
Merge branch 'main' of https://github.com/reidhowdy/back-end-inspirat…
cohC16 Dec 22, 2021
1cd95fb
Removed overlapping routes for /<board_id>
cohC16 Dec 22, 2021
c31c86e
Added route for delete_one_whole_entire_board
cohC16 Dec 22, 2021
32ee324
Delete teeny tiny card added.
cohC16 Dec 22, 2021
b66e1fb
Added guard clauses to the create endpoints
reidhowdy Dec 22, 2021
a88b770
Wrote get_one_card_from_a_board endpoint.
reidhowdy Dec 22, 2021
d6ba012
Removed card validator on create new card
cohC16 Dec 28, 2021
2ede819
Added some notes about a patch request I need to write for card.likes…
reidhowdy Dec 29, 2021
bb6405f
changes
reidhowdy Dec 29, 2021
46a9843
Commented out patch request (because it is in progress). Added likes_…
reidhowdy Dec 29, 2021
419e882
Finished PATCH request to update card.likes_count. Tested in Postman.
reidhowdy Dec 29, 2021
5da7194
Edited add a like endpoint.
reidhowdy Jan 4, 2022
91b8451
Added Slack-bot integration
cohC16 Jan 4, 2022
cb9aa93
create_card() now returns card_id of new_card.
reidhowdy Jan 4, 2022
c0bef2f
Added a missing comma. Oops
reidhowdy Jan 4, 2022
e5abceb
Misc changes
reidhowdy Jan 7, 2022
3588380
Deleted old Procfile because we are re-deploying. Created new Procfile.
reidhowdy Jan 7, 2022
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
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: gunicorn 'app:create_app()'
web: gunicorn 'app:create_app()'
10 changes: 8 additions & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ def create_app():
# Import models here for Alembic setup
# from app.models.ExampleModel import ExampleModel

from app.models.board import Board
from app.models.card import Card

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

# Register Blueprints here
# from .routes import example_bp
# app.register_blueprint(example_bp)
from .card_routes import cards_bp
app.register_blueprint(cards_bp)

from .board_routes import boards_bp
app.register_blueprint(boards_bp)

CORS(app)
return app
202 changes: 202 additions & 0 deletions app/board_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
from flask import Blueprint, request, jsonify, make_response
from sqlalchemy.orm.query import Query
from app import db
from app.models.board import Board
from app.models.card import Card
import requests
from dotenv import load_dotenv
load_dotenv()
from functools import wraps
import os

boards_bp=Blueprint("board", __name__, url_prefix="/board")

# Decorator for validation
# Can be used for any route that has <board_ID>
# Updated routes to use <board_ID> because <board_id> was overlapping with model values.
# Challange someone to try something similar for GET by card_ID
# Will explain decorators, args/kwargs, wraps tomorrow.

def validate_board(board_identity):
@wraps(board_identity)
def test_for_board (*args, board_ID, **kwargs):
if not board_ID.isnumeric():
return ({"message":f"Board {board_ID} does not exist.",}), 404

board_check = Board.query.get(board_ID)
if board_check:
return board_identity (*args, board_ID, **kwargs)
else:
return ({"message":f"Board {board_ID} does not exist",}), 404
return test_for_board

def validate_card(card_identity):
@wraps(card_identity)
def test_for_card (*args, card_ID, **kwargs):
if not card_ID.isnumeric():
return ({"message":f"Card {card_ID} does not exist.",}), 404

card_check = Card.query.get(card_ID)
if card_check:
return card_identity (*args, card_ID, **kwargs)
else:
return ({"message":f"Card {card_ID} does not exist",}), 404
return test_for_card


#CREATE ONE BOARD
@boards_bp.route("", methods=["POST"])
def create_board():
request_body = request.get_json()

if (("title" not in request_body.keys()) or
("owner" not in request_body.keys())):
return jsonify("Board not created. Must supply title and owner."), 404

new_board = Board(
title = request_body["title"],
owner = request_body["owner"]
)

db.session.add(new_board)
db.session.commit()

return jsonify({
"title" : new_board.title,
"owner" : new_board.owner,
"id" : new_board.board_id
}), 201


#CREATE ONE CARD ON A SPECIFIC BOARD
@boards_bp.route("/<board_ID>", methods=["POST"])
@validate_board
def create_card(board_ID):
request_body = request.get_json()

if ("message" not in request_body.keys()):
return jsonify ()

new_card= Card(
message = request_body["message"],
likes_count = 0,
board_id = board_ID
)
url= 'https://slack.com/api/chat.postMessage'
header_values = {'AUTHORIZATION': os.environ.get("AUTHORIZATION")}
slack_values = {"text" : f"Something inspirational posted on board {new_card.board_id}",
"channel" : "winspo-board"
}
requests.post(url, headers=header_values, params=slack_values)
db.session.add(new_card)
db.session.commit()

return jsonify({"message": new_card.message,
"board_id": new_card.board_id,
"likes_count":new_card.likes_count,
"card_id":new_card.card_id}), 201

#GET ALL CARDS FOR SPECIFIC BOARD BY ID
@boards_bp.route("/<board_ID>/cards", methods=["GET"])
@validate_board
def get_all_cards_from_a_board(board_id):
all_cards = Card.query.filter_by(board_id=board_id)
output_dicts_list = []
for card in all_cards:
output_dicts_list.append({
"card_id":card.card_id,
"message":card.message,
"board_id":card.board_id,
"likes_count":card.likes_count
#we want to return the likes_count in this dictionary here so that each card like count renders correctly
})
return jsonify(output_dicts_list), 201


#GET ONE CARD FOR SPECIFIC BOARD BY ID
@boards_bp.route("/<board_ID>/cards/<card_ID>", methods=["GET"])
@validate_board
@validate_card
def get_one_card_from_a_board(board_ID, card_ID):
#should we actually fetch this by board_ID and card_ID? Does it matter?
card = Card.query.get(card_ID)

return jsonify({
"card_id":card.card_id,
"message":card.message,
"board_id":card.board_id,
"likes_count":card.likes_count
}), 201

# GET ALL BOARDS
@boards_bp.route("", methods=["GET"])
def get_all_boards():
boards = Board.query.all()

response_body = []

for board in boards:
response_body.append(
{"id" : board.board_id,
"title" : board.title,
"owner" : board.owner}
)

return jsonify(response_body), 200

# GET ONE BOARD BY SUPPLYING board_id
@boards_bp.route("/<board_ID>", methods=["GET"])
@validate_board
def get_one_board(board_ID):
board = Board.query.get(board_ID)

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

response_body = {
"id" : board.board_id,
"title" : board.title,
"owner" : board.owner
}

return jsonify(response_body), 200

@boards_bp.route("/<board_ID>", methods=["DELETE"])
@validate_board
def delete_one_whole_entire_board(board_ID):
board = Board.query.get(board_ID)
db.session.delete(board)
db.session.commit()

#not sure if we need this?
all_cards = Card.query.filter_by(board_id=board_ID)
for card in all_cards:
db.session.delete(card)
db.session.commit()

response = {"message": f"Board {board.title} was deleted."}
return response, 200

@boards_bp.route("/<board_ID>/cards/<card_ID>", methods=["DELETE"])
@validate_board
def delete_one_teeny_tiny_card(board_ID, card_ID):
card = Card.query.get(card_ID)
if card:
db.session.delete(card)
db.session.commit()
response = {"message": f"Card {card.card_id} was deleted."}
return response, 200
return {"message": f"Card id {card_ID} isn't real."},400


#PATCH REQUEST TO INCREMENT CARD.LIKES_COUNT BY 1
@boards_bp.route("/<board_ID>/cards/<card_ID>", methods=["PATCH"])
@validate_board
def add_one_to_likes_count(board_ID, card_ID):
card = Card.query.get(card_ID)
if card:
card.likes_count = int(card.likes_count) + 1
db.session.commit()
response = {"message": f"Card {card.card_id} likes count was updated to {card.likes_count}"}
return response, 200
return {"message": f"Card id {card_ID} isn't real."},400
39 changes: 39 additions & 0 deletions app/card_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from flask import Blueprint, request, jsonify, make_response
from sqlalchemy.orm.query import Query
from app import db
from app.models.board import Board
from app.models.card import Card
import requests
from dotenv import load_dotenv
load_dotenv()

cards_bp=Blueprint("card", __name__, url_prefix="/card")

#I moved the create card blueprint to board_routes
#Because the endpoint for this should start with board
#like this:
#/board/board_id/card

#I think we might want to move everything over to board, too.
#maybe we actually only want one file for routes?
#because all of the routes for card are written as add-ons to the board endpoint(s)

#@cards_bp.route("", methods=["GET"])
# Update enpoint with board id at a later date
# "/<board_id>"
"""
def get_all_cards():
all_cards = Card.query.all()
output_dicts_list = []
for card in all_cards:
output_dicts_list.append(
{"id":card.card_id,
"message":card.message
})

return jsonify(output_dicts_list), 201
"""




9 changes: 9 additions & 0 deletions app/models/board.py
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
from app import db

class Board(db.Model):
board_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String)
owner = db.Column(db.String)

card = db.relationship("Card", backref="board", passive_deletes=True)


7 changes: 7 additions & 0 deletions app/models/card.py
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
from sqlalchemy.orm import backref
from app import db

class Card(db.Model):
card_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
message = db.Column(db.String)
likes_count = db.Column(db.Integer)
board_id = db.Column(db.Integer, db.ForeignKey('board.board_id', ondelete='cascade'))
4 changes: 0 additions & 4 deletions app/routes.py

This file was deleted.

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