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

Kunzite - Gabby Y, Amber S, Areeg J, Angelica Y #13

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
def6a09
Complete backend setup
gnyoung Jun 22, 2023
b7bd2bb
Replaced routes.py with boards.py and cards.py
arejib Jun 26, 2023
890c33a
Created and registered Blueprints for boards and cards
arejib Jun 26, 2023
8f97d3b
added seed board and seed card without linking just created the files
langyee1 Jun 26, 2023
001bd88
Merge branch 'main' of https://github.com/gnyoung/back-end-inspiratio…
langyee1 Jun 26, 2023
1a08c46
created boards validation and enter beginning tests
Maashad Jun 26, 2023
88ad783
Add relationship between Board and Card models
gnyoung Jun 26, 2023
d3c2f44
add class methods
Maashad Jun 26, 2023
f94ffb2
created working endpoint for post board
Maashad Jun 27, 2023
314be03
Add POST and GET routes for Card
gnyoung Jun 27, 2023
d28acbd
working get_one_board route
Maashad Jun 27, 2023
38ac449
Add class decorator to Board method
gnyoung Jun 28, 2023
82f08a7
working get_one_board route
Maashad Jun 28, 2023
c4497a7
working route for delete_one_board
Maashad Jun 28, 2023
a7c7736
create endpoint for read_all_boards
Maashad Jun 28, 2023
76f4401
HOT FIX updated create_app to include test_config positional argument
Maashad Jun 28, 2023
20b356f
delete routes ok
langyee1 Jun 28, 2023
ea5d1e3
Merge branch 'main' of https://github.com/gnyoung/back-end-inspiratio…
langyee1 Jun 28, 2023
801351e
Added PATCH request to cards_routes
arejib Jun 28, 2023
7196b45
Refactor and write tests for Card routes
gnyoung Jun 28, 2023
a4b8f2a
Added fixture to conftest.py and corresponding test for PATCH route
arejib Jul 3, 2023
55cac89
connected seed card with seed commit
langyee1 Jul 20, 2023
6593ff1
Cleaned up formatting
arejib Jul 20, 2023
04a089c
Refactor cards endpoint
gnyoung Jul 20, 2023
26b2e95
Merge branch 'main' of github.com:gnyoung/back-end-inspiration-board
gnyoung Jul 20, 2023
75c5215
delete all boards route added
Maashad Jul 20, 2023
8b66e6a
added delete all cards route
Maashad Jul 20, 2023
9e232e2
changed naming in cards delete and patch
langyee1 Jul 20, 2023
1905b10
missing tests for board added
langyee1 Jul 21, 2023
5e35bf2
Connect to render db
gnyoung Jul 21, 2023
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
24 changes: 20 additions & 4 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,38 @@
load_dotenv()


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

app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
"SQLALCHEMY_DATABASE_URI")
if test_config is None:
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("RENDER_DATABASE_URI")
# 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")

# Import models here for Alembic setup
# from app.models.ExampleModel import ExampleModel

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

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

# Register Blueprints here
# from .routes import example_bp
from .cards_routes import cards_bp
from .boards_routes import boards_bp

# app.register_blueprint(example_bp)
app.register_blueprint(cards_bp)
app.register_blueprint(boards_bp)



CORS(app)
return app
97 changes: 97 additions & 0 deletions app/boards_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from flask import Blueprint, request, jsonify, make_response, abort
from app import db
from app.models.board import Board
from app.models.card import Card
from sqlalchemy.types import DateTime
from sqlalchemy.sql.functions import now
from app.routes_helpers import validate_model
import requests, json
import os

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

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

is_valid_board_title = "title" in request_body
is_valid_board_owner = "owner" in request_body
if not is_valid_board_title:
abort(make_response({"details": "Please include title"}, 400))
if not is_valid_board_owner:
abort(make_response({"details": "Please include owner"}, 400))

new_board = Board.board_from_dict(request_body)

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

return make_response(jsonify(f"{new_board} successfully created!"), 201)

@boards_bp.route("", methods=["GET"])
def read_all_boards():
boards = Board.query.all()
boards_response = [board.board_to_dict() for board in boards]

return jsonify(boards_response)

@boards_bp.route("/<board_id>", methods=["GET"])
def read_one_board(board_id):
board = validate_model(Board, board_id)

response_body = {
"board": board.board_to_dict()
}

return jsonify(response_body)

@boards_bp.route("/<board_id>", methods=["DELETE"])
def delete_one_board(board_id):
board = validate_model(Board, board_id)

