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

Otters - Amy, Taingleap, Lulu, Vera #13

Open
wants to merge 18 commits into
base: main
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ __pycache__/
# C extensions
*.so


# Distribution / packaging
.Python
build/
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,16 @@ This file lists the dependencies we anticipate are needed for the project.
This file already has the contents needed for a Heroku deployment.

If the `create_app` function in `app/__init__.py` is renamed or moved, the contents of this file need to change. Otherwise, we don't anticipate this file to change.

## `Endpoints`
| Verb | Path | Body of Request | Body of Response | What it does |
|---|---|---|---|---|
| `GET` | `/boards/<board_id>` | None | `{ "board": { "board_id": 1, "owner": "Owner 1", "title": "Title 1" } }` | Retrieves a board |
| `GET` | `/boards` | None | `[ { "board_id": boardId, "owner": "ownerText", "title": "titleText" } ] `| Retrieves a list of boards |
| `POST` | `/boards` | `{ "title": "titleText", owner: "ownerText" }` | `{ "board": { "board_id": boardId, "owner": "ownerText", "title": "titleText" } }` | Creates a new board with title and owner informtion |
| `GET` | `/<board_id>/cards` | None | `{ "board_id": boardId, "owner": "ownerText", "title": "titleText", "cards": [ { "board_id": boardId, "card_id": cardId, "likes_count": likesCount, "message": "messageText" }, { "board_id": boardId, "card_id": cardId, "likes_count": likesCount, "message": "messageText" } ] }` | Retrieves a list of cards under one specific board |
| `GET` | `/<board_id>/cards/<card_id>` | None | `{ "board_id": boardId, "card_id": cardId, "likes_count": likesCount, "message": "messageText" }` | Retrieves one card under one specific board |
| `POST` | `/boards/<board_id>/cards` | `{ "message": "messageText" }` | `{ "board_id": boardId, "card_id": cardId, "likes_count": likesCount, "message": "messageText" }` | Creates a new card |
| `DELETE` | `/<card_id>'` | None | `{ "details": "Card {cardId} {messageText} successfully deleted" }` | Deletes a card |
| `PATCH` | `/<card_id>/like` | None | `{ "board_id": boardId, "card_id": cardId, "likes_count": likesCount, "message": "messageText" }` | Updates the like counts of a card by 1|
| `DELETE` | `/<board_id>'` | None | `{ "details": "Board {board_id} {board.title} successfully deleted" }` | Deletes a board |
24 changes: 16 additions & 8 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,30 @@
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 not test_config:
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
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 app.routes.card_routes import cards_bp
app.register_blueprint(cards_bp)

from app.routes.board_routes import boards_bp
app.register_blueprint(boards_bp)

CORS(app)
return app
57 changes: 57 additions & 0 deletions app/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from flask import Blueprint, jsonify, make_response, abort
from app.models.card import Card
from app.models.board import Board


# Helper functions
def get_card_or_abort(card_id):
try:
card_id = int(card_id)
except ValueError:
rsp = {"msg":f"Invalid id {card_id}"}
abort(make_response(jsonify(rsp), 400))

card = Card.query.get(card_id)
if card is None:
rsp = {"msg":f"Could not find card with id {card_id}"}
abort(make_response(jsonify(rsp), 404))

return card


def get_board_or_abort(board_id):
try:
board_id = int(board_id)
except ValueError:
rsp = {"msg":f"Invalid id {board_id}"}
abort(make_response(jsonify(rsp), 400))

board = Board.query.get(board_id)
if board is None:
rsp = {"msg":f"Could not find board with id {board_id}"}
abort(make_response(jsonify(rsp), 404))

return board


# Validate card has a short msg
def validate_card(request_body, board_id):
get_board_or_abort(board_id)

if "message" not in request_body:
rsp = {
"details": "Please enter a message. "
}
abort(make_response(jsonify(rsp), 400))
elif len(request_body["message"]) > 40:
rsp = {
"details": "Please enter a message shorter than 40 characters. "
}
abort(make_response(jsonify(rsp), 400))

message = request_body["message"]

