From c197008cf3f7f47185911c9e9c14f32e2d626c97 Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Mon, 8 Nov 2021 15:55:44 -0800 Subject: [PATCH 01/24] adds Customer and Video models --- app/models/customer.py | 6 +- app/models/video.py | 5 +- migrations/README | 1 + migrations/alembic.ini | 45 +++++++++ migrations/env.py | 96 +++++++++++++++++++ migrations/script.py.mako | 24 +++++ ...01abd76b_adds_video_and_customer_models.py | 48 ++++++++++ 7 files changed, 223 insertions(+), 2 deletions(-) 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/b79801abd76b_adds_video_and_customer_models.py diff --git a/app/models/customer.py b/app/models/customer.py index e3aece97b..9111f7314 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,4 +1,8 @@ from app import db class Customer(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String) + postal_code = db.Column(db.Integer) + phone_num = db.Column(db.Integer) + register_at = db.Column(db.DateTime) \ No newline at end of file diff --git a/app/models/video.py b/app/models/video.py index 9893a6ef9..caf4aa9d7 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -1,4 +1,7 @@ from app import db class Video(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String) + release_date = db.Column(db.DateTime) + total_inventory = db.Column(db.Integer) \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..98e4f9c44 --- /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 000000000..f8ed4801f --- /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 000000000..8b3fb3353 --- /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 000000000..2c0156303 --- /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/b79801abd76b_adds_video_and_customer_models.py b/migrations/versions/b79801abd76b_adds_video_and_customer_models.py new file mode 100644 index 000000000..ceee1c22c --- /dev/null +++ b/migrations/versions/b79801abd76b_adds_video_and_customer_models.py @@ -0,0 +1,48 @@ +"""adds Video and Customer models + +Revision ID: b79801abd76b +Revises: +Create Date: 2021-11-08 15:55:00.662842 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b79801abd76b' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('customer', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('postal_code', sa.Integer(), nullable=True), + sa.Column('phone_num', sa.Integer(), nullable=True), + sa.Column('register_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('rental', + sa.Column('id', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('video', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('release_date', sa.DateTime(), nullable=True), + sa.Column('total_inventory', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('video') + op.drop_table('rental') + op.drop_table('customer') + # ### end Alembic commands ### From dfa2b26b82bb502b77ce1dcb6a09cffb48c24e22 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Mon, 8 Nov 2021 21:07:33 -0800 Subject: [PATCH 02/24] modify Customers & Video Models. Add POST method to customers_routes.py --- app/__init__.py | 2 + app/customers_routes.py | 26 ++++++++++ app/models/customer.py | 15 ++++-- app/models/video.py | 2 +- app/routes.py | 0 ...01abd76b_adds_video_and_customer_models.py | 48 ------------------- 6 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 app/customers_routes.py delete mode 100644 app/routes.py delete mode 100644 migrations/versions/b79801abd76b_adds_video_and_customer_models.py diff --git a/app/__init__.py b/app/__init__.py index 4ab3975b8..96c5060e0 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -32,5 +32,7 @@ def create_app(test_config=None): migrate.init_app(app, db) #Register Blueprints Here + from app.customers_routes import customers_bp + app.register_blueprint(customers_bp) return app \ No newline at end of file diff --git a/app/customers_routes.py b/app/customers_routes.py new file mode 100644 index 000000000..fa58c9a42 --- /dev/null +++ b/app/customers_routes.py @@ -0,0 +1,26 @@ +from app import db +from app.models.customer import Customer +from flask import Blueprint, jsonify, request +from datetime import datetime + +customers_bp = Blueprint("customers_bp", __name__, url_prefix="/customers") + + +@customers_bp.route("", methods=["POST"]) +def customer_create(): + request_body = request.get_json() + + if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body or "registered_at" not in request_body: + return jsonify({"details": "Invalid data"}), 400 + + new_customer = Customer( + name=request_body["name"], + phone=request_body["phone"], + postal_code=request_body["postal_code"], + registered_at=request_body["registered_at"] + ) + db.session.add(new_customer) + db.session.commit() + + response_body = {new_customer.customer_dict()} + return jsonify(response_body), 201 diff --git a/app/models/customer.py b/app/models/customer.py index 9111f7314..c65f7d05e 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,8 +1,17 @@ from app import db class Customer(db.Model): - id = db.Column(db.Integer, primary_key=True) + customer_id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String) + phone = db.Column(db.String) postal_code = db.Column(db.Integer) - phone_num = db.Column(db.Integer) - register_at = db.Column(db.DateTime) \ No newline at end of file + registered_at = db.Column(db.DateTime) + + def customer_dict(self): + return { + "id": self.customer_id, + "name": self.name, + "phone": self.phone, + "postal_code": self.postal_code, + "registered_at": self.registered_at + } \ No newline at end of file diff --git a/app/models/video.py b/app/models/video.py index caf4aa9d7..b044fd03e 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -1,7 +1,7 @@ from app import db class Video(db.Model): - id = db.Column(db.Integer, primary_key=True) + video_id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String) release_date = db.Column(db.DateTime) total_inventory = db.Column(db.Integer) \ No newline at end of file diff --git a/app/routes.py b/app/routes.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/migrations/versions/b79801abd76b_adds_video_and_customer_models.py b/migrations/versions/b79801abd76b_adds_video_and_customer_models.py deleted file mode 100644 index ceee1c22c..000000000 --- a/migrations/versions/b79801abd76b_adds_video_and_customer_models.py +++ /dev/null @@ -1,48 +0,0 @@ -"""adds Video and Customer models - -Revision ID: b79801abd76b -Revises: -Create Date: 2021-11-08 15:55:00.662842 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'b79801abd76b' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('customer', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(), nullable=True), - sa.Column('postal_code', sa.Integer(), nullable=True), - sa.Column('phone_num', sa.Integer(), nullable=True), - sa.Column('register_at', sa.DateTime(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('rental', - sa.Column('id', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('video', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('title', sa.String(), nullable=True), - sa.Column('release_date', sa.DateTime(), nullable=True), - sa.Column('total_inventory', sa.Integer(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('video') - op.drop_table('rental') - op.drop_table('customer') - # ### end Alembic commands ### From 62088657c1f7c17e67de6b9333812af239a7ed90 Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Mon, 8 Nov 2021 21:11:14 -0800 Subject: [PATCH 03/24] adds Video endpoints for GET and starts POST --- app/__init__.py | 2 ++ app/models/customer.py | 2 +- app/models/video.py | 12 ++++++++++-- app/video_routes.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 app/video_routes.py diff --git a/app/__init__.py b/app/__init__.py index 4ab3975b8..b67448fc1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -32,5 +32,7 @@ def create_app(test_config=None): migrate.init_app(app, db) #Register Blueprints Here + from .video_routes import video_bp + app.register_blueprint(video_bp) return app \ No newline at end of file diff --git a/app/models/customer.py b/app/models/customer.py index 9111f7314..8f2f0ecc8 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,7 +1,7 @@ from app import db class Customer(db.Model): - id = db.Column(db.Integer, primary_key=True) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String) postal_code = db.Column(db.Integer) phone_num = db.Column(db.Integer) diff --git a/app/models/video.py b/app/models/video.py index caf4aa9d7..8177fad63 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -1,7 +1,15 @@ from app import db class Video(db.Model): - id = db.Column(db.Integer, primary_key=True) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String) release_date = db.Column(db.DateTime) - total_inventory = db.Column(db.Integer) \ No newline at end of file + total_inventory = db.Column(db.Integer) + + def to_dict(self): + return { + "id": self.id, + "title": self.title, + "release_date": self.release_date, + "total_inventory": self.total_inventory + } \ No newline at end of file diff --git a/app/video_routes.py b/app/video_routes.py new file mode 100644 index 000000000..f509e0d3c --- /dev/null +++ b/app/video_routes.py @@ -0,0 +1,28 @@ +from app.models.video import Video +from app import db +from flask import Blueprint, request, jsonify +from datetime import datetime +import os, requests, json + +video_bp = Blueprint("videos", __name__, url_prefix="/videos") + +@video_bp.route("", methods=["GET"]) +def get_videos(): + videos = Video.query.all() + if videos is None: + return jsonify([]) + response_body = [] + for video in videos: + response_body.append(video.to_dict()) + return jsonify(response_body) + +@video_bp.route("/", methods=["GET"]) +def get_video(video_id): + # if not video_id.is_integer(): + # return jsonify(), 400 + + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": "Video 1 was not found"}), 404 + response_body = video.to_dict() + return jsonify(response_body) \ No newline at end of file From 979373de00b445034be435887b6036a17db6e1ae Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Mon, 8 Nov 2021 21:53:05 -0800 Subject: [PATCH 04/24] edits Video's to_dict method and adds Video POST route --- app/models/video.py | 2 +- app/video_routes.py | 42 +++++++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/app/models/video.py b/app/models/video.py index 089c6400f..cf4435320 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -8,7 +8,7 @@ class Video(db.Model): def to_dict(self): return { - "id": self.id, + "id": self.video_id, "title": self.title, "release_date": self.release_date, "total_inventory": self.total_inventory diff --git a/app/video_routes.py b/app/video_routes.py index 93b44db15..c02093e4d 100644 --- a/app/video_routes.py +++ b/app/video_routes.py @@ -18,8 +18,11 @@ def get_videos(): @video_bp.route("/", methods=["GET"]) def get_video(video_id): - # if not video_id.is_integer(): - # return jsonify(), 400 + ## try + # if not isinstance(video_id, int): + ## or + # if not url_path.is_integer(): + # return jsonify(), 400 video = Video.query.get(video_id) if video is None: @@ -27,14 +30,27 @@ def get_video(video_id): response_body = video.to_dict() return jsonify(response_body) -# @video_bp.route("", methods=["POST"]) -# def create_video(): -# request_body = request.get_json() -# if "title" not in request_body or "release_date" not in request_body or "total_inventory" not in request_body: -# response_body = {} -# if "title" not in request_body: -# response_body["details"] = "Request body must include title." -# elif "release_date" not in request_body: -# response_body["details"] = "Request body must include release_date." -# elif "t"] -# return jsonify() +@video_bp.route("", methods=["POST"]) +def create_video(): + request_body = request.get_json() + if "title" not in request_body or "release_date" not in request_body or "total_inventory" not in request_body: + response_body = {} + if "title" not in request_body: + response_body["details"] = "Request body must include title." + elif "release_date" not in request_body: + response_body["details"] = "Request body must include release_date." + elif "total_inventory" not in request_body: + response_body["details"] = "Request body must include total_inventory" + return jsonify(response_body), 400 + + new_video = Video( + title=request_body["title"], + release_date=request_body["release_date"], + total_inventory=request_body["total_inventory"] + ) + db.session.add(new_video) + db.session.commit() + + response_body={} + response_body["id"] = new_video.video_id + return jsonify(response_body), 201 \ No newline at end of file From a5df82318f09fc536db2792f36382b6496ed0496 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Tue, 9 Nov 2021 12:50:33 -0800 Subject: [PATCH 05/24] modify Customer's Model and add lots of buggie functions. Don't hate me. lol --- app/__init__.py | 8 +- app/customer_routes.py | 86 +++++++++++++++++++ app/customers_routes.py | 26 ------ app/models/customer.py | 16 ++-- .../d4e5fb64e076_create_base_models.py | 48 +++++++++++ tests/test_wave_01.py | 37 ++++++-- 6 files changed, 178 insertions(+), 43 deletions(-) create mode 100644 app/customer_routes.py delete mode 100644 app/customers_routes.py create mode 100644 migrations/versions/d4e5fb64e076_create_base_models.py diff --git a/app/__init__.py b/app/__init__.py index 2fec5759b..c0f2d734c 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,6 +8,7 @@ migrate = Migrate() load_dotenv() + def create_app(test_config=None): app = Flask(__name__) app.url_map.strict_slashes = False @@ -21,7 +22,6 @@ def create_app(test_config=None): app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_TEST_DATABASE_URI") - # import models for Alembic Setup from app.models.customer import Customer from app.models.video import Video @@ -31,10 +31,10 @@ def create_app(test_config=None): db.init_app(app) migrate.init_app(app, db) - #Register Blueprints Here + # Register Blueprints Here from .video_routes import video_bp app.register_blueprint(video_bp) - from app.customers_routes import customers_bp + from .customer_routes import customers_bp app.register_blueprint(customers_bp) - return app \ No newline at end of file + return app diff --git a/app/customer_routes.py b/app/customer_routes.py new file mode 100644 index 000000000..616030a60 --- /dev/null +++ b/app/customer_routes.py @@ -0,0 +1,86 @@ +from app import db +from app.models.customer import Customer +from flask import Blueprint, jsonify, request +from datetime import datetime + +customers_bp = Blueprint("customers_bp", __name__, url_prefix="/customers") + + +@customers_bp.route("", methods=["POST"]) +def customer_create(): + request_body = request.get_json() + + # is the registered_at not required? + if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: + return jsonify({"details": "Invalid data"}), 400 + + new_customer = Customer( + name=request_body["name"], + phone=request_body["phone"], + postal_code=request_body["postal_code"], + registered_at=request_body["registered_at"] + ) + db.session.add(new_customer) + db.session.commit() + + # response_body = {new_customer.customer_dict()} + # return jsonify(response_body), 201 + return jsonify({"id": new_customer.customer_id}) + + +@customers_bp.route("", methods=["GET"]) +def handle_customers(): + customers = Customer.query.all() + response_body = [] + # for customer in customers: + # response_body.append({customers.customer_dict()}) + for customer in customers: + response_body.append({ + "id": customers.customer_id, + "name": customers.name, + # "registered_at": customers.registered_at, + "postal_code": customers.postal_code, + "phone": customers.phone +}) + + return jsonify(response_body), 200 + + +@customers_bp.route("/", methods=["GET"]) +def customer_get(customer_id): + customer = Customer.query.get(customer_id) + if customer == None: + return jsonify("Not Found", 404) + + return jsonify(customer.customer_dict), 200 + + +@customers_bp.route("/", methods=["PUT"]) +def customer_put(customer_id): + customer = Customer.query.get(customer_id) + + if customer == None: + return jsonify("Not Found", 404) + + request_body = request.get_json() + + customer.name = request_body["name"] + customer.phone = request_body["phone"] + customer.postal_code = request_body["postal_code"] + # is registered not required? + response_body = {customer.customer_dict} + + return jsonify(response_body), 200 + + +@customers_bp.route("/", methods=["DELETE"]) +def customer_delete(customer_id): + customer = Customer.query.get(customer_id) + + if customer == None: + return jsonify("Not Found", 404) + + db.session.delete(customer) + db.session.commit() + + return jsonify({"id": {customer_id}}) diff --git a/app/customers_routes.py b/app/customers_routes.py deleted file mode 100644 index fa58c9a42..000000000 --- a/app/customers_routes.py +++ /dev/null @@ -1,26 +0,0 @@ -from app import db -from app.models.customer import Customer -from flask import Blueprint, jsonify, request -from datetime import datetime - -customers_bp = Blueprint("customers_bp", __name__, url_prefix="/customers") - - -@customers_bp.route("", methods=["POST"]) -def customer_create(): - request_body = request.get_json() - - if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body or "registered_at" not in request_body: - return jsonify({"details": "Invalid data"}), 400 - - new_customer = Customer( - name=request_body["name"], - phone=request_body["phone"], - postal_code=request_body["postal_code"], - registered_at=request_body["registered_at"] - ) - db.session.add(new_customer) - db.session.commit() - - response_body = {new_customer.customer_dict()} - return jsonify(response_body), 201 diff --git a/app/models/customer.py b/app/models/customer.py index c65f7d05e..e0895d355 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,17 +1,21 @@ from app import db +from sqlalchemy.sql.functions import func class Customer(db.Model): customer_id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String) phone = db.Column(db.String) postal_code = db.Column(db.Integer) - registered_at = db.Column(db.DateTime) + # server_default tells sqlA to pass the default value as part of the CREATE TABLE + # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself + registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) def customer_dict(self): return { - "id": self.customer_id, - "name": self.name, - "phone": self.phone, - "postal_code": self.postal_code, - "registered_at": self.registered_at + "id": self.customer_id, + "name": self.name, + "phone": self.phone, + "postal_code": self.postal_code, + # weekday|day of month (16)|month name|year|local version of time|, UTC offset +HHMM or -HHMM + "registered_at": self.registered_at.strfttime("%a, %-d %b %Y %X %z") } \ No newline at end of file diff --git a/migrations/versions/d4e5fb64e076_create_base_models.py b/migrations/versions/d4e5fb64e076_create_base_models.py new file mode 100644 index 000000000..d8bac7d5f --- /dev/null +++ b/migrations/versions/d4e5fb64e076_create_base_models.py @@ -0,0 +1,48 @@ +"""create base models + +Revision ID: d4e5fb64e076 +Revises: +Create Date: 2021-11-09 07:19:35.458780 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd4e5fb64e076' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('customer', + sa.Column('customer_id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('phone', sa.String(), nullable=True), + sa.Column('postal_code', sa.Integer(), nullable=True), + sa.Column('registered_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('customer_id') + ) + op.create_table('rental', + sa.Column('id', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('video', + sa.Column('video_id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('release_date', sa.DateTime(), nullable=True), + sa.Column('total_inventory', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('video_id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('video') + op.drop_table('rental') + op.drop_table('customer') + # ### end Alembic commands ### diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 8d32038f2..d4cd447bf 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -17,6 +17,8 @@ # -------------------------------- # READ + + def test_get_videos_no_saved_videos(client): # Act response = client.get("/videos") @@ -26,6 +28,7 @@ def test_get_videos_no_saved_videos(client): assert response.status_code == 200 assert response_body == [] + def test_get_videos_one_saved_video(client, one_video): # Act response = client.get("/videos") @@ -38,6 +41,7 @@ def test_get_videos_one_saved_video(client, one_video): assert response_body[0]["id"] == VIDEO_ID assert response_body[0]["total_inventory"] == VIDEO_INVENTORY + def test_get_video(client, one_video): # Act response = client.get("/videos/1") @@ -49,6 +53,7 @@ def test_get_video(client, one_video): assert response_body["id"] == VIDEO_ID assert response_body["total_inventory"] == VIDEO_INVENTORY + def test_get_video_not_found(client): # Act response = client.get("/videos/1") @@ -58,6 +63,7 @@ def test_get_video_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Video 1 was not found"} + def test_get_invalid_video_id(client, one_video): # Act response = client.get("/videos/hello") @@ -89,6 +95,7 @@ def test_create_video(client): assert new_video assert new_video.title == VIDEO_TITLE + def test_create_video_must_contain_title(client): # Act response = client.post("/videos", json={ @@ -103,6 +110,7 @@ def test_create_video_must_contain_title(client): assert response.status_code == 400 assert Video.query.all() == [] + def test_create_video_must_contain_release_date(client): # Act response = client.post("/videos", json={ @@ -117,6 +125,7 @@ def test_create_video_must_contain_release_date(client): assert response.status_code == 400 assert Video.query.all() == [] + def test_create_video_must_contain_inventory(client): # Act response = client.post("/videos", json={ @@ -131,6 +140,8 @@ def test_create_video_must_contain_inventory(client): assert Video.query.all() == [] # DELETE + + def test_delete_video(client, one_video): # Act response = client.delete("/videos/1") @@ -142,6 +153,7 @@ def test_delete_video(client, one_video): assert response.status_code == 200 assert Video.query.get(1) == None + def test_delete_video_not_found(client): # Act response = client.delete("/videos/1") @@ -152,6 +164,7 @@ def test_delete_video_not_found(client): assert response.status_code == 404 assert Video.query.all() == [] + def test_update_video(client, one_video): # Act response = client.put("/videos/1", json={ @@ -171,6 +184,7 @@ def test_update_video(client, one_video): assert video.title == "Updated Video Title" assert video.total_inventory == 2 + def test_update_video_not_found(client): # Act response = client.put("/videos/1", json={ @@ -184,6 +198,7 @@ def test_update_video_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Video 1 was not found"} + def test_update_video_invalid_data(client, one_video): # Act response = client.put("/videos/1", json={ @@ -209,6 +224,7 @@ def test_get_customers_no_saved_customers(client): assert response.status_code == 200 assert response_body == [] + def test_get_customers_one_saved_customer(client, one_customer): # Act response = client.get("/customers") @@ -222,6 +238,7 @@ def test_get_customers_one_saved_customer(client, one_customer): assert response_body[0]["phone"] == CUSTOMER_PHONE assert response_body[0]["postal_code"] == CUSTOMER_POSTAL_CODE + def test_get_customer(client, one_customer): # Act response = client.get("/customers/1") @@ -234,6 +251,7 @@ def test_get_customer(client, one_customer): assert response_body["phone"] == CUSTOMER_PHONE assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE + def test_get_customer_not_found(client): # Act response = client.get("/customers/1") @@ -243,6 +261,7 @@ def test_get_customer_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Customer 1 was not found"} + def test_get_invalid_customer_id(client, one_customer): # Act response = client.get("/customers/hello") @@ -251,6 +270,8 @@ def test_get_invalid_customer_id(client, one_customer): assert response.status_code == 400 # CREATE + + def test_create_customer(client): # Act response = client.post("/customers", json={ @@ -272,6 +293,7 @@ def test_create_customer(client): assert new_customer.postal_code == CUSTOMER_POSTAL_CODE assert new_customer.phone == CUSTOMER_PHONE + def test_create_customer_must_contain_postal(client): # Act response = client.post("/customers", json={ @@ -286,6 +308,7 @@ def test_create_customer_must_contain_postal(client): assert response.status_code == 400 assert Customer.query.all() == [] + def test_create_customer_must_contain_name(client): # Act response = client.post("/customers", json={ @@ -300,6 +323,7 @@ def test_create_customer_must_contain_name(client): assert response.status_code == 400 assert Customer.query.all() == [] + def test_create_customer_must_contain_phone(client): # Act response = client.post("/customers", json={ @@ -314,6 +338,8 @@ def test_create_customer_must_contain_phone(client): assert Customer.query.all() == [] # DELETE + + def test_delete_customer(client, one_customer): # Act response = client.delete("/customers/1") @@ -324,6 +350,7 @@ def test_delete_customer(client, one_customer): assert response.status_code == 200 assert Customer.query.get(1) == None + def test_delete_customer_not_found(client): # Act response = client.delete("/customers/1") @@ -334,6 +361,7 @@ def test_delete_customer_not_found(client): assert response.status_code == 404 assert Customer.query.all() == [] + def test_update_customer(client, one_customer): # Act response = client.put("/customers/1", json={ @@ -353,7 +381,7 @@ def test_update_customer(client, one_customer): assert customer.name == f"Updated ${CUSTOMER_NAME}" assert customer.phone == f"Updated ${CUSTOMER_PHONE}" assert customer.postal_code == f"Updated ${CUSTOMER_POSTAL_CODE}" - + def test_update_customer_not_found(client): # Act @@ -368,6 +396,7 @@ def test_update_customer_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Customer 1 was not found"} + def test_update_customer_invalid_data(client, one_customer): # Act response = client.put("/customers/1", json={ @@ -377,9 +406,3 @@ def test_update_customer_invalid_data(client, one_customer): # Assert assert response.status_code == 400 - - - - - - From 871a7685108a19219d57e1fd396d629486a669ac Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Tue, 9 Nov 2021 12:52:02 -0800 Subject: [PATCH 06/24] edits POST video route and starts DELETE video route --- app/video_routes.py | 49 ++++++++++++++++++++++++++++--------------- tests/test_wave_01.py | 8 +------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/app/video_routes.py b/app/video_routes.py index c02093e4d..f1c1500e9 100644 --- a/app/video_routes.py +++ b/app/video_routes.py @@ -6,6 +6,7 @@ video_bp = Blueprint("videos", __name__, url_prefix="/videos") +# /videos routes @video_bp.route("", methods=["GET"]) def get_videos(): videos = Video.query.all() @@ -16,20 +17,6 @@ def get_videos(): response_body.append(video.to_dict()) return jsonify(response_body) -@video_bp.route("/", methods=["GET"]) -def get_video(video_id): - ## try - # if not isinstance(video_id, int): - ## or - # if not url_path.is_integer(): - # return jsonify(), 400 - - video = Video.query.get(video_id) - if video is None: - return jsonify({"message": "Video 1 was not found"}), 404 - response_body = video.to_dict() - return jsonify(response_body) - @video_bp.route("", methods=["POST"]) def create_video(): request_body = request.get_json() @@ -40,7 +27,7 @@ def create_video(): elif "release_date" not in request_body: response_body["details"] = "Request body must include release_date." elif "total_inventory" not in request_body: - response_body["details"] = "Request body must include total_inventory" + response_body["details"] = "Request body must include total_inventory." return jsonify(response_body), 400 new_video = Video( @@ -51,6 +38,34 @@ def create_video(): db.session.add(new_video) db.session.commit() - response_body={} + response_body=new_video.to_dict() response_body["id"] = new_video.video_id - return jsonify(response_body), 201 \ No newline at end of file + return jsonify(response_body), 201 + + + +# /videos/ routes +@video_bp.route("/", methods=["GET"]) +def get_video(video_id): + ## try + # if not isinstance(video_id, int): + ## or + # if not url_path.is_integer(): + # return jsonify(), 400 + + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + response_body = video.to_dict() + return jsonify(response_body) + +@video_bp.route("/", methods=["DELETE"]) +def delete_video(video_id): + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + db.session.delete(video) + db.session.commit() + + response_body = {"id":f"{video.video_id}"} + return jsonify(response_body) diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 8d32038f2..857092c75 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -376,10 +376,4 @@ def test_update_customer_invalid_data(client, one_customer): }) # Assert - assert response.status_code == 400 - - - - - - + assert response.status_code == 400 \ No newline at end of file From 8488bb1adf6b7416fb39ad558e8b376dabe02a3f Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Tue, 9 Nov 2021 13:07:56 -0800 Subject: [PATCH 07/24] adds f-string to DELETE customer route response body --- app/customer_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index 616030a60..f7138df32 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -83,4 +83,4 @@ def customer_delete(customer_id): db.session.delete(customer) db.session.commit() - return jsonify({"id": {customer_id}}) + return jsonify({"id": f"{customer.customer_id}"}) From df7485827c72cd7c4ddfbf04065db481c0ab4fb4 Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Tue, 9 Nov 2021 13:22:48 -0800 Subject: [PATCH 08/24] removes f string from DELETE customer route --- app/customer_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index f7138df32..4a342804a 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -83,4 +83,4 @@ def customer_delete(customer_id): db.session.delete(customer) db.session.commit() - return jsonify({"id": f"{customer.customer_id}"}) + return jsonify({"id": customer.customer_id}) \ No newline at end of file From 7a35f947c0ad3c9ed14a0ca87dd8cb4af99861b4 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Tue, 9 Nov 2021 16:04:15 -0800 Subject: [PATCH 09/24] make change to POST method. Still need more work. --- app/customer_routes.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index 616030a60..3c146e4d5 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -10,38 +10,37 @@ def customer_create(): request_body = request.get_json() - # is the registered_at not required? if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: return jsonify({"details": "Invalid data"}), 400 - + new_customer = Customer( name=request_body["name"], phone=request_body["phone"], postal_code=request_body["postal_code"], - registered_at=request_body["registered_at"] + registered_at=["registered_at"] ) db.session.add(new_customer) db.session.commit() # response_body = {new_customer.customer_dict()} # return jsonify(response_body), 201 - return jsonify({"id": new_customer.customer_id}) + return jsonify({"id": new_customer.customer_id}), 201 @customers_bp.route("", methods=["GET"]) def handle_customers(): customers = Customer.query.all() response_body = [] - # for customer in customers: - # response_body.append({customers.customer_dict()}) for customer in customers: - response_body.append({ - "id": customers.customer_id, - "name": customers.name, - # "registered_at": customers.registered_at, - "postal_code": customers.postal_code, - "phone": customers.phone -}) + response_body.append({customers.customer_dict()}) +# for customer in customers: +# response_body.append({ +# "id": customers.customer_id, +# "name": customers.name, +# # "registered_at": customers.registered_at, +# "postal_code": customers.postal_code, +# "phone": customers.phone +# }) return jsonify(response_body), 200 From 8a30508e833a9d34784dc821094d9c70105161be Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Tue, 9 Nov 2021 17:11:16 -0800 Subject: [PATCH 10/24] adds PUT video route --- app/video_routes.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/app/video_routes.py b/app/video_routes.py index f1c1500e9..f8aa4c888 100644 --- a/app/video_routes.py +++ b/app/video_routes.py @@ -50,8 +50,9 @@ def get_video(video_id): ## try # if not isinstance(video_id, int): ## or - # if not url_path.is_integer(): - # return jsonify(), 400 + # if not video_id.is_integer(): + # return jsonify(), 400 + # maybe try-except? for invalid id test video = Video.query.get(video_id) if video is None: @@ -59,6 +60,24 @@ def get_video(video_id): response_body = video.to_dict() return jsonify(response_body) +@video_bp.route("/", methods=["PUT"]) +def update_video(video_id): + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + + request_body = request.get_json() + if "title" not in request_body or "release_date" not in request_body or "total_inventory" not in request_body: + return jsonify(), 400 + + video.title = request_body["title"] + video.release_date = request_body["release_date"] + video.total_inventory = request_body["total_inventory"] + db.session.commit() + + response_body = video.to_dict() + return jsonify(response_body) + @video_bp.route("/", methods=["DELETE"]) def delete_video(video_id): video = Video.query.get(video_id) @@ -67,5 +86,5 @@ def delete_video(video_id): db.session.delete(video) db.session.commit() - response_body = {"id":f"{video.video_id}"} - return jsonify(response_body) + response_body = {"id": video.video_id} + return jsonify(response_body) \ No newline at end of file From ce7116cc367de7e7d84c96ab319e21210bf0d5a8 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Tue, 9 Nov 2021 19:25:23 -0800 Subject: [PATCH 11/24] made change to POST and GET methods. PUT is work in progress --- app/customer_routes.py | 45 +++++++++++++++++++++++++++++++----------- app/models/customer.py | 12 +++++++---- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index 18cc5e9c5..8e5ac11fa 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -11,7 +11,14 @@ def customer_create(): request_body = request.get_json() if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: - return jsonify({"details": "Invalid data"}), 400 + response_body ={} + if "name" not in request_body: + response_body["details"] = "Request body must include name." + elif "phone" not in request_body: + response_body["details"] = "Request body must include phone." + elif "postal_code" not in request_body: + response_body["details"] = "Request body must include postal_code." + return jsonify(response_body), 400 new_customer = Customer( name=request_body["name"], @@ -24,7 +31,8 @@ def customer_create(): # response_body = {new_customer.customer_dict()} # return jsonify(response_body), 201 - return jsonify({"id": new_customer.customer_id}), 201 + # return jsonify({"id": new_customer.customer_id}), 201 + return jsonify({"parameters": new_customer.customer_id}), 201 @customers_bp.route("", methods=["GET"]) @@ -32,7 +40,7 @@ def handle_customers(): customers = Customer.query.all() response_body = [] for customer in customers: - response_body.append({customers.customer_dict()}) + response_body.append(customer.customer_dict()) # for customer in customers: # response_body.append({ # "id": customers.customer_id, @@ -48,12 +56,13 @@ def handle_customers(): @customers_bp.route("/", methods=["GET"]) def customer_get(customer_id): customer = Customer.query.get(customer_id) - if customer == None: - return jsonify("Not Found", 404) - - return jsonify(customer.customer_dict), 200 + if customer is None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 + + return jsonify(customer.customer_dict()), 200 +# WORK IN PROGRESS @customers_bp.route("/", methods=["PUT"]) def customer_put(customer_id): customer = Customer.query.get(customer_id) @@ -66,10 +75,22 @@ def customer_put(customer_id): customer.name = request_body["name"] customer.phone = request_body["phone"] customer.postal_code = request_body["postal_code"] - # is registered not required? - response_body = {customer.customer_dict} - return jsonify(response_body), 200 + + if "name" in request_body or "phone" in request_body or "postal_code" in request_body: + response_body ={} + if "name" in request_body: + response_body = f"Updated ${customer.name}" + elif "phone" in request_body: + response_body = f"Updated ${customer.phone}" + elif "postal_code" in request_body: + response_body = f"Updated ${customer.postal_code}" + return response_body + + + # response_body = customer.customer_dict() + + # return jsonify(response_body), 200 @customers_bp.route("/", methods=["DELETE"]) @@ -77,9 +98,9 @@ def customer_delete(customer_id): customer = Customer.query.get(customer_id) if customer == None: - return jsonify("Not Found", 404) + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 db.session.delete(customer) db.session.commit() - return jsonify({"id": customer.customer_id}) \ No newline at end of file + return jsonify({"id": customer.customer_id}), 200 \ No newline at end of file diff --git a/app/models/customer.py b/app/models/customer.py index e0895d355..b7a3f274e 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -11,11 +11,15 @@ class Customer(db.Model): registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) def customer_dict(self): - return { + dict = { "id": self.customer_id, "name": self.name, "phone": self.phone, - "postal_code": self.postal_code, + "postal_code": str(self.postal_code), # weekday|day of month (16)|month name|year|local version of time|, UTC offset +HHMM or -HHMM - "registered_at": self.registered_at.strfttime("%a, %-d %b %Y %X %z") - } \ No newline at end of file + "registered_at": self.registered_at.strftime("%a, %-d %b %Y %X %z") + } + # if self.registered_at is not None: + # dict["registered_at"] = self.registered_at.strfttime("%a, %-d %b %Y %X %z") + + return dict \ No newline at end of file From e361dce2260e34e062800f0bdc3106302fb7e7ce Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Tue, 9 Nov 2021 19:29:32 -0800 Subject: [PATCH 12/24] adds 201 to POST customer HTTP response --- app/customer_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index 4a342804a..dc5a63cee 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -25,7 +25,7 @@ def customer_create(): # response_body = {new_customer.customer_dict()} # return jsonify(response_body), 201 - return jsonify({"id": new_customer.customer_id}) + return jsonify({"id": new_customer.customer_id}), 201 @customers_bp.route("", methods=["GET"]) From ea9febeb959777e286ae27ac35d5657ce4d5f44b Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Tue, 9 Nov 2021 22:19:03 -0800 Subject: [PATCH 13/24] debugs Customer routes. Code passing 20 tests --- app/customer_routes.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index dc5a63cee..c34896155 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -11,8 +11,12 @@ def customer_create(): request_body = request.get_json() # is the registered_at not required? - if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: - return jsonify({"details": "Invalid data"}), 400 + if "name" not in request_body: + return jsonify({"details": "Request body must include name."}), 400 + elif "phone" not in request_body: + return jsonify({"details": "Request body must include phone."}), 400 + elif "postal_code" not in request_body: + return jsonify({"details": "Request body must include postal_code."}), 400 new_customer = Customer( name=request_body["name"], @@ -36,11 +40,11 @@ def handle_customers(): # response_body.append({customers.customer_dict()}) for customer in customers: response_body.append({ - "id": customers.customer_id, - "name": customers.name, + "id": customer.customer_id, + "name": customer.name, # "registered_at": customers.registered_at, - "postal_code": customers.postal_code, - "phone": customers.phone + "postal_code": customer.postal_code, + "phone": customer.phone }) return jsonify(response_body), 200 @@ -49,10 +53,10 @@ def handle_customers(): @customers_bp.route("/", methods=["GET"]) def customer_get(customer_id): customer = Customer.query.get(customer_id) - if customer == None: - return jsonify("Not Found", 404) + if customer is None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 - return jsonify(customer.customer_dict), 200 + return jsonify(customer.customer_dict()), 200 @customers_bp.route("/", methods=["PUT"]) @@ -60,7 +64,7 @@ def customer_put(customer_id): customer = Customer.query.get(customer_id) if customer == None: - return jsonify("Not Found", 404) + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 request_body = request.get_json() @@ -68,7 +72,7 @@ def customer_put(customer_id): customer.phone = request_body["phone"] customer.postal_code = request_body["postal_code"] # is registered not required? - response_body = {customer.customer_dict} + response_body = {customer.customer_dict()} return jsonify(response_body), 200 From 0131c77502ee93ac8a2fdec99d8169f8470a2c93 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Wed, 10 Nov 2021 09:21:29 -0800 Subject: [PATCH 14/24] make change to GET methods --- app/customer_routes.py | 60 ++++++++++++++++++++---------------------- app/models/customer.py | 7 ++--- tests/test_wave_01.py | 9 +++++-- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index 8e5ac11fa..6347e109e 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -24,15 +24,12 @@ def customer_create(): name=request_body["name"], phone=request_body["phone"], postal_code=request_body["postal_code"], - registered_at=["registered_at"] + ) db.session.add(new_customer) db.session.commit() - # response_body = {new_customer.customer_dict()} - # return jsonify(response_body), 201 - # return jsonify({"id": new_customer.customer_id}), 201 - return jsonify({"parameters": new_customer.customer_id}), 201 + return jsonify({"id": new_customer.customer_id}), 201 @customers_bp.route("", methods=["GET"]) @@ -41,21 +38,17 @@ def handle_customers(): response_body = [] for customer in customers: response_body.append(customer.customer_dict()) -# for customer in customers: -# response_body.append({ -# "id": customers.customer_id, -# "name": customers.name, -# # "registered_at": customers.registered_at, -# "postal_code": customers.postal_code, -# "phone": customers.phone -# }) return jsonify(response_body), 200 @customers_bp.route("/", methods=["GET"]) def customer_get(customer_id): - customer = Customer.query.get(customer_id) + try: + customer = Customer.query.get(customer_id) + except: + return jsonify({"message": f"Customer {customer_id} was not found"}), 400 + if customer is None: return jsonify({"message": f"Customer {customer_id} was not found"}), 404 @@ -65,32 +58,37 @@ def customer_get(customer_id): # WORK IN PROGRESS @customers_bp.route("/", methods=["PUT"]) def customer_put(customer_id): - customer = Customer.query.get(customer_id) + # customer = Customer.query.get(customer_id) + try: + customer = Customer.query.get(customer_id) + except: + return jsonify({"message": f"Customer {customer_id} was not found"}), 400 if customer == None: - return jsonify("Not Found", 404) + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 request_body = request.get_json() - customer.name = request_body["name"] - customer.phone = request_body["phone"] - customer.postal_code = request_body["postal_code"] + if "name" in request_body: + customer.name = request_body["name"] + if "phone" in request_body: + customer.phone = request_body["phone"] + if "postal_code" in request_body: + customer.postal_code = request_body["postal_code"] - if "name" in request_body or "phone" in request_body or "postal_code" in request_body: response_body ={} - if "name" in request_body: - response_body = f"Updated ${customer.name}" - elif "phone" in request_body: - response_body = f"Updated ${customer.phone}" - elif "postal_code" in request_body: - response_body = f"Updated ${customer.postal_code}" - return response_body - - - # response_body = customer.customer_dict() + response_body["name"] = customer.name + response_body["phone"] = customer.phone + response_body["postal_code"] = customer.postal_code - # return jsonify(response_body), 200 + # db.session.commit() + try: + db.session.commit() + except: + return jsonify("Invalid"), 400 + + return jsonify(response_body), 200 @customers_bp.route("/", methods=["DELETE"]) diff --git a/app/models/customer.py b/app/models/customer.py index b7a3f274e..a91b984ac 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -5,7 +5,7 @@ class Customer(db.Model): customer_id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String) phone = db.Column(db.String) - postal_code = db.Column(db.Integer) + postal_code = db.Column(db.String) # server_default tells sqlA to pass the default value as part of the CREATE TABLE # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) @@ -15,11 +15,12 @@ def customer_dict(self): "id": self.customer_id, "name": self.name, "phone": self.phone, - "postal_code": str(self.postal_code), + "postal_code": self.postal_code, # weekday|day of month (16)|month name|year|local version of time|, UTC offset +HHMM or -HHMM "registered_at": self.registered_at.strftime("%a, %-d %b %Y %X %z") } # if self.registered_at is not None: # dict["registered_at"] = self.registered_at.strfttime("%a, %-d %b %Y %X %z") - return dict \ No newline at end of file + return dict + diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index d4cd447bf..39346455a 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -10,6 +10,7 @@ CUSTOMER_NAME = "A Brand New Customer" CUSTOMER_ID = 1 CUSTOMER_POSTAL_CODE = "12345" +# CUSTOMER_POSTAL_CODE = 12345 CUSTOMER_PHONE = "123-123-1234" # -------------------------------- @@ -236,7 +237,7 @@ def test_get_customers_one_saved_customer(client, one_customer): assert response_body[0]["name"] == CUSTOMER_NAME assert response_body[0]["id"] == CUSTOMER_ID assert response_body[0]["phone"] == CUSTOMER_PHONE - assert response_body[0]["postal_code"] == CUSTOMER_POSTAL_CODE + assert response_body[0]["postal_code"] == CUSTOMER_POSTAL_CODE #another string def test_get_customer(client, one_customer): @@ -249,7 +250,7 @@ def test_get_customer(client, one_customer): assert response_body["name"] == CUSTOMER_NAME assert response_body["id"] == CUSTOMER_ID assert response_body["phone"] == CUSTOMER_PHONE - assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE + assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE #another string def test_get_customer_not_found(client): @@ -367,7 +368,9 @@ def test_update_customer(client, one_customer): response = client.put("/customers/1", json={ "name": f"Updated ${CUSTOMER_NAME}", "phone": f"Updated ${CUSTOMER_PHONE}", + # postal_code data type is an integer but the test case is passing in a string. "postal_code": f"Updated ${CUSTOMER_POSTAL_CODE}" + # "postal_code": CUSTOMER_POSTAL_CODE+5 }) response_body = response.get_json() @@ -376,11 +379,13 @@ def test_update_customer(client, one_customer): assert response_body["name"] == f"Updated ${CUSTOMER_NAME}" assert response_body["phone"] == f"Updated ${CUSTOMER_PHONE}" assert response_body["postal_code"] == f"Updated ${CUSTOMER_POSTAL_CODE}" + # assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE+5 customer = Customer.query.get(1) assert customer.name == f"Updated ${CUSTOMER_NAME}" assert customer.phone == f"Updated ${CUSTOMER_PHONE}" assert customer.postal_code == f"Updated ${CUSTOMER_POSTAL_CODE}" + # assert customer.postal_code == CUSTOMER_POSTAL_CODE+5 def test_update_customer_not_found(client): From c9aca981d393a162939932494fbe5a4f18d072cd Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Wed, 10 Nov 2021 10:10:36 -0800 Subject: [PATCH 15/24] pair programs with Z to get all wave 1 tests --- app/customer_routes.py | 30 +++++++----------------------- app/video_routes.py | 8 +++++--- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/app/customer_routes.py b/app/customer_routes.py index 5c9c62374..90caf77d4 100644 --- a/app/customer_routes.py +++ b/app/customer_routes.py @@ -59,29 +59,17 @@ def customer_get(customer_id): # WORK IN PROGRESS @customers_bp.route("/", methods=["PUT"]) def customer_put(customer_id): - # customer = Customer.query.get(customer_id) - try: - customer = Customer.query.get(customer_id) - except: - return jsonify({"message": f"Customer {customer_id} was not found"}), 400 - + customer = Customer.query.get(customer_id) if customer == None: return jsonify({"message": f"Customer {customer_id} was not found"}), 404 request_body = request.get_json() + if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: + return jsonify(), 400 - # merge conflict - # customer.name = request_body["name"] - # customer.phone = request_body["phone"] - # customer.postal_code = request_body["postal_code"] - # # is registered not required? - # response_body = {customer.customer_dict()} - # if "name" in request_body: - # customer.name = request_body["name"] - # if "phone" in request_body: - # customer.phone = request_body["phone"] - # if "postal_code" in request_body: - # customer.postal_code = request_body["postal_code"] + customer.name = request_body["name"] + customer.phone = request_body["phone"] + customer.postal_code = request_body["postal_code"] if "name" in request_body or "phone" in request_body or "postal_code" in request_body: response_body ={} @@ -89,11 +77,7 @@ def customer_put(customer_id): response_body["phone"] = customer.phone response_body["postal_code"] = customer.postal_code - # db.session.commit() - try: - db.session.commit() - except: - return jsonify("Invalid"), 400 + db.session.commit() return jsonify(response_body), 200 diff --git a/app/video_routes.py b/app/video_routes.py index f8aa4c888..96b1e9771 100644 --- a/app/video_routes.py +++ b/app/video_routes.py @@ -52,9 +52,11 @@ def get_video(video_id): ## or # if not video_id.is_integer(): # return jsonify(), 400 - # maybe try-except? for invalid id test - - video = Video.query.get(video_id) + # maybe try-except? for invalid id test + try: + video = Video.query.get(video_id) + except: + return jsonify(), 400 if video is None: return jsonify({"message": f"Video {video_id} was not found"}), 404 response_body = video.to_dict() From de5f440588f66868a1d435d7d16e5638f1f1136e Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Wed, 10 Nov 2021 21:06:45 -0800 Subject: [PATCH 16/24] add rental model and pseudocode checkout route --- app/__init__.py | 2 ++ app/models/customer.py | 2 ++ app/models/rental.py | 6 +++++- app/models/video.py | 1 + app/rental_routes.py | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/rental_routes.py diff --git a/app/__init__.py b/app/__init__.py index c0f2d734c..8f89d9ee1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -36,5 +36,7 @@ def create_app(test_config=None): app.register_blueprint(video_bp) from .customer_routes import customers_bp app.register_blueprint(customers_bp) + from .rental_routes import rentals_bp + app.register_blueprint(rentals_bp) return app diff --git a/app/models/customer.py b/app/models/customer.py index a91b984ac..ff8f4df11 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -9,6 +9,8 @@ class Customer(db.Model): # server_default tells sqlA to pass the default value as part of the CREATE TABLE # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) + #I DON'T KNOW IF THIS IS CORRECT + videos = db.relationship("Video", secondary="rentals", backref="customers") def customer_dict(self): dict = { diff --git a/app/models/rental.py b/app/models/rental.py index 11009e593..5e72e6155 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -1,4 +1,8 @@ from app import db +from datetime import datetime, timedelta class Rental(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + rental_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + customer_id = db.Column(db.Integer, db.ForeignKey('customer.customer_id'), primary_key=True,nullable=False) + video_id = db.Column(db.Integer, db.ForeignKey('video.video_id'), primary_key=True,nullable=False) + due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + datetime.timedelta(days = 7)) \ No newline at end of file diff --git a/app/models/video.py b/app/models/video.py index cf4435320..c169036a5 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -5,6 +5,7 @@ class Video(db.Model): title = db.Column(db.String) release_date = db.Column(db.DateTime) total_inventory = db.Column(db.Integer) + def to_dict(self): return { diff --git a/app/rental_routes.py b/app/rental_routes.py new file mode 100644 index 000000000..fe241ce07 --- /dev/null +++ b/app/rental_routes.py @@ -0,0 +1,36 @@ +from app import db +from customer_routes import customers_bp +from video_routes import video_bp +from app.models.customer import Customer +from app.models.video import Video +from app.models.rental import Rental +from flask import Blueprint, jsonify, request +from datetime import datetime + +rentals_bp = Blueprint("rentals_bp", __name__, url_prefix="/rentals") + +@rentals_bp.route("/check-out", methods=["POST"]) +def rentals_checkout(): + request_body = request.get_sjson() + """ + get request body from client + check to make sure required attributes are in request body + Instantiate a new instance for rental + ?customer.videos or video.customers? + add rental instance to database + commit to database + return the response body and status code + + """ + +@rentals_bp.route("/check-in", methods=["POST"]) +def rentals_checkin(): + pass + +@customers_bp.route("//rentals", methods=["GET"]) +def customer_read(): + pass + +@video_bp.route("//rentals", methods=["GET"]) +def video_read(): + pass \ No newline at end of file From a55248d03cb5f4e9563802e49ad4107c55fedf33 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Thu, 11 Nov 2021 12:00:03 -0800 Subject: [PATCH 17/24] add rentals_checkout function. of course more garbage code. --- app/models/customer.py | 4 ++- app/models/rental.py | 4 ++- app/models/video.py | 2 +- app/rental_routes.py | 66 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/app/models/customer.py b/app/models/customer.py index ff8f4df11..42c454c75 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -10,8 +10,10 @@ class Customer(db.Model): # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) #I DON'T KNOW IF THIS IS CORRECT - videos = db.relationship("Video", secondary="rentals", backref="customers") + videos = db.relationship("Video", secondary="rental", backref="customers") + #videos = db.relationship("Rental", back_populates="customer") + def customer_dict(self): dict = { "id": self.customer_id, diff --git a/app/models/rental.py b/app/models/rental.py index 5e72e6155..d3a9a851c 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -5,4 +5,6 @@ class Rental(db.Model): rental_id = db.Column(db.Integer, primary_key=True, autoincrement=True) customer_id = db.Column(db.Integer, db.ForeignKey('customer.customer_id'), primary_key=True,nullable=False) video_id = db.Column(db.Integer, db.ForeignKey('video.video_id'), primary_key=True,nullable=False) - due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + datetime.timedelta(days = 7)) \ No newline at end of file + due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + timedelta(days = 7)) + #video = db.relationship("Video", back_populates="customers") + #customer = db.releationship("Customer", back_populates="videos") \ No newline at end of file diff --git a/app/models/video.py b/app/models/video.py index c169036a5..9251bc50b 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -5,7 +5,7 @@ class Video(db.Model): title = db.Column(db.String) release_date = db.Column(db.DateTime) total_inventory = db.Column(db.Integer) - + #customers = db.relationship("Rental", back_populates="video") def to_dict(self): return { diff --git a/app/rental_routes.py b/app/rental_routes.py index fe241ce07..6ed5f2791 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -1,6 +1,6 @@ from app import db -from customer_routes import customers_bp -from video_routes import video_bp +from app.customer_routes import customers_bp +from app.video_routes import video_bp from app.models.customer import Customer from app.models.video import Video from app.models.rental import Rental @@ -11,17 +11,56 @@ @rentals_bp.route("/check-out", methods=["POST"]) def rentals_checkout(): - request_body = request.get_sjson() - """ - get request body from client - check to make sure required attributes are in request body - Instantiate a new instance for rental - ?customer.videos or video.customers? - add rental instance to database - commit to database - return the response body and status code + #get request body from client + #{"c_id": [1], "v_id": [4]} + request_body = request.get_json() + + customer = Customer.query.get(request_body["customer_id"]) + if customer is None: + return jsonify(), 404 + + video = Customer.query.get(request_body["video_id"]) + if video is None: + return jsonify(), 404 + + # another if statement for video + + # check to make sure required attributes are in request body + if "customer_id" not in request_body or "video_id" not in request_body: + return jsonify(), 400 + + + # ?customer.videos or video.customers? + # customer_ids = [] + # for customer_id in request_body["customer_id"]: + # customer_ids.append(Customer.query.get(customer_id)) - """ + + # video_ids = [] + # for video_id in request_body["video_id"]: + # video_ids.append(Video.query.get(video_id)) + + + # Instantiate a new instance for rental + new_rental = Rental( + video_id=video.video_id, + customer_id=customer.customer_id + ) + + # add rental instance to database + db.session.add(new_rental) + # commit to database + db.session.commit() + + # return the response body and status code + return jsonify({ + "video_id": new_rental.video_id, + "customer_id": new_rental.cusomer_id, + "videos_checked_out_count": len(customer.videos), + "available_inventory": video.total_inventory - len(video.customers) + }), 200 + + @rentals_bp.route("/check-in", methods=["POST"]) def rentals_checkin(): @@ -33,4 +72,7 @@ def customer_read(): @video_bp.route("//rentals", methods=["GET"]) def video_read(): + # videos_list = [] + # for video in self.videos: + # videos_list(video.rental_id) pass \ No newline at end of file From 73c188b11c7c8f6a13522108351856b86366e2f4 Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Thu, 11 Nov 2021 12:00:57 -0800 Subject: [PATCH 18/24] messes around with models --- app/models/customer.py | 2 +- app/models/rental.py | 4 ++- app/models/video.py | 1 + app/rental_routes.py | 56 ++++++++++++++++++++++++------------------ 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/app/models/customer.py b/app/models/customer.py index ff8f4df11..478aaa787 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -10,7 +10,7 @@ class Customer(db.Model): # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) #I DON'T KNOW IF THIS IS CORRECT - videos = db.relationship("Video", secondary="rentals", backref="customers") + videos = db.relationship("Rental", back_populates="customer") def customer_dict(self): dict = { diff --git a/app/models/rental.py b/app/models/rental.py index 5e72e6155..95e9bf2e2 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -5,4 +5,6 @@ class Rental(db.Model): rental_id = db.Column(db.Integer, primary_key=True, autoincrement=True) customer_id = db.Column(db.Integer, db.ForeignKey('customer.customer_id'), primary_key=True,nullable=False) video_id = db.Column(db.Integer, db.ForeignKey('video.video_id'), primary_key=True,nullable=False) - due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + datetime.timedelta(days = 7)) \ No newline at end of file + due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + timedelta(days = 7)) + video = db.relationship("Video", back_populates="customers") + customer = db.relationship("Customer", back_populates="videos") \ No newline at end of file diff --git a/app/models/video.py b/app/models/video.py index c169036a5..62ae75950 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -5,6 +5,7 @@ class Video(db.Model): title = db.Column(db.String) release_date = db.Column(db.DateTime) total_inventory = db.Column(db.Integer) + customers = db.relationship("Rental", back_populates="video") def to_dict(self): diff --git a/app/rental_routes.py b/app/rental_routes.py index fe241ce07..a4d37caee 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -1,6 +1,7 @@ +from flask.wrappers import Response from app import db -from customer_routes import customers_bp -from video_routes import video_bp +from app.customer_routes import customers_bp +from app.video_routes import video_bp from app.models.customer import Customer from app.models.video import Video from app.models.rental import Rental @@ -9,28 +10,35 @@ rentals_bp = Blueprint("rentals_bp", __name__, url_prefix="/rentals") -@rentals_bp.route("/check-out", methods=["POST"]) -def rentals_checkout(): - request_body = request.get_sjson() - """ - get request body from client - check to make sure required attributes are in request body - Instantiate a new instance for rental - ?customer.videos or video.customers? - add rental instance to database - commit to database - return the response body and status code - - """ +# @rentals_bp.route("/check-out", methods=["POST"]) +# def rentals_checkout(): +# request_body = request.get_sjson() +# """ +# get request body from client +# check to make sure required attributes are in request body +# Instantiate a new instance for rental +# ?customer.videos or video.customers? +# add rental instance to database +# commit to database +# return the response body and status code +# """ -@rentals_bp.route("/check-in", methods=["POST"]) -def rentals_checkin(): - pass +# # response body might look something like this: +# { +# "customer_id": rental.customer_id, +# "video_id": rental.video_id, +# "videos_checked_out_count": rental.customer.videos, +# "available_inventory": video.total_inventory - video.customers +# } -@customers_bp.route("//rentals", methods=["GET"]) -def customer_read(): - pass +# @rentals_bp.route("/check-in", methods=["POST"]) +# def rentals_checkin(): +# pass -@video_bp.route("//rentals", methods=["GET"]) -def video_read(): - pass \ No newline at end of file +# @customers_bp.route("//rentals", methods=["GET"]) +# def customer_read(): +# pass + +# @video_bp.route("//rentals", methods=["GET"]) +# def video_read(): +# pass \ No newline at end of file From f4b07cc4a3103ab5519ff159f47d94f254ba2a3c Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Thu, 11 Nov 2021 12:33:12 -0800 Subject: [PATCH 19/24] add checkout route. need to pass 1 more test --- app/rental_routes.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/app/rental_routes.py b/app/rental_routes.py index 6ed5f2791..58c97c878 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -11,36 +11,23 @@ @rentals_bp.route("/check-out", methods=["POST"]) def rentals_checkout(): - #get request body from client #{"c_id": [1], "v_id": [4]} request_body = request.get_json() + if "customer_id" not in request_body or "video_id" not in request_body: + return jsonify(), 400 + customer = Customer.query.get(request_body["customer_id"]) if customer is None: return jsonify(), 404 - video = Customer.query.get(request_body["video_id"]) + video = Video.query.get(request_body["video_id"]) if video is None: return jsonify(), 404 - # another if statement for video - - # check to make sure required attributes are in request body - if "customer_id" not in request_body or "video_id" not in request_body: - return jsonify(), 400 - - - # ?customer.videos or video.customers? - # customer_ids = [] - # for customer_id in request_body["customer_id"]: - # customer_ids.append(Customer.query.get(customer_id)) - - - # video_ids = [] - # for video_id in request_body["video_id"]: - # video_ids.append(Video.query.get(video_id)) + if video.total_inventory == 0: + return jsonify({"message": "Could not perform checkout"}), 400 - # Instantiate a new instance for rental new_rental = Rental( video_id=video.video_id, @@ -55,7 +42,7 @@ def rentals_checkout(): # return the response body and status code return jsonify({ "video_id": new_rental.video_id, - "customer_id": new_rental.cusomer_id, + "customer_id": new_rental.customer_id, "videos_checked_out_count": len(customer.videos), "available_inventory": video.total_inventory - len(video.customers) }), 200 From 25a25afcf6bf2d855417063393d166e0bd49a6e8 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Thu, 11 Nov 2021 16:21:39 -0800 Subject: [PATCH 20/24] add comments and params in GET functions --- app/models/customer.py | 1 + app/models/video.py | 3 ++- app/rental_routes.py | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/customer.py b/app/models/customer.py index 42c454c75..e1c010e31 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -10,6 +10,7 @@ class Customer(db.Model): # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) #I DON'T KNOW IF THIS IS CORRECT + #adding videos attribute to Customer Model videos = db.relationship("Video", secondary="rental", backref="customers") #videos = db.relationship("Rental", back_populates="customer") diff --git a/app/models/video.py b/app/models/video.py index 9251bc50b..d41f5cc98 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -4,9 +4,10 @@ class Video(db.Model): video_id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String) release_date = db.Column(db.DateTime) + #how many I own total_inventory = db.Column(db.Integer) #customers = db.relationship("Rental", back_populates="video") - + #checked_out column def to_dict(self): return { "id": self.video_id, diff --git a/app/rental_routes.py b/app/rental_routes.py index 58c97c878..c2e5674b4 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -54,11 +54,13 @@ def rentals_checkin(): pass @customers_bp.route("//rentals", methods=["GET"]) -def customer_read(): +def customer_read(customer_id): + """ List the videos a customer currently has checked out """ pass @video_bp.route("//rentals", methods=["GET"]) -def video_read(): +def video_read(video_id): + """ List the customers who currently have the video checked out """ # videos_list = [] # for video in self.videos: # videos_list(video.rental_id) From b400e27f60395767b2517f080b05a862dfff8c02 Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Thu, 11 Nov 2021 20:34:42 -0800 Subject: [PATCH 21/24] adds POST check-in and GET rentals by customer routes. 16/20 wave 2 tests passing. --- app/rental_routes.py | 86 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/app/rental_routes.py b/app/rental_routes.py index bb2b44a6a..1e8edd15d 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -25,9 +25,10 @@ def rentals_checkout(): video = Video.query.get(request_body["video_id"]) if video is None: return jsonify(), 404 - - if video.total_inventory == 0: - return jsonify({"message": "Could not perform checkout"}), 400 + + # # revisit later + # if video.total_inventory == 0: + # return jsonify({"message": "Could not perform checkout"}), 400 # Instantiate a new instance for rental new_rental = Rental( @@ -48,20 +49,83 @@ def rentals_checkout(): "available_inventory": video.total_inventory - len(video.customers) }), 200 +@rentals_bp.route("/check-in", methods=["POST"]) +def rentals_checkin(): + request_body = request.get_json() + ## revisit later + # if video and customer do not match a current rental, return 400 + + # checks that the required request body parameters are in request + if "customer_id" not in request_body or "video_id" not in request_body: + return jsonify(), 400 + + # stores customer instance into variable. If customer does not exist, returns 404 + customer = Customer.query.get(request_body["customer_id"]) + if customer is None: + return jsonify(), 404 + # stores video instance into variable. If video does not exist, returns 404 + video = Video.query.get(request_body["video_id"]) + if video is None: + return jsonify(), 404 + + # iterate through all rentals instances to find rental with matching customer and video ids + rentals = Rental.query.all() + checked_in = None + for rental in rentals: + if rental.customer_id == request_body["customer_id"] and rental.video_id == request_body["video_id"]: + checked_in = rental + + # if there is no matching rental for the given customer and video ids, return 400 + if checked_in is None: + return jsonify({"message": "No outstanding rentals for customer 1 and video 1"}), 400 + + # delete rental that is being checked in + db.session.delete(checked_in) + # commit to database + db.session.commit() -# # response body might look something like this: -# { -# "customer_id": rental.customer_id, -# "video_id": rental.video_id, -# "videos_checked_out_count": rental.customer.videos, -# "available_inventory": video.total_inventory - video.customers -# } + # return response body + return jsonify({ + "video_id": checked_in.video_id, + "customer_id": checked_in.customer_id, + "videos_checked_out_count": len(customer.videos), + "available_inventory": video.total_inventory - len(video.customers) + }), 200 @customers_bp.route("//rentals", methods=["GET"]) def customer_read(customer_id): """ List the videos a customer currently has checked out """ - pass + request_body = request.get_json() + + # checks to see if customer exists. If not, returns 404 + customer = Customer.query.get(customer_id) + if customer is None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 + + # sets up empty list to store a customer's checked out videos + # iterates through customer.videos to retreive all videos a customer has + checked_out = [] + for video in customer.videos: + checked_out.append(video) + + # gets rental instance for each video a customer has + + rentals = Rental.query.all() + customer_rentals = [] + for video in checked_out: + for rental in rentals: + if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: + customer_rentals.append(rental) + + response_body = [] + for rental in customer_rentals: + response_body.append({ + "release_date": video.release_date, + "title": video.title, + "due_date": rental.due_date + }) + return jsonify(response_body) @video_bp.route("//rentals", methods=["GET"]) def video_read(video_id): From 3123fd43b50fefb051ed764bf044985d76862fd2 Mon Sep 17 00:00:00 2001 From: Arianna Cabebe Date: Thu, 11 Nov 2021 21:02:00 -0800 Subject: [PATCH 22/24] adds GET rentals by video routes. Passes 19/20 wave 2 tests. --- app/rental_routes.py | 45 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/app/rental_routes.py b/app/rental_routes.py index 1e8edd15d..332312d64 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -26,7 +26,7 @@ def rentals_checkout(): if video is None: return jsonify(), 404 - # # revisit later + # # revisit later - Zandra's solution for test_checkout_video_no_inventory # if video.total_inventory == 0: # return jsonify({"message": "Could not perform checkout"}), 400 @@ -47,13 +47,11 @@ def rentals_checkout(): "customer_id": new_rental.customer_id, "videos_checked_out_count": len(customer.videos), "available_inventory": video.total_inventory - len(video.customers) - }), 200 + }) @rentals_bp.route("/check-in", methods=["POST"]) def rentals_checkin(): request_body = request.get_json() - ## revisit later - # if video and customer do not match a current rental, return 400 # checks that the required request body parameters are in request if "customer_id" not in request_body or "video_id" not in request_body: @@ -91,7 +89,7 @@ def rentals_checkin(): "customer_id": checked_in.customer_id, "videos_checked_out_count": len(customer.videos), "available_inventory": video.total_inventory - len(video.customers) - }), 200 + }) @customers_bp.route("//rentals", methods=["GET"]) def customer_read(customer_id): @@ -103,14 +101,12 @@ def customer_read(customer_id): if customer is None: return jsonify({"message": f"Customer {customer_id} was not found"}), 404 - # sets up empty list to store a customer's checked out videos - # iterates through customer.videos to retreive all videos a customer has + # sets up empty list to store a customer's checked out videos & iterates through customer.videos to retreive all videos a customer currently has checked_out = [] for video in customer.videos: checked_out.append(video) # gets rental instance for each video a customer has - rentals = Rental.query.all() customer_rentals = [] for video in checked_out: @@ -118,6 +114,7 @@ def customer_read(customer_id): if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: customer_rentals.append(rental) + # create response body response_body = [] for rental in customer_rentals: response_body.append({ @@ -130,7 +127,37 @@ def customer_read(customer_id): @video_bp.route("//rentals", methods=["GET"]) def video_read(video_id): """ List the customers who currently have the video checked out """ + request_body = request.get_json() + + # checks to see if video exists. If not, returns 404 + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + + # sets up empty list to store the video's current customers & iterates through video.customers to retreive all customers that currently have the video + current_customers = [] + for customer in video.customers: + current_customers.append(customer) + + # gets rental instance for each customer a video has + rentals = Rental.query.all() + video_rentals = [] + for customer in current_customers: + for rental in rentals: + if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: + video_rentals.append(rental) + + # create response body + response_body = [] + for rental in video_rentals: + response_body.append({ + "due_date": rental.due_date, + "name": customer.name, + "phone": customer.phone, + "postal_code": customer.postal_code + }) + return jsonify(response_body) + # videos_list = [] # for video in self.videos: # videos_list(video.rental_id) - pass From 0ec6c1b07f618c4ca16d946c4f6c7067817ae996 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Fri, 12 Nov 2021 14:29:22 -0800 Subject: [PATCH 23/24] modify checked_out & check_in endpoints --- app/models/rental.py | 3 ++ app/rental_routes.py | 81 ++++++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/app/models/rental.py b/app/models/rental.py index a340a835f..92fec9e77 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -6,5 +6,8 @@ class Rental(db.Model): customer_id = db.Column(db.Integer, db.ForeignKey('customer.customer_id'), primary_key=True,nullable=False) video_id = db.Column(db.Integer, db.ForeignKey('video.video_id'), primary_key=True,nullable=False) due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + timedelta(days = 7)) + #bool is truthy and falsey + checked_out = db.Column(db.Boolean, default=False) + #need to update check_in and check_out routes. Change check_in to True and False for checkout #video = db.relationship("Video", back_populates="customers") #customer = db.releationship("Customer", back_populates="videos") diff --git a/app/rental_routes.py b/app/rental_routes.py index 332312d64..054a781f0 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -17,18 +17,23 @@ def rentals_checkout(): if "customer_id" not in request_body or "video_id" not in request_body: return jsonify(), 400 - + customer = Customer.query.get(request_body["customer_id"]) if customer is None: return jsonify(), 404 - + + #instance of a video video = Video.query.get(request_body["video_id"]) if video is None: return jsonify(), 404 - # # revisit later - Zandra's solution for test_checkout_video_no_inventory - # if video.total_inventory == 0: - # return jsonify({"message": "Could not perform checkout"}), 400 + rentals = Rental.query.filter_by(video_id=video.video_id, checked_out = True).count() + + #accessing number of inventory + available_inventory = video.total_inventory - rentals + + if available_inventory == 0: + return jsonify({"message": "Could not perform checkout"}), 400 # Instantiate a new instance for rental new_rental = Rental( @@ -38,16 +43,30 @@ def rentals_checkout(): # add rental instance to database db.session.add(new_rental) + + new_rental.checked_out = True + # commit to database db.session.commit() + # iterate through all videos instances to find video with matching video id + # filter keyword is filtering all columns will filter_by is filtering for specific columns + # count()- tells how many rentals are currently checked out + #This is for video Table + rentals = Rental.query.filter_by(video_id=video.video_id, checked_out = True).count() + + #accessing number of inventory + available_inventory = video.total_inventory - rentals + + videos_check_out = Rental.query.filter_by(customer_id=customer.customer_id, checked_out = True).count() + # return the response body and status code return jsonify({ "video_id": new_rental.video_id, "customer_id": new_rental.customer_id, - "videos_checked_out_count": len(customer.videos), - "available_inventory": video.total_inventory - len(video.customers) - }) + "videos_checked_out_count": videos_check_out, + "available_inventory": available_inventory + }), 200 @rentals_bp.route("/check-in", methods=["POST"]) def rentals_checkin(): @@ -66,29 +85,44 @@ def rentals_checkin(): video = Video.query.get(request_body["video_id"]) if video is None: return jsonify(), 404 - + #*******information getting an incorrect response body # iterate through all rentals instances to find rental with matching customer and video ids - rentals = Rental.query.all() - checked_in = None - for rental in rentals: - if rental.customer_id == request_body["customer_id"] and rental.video_id == request_body["video_id"]: - checked_in = rental + rental = Rental.query.filter_by(customer_id=customer.customer_id, video_id=video.video_id, checked_out=True) - # if there is no matching rental for the given customer and video ids, return 400 - if checked_in is None: + # is None is 50% faster than ==. "=="" is comparing values. "is" compares location in memory + if rental is None: return jsonify({"message": "No outstanding rentals for customer 1 and video 1"}), 400 + + rental.checked_out = False + + # rentals = Rental.query.all() + # checked_in = None + # for rental in rentals: + # if rental.customer_id == request_body["customer_id"] and rental.video_id == request_body["video_id"]: + # checked_in = rental + + # if there is no matching rental for the given customer and video ids, return 400 + # if checked_in is None: + # return jsonify({"message": "No outstanding rentals for customer 1 and video 1"}), 400 # delete rental that is being checked in - db.session.delete(checked_in) + # db.session.delete(checked_in) # commit to database db.session.commit() + #need to count this after database + rentals = Rental.query.filter_by(video_id=video.video_id, checked_out = True).count() + + #accessing number of inventory + available_inventory = video.total_inventory - rentals # return response body + videos_check_out = Rental.query.filter_by(customer_id=customer.customer_id, checked_out = True).count() + return jsonify({ - "video_id": checked_in.video_id, - "customer_id": checked_in.customer_id, - "videos_checked_out_count": len(customer.videos), - "available_inventory": video.total_inventory - len(video.customers) + "video_id": video.video_id, + "customer_id": customer.customer_id, + "videos_checked_out_count": videos_check_out, + "available_inventory": available_inventory }) @customers_bp.route("//rentals", methods=["GET"]) @@ -157,7 +191,4 @@ def video_read(video_id): "postal_code": customer.postal_code }) return jsonify(response_body) - - # videos_list = [] - # for video in self.videos: - # videos_list(video.rental_id) + \ No newline at end of file From 145f11435ef0bacc5c8248e6c06193bf4cb7b411 Mon Sep 17 00:00:00 2001 From: Zandra Nguyen Date: Sun, 14 Nov 2021 20:10:51 -0800 Subject: [PATCH 24/24] clean up check-out and check-in methods --- app/rental_routes.py | 128 ++++++++++++++++++------------------------- app/video_routes.py | 2 - 2 files changed, 54 insertions(+), 76 deletions(-) diff --git a/app/rental_routes.py b/app/rental_routes.py index 054a781f0..8a52a5fb5 100644 --- a/app/rental_routes.py +++ b/app/rental_routes.py @@ -10,26 +10,27 @@ rentals_bp = Blueprint("rentals_bp", __name__, url_prefix="/rentals") + @rentals_bp.route("/check-out", methods=["POST"]) def rentals_checkout(): #{"c_id": [1], "v_id": [4]} - request_body = request.get_json() + request_body = request.get_json() + # is this key string not in the request_body return 400 if "customer_id" not in request_body or "video_id" not in request_body: return jsonify(), 400 - + + # get instance of a customer and an instance of a video and return 404 if it's None customer = Customer.query.get(request_body["customer_id"]) - if customer is None: - return jsonify(), 404 - - #instance of a video video = Video.query.get(request_body["video_id"]) - if video is None: + if video is None or customer is None: return jsonify(), 404 - - rentals = Rental.query.filter_by(video_id=video.video_id, checked_out = True).count() - - #accessing number of inventory + + # filtering the video ID & checked_out attributes and count the records + rentals = Rental.query.filter_by( + video_id=video.video_id, checked_out=True).count() + + # finding avaliable inventory available_inventory = video.total_inventory - rentals if available_inventory == 0: @@ -39,102 +40,81 @@ def rentals_checkout(): new_rental = Rental( video_id=video.video_id, customer_id=customer.customer_id - ) - - # add rental instance to database - db.session.add(new_rental) + ) + # staging rental instance to database + db.session.add(new_rental) + # set rental instance to True new_rental.checked_out = True - # commit to database db.session.commit() - - # iterate through all videos instances to find video with matching video id - # filter keyword is filtering all columns will filter_by is filtering for specific columns - # count()- tells how many rentals are currently checked out - #This is for video Table - rentals = Rental.query.filter_by(video_id=video.video_id, checked_out = True).count() - - #accessing number of inventory - available_inventory = video.total_inventory - rentals - - videos_check_out = Rental.query.filter_by(customer_id=customer.customer_id, checked_out = True).count() - + + # count() - tells how many rentals are currently checked out + videos_checked_out = Rental.query.filter_by( + video_id=video.video_id, checked_out=True).count() + + available_inventory = video.total_inventory - videos_checked_out + # return the response body and status code return jsonify({ "video_id": new_rental.video_id, "customer_id": new_rental.customer_id, - "videos_checked_out_count": videos_check_out, + "videos_checked_out_count": videos_checked_out, "available_inventory": available_inventory }), 200 + @rentals_bp.route("/check-in", methods=["POST"]) def rentals_checkin(): request_body = request.get_json() - # checks that the required request body parameters are in request + # checks that the required request body parameters are in request if "customer_id" not in request_body or "video_id" not in request_body: return jsonify(), 400 - - # stores customer instance into variable. If customer does not exist, returns 404 - customer = Customer.query.get(request_body["customer_id"]) - if customer is None: - return jsonify(), 404 - # stores video instance into variable. If video does not exist, returns 404 + # store customer/video instance into variables. If customer/video does not exist, returns 404 + customer = Customer.query.get(request_body["customer_id"]) video = Video.query.get(request_body["video_id"]) - if video is None: + if customer is None or video is None: return jsonify(), 404 - #*******information getting an incorrect response body - # iterate through all rentals instances to find rental with matching customer and video ids - rental = Rental.query.filter_by(customer_id=customer.customer_id, video_id=video.video_id, checked_out=True) - - # is None is 50% faster than ==. "=="" is comparing values. "is" compares location in memory + + # query through Rental to find matching customer and video ids and return first on the list + rental = Rental.query.filter_by( + customer_id=customer.customer_id, video_id=video.video_id).first() + # (customer_id=customer.customer_id, video_id=video.video_id, checked_out=True).first() #this also passed the test + + # "is" (compare location in memory) is 50% faster than "==" (comparing values) if rental is None: - return jsonify({"message": "No outstanding rentals for customer 1 and video 1"}), 400 - - rental.checked_out = False - - # rentals = Rental.query.all() - # checked_in = None - # for rental in rentals: - # if rental.customer_id == request_body["customer_id"] and rental.video_id == request_body["video_id"]: - # checked_in = rental - - # if there is no matching rental for the given customer and video ids, return 400 - # if checked_in is None: - # return jsonify({"message": "No outstanding rentals for customer 1 and video 1"}), 400 - - # delete rental that is being checked in - # db.session.delete(checked_in) - # commit to database + return jsonify({"message": f"No outstanding rentals for customer {customer.customer_id} and video {video.video_id}"}), 400 + + # rental.checked_out = True db.session.commit() - #need to count this after database - rentals = Rental.query.filter_by(video_id=video.video_id, checked_out = True).count() - - #accessing number of inventory - available_inventory = video.total_inventory - rentals # return response body - videos_check_out = Rental.query.filter_by(customer_id=customer.customer_id, checked_out = True).count() - + videos_checked_out = Rental.query.filter_by( + video_id=video.video_id, checked_out=False).count() + + # finding avaiable inventory + available_inventory = video.total_inventory - videos_checked_out + return jsonify({ "video_id": video.video_id, "customer_id": customer.customer_id, - "videos_checked_out_count": videos_check_out, + "videos_checked_out_count": videos_checked_out, "available_inventory": available_inventory }) + @customers_bp.route("//rentals", methods=["GET"]) def customer_read(customer_id): """ List the videos a customer currently has checked out """ request_body = request.get_json() - + # checks to see if customer exists. If not, returns 404 customer = Customer.query.get(customer_id) if customer is None: return jsonify({"message": f"Customer {customer_id} was not found"}), 404 - + # sets up empty list to store a customer's checked out videos & iterates through customer.videos to retreive all videos a customer currently has checked_out = [] for video in customer.videos: @@ -148,7 +128,7 @@ def customer_read(customer_id): if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: customer_rentals.append(rental) - # create response body + # create response body response_body = [] for rental in customer_rentals: response_body.append({ @@ -158,11 +138,12 @@ def customer_read(customer_id): }) return jsonify(response_body) + @video_bp.route("//rentals", methods=["GET"]) def video_read(video_id): """ List the customers who currently have the video checked out """ request_body = request.get_json() - + # checks to see if video exists. If not, returns 404 video = Video.query.get(video_id) if video is None: @@ -180,8 +161,8 @@ def video_read(video_id): for rental in rentals: if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: video_rentals.append(rental) - - # create response body + + # create response body response_body = [] for rental in video_rentals: response_body.append({ @@ -191,4 +172,3 @@ def video_read(video_id): "postal_code": customer.postal_code }) return jsonify(response_body) - \ No newline at end of file diff --git a/app/video_routes.py b/app/video_routes.py index 96b1e9771..71124304f 100644 --- a/app/video_routes.py +++ b/app/video_routes.py @@ -42,8 +42,6 @@ def create_video(): response_body["id"] = new_video.video_id return jsonify(response_body), 201 - - # /videos/ routes @video_bp.route("/", methods=["GET"]) def get_video(video_id):