From def6a090098f75be5798371f9b7d0c684a3b2e2b Mon Sep 17 00:00:00 2001 From: Gabby Date: Thu, 22 Jun 2023 17:09:46 -0500 Subject: [PATCH 01/27] Complete backend setup --- app/__init__.py | 4 +- app/models/board.py | 7 ++ app/models/card.py | 6 ++ migrations/README | 1 + migrations/alembic.ini | 45 +++++++++ migrations/env.py | 96 +++++++++++++++++++ migrations/script.py.mako | 24 +++++ ...cdb4c3b0a792_adds_board_and_card_models.py | 40 ++++++++ 8 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/cdb4c3b0a792_adds_board_and_card_models.py diff --git a/app/__init__.py b/app/__init__.py index 1c821436..1364cbd5 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -19,10 +19,12 @@ def create_app(): # 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 # app.register_blueprint(example_bp) diff --git a/app/models/board.py b/app/models/board.py index 147eb748..a959c5d1 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -1 +1,8 @@ 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) + + diff --git a/app/models/card.py b/app/models/card.py index 147eb748..67a9dbbf 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -1 +1,7 @@ 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) \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 00000000..f8ed4801 --- /dev/null +++ b/migrations/alembic.ini @@ -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 diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 00000000..8b3fb335 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/cdb4c3b0a792_adds_board_and_card_models.py b/migrations/versions/cdb4c3b0a792_adds_board_and_card_models.py new file mode 100644 index 00000000..b683189b --- /dev/null +++ b/migrations/versions/cdb4c3b0a792_adds_board_and_card_models.py @@ -0,0 +1,40 @@ +"""adds Board and Card models + +Revision ID: cdb4c3b0a792 +Revises: +Create Date: 2023-06-22 17:07:27.584747 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'cdb4c3b0a792' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('board', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('owner', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('card', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('message', sa.String(), nullable=True), + sa.Column('likes_count', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('card') + op.drop_table('board') + # ### end Alembic commands ### From b7bd2bb4b7255563cf9aa375b6004e551bc4d66b Mon Sep 17 00:00:00 2001 From: areeg <127351347+arejib@users.noreply.github.com> Date: Mon, 26 Jun 2023 15:03:31 -0500 Subject: [PATCH 02/27] Replaced routes.py with boards.py and cards.py --- app/{routes.py => boards_routes.py} | 0 app/cards_routes.py | 4 ++++ 2 files changed, 4 insertions(+) rename app/{routes.py => boards_routes.py} (100%) create mode 100644 app/cards_routes.py diff --git a/app/routes.py b/app/boards_routes.py similarity index 100% rename from app/routes.py rename to app/boards_routes.py diff --git a/app/cards_routes.py b/app/cards_routes.py new file mode 100644 index 00000000..480b8c4b --- /dev/null +++ b/app/cards_routes.py @@ -0,0 +1,4 @@ +from flask import Blueprint, request, jsonify, make_response +from app import db + +# example_bp = Blueprint('example_bp', __name__) From 890c33a394784d180951b59c33ddd5727b3f7f7c Mon Sep 17 00:00:00 2001 From: areeg <127351347+arejib@users.noreply.github.com> Date: Mon, 26 Jun 2023 15:10:06 -0500 Subject: [PATCH 03/27] Created and registered Blueprints for boards and cards --- app/__init__.py | 7 +++++++ app/boards_routes.py | 1 + app/cards_routes.py | 1 + 3 files changed, 9 insertions(+) diff --git a/app/__init__.py b/app/__init__.py index 1364cbd5..7b1102a6 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -27,7 +27,14 @@ def create_app(): # 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 diff --git a/app/boards_routes.py b/app/boards_routes.py index 480b8c4b..3d928e13 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -2,3 +2,4 @@ from app import db # example_bp = Blueprint('example_bp', __name__) +boards_bp = Blueprint("boards", __name__, url_prefix="/boards") diff --git a/app/cards_routes.py b/app/cards_routes.py index 480b8c4b..13562451 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -2,3 +2,4 @@ from app import db # example_bp = Blueprint('example_bp', __name__) +cards_bp = Blueprint("cards", __name__, url_prefix="/cards") \ No newline at end of file From 8f97d3b6a668a63e663c6a9096d6ecb97d50d600 Mon Sep 17 00:00:00 2001 From: Angelica Date: Mon, 26 Jun 2023 15:44:12 -0500 Subject: [PATCH 04/27] added seed board and seed card without linking just created the files --- seed_board.py | 11 +++++++++++ seed_card.py | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 seed_board.py create mode 100644 seed_card.py diff --git a/seed_board.py b/seed_board.py new file mode 100644 index 00000000..decdfca0 --- /dev/null +++ b/seed_board.py @@ -0,0 +1,11 @@ +from app import create_app, db +from app.models.board import Board + +my_app = create_app() +with my_app.app_context(): + db.session.add(Board(title="reminders", owner="Amber")) + db.session.add(Board(title="capstone ideas", owner="Amber")) + db.session.add(Board(title="quotes",owner="Areeg")) + db.session.add(Board(title="advice",owner="Gabby")) + db.session.add(Board(title="jokes",owner="Angelica")) + db.session.commit() \ No newline at end of file diff --git a/seed_card.py b/seed_card.py new file mode 100644 index 00000000..690829aa --- /dev/null +++ b/seed_card.py @@ -0,0 +1,11 @@ +from app import create_app, db +from app.models.card import Card + +my_app = create_app() +with my_app.app_context(): + db.session.add(Card(message="dont stay up late, it will make you old", likes_count=0)) + db.session.add(Card(message="a social media platform called fakebook", likes_count=0)) + db.session.add(Card(message="I think then I code", likes_count=0)) + db.session.add(Card(message="never leave your keys in the car", likes_count=0)) + db.session.add(Card(message="knock knock... nothing I dont know any jokes", likes_count=0)) + db.session.commit() \ No newline at end of file From 1a08c46ecee1ed1aee43188563f0bdf21ca54c63 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Mon, 26 Jun 2023 16:14:18 -0600 Subject: [PATCH 05/27] created boards validation and enter beginning tests --- app/boards_routes.py | 17 ++++++++++++++++- tests/test_routes.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index 3d928e13..92c468b9 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -1,5 +1,20 @@ -from flask import Blueprint, request, jsonify, make_response +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 +import requests, json +import os # example_bp = Blueprint('example_bp', __name__) boards_bp = Blueprint("boards", __name__, url_prefix="/boards") + +# validate board input +def validate_model_boards(cls, board_id): + try: + board_id = int(board_id) + except: + abort(make_response({"message": f"{cls.__name__} {board_id} invalid"}, 400)) + + board = cls.query.get(board_id) \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py index e69de29b..77a5846c 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -0,0 +1,31 @@ +# Routes for Board + +from app.models.board import Board +import pytest + +@pytest.mark.skip(reason="Feature not yet built") +def test_get_board_no_board_saved(client): + response = client.get("/boards") + response_body = response.get_json() + + assert response.status.code == 200 + assert response_body ==[] + +@pytest.mark.skip(reason="Feature not yet built") +def test_create_board(client): + pass + +@pytest.mark.skip(reason="Feature not yet built") +def test_get_one_saved_board(client): + pass + +@pytest.mark.skip(reason="Feature not yet built") +def test_delete_board(client): + pass + +@pytest.mark.skip(reason="Feature not yet built") +def test_delete_board_not_found(client): + pass + +@pytest.mark.skip(reason="Feature not yet built") +def From 88ad783126cdb2c0bc0d504eb4201fd4b4e7534d Mon Sep 17 00:00:00 2001 From: Gabby Date: Mon, 26 Jun 2023 17:59:52 -0500 Subject: [PATCH 06/27] Add relationship between Board and Card models --- app/models/board.py | 4 +-- app/models/card.py | 4 ++- ...dds_relationship_between_board_and_card.py | 30 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 migrations/versions/ca3d831c4542_adds_relationship_between_board_and_card.py diff --git a/app/models/board.py b/app/models/board.py index a959c5d1..59f6394e 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -1,8 +1,8 @@ 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") diff --git a/app/models/card.py b/app/models/card.py index 67a9dbbf..574131f0 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -4,4 +4,6 @@ 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) \ No newline at end of file + likes_count = db.Column(db.Integer) + board_id = db.Column(db.Integer, db.ForeignKey("board.id")) + board = db.relationship("Board", back_populates="cards") diff --git a/migrations/versions/ca3d831c4542_adds_relationship_between_board_and_card.py b/migrations/versions/ca3d831c4542_adds_relationship_between_board_and_card.py new file mode 100644 index 00000000..2fceabff --- /dev/null +++ b/migrations/versions/ca3d831c4542_adds_relationship_between_board_and_card.py @@ -0,0 +1,30 @@ +"""adds relationship between Board and Card + +Revision ID: ca3d831c4542 +Revises: cdb4c3b0a792 +Create Date: 2023-06-26 17:49:32.575635 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ca3d831c4542' +down_revision = 'cdb4c3b0a792' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('card', sa.Column('board_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'card', 'board', ['board_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'card', type_='foreignkey') + op.drop_column('card', 'board_id') + # ### end Alembic commands ### From d3c2f44495ca509c453c257aaf5db478e1dd4a81 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Mon, 26 Jun 2023 17:09:50 -0600 Subject: [PATCH 07/27] add class methods --- app/models/board.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/models/board.py b/app/models/board.py index 59f6394e..5a20fda8 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -1,8 +1,24 @@ from app import db - class Board(db.Model): - id = db.Column(db.Integer, primary_key=True, autoincrement=True) + 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") + +@classmethod +def board_from_dict(cls, board_data): + new_board = Board(title=board_data["title"]) + return new_board + +def board_to_dict(self): + board_as_dict = {} + + board_as_dict["id"] = self.board_id + board_as_dict["title"] = self.title + board_as_dict["owner"] = self.owner + + # code here for cards + + return board_as_dict + From f94ffb230ad82fc77dd3e297d1595a23ecf31ee3 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Tue, 27 Jun 2023 11:49:34 -0600 Subject: [PATCH 08/27] created working endpoint for post board --- app/boards_routes.py | 25 ++++++++++++++++++++----- app/models/board.py | 25 +++++++++++-------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index 92c468b9..d5168512 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -10,11 +10,26 @@ # example_bp = Blueprint('example_bp', __name__) boards_bp = Blueprint("boards", __name__, url_prefix="/boards") -# validate board input -def validate_model_boards(cls, board_id): +# validate input +def validate_model(cls, model_id): try: - board_id = int(board_id) + model_id = int(model_id) except: - abort(make_response({"message": f"{cls.__name__} {board_id} invalid"}, 400)) + abort(make_response({"message": f"{cls.__name__} {model_id} invalid"}, 400)) - board = cls.query.get(board_id) \ No newline at end of file + model = cls.query.get(model_id) + + if not model: + abort(make_response({"message": f"board {model_id} not found"})) + + +@boards_bp.route("", methods=["POST"]) +def create_board(): + request_body = request.get_json() + new_board = Board.board_from_dict(request_body) + # new_board = Board(title=request_body["title"], owner=request_body["owner"]) + + db.session.add(new_board) + db.session.commit() + + return make_response(jsonify(f"{new_board} successfully created!"), 201) \ No newline at end of file diff --git a/app/models/board.py b/app/models/board.py index 5a20fda8..a8a0659d 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -1,24 +1,21 @@ from app import db class Board(db.Model): - board_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + 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"]) - return new_board -def board_to_dict(self): - board_as_dict = {} + 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 - board_as_dict["id"] = self.board_id - board_as_dict["title"] = self.title - board_as_dict["owner"] = self.owner + return board_as_dict - # code here for cards - - return board_as_dict + @classmethod + def board_from_dict(cls, board_data): + new_board = cls(title=board_data["title"], owner=board_data["owner"]) + return new_board From 314be03a6e784bde445749f3696b1f27ddab4a4f Mon Sep 17 00:00:00 2001 From: Gabby Date: Tue, 27 Jun 2023 15:54:41 -0500 Subject: [PATCH 09/27] Add POST and GET routes for Card --- app/boards_routes.py | 51 ++++++++++++++++++++++++++++++++----------- app/cards_routes.py | 27 +++++++++++++++++++++-- app/models/board.py | 2 +- app/models/card.py | 15 +++++++++++++ app/routes_helpers.py | 13 +++++++++++ 5 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 app/routes_helpers.py diff --git a/app/boards_routes.py b/app/boards_routes.py index d5168512..864d3598 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -4,24 +4,16 @@ from app.models.card import Card from sqlalchemy.types import DateTime from sqlalchemy.sql.functions import now +from app.routes_helpers import validate_model +from app.models.board import Board +from app.models.card import Card import requests, json import os + # example_bp = Blueprint('example_bp', __name__) boards_bp = Blueprint("boards", __name__, url_prefix="/boards") -# validate input -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"board {model_id} not found"})) - @boards_bp.route("", methods=["POST"]) def create_board(): @@ -32,4 +24,37 @@ def create_board(): db.session.add(new_board) db.session.commit() - return make_response(jsonify(f"{new_board} successfully created!"), 201) \ No newline at end of file + return make_response(jsonify(f"{new_board} successfully created!"), 201) + + +@boards_bp.route("//cards", methods=["POST"]) +def create_one_card_for_board(board_id): + # board = validate_model(Board, board_id) + board = Board.query.get(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"]}, 200) + + +@boards_bp.route("//cards", methods=["GET"]) +def get_cards_for_one_board(board_id): + # board = validate_model(Board, board_id) + board = Board.query.get(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) diff --git a/app/cards_routes.py b/app/cards_routes.py index 13562451..20b8f81b 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -1,5 +1,28 @@ 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 -# example_bp = Blueprint('example_bp', __name__) -cards_bp = Blueprint("cards", __name__, url_prefix="/cards") \ No newline at end of file + +cards_bp = Blueprint("cards", __name__, url_prefix="/cards") + + +@cards_bp.route("", methods=["POST"]) +def create_card(): + request_body = request.get_json() + + new_card = Card.card_from_dict(request_body) + + db.session.add(new_card) + db.session.commit() + + return make_response(f"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) \ No newline at end of file diff --git a/app/models/board.py b/app/models/board.py index a8a0659d..008cd7cd 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -13,7 +13,7 @@ def board_to_dict(self): board_as_dict["owner"] = self.owner return board_as_dict - + @classmethod def board_from_dict(cls, board_data): new_board = cls(title=board_data["title"], owner=board_data["owner"]) diff --git a/app/models/card.py b/app/models/card.py index 574131f0..3c0d91e7 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -7,3 +7,18 @@ class Card(db.Model): 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 diff --git a/app/routes_helpers.py b/app/routes_helpers.py new file mode 100644 index 00000000..705682d8 --- /dev/null +++ b/app/routes_helpers.py @@ -0,0 +1,13 @@ +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"board {model_id} not found"})) \ No newline at end of file From d28acbd13b8f47fe2387ca477a2d5a1f8dfad992 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Tue, 27 Jun 2023 16:13:43 -0600 Subject: [PATCH 10/27] working get_one_board route --- app/boards_routes.py | 14 ++++++++++---- app/models/board.py | 3 +-- app/routes_helpers.py | 18 +++++++++++++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index 864d3598..9465ad86 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -5,8 +5,6 @@ from sqlalchemy.types import DateTime from sqlalchemy.sql.functions import now from app.routes_helpers import validate_model -from app.models.board import Board -from app.models.card import Card import requests, json import os @@ -14,18 +12,26 @@ # example_bp = Blueprint('example_bp', __name__) boards_bp = Blueprint("boards", __name__, url_prefix="/boards") - @boards_bp.route("", methods=["POST"]) def create_board(): request_body = request.get_json() new_board = Board.board_from_dict(request_body) - # new_board = Board(title=request_body["title"], owner=request_body["owner"]) 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_one_board(board_id): + # board = validate_model(Board, board_id) + board = Board.query.get(board_id) + + return make_response({ + "id": board.id, + "title": board.title, + "owner": board.owner + }, 200) @boards_bp.route("//cards", methods=["POST"]) def create_one_card_for_board(board_id): diff --git a/app/models/board.py b/app/models/board.py index 008cd7cd..9cb6ff8e 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -14,8 +14,7 @@ def board_to_dict(self): return board_as_dict - @classmethod def board_from_dict(cls, board_data): - new_board = cls(title=board_data["title"], owner=board_data["owner"]) + new_board = Board(title=board_data["title"], owner=board_data["owner"]) return new_board diff --git a/app/routes_helpers.py b/app/routes_helpers.py index 705682d8..670aaf70 100644 --- a/app/routes_helpers.py +++ b/app/routes_helpers.py @@ -10,4 +10,20 @@ def validate_model(cls, model_id): model = cls.query.get(model_id) if not model: - abort(make_response({"message": f"board {model_id} not found"})) \ No newline at end of file + abort(make_response({"message": f"{cls.__name__} {model_id} not found"}, 404)) + + return model + + +# def validate_model(cls, model_id): +# try: +# model_id = int(model_id) +# except: +# abort(make_response({"message": f"{cls.__name__} {model_id} invalid"}, 400)) + +# board = cls.query.get(model_id) + +# if not board: +# abort(make_response({"message": f"{cls.__name__} {model_id} not found"}, 404)) + +# return board \ No newline at end of file From 38ac449ed0990b3ffdc4339bd1bd8d599248648a Mon Sep 17 00:00:00 2001 From: Gabby Date: Tue, 27 Jun 2023 22:47:32 -0500 Subject: [PATCH 11/27] Add class decorator to Board method --- app/models/board.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/board.py b/app/models/board.py index 9cb6ff8e..25acd551 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -13,7 +13,8 @@ def board_to_dict(self): board_as_dict["owner"] = self.owner return board_as_dict - + + @classmethod def board_from_dict(cls, board_data): new_board = Board(title=board_data["title"], owner=board_data["owner"]) From 82f08a7388e3f4053be7902052e2f9b89b68e1e1 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Tue, 27 Jun 2023 22:26:28 -0600 Subject: [PATCH 12/27] working get_one_board route --- app/boards_routes.py | 13 ++++++------- app/models/board.py | 14 ++++++++------ app/models/card.py | 3 +-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index 9465ad86..41e5fac9 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -24,14 +24,13 @@ def create_board(): @boards_bp.route("/", methods=["GET"]) def read_one_board(board_id): - # board = validate_model(Board, board_id) - board = Board.query.get(board_id) + board = validate_model(Board, board_id) - return make_response({ - "id": board.id, - "title": board.title, - "owner": board.owner - }, 200) + response_body = { + "board": board.board_to_dict() + } + + return jsonify(response_body) @boards_bp.route("//cards", methods=["POST"]) def create_one_card_for_board(board_id): diff --git a/app/models/board.py b/app/models/board.py index 25acd551..c586d554 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -6,6 +6,13 @@ class Board(db.Model): 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 @@ -13,9 +20,4 @@ def board_to_dict(self): board_as_dict["owner"] = self.owner return board_as_dict - - @classmethod - def board_from_dict(cls, board_data): - new_board = Board(title=board_data["title"], owner=board_data["owner"]) - - return new_board + \ No newline at end of file diff --git a/app/models/card.py b/app/models/card.py index 3c0d91e7..c6ae4eb1 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -18,7 +18,6 @@ def card_to_dict(self): @classmethod def card_from_dict(cls, card_data): - new_card = cls(message=card_data["message"], - likes_count=card_data.get("likes_count", 0)) + new_card = cls(message=card_data["message"], likes_count=card_data.get("likes_count", 0)) return new_card From c4497a7f41f0200df62a3b4fb28695de4b17a60b Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Tue, 27 Jun 2023 22:57:36 -0600 Subject: [PATCH 13/27] working route for delete_one_board --- app/boards_routes.py | 14 ++++++++++++++ app/routes_helpers.py | 14 -------------- tests/test_routes.py | 15 ++++++++++----- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index 41e5fac9..6de3e77b 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -32,6 +32,20 @@ def read_one_board(board_id): return jsonify(response_body) +@boards_bp.route("/", 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("//cards", methods=["POST"]) def create_one_card_for_board(board_id): # board = validate_model(Board, board_id) diff --git a/app/routes_helpers.py b/app/routes_helpers.py index 670aaf70..154d4279 100644 --- a/app/routes_helpers.py +++ b/app/routes_helpers.py @@ -13,17 +13,3 @@ def validate_model(cls, model_id): abort(make_response({"message": f"{cls.__name__} {model_id} not found"}, 404)) return model - - -# def validate_model(cls, model_id): -# try: -# model_id = int(model_id) -# except: -# abort(make_response({"message": f"{cls.__name__} {model_id} invalid"}, 400)) - -# board = cls.query.get(model_id) - -# if not board: -# abort(make_response({"message": f"{cls.__name__} {model_id} not found"}, 404)) - -# return board \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py index 77a5846c..fa72afd6 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -15,9 +15,17 @@ def test_get_board_no_board_saved(client): def test_create_board(client): pass -@pytest.mark.skip(reason="Feature not yet built") +# @pytest.mark.skip(reason="Feature not yet built") def test_get_one_saved_board(client): - pass + response = client.get("/boards/1") + response_body = response.get_json() + + assert response.status_code == 200 + assert response_body == { + "id": 1, + "title": "My Happy Place", + "owner": "Amber" + } @pytest.mark.skip(reason="Feature not yet built") def test_delete_board(client): @@ -26,6 +34,3 @@ def test_delete_board(client): @pytest.mark.skip(reason="Feature not yet built") def test_delete_board_not_found(client): pass - -@pytest.mark.skip(reason="Feature not yet built") -def From a7c77362703d15503e5489d9a5f898983c392432 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Wed, 28 Jun 2023 09:25:22 -0600 Subject: [PATCH 14/27] create endpoint for read_all_boards --- app/boards_routes.py | 7 +++++++ tests/test_routes.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index 6de3e77b..45b08d67 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -22,6 +22,13 @@ def create_board(): 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("/", methods=["GET"]) def read_one_board(board_id): board = validate_model(Board, board_id) diff --git a/tests/test_routes.py b/tests/test_routes.py index fa72afd6..cb77d9a2 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -3,7 +3,7 @@ from app.models.board import Board import pytest -@pytest.mark.skip(reason="Feature not yet built") +# @pytest.mark.skip(reason="Feature not yet built") def test_get_board_no_board_saved(client): response = client.get("/boards") response_body = response.get_json() From 76f4401ba928fae97e0ceee0272e1fa35c7e80df Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Wed, 28 Jun 2023 10:07:18 -0600 Subject: [PATCH 15/27] HOT FIX updated create_app to include test_config positional argument --- app/__init__.py | 12 +++++++++--- app/boards_routes.py | 8 ++++++++ tests/test_routes.py | 6 +++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 7b1102a6..3b756167 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -10,12 +10,18 @@ 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( + "SQLALCHEMY_DATABASE_URI") + + else: + app.config["TESTING"] = True + app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( + "SQLALCHEMY_DATABASE_URI") # Import models here for Alembic setup # from app.models.ExampleModel import ExampleModel diff --git a/app/boards_routes.py b/app/boards_routes.py index 45b08d67..a26f1875 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -15,6 +15,14 @@ @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) diff --git a/tests/test_routes.py b/tests/test_routes.py index cb77d9a2..b4b3f377 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -22,9 +22,9 @@ def test_get_one_saved_board(client): assert response.status_code == 200 assert response_body == { - "id": 1, - "title": "My Happy Place", - "owner": "Amber" + "id": 3, + "owner": "Amber", + "title": "The Keep on Keepin On Board" } @pytest.mark.skip(reason="Feature not yet built") From 20b356fab6c3f5457dddc69c79a1bdff9cac4b4d Mon Sep 17 00:00:00 2001 From: Angelica Date: Wed, 28 Jun 2023 11:19:51 -0500 Subject: [PATCH 16/27] delete routes ok --- app/cards_routes.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/cards_routes.py b/app/cards_routes.py index 20b8f81b..02f4c2cc 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -25,4 +25,17 @@ def read_all_cards(): cards_response = [card.card_to_dict() for card in cards] - return make_response(jsonify(cards_response), 200) \ No newline at end of file + return make_response(jsonify(cards_response), 200) + +@cards_bp.route("/", methods=["DELETE"]) +def delete_one_board(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) \ No newline at end of file From 801351e9dfef1406823a162af643bdb41f3ea525 Mon Sep 17 00:00:00 2001 From: areeg <127351347+arejib@users.noreply.github.com> Date: Wed, 28 Jun 2023 12:13:23 -0500 Subject: [PATCH 17/27] Added PATCH request to cards_routes --- app/cards_routes.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/cards_routes.py b/app/cards_routes.py index 02f4c2cc..28b42873 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -38,4 +38,13 @@ def delete_one_board(card_id): db.session.delete(card) db.session.commit() - return jsonify(response_body) \ No newline at end of file + return jsonify(response_body) + +@cards_bp.route("/", 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) \ No newline at end of file From 7196b45886c5dbf977b9217d5a4818870b059112 Mon Sep 17 00:00:00 2001 From: Gabby Date: Wed, 28 Jun 2023 16:39:42 -0500 Subject: [PATCH 18/27] Refactor and write tests for Card routes --- app/__init__.py | 2 +- app/boards_routes.py | 12 ++++++----- app/cards_routes.py | 4 +++- requirements.txt | 1 + tests/conftest.py | 50 +++++++++++++++++++++++++++++++++++++++++++ tests/test_routes.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 7 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 3b756167..9a942c9a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -21,7 +21,7 @@ def create_app(test_config=None): else: app.config["TESTING"] = True app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( - "SQLALCHEMY_DATABASE_URI") + "SQLALCHEMY_TEST_DATABASE_URI") # Import models here for Alembic setup # from app.models.ExampleModel import ExampleModel diff --git a/app/boards_routes.py b/app/boards_routes.py index a26f1875..f98bcf47 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -12,6 +12,7 @@ # example_bp = Blueprint('example_bp', __name__) boards_bp = Blueprint("boards", __name__, url_prefix="/boards") + @boards_bp.route("", methods=["POST"]) def create_board(): request_body = request.get_json() @@ -30,6 +31,7 @@ def create_board(): return make_response(jsonify(f"{new_board} successfully created!"), 201) + @boards_bp.route("", methods=["GET"]) def read_all_boards(): boards = Board.query.all() @@ -37,6 +39,7 @@ def read_all_boards(): return jsonify(boards_response) + @boards_bp.route("/", methods=["GET"]) def read_one_board(board_id): board = validate_model(Board, board_id) @@ -47,6 +50,7 @@ def read_one_board(board_id): return jsonify(response_body) + @boards_bp.route("/", methods=["DELETE"]) def delete_one_board(board_id): board = validate_model(Board, board_id) @@ -63,8 +67,7 @@ def delete_one_board(board_id): @boards_bp.route("//cards", methods=["POST"]) def create_one_card_for_board(board_id): - # board = validate_model(Board, board_id) - board = Board.query.get(board_id) + board = validate_model(Board, board_id) request_body = request.get_json() @@ -76,13 +79,12 @@ def create_one_card_for_board(board_id): return make_response({ "id": board.id, - "card_id": request_body["id"]}, 200) + "card_id": request_body["id"]}, 201) @boards_bp.route("//cards", methods=["GET"]) def get_cards_for_one_board(board_id): - # board = validate_model(Board, board_id) - board = Board.query.get(board_id) + board = validate_model(Board, board_id) cards = board.cards diff --git a/app/cards_routes.py b/app/cards_routes.py index 28b42873..2db7dda1 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -16,7 +16,7 @@ def create_card(): db.session.add(new_card) db.session.commit() - return make_response(f"Card successfully created", 201) + return make_response(jsonify("Card successfully created"), 201) @cards_bp.route("", methods=["GET"]) @@ -27,6 +27,7 @@ def read_all_cards(): return make_response(jsonify(cards_response), 200) + @cards_bp.route("/", methods=["DELETE"]) def delete_one_board(card_id): card = validate_model(Card, card_id) @@ -40,6 +41,7 @@ def delete_one_board(card_id): return jsonify(response_body) + @cards_bp.route("/", methods=["PATCH"]) def increase_like_count(card_id): card = validate_model(Card, card_id) diff --git a/requirements.txt b/requirements.txt index 76f1f000..253ec4a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ alembic==1.5.4 attrs==21.2.0 autopep8==1.5.5 +blinker==1.4 certifi==2020.12.5 chardet==4.0.0 click==7.1.2 diff --git a/tests/conftest.py b/tests/conftest.py index 2b7296d5..7ef189b3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,9 @@ import pytest from app import create_app +from flask.signals import request_finished from app import db +from app.models.board import Board +from app.models.card import Card @pytest.fixture @@ -8,6 +11,10 @@ def app(): # create the app with a test config dictionary app = create_app({"TESTING": True}) + @request_finished.connect_via(app) + def expire_session(sender, response, **extra): + db.session.remove() + with app.app_context(): db.create_all() yield app @@ -20,3 +27,46 @@ def app(): @pytest.fixture def client(app): return app.test_client() + + +@pytest.fixture +def two_saved_boards(app): + shakespeare_board = Board(title="Shakespeare Quotes", + owner="William Shakespeare") + + movie_board = Board(title="Movie Quotes", + owner="Meryl Streep") + + db.session.add_all([shakespeare_board, movie_board]) + db.session.commit() + + return shakespeare_board, movie_board + + +@pytest.fixture +def three_saved_cards(app): + card_1 = Card(message="To be, or not to be, that is the question", + likes_count=5) + card_2 = Card(message="Romeo, Romeo! Wherefore art thou Romeo?", + likes_count=3) + card_3 = Card(message="There's no place like home.", + likes_count=2) + + db.session.add_all([card_1, card_2, card_3]) + db.session.commit() + + return card_1, card_2, card_3 + + +@pytest.fixture +def three_saved_cards_and_two_boards(three_saved_cards, two_saved_boards): + card_1, card_2, card_3 = three_saved_cards + shakespeare_board, movies_board = two_saved_boards + + shakespeare_board.cards.extend([card_1, card_2]) + + db.session.commit() + + + + diff --git a/tests/test_routes.py b/tests/test_routes.py index b4b3f377..1616ab92 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -34,3 +34,54 @@ def test_delete_board(client): @pytest.mark.skip(reason="Feature not yet built") def test_delete_board_not_found(client): pass + + +# @pytest.mark.skip(reason="Feature not yet built") +def test_create_card(client, three_saved_cards): + test_data = {"message": "May the Force be with you.", "likes_count": 0} + + response = client.post("/cards", json=test_data) + + response_body = response.get_json() + + assert response.status_code == 201 + assert response_body == "Card successfully created" + + +# @pytest.mark.skip(reason="Feature not yet built") +def test_create_card_for_one_board(client, three_saved_cards_and_two_boards): + test_data = {"id": 3} + + response = client.post("boards/2/cards", json=test_data) + + response_body = response.get_json() + + assert response.status_code == 201 + assert response_body == {"id": 2, "card_id": 3} + + +# @pytest.mark.skip(reason="Feature not yet built") +def test_get_all_cards_for_one_board(client, three_saved_cards_and_two_boards): + response = client.get("boards/1/cards") + + response_body = response.get_json() + + assert response.status_code == 200 + assert response_body == { + "id": 1, + "title": "Shakespeare Quotes", + "cards": [{"id": 1, "message": "To be, or not to be, that is the question", + "likes_count": 5}, {"id": 2, "message": "Romeo, Romeo! Wherefore art thou Romeo?", + "likes_count": 3}] + } + + +# @pytest.mark.skip(reason="Feature not yet built") +def test_get_cards_for_all_boards(client, three_saved_cards_and_two_boards): + response = client.get("/cards") + + response_body = response.get_json() + + assert response.status_code == 200 + assert len(response_body) == 3 + From a4b8f2abc2c139383a16fbe9825401e493edff1b Mon Sep 17 00:00:00 2001 From: areeg <127351347+arejib@users.noreply.github.com> Date: Mon, 3 Jul 2023 14:47:12 -0500 Subject: [PATCH 19/27] Added fixture to conftest.py and corresponding test for PATCH route --- tests/conftest.py | 9 +++++++++ tests/test_routes.py | 14 ++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7ef189b3..34e1f990 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -67,6 +67,15 @@ def three_saved_cards_and_two_boards(three_saved_cards, two_saved_boards): db.session.commit() +@pytest.fixture +def one_saved_card(app): + card_1 = Card(message="To be, or not to be, that is the question", + likes_count=1) + + + db.session.add_all([card_1]) + db.session.commit() + return card_1 diff --git a/tests/test_routes.py b/tests/test_routes.py index 1616ab92..8e76cd30 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,5 +1,3 @@ -# Routes for Board - from app.models.board import Board import pytest @@ -35,7 +33,6 @@ def test_delete_board(client): def test_delete_board_not_found(client): pass - # @pytest.mark.skip(reason="Feature not yet built") def test_create_card(client, three_saved_cards): test_data = {"message": "May the Force be with you.", "likes_count": 0} @@ -47,7 +44,6 @@ def test_create_card(client, three_saved_cards): assert response.status_code == 201 assert response_body == "Card successfully created" - # @pytest.mark.skip(reason="Feature not yet built") def test_create_card_for_one_board(client, three_saved_cards_and_two_boards): test_data = {"id": 3} @@ -59,7 +55,6 @@ def test_create_card_for_one_board(client, three_saved_cards_and_two_boards): assert response.status_code == 201 assert response_body == {"id": 2, "card_id": 3} - # @pytest.mark.skip(reason="Feature not yet built") def test_get_all_cards_for_one_board(client, three_saved_cards_and_two_boards): response = client.get("boards/1/cards") @@ -75,7 +70,6 @@ def test_get_all_cards_for_one_board(client, three_saved_cards_and_two_boards): "likes_count": 3}] } - # @pytest.mark.skip(reason="Feature not yet built") def test_get_cards_for_all_boards(client, three_saved_cards_and_two_boards): response = client.get("/cards") @@ -85,3 +79,11 @@ def test_get_cards_for_all_boards(client, three_saved_cards_and_two_boards): assert response.status_code == 200 assert len(response_body) == 3 +# @pytest.mark.skip(reason="Feature not yet built") +def test_likes_increments_by_one(client, one_saved_card): + response = client.patch("/cards/1") + response_body = response.get_json() + updated_likes = response_body["likes_count"] + + assert response.status_code == 200 + assert updated_likes == 2 \ No newline at end of file From 55cac890daaa233df41f41e4e2f4621741cdddd7 Mon Sep 17 00:00:00 2001 From: Angelica Date: Wed, 19 Jul 2023 19:03:59 -0500 Subject: [PATCH 20/27] connected seed card with seed commit --- seed_card.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/seed_card.py b/seed_card.py index 690829aa..5265c2de 100644 --- a/seed_card.py +++ b/seed_card.py @@ -1,11 +1,23 @@ from app import create_app, db from app.models.card import Card +from app.models.board import Board my_app = create_app() with my_app.app_context(): - db.session.add(Card(message="dont stay up late, it will make you old", likes_count=0)) - db.session.add(Card(message="a social media platform called fakebook", likes_count=0)) - db.session.add(Card(message="I think then I code", likes_count=0)) - db.session.add(Card(message="never leave your keys in the car", likes_count=0)) - db.session.add(Card(message="knock knock... nothing I dont know any jokes", likes_count=0)) + # Get the "reminders" board from the database + reminders_board = Board.query.filter_by(title="reminders").first() + capstone_ideas_board = Board.query.filter_by(title="capstone ideas").first() + quotes_board = Board.query.filter_by(title="quotes").first() + advice_board = Board.query.filter_by(title="advice").first() + jokes_board = Board.query.filter_by(title="jokes").first() + + # Create and add cards associated with the "reminders" board + db.session.add(Card(message="Buy groceries", likes_count=0, board_id=reminders_board.id)) + db.session.add(Card(message="Call Mom", likes_count=0, board_id=reminders_board.id)) + db.session.add(Card(message="Finish homework", likes_count=0, board_id=reminders_board.id)) + db.session.add(Card(message="dont stay up late, it will make you old", likes_count=0, board_id=advice_board.id)) + db.session.add(Card(message="a social media platform called fakebook", likes_count=0, board_id=capstone_ideas_board.id)) + db.session.add(Card(message="I think then I code", likes_count=0,board_id=quotes_board.id)) + db.session.add(Card(message="never leave your keys in the car", likes_count=0, board_id=advice_board.id)) + db.session.add(Card(message="knock knock... nothing I dont know any jokes", likes_count=0, board_id=jokes_board.id)) db.session.commit() \ No newline at end of file From 6593ff1d13c0e3447f214283df7aa97b7c609a32 Mon Sep 17 00:00:00 2001 From: areeg <127351347+arejib@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:52:21 -0500 Subject: [PATCH 21/27] Cleaned up formatting --- app/boards_routes.py | 8 -------- app/cards_routes.py | 5 ----- app/models/board.py | 3 +-- app/routes_helpers.py | 1 - tests/conftest.py | 9 +-------- 5 files changed, 2 insertions(+), 24 deletions(-) diff --git a/app/boards_routes.py b/app/boards_routes.py index f98bcf47..c1e537e9 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -8,11 +8,8 @@ import requests, json import os - -# example_bp = Blueprint('example_bp', __name__) boards_bp = Blueprint("boards", __name__, url_prefix="/boards") - @boards_bp.route("", methods=["POST"]) def create_board(): request_body = request.get_json() @@ -31,7 +28,6 @@ def create_board(): return make_response(jsonify(f"{new_board} successfully created!"), 201) - @boards_bp.route("", methods=["GET"]) def read_all_boards(): boards = Board.query.all() @@ -39,7 +35,6 @@ def read_all_boards(): return jsonify(boards_response) - @boards_bp.route("/", methods=["GET"]) def read_one_board(board_id): board = validate_model(Board, board_id) @@ -50,7 +45,6 @@ def read_one_board(board_id): return jsonify(response_body) - @boards_bp.route("/", methods=["DELETE"]) def delete_one_board(board_id): board = validate_model(Board, board_id) @@ -64,7 +58,6 @@ def delete_one_board(board_id): return jsonify(response_body) - @boards_bp.route("//cards", methods=["POST"]) def create_one_card_for_board(board_id): board = validate_model(Board, board_id) @@ -81,7 +74,6 @@ def create_one_card_for_board(board_id): "id": board.id, "card_id": request_body["id"]}, 201) - @boards_bp.route("//cards", methods=["GET"]) def get_cards_for_one_board(board_id): board = validate_model(Board, board_id) diff --git a/app/cards_routes.py b/app/cards_routes.py index 2db7dda1..3389307f 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -3,10 +3,8 @@ 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(): request_body = request.get_json() @@ -18,7 +16,6 @@ def create_card(): return make_response(jsonify("Card successfully created"), 201) - @cards_bp.route("", methods=["GET"]) def read_all_cards(): cards = Card.query.all() @@ -27,7 +24,6 @@ def read_all_cards(): return make_response(jsonify(cards_response), 200) - @cards_bp.route("/", methods=["DELETE"]) def delete_one_board(card_id): card = validate_model(Card, card_id) @@ -41,7 +37,6 @@ def delete_one_board(card_id): return jsonify(response_body) - @cards_bp.route("/", methods=["PATCH"]) def increase_like_count(card_id): card = validate_model(Card, card_id) diff --git a/app/models/board.py b/app/models/board.py index c586d554..38873c7c 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -5,8 +5,7 @@ class Board(db.Model): 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"]) diff --git a/app/routes_helpers.py b/app/routes_helpers.py index 154d4279..e69e7146 100644 --- a/app/routes_helpers.py +++ b/app/routes_helpers.py @@ -1,6 +1,5 @@ from flask import make_response, abort - def validate_model(cls, model_id): try: model_id = int(model_id) diff --git a/tests/conftest.py b/tests/conftest.py index 34e1f990..f145175e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,6 @@ from app.models.board import Board from app.models.card import Card - @pytest.fixture def app(): # create the app with a test config dictionary @@ -23,12 +22,10 @@ def expire_session(sender, response, **extra): with app.app_context(): db.drop_all() - @pytest.fixture def client(app): return app.test_client() - @pytest.fixture def two_saved_boards(app): shakespeare_board = Board(title="Shakespeare Quotes", @@ -42,7 +39,6 @@ def two_saved_boards(app): return shakespeare_board, movie_board - @pytest.fixture def three_saved_cards(app): card_1 = Card(message="To be, or not to be, that is the question", @@ -57,7 +53,6 @@ def three_saved_cards(app): return card_1, card_2, card_3 - @pytest.fixture def three_saved_cards_and_two_boards(three_saved_cards, two_saved_boards): card_1, card_2, card_3 = three_saved_cards @@ -76,6 +71,4 @@ def one_saved_card(app): db.session.add_all([card_1]) db.session.commit() - return card_1 - - + return card_1 \ No newline at end of file From 04a089ce14b8668f454ec35b78fbf5bf5db7607d Mon Sep 17 00:00:00 2001 From: Gabby Date: Thu, 20 Jul 2023 15:06:33 -0500 Subject: [PATCH 22/27] Refactor cards endpoint --- app/cards_routes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/cards_routes.py b/app/cards_routes.py index 2db7dda1..269360f8 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -16,7 +16,9 @@ def create_card(): db.session.add(new_card) db.session.commit() - return make_response(jsonify("Card successfully created"), 201) + new_card_id = new_card.id + + return make_response(jsonify({"id": new_card_id, "message": "Card successfully created"}), 201) @cards_bp.route("", methods=["GET"]) From 75c52157d575ed1d43602608215fc0f2ed5c7642 Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Thu, 20 Jul 2023 14:10:38 -0600 Subject: [PATCH 23/27] delete all boards route added --- app/boards_routes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/boards_routes.py b/app/boards_routes.py index c1e537e9..e2e983ef 100644 --- a/app/boards_routes.py +++ b/app/boards_routes.py @@ -58,6 +58,15 @@ def delete_one_board(board_id): 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("//cards", methods=["POST"]) def create_one_card_for_board(board_id): board = validate_model(Board, board_id) From 8b66e6a433baed78a5e0fe5202ceb77c3458cb7a Mon Sep 17 00:00:00 2001 From: Amber Shay Date: Thu, 20 Jul 2023 15:16:34 -0600 Subject: [PATCH 24/27] added delete all cards route --- app/cards_routes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/cards_routes.py b/app/cards_routes.py index fa2832db..526ce5ce 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -39,6 +39,15 @@ def delete_one_board(card_id): 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("/", methods=["PATCH"]) def increase_like_count(card_id): card = validate_model(Card, card_id) From 9e232e27b57ce0d04e1719a3e7e73b1d4a886377 Mon Sep 17 00:00:00 2001 From: Angelica Date: Thu, 20 Jul 2023 18:54:52 -0500 Subject: [PATCH 25/27] changed naming in cards delete and patch --- app/cards_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cards_routes.py b/app/cards_routes.py index 526ce5ce..c44d3b70 100644 --- a/app/cards_routes.py +++ b/app/cards_routes.py @@ -27,7 +27,7 @@ def read_all_cards(): return make_response(jsonify(cards_response), 200) @cards_bp.route("/", methods=["DELETE"]) -def delete_one_board(card_id): +def delete_one_card(card_id): card = validate_model(Card, card_id) response_body = { From 1905b107c4ac95d07cbc9c0462e226cffdc0da0c Mon Sep 17 00:00:00 2001 From: Angelica Date: Thu, 20 Jul 2023 19:57:57 -0500 Subject: [PATCH 26/27] missing tests for board added --- tests/test_routes.py | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/tests/test_routes.py b/tests/test_routes.py index 8e76cd30..cd1391e1 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -9,9 +9,19 @@ def test_get_board_no_board_saved(client): assert response.status.code == 200 assert response_body ==[] -@pytest.mark.skip(reason="Feature not yet built") +#@pytest.mark.skip(reason="Feature not yet built") def test_create_board(client): - pass + test_data = { + "id": 1, + "owner": "Angelica", + "title": "boardie" + } + response = client.post("/boards", json=test_data) + + response_body = response.get_json() + + assert response.status_code == 201 + assert response_body == " successfully created!" # @pytest.mark.skip(reason="Feature not yet built") def test_get_one_saved_board(client): @@ -25,13 +35,27 @@ def test_get_one_saved_board(client): "title": "The Keep on Keepin On Board" } -@pytest.mark.skip(reason="Feature not yet built") +#@pytest.mark.skip(reason="Feature not yet built") def test_delete_board(client): - pass + response = client.delete("/boards/1") + + response_body = response.get_json() -@pytest.mark.skip(reason="Feature not yet built") + assert response.status_code == 200 + assert response_body == { + "details": "Board 1 \"boardie\" successfully deleted" + } + +#@pytest.mark.skip(reason="Feature not yet built") def test_delete_board_not_found(client): - pass + response = client.delete("/boards/333") + + response_body = response.get_json() + + assert response.status_code == 404 + assert response_body == { + "message": "Board 333 not found" + } # @pytest.mark.skip(reason="Feature not yet built") def test_create_card(client, three_saved_cards): From 5e35bf299e7a22749c59172fe5d28dfb16dc3b60 Mon Sep 17 00:00:00 2001 From: Gabby Date: Thu, 20 Jul 2023 21:29:56 -0500 Subject: [PATCH 27/27] Connect to render db --- app/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 9a942c9a..291f65b1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -15,8 +15,9 @@ def create_app(test_config=None): app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False if test_config is None: - app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( - "SQLALCHEMY_DATABASE_URI") + 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