if message and len(message) <= 40:
new_card = Card(message=message, likes_count=0, board_id=board_id)
return new_card

13 changes: 13 additions & 0 deletions app/models/board.py
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
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)
cards = db.relationship("Card", back_populates="board", lazy=True)

def to_dict(self):
return {
"board_id": self.board_id,
"title": self.title,
"owner": self.owner
}
15 changes: 15 additions & 0 deletions app/models/card.py
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
from app import db

class Card (db.Model):
card_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
message = db.Column(db.String(500))
likes_count = db.Column(db.Integer)
board_id = db.Column(db.Integer, db.ForeignKey("board.board_id"))
board = db.relationship("Board", back_populates="cards", lazy=True)

def to_dict(self):
return {
"card_id": self.card_id,
"message": self.message,
"likes_count": self.likes_count,
"board_id": self.board_id
}
4 changes: 0 additions & 4 deletions app/routes.py

This file was deleted.

File renamed without changes.
57 changes: 57 additions & 0 deletions app/routes/board_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from flask import Blueprint, request, jsonify
from app import db
from app.models.board import Board
from app.helper import get_board_or_abort


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


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

try:
new_board = Board(title = request_body["title"],owner = request_body["owner"])
except KeyError:
return { "details": "Invalid data"}, 400
db.session.add(new_board)
db.session.commit()
return jsonify(
{
"board": new_board.to_dict()
}
),201


@boards_bp.route("", methods=['GET'])
def get_all_boards():
boards = Board.query.all()
boards_response = []
for board in boards:
boards_response.append(board.to_dict())

return jsonify(boards_response), 200


# GET /boards
@boards_bp.route('/<board_id>', methods=['GET'])
def get_one_board(board_id):
board = get_board_or_abort(board_id)
# return jsonify(board.to_dict()), 200
return jsonify(
{
"board": board.to_dict()
}
), 200

# DELETE /boards/<board_id>
@boards_bp.route('/<board_id>', methods=['DELETE'])
def delete_one_board(board_id):
board = get_board_or_abort(board_id)
db.session.delete(board)
db.session.commit()

return {
"details": f'Board {board.board_id} "{board.title}" successfully deleted'
}, 200
68 changes: 68 additions & 0 deletions app/routes/card_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from flask import Blueprint, request, jsonify
from app import db
from app.models.card import Card
from app.models.board import Board
from app.helper import get_card_or_abort, get_board_or_abort, validate_card
from app.routes.board_routes import boards_bp

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


# POST /boards/<board_id>/cards
# CREATE A CARD
@boards_bp.route('/<board_id>/cards', methods=['POST'])
def add_cards_to_boards(board_id):
request_body = request.get_json()
new_card = validate_card(request_body, board_id)

# add record to the table
db.session.add(new_card)
db.session.commit()

return jsonify(new_card.to_dict()), 201


# GET /boards/<board_id>/cards
@boards_bp.route('/<board_id>/cards', methods=['GET'])
def get_cards_at_one_board(board_id):
board = get_board_or_abort(board_id)
cards = []
for card in board.cards:
cards.append(card.to_dict())

return ({
"board_id": board.board_id,
"title": board.title,
"owner": board.owner,
"cards": cards
}), 200


# GET one card
@boards_bp.route('/<board_id>/cards/<card_id>', methods=['GET'])
def get_one_card_at_one_board(board_id, card_id):
card = get_card_or_abort(card_id)

return jsonify(card.to_dict()), 200


# DELETE /boards/<board_id>/cards
@cards_bp.route('/<card_id>', methods=['DELETE'])
def delete_one_card(card_id):
card = get_card_or_abort(card_id)
db.session.delete(card)
db.session.commit()

return {
"details" : f'Card {card.card_id} {card.message} successfully deleted'
}, 200


# PATCH /cards/<card_id>/like
@cards_bp.route('/<card_id>/like', methods=['PATCH'])
def update_card(card_id):
card = get_card_or_abort(card_id)
card.likes_count += 1
db.session.commit()

return jsonify(card.to_dict()), 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.
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