response_body = {
"details": f"Board {board.id} \"{board.title}\" successfully deleted"
}

db.session.delete(board)
db.session.commit()

return jsonify(response_body)

@boards_bp.route("", methods=["DELETE"])
def delete_all_boards():
boards = Board.query.all()
for board in boards:
db.session.delete(board)
db.session.commit()

return make_response(jsonify("All boards successfully deleted!"), 200)

@boards_bp.route("/<board_id>/cards", methods=["POST"])
def create_one_card_for_board(board_id):
board = validate_model(Board, board_id)

request_body = request.get_json()

card = Card.query.get(request_body["id"])
board.cards.append(card)

db.session.add(board)
db.session.commit()

return make_response({
"id": board.id,
"card_id": request_body["id"]}, 201)

@boards_bp.route("/<board_id>/cards", methods=["GET"])
def get_cards_for_one_board(board_id):
board = validate_model(Board, board_id)

cards = board.cards

cards_list = [card.card_to_dict() for card in cards]

return make_response({
"id": board.id,
"title": board.title,
"cards": cards_list}, 200)
58 changes: 58 additions & 0 deletions app/cards_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from flask import Blueprint, request, jsonify, make_response
from app import db
from app.models.card import Card
from app.routes_helpers import validate_model

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

@cards_bp.route("", methods=["POST"])
def create_card():
Comment on lines +8 to +9

Choose a reason for hiding this comment

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

👀 We shouldn't be able to create a Card that doesn't belong to a Board. Consider using the nested POST under Board to create a new Card which belongs to that Board, rather than associating an existing Card to an existing Board. That approach was slightly peculiar to Task List, which allowed Tasks to exist that weren't part of an overarching Goal.

request_body = request.get_json()

new_card = Card.card_from_dict(request_body)

db.session.add(new_card)
db.session.commit()

new_card_id = new_card.id

return make_response(jsonify({"id": new_card_id, "message": "Card successfully created"}), 201)

@cards_bp.route("", methods=["GET"])
def read_all_cards():
cards = Card.query.all()

cards_response = [card.card_to_dict() for card in cards]

return make_response(jsonify(cards_response), 200)

@cards_bp.route("/<card_id>", methods=["DELETE"])
def delete_one_card(card_id):
card = validate_model(Card, card_id)

response_body = {
"details": f"Card {card.id} \"{card.message}\" successfully deleted"
}

db.session.delete(card)
db.session.commit()

return jsonify(response_body)

@cards_bp.route("", methods=["DELETE"])
def delete_all_cards():
cards = Card.query.all()
for card in cards:
db.session.delete(card)
db.session.commit()

return make_response(jsonify("All cards successfully deleted!"), 200)

@cards_bp.route("/<card_id>", methods=["PATCH"])

Choose a reason for hiding this comment

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

Since this isn't a general purpose PATCH method, consider adding an additional qualifier to the end (like the mark (in)complete endpoints in task list)

@cards_bp.route("/<card_id>/like", methods=["PATCH"])

def increase_like_count(card_id):
card = validate_model(Card, card_id)

card.likes_count += 1

db.session.commit()
return make_response(card.card_to_dict(), 200)
21 changes: 21 additions & 0 deletions app/models/board.py
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
from app import db

class Board(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String)
owner = db.Column(db.String)
cards = db.relationship("Card", back_populates="board")

@classmethod
def board_from_dict(cls, board_data):
new_board = Board(title=board_data["title"], owner=board_data["owner"])

return new_board

def board_to_dict(self):
board_as_dict = {}
board_as_dict["id"] = self.id
board_as_dict["title"] = self.title
board_as_dict["owner"] = self.owner

return board_as_dict

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


class Card(db.Model):
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.id"))
board = db.relationship("Board", back_populates="cards")

def card_to_dict(self):
card_as_dict = {}
card_as_dict["id"] = self.id
card_as_dict["message"] = self.message
card_as_dict["likes_count"] = self.likes_count

return card_as_dict

@classmethod
def card_from_dict(cls, card_data):
new_card = cls(message=card_data["message"], likes_count=card_data.get("likes_count", 0))

return new_card
4 changes: 0 additions & 4 deletions app/routes.py

This file was deleted.

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

def validate_model(cls, model_id):
try:
model_id = int(model_id)
except:
abort(make_response({"message": f"{cls.__name__} {model_id} invalid"}, 400))

model = cls.query.get(model_id)

if not model:
abort(make_response({"message": f"{cls.__name__} {model_id} not found"}, 404))

return model
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