From a7f93f7a8cfc89c246d9f341da1aa5a9cd7db645 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 09:30:04 -0800 Subject: [PATCH 01/46] create customer model --- app/models/customer.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/app/models/customer.py b/app/models/customer.py index 54d10b49a..50094fd22 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,4 +1,29 @@ from app import db +import datetime 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,nullable = False) + registered_at = db.Column(db.DateTime,default=(datetime.date.today())) + postal_code = db.Column(db.String) + phone = db.Column(db.String,nullable = False) + + + + def to_dict(self): + customer_dict = {} + customer_dict["id"] = self.id + customer_dict["name"] = self.name + customer_dict["registered_at"] = self.registered_at + customer_dict["phone"] = self.phone + customer_dict["postal_code"] = self.postal_code + return customer_dict + + @classmethod + def from_dict(cls, customer_data): + new_customer = Customer(name=customer_data["name"], + phone=customer_data["phone"], + postal_code = customer_data["postal_code"], + registered_at = customer_data["registered_at"] + ) + return new_customer \ No newline at end of file From 970b865fcdd605d35ff651e95c5e95de5b3ac2f4 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 09:30:19 -0800 Subject: [PATCH 02/46] create video model --- app/models/video.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/video.py b/app/models/video.py index db3bf3aeb..17a653c28 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) + id = db.Column(db.Integer, primary_key=True,autoincrement=True) + title = db.Column(db.String,nullable = False) + release_date = db.Column(db.String) + total_inventory = db.Column(db.Integer) From f3c0a444ce39a6f62833fcd16cdeb87fb6984485 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 09:49:35 -0800 Subject: [PATCH 03/46] create video to dict --- app/models/video.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/models/video.py b/app/models/video.py index 17a653c28..c7697f6dc 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -5,3 +5,12 @@ class Video(db.Model): title = db.Column(db.String,nullable = False) release_date = db.Column(db.String) total_inventory = db.Column(db.Integer) + + + def to_dict(self): + video_dict = {} + video_dict["title"] = self.id + video_dict["release_date"] = self.release_date + video_dict["total_inventory"] = self.total_inventory + + return video_dict \ No newline at end of file From 92ba167f44c7da9bdbe6a9ab7ac796fb086d7a2e Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 09:52:31 -0800 Subject: [PATCH 04/46] create video from_dict --- app/models/video.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/video.py b/app/models/video.py index c7697f6dc..24e8cbe23 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -13,4 +13,13 @@ def to_dict(self): video_dict["release_date"] = self.release_date video_dict["total_inventory"] = self.total_inventory - return video_dict \ No newline at end of file + return video_dict + + @classmethod + def from_dict(cls, video_data): + new_video = Video(title=video_data["title"], + phone=video_data["phone"], + total_inventory = video_data["total_inventory"] + ) + + return new_video \ No newline at end of file From 21c14125a301cf291d3d6696e0c91b6e48124fec Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 11:34:12 -0800 Subject: [PATCH 05/46] create routes folders --- app/{routes.py => routes/__init__.py} | 0 app/routes/customer_route.py | 0 app/routes/video_route.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename app/{routes.py => routes/__init__.py} (100%) create mode 100644 app/routes/customer_route.py create mode 100644 app/routes/video_route.py diff --git a/app/routes.py b/app/routes/__init__.py similarity index 100% rename from app/routes.py rename to app/routes/__init__.py diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/routes/video_route.py b/app/routes/video_route.py new file mode 100644 index 000000000..e69de29bb From 6cbed0b539f5fcde50824ea1360673e5cec8dfcb Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 11:44:12 -0800 Subject: [PATCH 06/46] create customer_bp --- app/routes/customer_route.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index e69de29bb..a4ff8f669 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -0,0 +1,5 @@ +from app import db +from app.models.customer import Customer +from flask import Blueprint, jsonify, abort, make_response, request + +customers_bp = Blueprint("customers_bp",__name__, url_prefix="/customers") \ No newline at end of file From 5ef2b928f53ca68b3818009e0bece2a0dbacdd3a Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 11:44:25 -0800 Subject: [PATCH 07/46] create videos_bp --- app/routes/video_route.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index e69de29bb..565b30066 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -0,0 +1,5 @@ +from app import db +from app.models.video import Video +from flask import Blueprint, jsonify, abort, make_response, request + +videos_bp = Blueprint("videos_bp",__name__, url_prefix="/videos") \ No newline at end of file From de7c76b0bd5bfd4eecf6b46c55a93d769bd7e723 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 11:47:43 -0800 Subject: [PATCH 08/46] register blueprint --- app/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/__init__.py b/app/__init__.py index 4ab3975b8..b9b292e6f 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -32,5 +32,11 @@ def create_app(test_config=None): migrate.init_app(app, db) #Register Blueprints Here + from app.routes.customer_route import customers_bp + app.register_blueprint(customers_bp) + + from app.routes.video_route import videos_bp + app.register_blueprint(videos_bp) + return app \ No newline at end of file From fe44a9cab59f086bc2e53f0b3dca75e1cfb5ac43 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 11:49:48 -0800 Subject: [PATCH 09/46] add validate_model --- app/routes/customer_route.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index a4ff8f669..8394c30ea 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -2,4 +2,26 @@ from app.models.customer import Customer from flask import Blueprint, jsonify, abort, make_response, request -customers_bp = Blueprint("customers_bp",__name__, url_prefix="/customers") \ No newline at end of file +customers_bp = Blueprint("customers_bp",__name__, url_prefix="/customers") + +def validate_model(cls, model_id): + try: + model_id = int(model_id) + except: + abort(make_response({"message":f"{cls.__name__} {model_id} invalid"}, 400)) + + model = cls.query.get(model_id) + + if not model: + abort(make_response({"message":f"{cls.__name__} {model_id} not found"}, 404)) + + return model + +#============================== planets_bp.route ============================= +#============================================================================ +#GET /customers + +GET /customers/ +POST /customers +PUT /customers/ +DELETE /customers/ \ No newline at end of file From 267546281da944361adab22af4067a7346e0c668 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 11:52:42 -0800 Subject: [PATCH 10/46] add model to db --- migrations/README | 1 + migrations/alembic.ini | 45 +++++++++ migrations/env.py | 96 +++++++++++++++++++ migrations/script.py.mako | 24 +++++ ...db262353e_adds_customer_and_video_model.py | 48 ++++++++++ 5 files changed, 214 insertions(+) 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/362db262353e_adds_customer_and_video_model.py 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/362db262353e_adds_customer_and_video_model.py b/migrations/versions/362db262353e_adds_customer_and_video_model.py new file mode 100644 index 000000000..03cd79ee2 --- /dev/null +++ b/migrations/versions/362db262353e_adds_customer_and_video_model.py @@ -0,0 +1,48 @@ +"""adds customer and video model + +Revision ID: 362db262353e +Revises: +Create Date: 2023-01-08 11:51:20.401871 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '362db262353e' +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(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('registered_at', sa.DateTime(), nullable=True), + sa.Column('postal_code', sa.String(), nullable=True), + sa.Column('phone', sa.String(), nullable=False), + 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(), autoincrement=True, nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('release_date', sa.String(), 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 3ac152d002cfcc80a33244a42681aeb5838f9310 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 12:22:20 -0800 Subject: [PATCH 11/46] delete registered_at from_dict because generate auto --- app/models/customer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/customer.py b/app/models/customer.py index 50094fd22..06a28ae38 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,5 +1,5 @@ -from app import db import datetime +from app import db class Customer(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) @@ -23,7 +23,6 @@ def to_dict(self): def from_dict(cls, customer_data): new_customer = Customer(name=customer_data["name"], phone=customer_data["phone"], - postal_code = customer_data["postal_code"], - registered_at = customer_data["registered_at"] + postal_code = customer_data["postal_code"] ) return new_customer \ No newline at end of file From 3d2bc1da1ec32a13227830c5b41e6f667ddb08cd Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 12:25:45 -0800 Subject: [PATCH 12/46] add create customer test pass --- app/routes/customer_route.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index 8394c30ea..a2ed549fc 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -1,6 +1,8 @@ from app import db from app.models.customer import Customer from flask import Blueprint, jsonify, abort, make_response, request +from datetime import datetime + customers_bp = Blueprint("customers_bp",__name__, url_prefix="/customers") @@ -20,8 +22,22 @@ def validate_model(cls, model_id): #============================== planets_bp.route ============================= #============================================================================ #GET /customers +@customers_bp.route("", methods=["POST"]) +def create_customer(): + try: + request_body = request.get_json() + new_customer = Customer.from_dict(request_body) + except: + abort(make_response({"details":"Request body must include name.,Request body must include phone.,Request body must include postal_code."}, 400)) + + db.session.add(new_customer) + db.session.commit() + + response_body = new_customer.to_dict() + return make_response(response_body, 201) +# GET /customers/ +# POST /customers + -GET /customers/ -POST /customers -PUT /customers/ -DELETE /customers/ \ No newline at end of file +# PUT /customers/ +# DELETE /customers/ \ No newline at end of file From e218a73b0f56479b798cb5f1addd64416cd6ff4b Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 12:33:55 -0800 Subject: [PATCH 13/46] add get customers route test pass --- app/routes/customer_route.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index a2ed549fc..1332b1e40 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -22,6 +22,16 @@ def validate_model(cls, model_id): #============================== planets_bp.route ============================= #============================================================================ #GET /customers +@customers_bp.route("", methods=["GET"]) +def get_customers(): + customer_query = Customer.query.all() + response_body = [] + + for customer in customer_query: + response_body.append(customer.to_dict()) + return jsonify(response_body) + +# POST /customers @customers_bp.route("", methods=["POST"]) def create_customer(): try: @@ -36,8 +46,8 @@ def create_customer(): response_body = new_customer.to_dict() return make_response(response_body, 201) # GET /customers/ -# POST /customers +#GET /customers # PUT /customers/ # DELETE /customers/ \ No newline at end of file From 2b863140e4b8cbf64d294be5f724eb02ccf39137 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 12:43:44 -0800 Subject: [PATCH 14/46] add get customer by id --- app/routes/customer_route.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index 1332b1e40..1b2ee280d 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -15,7 +15,7 @@ def validate_model(cls, model_id): model = cls.query.get(model_id) if not model: - abort(make_response({"message":f"{cls.__name__} {model_id} not found"}, 404)) + abort(make_response({"message":f"{cls.__name__} {model_id} was not found"}, 404)) return model @@ -46,8 +46,12 @@ def create_customer(): response_body = new_customer.to_dict() return make_response(response_body, 201) # GET /customers/ +@customers_bp.route("/", methods=["GET"]) +def get_customers_by_id(id): + customer = validate_model(Customer, id) + + return jsonify(customer.to_dict()) -#GET /customers # PUT /customers/ # DELETE /customers/ \ No newline at end of file From eafc8c2e41af36871ce6620ef6994173ec6e01bc Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 12:53:19 -0800 Subject: [PATCH 15/46] add delete custom by id --- app/routes/customer_route.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index 1b2ee280d..0070b4153 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -15,7 +15,7 @@ def validate_model(cls, model_id): model = cls.query.get(model_id) if not model: - abort(make_response({"message":f"{cls.__name__} {model_id} was not found"}, 404)) + abort(make_response({"message":f"{cls.__name__} {model_id} was not found"}, 404)) return model @@ -54,4 +54,12 @@ def get_customers_by_id(id): # PUT /customers/ -# DELETE /customers/ \ No newline at end of file +# DELETE /customers/ +@customers_bp.route("/", methods=["DELETE"]) +def delete_customers_by_id(id): + customer = validate_model(Customer, id) + + db.session.delete(customer) + db.session.commit() + + return jsonify(customer.to_dict()),200 \ No newline at end of file From f3edbcf93ee3d273a0cce4940d59f4bbda88de52 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:04:13 -0800 Subject: [PATCH 16/46] add put customer by id --- app/routes/customer_route.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index 0070b4153..ad34b7094 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -53,13 +53,29 @@ def get_customers_by_id(id): return jsonify(customer.to_dict()) -# PUT /customers/ + # DELETE /customers/ @customers_bp.route("/", methods=["DELETE"]) -def delete_customers_by_id(id): +def put_customers_by_id(id): customer = validate_model(Customer, id) db.session.delete(customer) db.session.commit() + return jsonify(customer.to_dict()),200 + +# PUT /customers/ +@customers_bp.route("/", methods=["PUT"]) +def delete_customers_by_id(id): + customer = validate_model(Customer, id) + try: + request_body = request.get_json() + customer.name = request_body["name"] + customer.postal_code = request_body["postal_code"] + customer.phone = request_body["phone"] + except: + abort(make_response(jsonify("Bad Request"), 400)) + + db.session.commit() + return jsonify(customer.to_dict()),200 \ No newline at end of file From ef22036b16c8add2b1276202da3e35c729720bc0 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:14:58 -0800 Subject: [PATCH 17/46] modify --- app/models/video.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/video.py b/app/models/video.py index 24e8cbe23..ce6b8c994 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -18,7 +18,7 @@ def to_dict(self): @classmethod def from_dict(cls, video_data): new_video = Video(title=video_data["title"], - phone=video_data["phone"], + release_date=video_data["release_date"], total_inventory = video_data["total_inventory"] ) From 1c45fe8dd10ece9dcbebdc1d0b1e67c7e6984ffe Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:20:16 -0800 Subject: [PATCH 18/46] video_dict["title"] = self.title --- app/models/video.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/video.py b/app/models/video.py index ce6b8c994..da9cf32cf 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -9,7 +9,7 @@ class Video(db.Model): def to_dict(self): video_dict = {} - video_dict["title"] = self.id + video_dict["title"] = self.title video_dict["release_date"] = self.release_date video_dict["total_inventory"] = self.total_inventory From b46fe905b17d68aab24a55da297a0cdc2340946a Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:22:22 -0800 Subject: [PATCH 19/46] add video_dict["id"] = self.id --- app/models/video.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/video.py b/app/models/video.py index da9cf32cf..42cfece3d 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -9,6 +9,7 @@ class Video(db.Model): def to_dict(self): video_dict = {} + video_dict["id"] = self.id video_dict["title"] = self.title video_dict["release_date"] = self.release_date video_dict["total_inventory"] = self.total_inventory From 27e7097d58759931fe5d0b6810e2115920858e7b Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:22:36 -0800 Subject: [PATCH 20/46] modify --- app/routes/customer_route.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index ad34b7094..eb0752d8d 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -19,7 +19,7 @@ def validate_model(cls, model_id): return model -#============================== planets_bp.route ============================= +#============================== customers_bp.route ============================= #============================================================================ #GET /customers @customers_bp.route("", methods=["GET"]) From f76d9034b9c6a9c0cb675866f2ad55022751542b Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:23:14 -0800 Subject: [PATCH 21/46] add get videos,post videos, get video by id --- app/routes/video_route.py | 63 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index 565b30066..b3ebe3853 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -1,5 +1,66 @@ from app import db from app.models.video import Video from flask import Blueprint, jsonify, abort, make_response, request +from app.routes.customer_route import validate_model +videos_bp = Blueprint("videos_bp",__name__, url_prefix="/videos") -videos_bp = Blueprint("videos_bp",__name__, url_prefix="/videos") \ No newline at end of file +#============================== videos_bp.route ============================= +#============================================================================ +#GET /videos +@videos_bp.route("", methods=["GET"]) +def get_videos(): + videos = Video.query.all() + response_body = [] + + for video in videos: + response_body.append(video.to_dict()) + return jsonify(response_body) + +# POST /videos +@videos_bp.route("", methods=["POST"]) +def create_video(): + try: + request_body = request.get_json() + new_video = Video.from_dict(request_body) + except: + abort(make_response({"details":"Request body must include title.,Request body must include release_date.,Request body must include total_inventory."}, 400)) + + db.session.add(new_video) + db.session.commit() + + response_body = new_video.to_dict() + return make_response(response_body, 201) +# GET /videos/ +@videos_bp.route("/", methods=["GET"]) +def get_video_by_id(id): + video = validate_model(Video, id) + + return jsonify(video.to_dict()) + + + +# DELETE /customers/ +@videos_bp.route("/", methods=["DELETE"]) +def put_customers_by_id(id): + customer = validate_model(Customer, id) + + db.session.delete(customer) + db.session.commit() + + return jsonify(customer.to_dict()),200 + +# PUT /customers/ +@videos_bp.route("/", methods=["PUT"]) +def delete_customers_by_id(id): + customer = validate_model(Customer, id) + try: + request_body = request.get_json() + customer.name = request_body["name"] + customer.postal_code = request_body["postal_code"] + customer.phone = request_body["phone"] + except: + abort(make_response(jsonify("Bad Request"), 400)) + + db.session.commit() + + return jsonify(customer.to_dict()),200 \ No newline at end of file From 503c5cba3bf65df8545be13da1664753e7c84e2d Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:25:20 -0800 Subject: [PATCH 22/46] add delete video by id test pass --- app/routes/video_route.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index b3ebe3853..1ad00cc4d 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -39,19 +39,19 @@ def get_video_by_id(id): -# DELETE /customers/ +# DELETE /videos/ @videos_bp.route("/", methods=["DELETE"]) -def put_customers_by_id(id): - customer = validate_model(Customer, id) +def delete_customers_by_id(id): + video = validate_model(Video, id) - db.session.delete(customer) + db.session.delete(video) db.session.commit() - return jsonify(customer.to_dict()),200 + return jsonify(video.to_dict()),200 # PUT /customers/ @videos_bp.route("/", methods=["PUT"]) -def delete_customers_by_id(id): +def put_customers_by_id(id): customer = validate_model(Customer, id) try: request_body = request.get_json() From 4d3a93e6218c49f437db6a1d64a98ae1f534dc9a Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:27:41 -0800 Subject: [PATCH 23/46] add put video by id wave 1 done --- app/routes/video_route.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index 1ad00cc4d..53c49cf3d 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -49,18 +49,18 @@ def delete_customers_by_id(id): return jsonify(video.to_dict()),200 -# PUT /customers/ +# PUT /videos/ @videos_bp.route("/", methods=["PUT"]) -def put_customers_by_id(id): - customer = validate_model(Customer, id) +def put_videos_by_id(id): + video = validate_model(Video, id) try: request_body = request.get_json() - customer.name = request_body["name"] - customer.postal_code = request_body["postal_code"] - customer.phone = request_body["phone"] + video.title = request_body["title"] + video.release_date = request_body["release_date"] + video.total_inventory = request_body["total_inventory"] except: abort(make_response(jsonify("Bad Request"), 400)) db.session.commit() - return jsonify(customer.to_dict()),200 \ No newline at end of file + return jsonify(video.to_dict()),200 \ No newline at end of file From 1d596ada503c8183e1e2dadc36dc022f892109d4 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:54:25 -0800 Subject: [PATCH 24/46] register rentals_bp --- app/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/__init__.py b/app/__init__.py index b9b292e6f..bfd206480 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -38,5 +38,8 @@ def create_app(test_config=None): from app.routes.video_route import videos_bp app.register_blueprint(videos_bp) + from app.routes.rental_route import rentals_bp + app.register_blueprint(rentals_bp) + return app \ No newline at end of file From be161ba20cd768345c54ec56d11cc267069d808b Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:54:49 -0800 Subject: [PATCH 25/46] setup rental model --- app/routes/rental_route.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/routes/rental_route.py diff --git a/app/routes/rental_route.py b/app/routes/rental_route.py new file mode 100644 index 000000000..efae19085 --- /dev/null +++ b/app/routes/rental_route.py @@ -0,0 +1,6 @@ +from app import db +from app.models.rental import Rental +from flask import Blueprint, jsonify, abort, make_response, request +from datetime import datetime + +rentals_bp = Blueprint("rentals_bp",__name__, url_prefix="/rentals") \ No newline at end of file From 9809f3eaf8f870aacd7f5acbeac5b7a9201800fb Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:55:17 -0800 Subject: [PATCH 26/46] relationship with rental --- app/models/video.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/video.py b/app/models/video.py index 42cfece3d..82424bf90 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -5,6 +5,7 @@ class Video(db.Model): title = db.Column(db.String,nullable = False) release_date = db.Column(db.String) total_inventory = db.Column(db.Integer) + rentals = db.relationship("Rental", back_populates="video") def to_dict(self): From f06397c0f91eedce49e2902ce2c0463b542c1f4a Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:55:30 -0800 Subject: [PATCH 27/46] rental model --- app/models/rental.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/app/models/rental.py b/app/models/rental.py index 11009e593..28f4db7e1 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -1,4 +1,29 @@ +import datetime from app import db class Rental(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + id = db.Column(db.Integer, primary_key=True) + customer_id = db.Column(db.Integer, db.ForeignKey("customer.id")) + video_id = db.Column(db.Integer, db.ForeignKey("video.id")) + due_date = db.Column(db.DateTime, default=(datetime.date.today()+datetime.timedelta(days=7))) + #videos_checked_out_count = db.Column(db.Integer) + available_inventory = db.Column(db.Integer) + customer = db.relationship("Customer", back_populates="rentals") + video = db.relationship("Video", back_populates="rentals") + + def to_dict(self): + rental_dict = {} + rental_dict["id"] = self.id + rental_dict["customer_id"] = self.customer_id + rental_dict["video_id"] = self.video_id + rental_dict["due_date"] = self.due_date + #rental_dict["check_out_status"] = self.check_out_status + return rental_dict + + @classmethod + def from_dict(cls, rental_data): + new_rental = Rental( + customer_id = rental_data["customer_id"], + video_id = rental_data["video_id"] + ) + return new_rental \ No newline at end of file From 23c9a3ce68bd873f85fe9e2d18a58fc1cdaaf430 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 13:55:50 -0800 Subject: [PATCH 28/46] relationship with rental --- app/models/customer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/customer.py b/app/models/customer.py index 06a28ae38..52bbbde4f 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -7,6 +7,7 @@ class Customer(db.Model): registered_at = db.Column(db.DateTime,default=(datetime.date.today())) postal_code = db.Column(db.String) phone = db.Column(db.String,nullable = False) + rentals = db.relationship("Rental", back_populates="customer") From ec8615bd90507ca0d9bbc136b1bc771e7f493df4 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 14:59:31 -0800 Subject: [PATCH 29/46] add videos_checked_out_count --- app/models/customer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/customer.py b/app/models/customer.py index 52bbbde4f..de03def4e 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -7,6 +7,7 @@ class Customer(db.Model): registered_at = db.Column(db.DateTime,default=(datetime.date.today())) postal_code = db.Column(db.String) phone = db.Column(db.String,nullable = False) + videos_checked_out_count = db.Column(db.Integer, default=0) rentals = db.relationship("Rental", back_populates="customer") From 77a328c48ec76cd32cb530da59f909fccc23889d Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 15:35:14 -0800 Subject: [PATCH 30/46] modify --- app/routes/customer_route.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index eb0752d8d..21aa3f062 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -78,4 +78,7 @@ def delete_customers_by_id(id): db.session.commit() - return jsonify(customer.to_dict()),200 \ No newline at end of file + return jsonify(customer.to_dict()),200 + + +# `GET /customers//rentals` \ No newline at end of file From 5cdaa458dbb8210e8aa5f9caf95934da632f8075 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 15:35:48 -0800 Subject: [PATCH 31/46] add post rental check-out test pass --- app/routes/rental_route.py | 75 +++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/app/routes/rental_route.py b/app/routes/rental_route.py index efae19085..b075c4f00 100644 --- a/app/routes/rental_route.py +++ b/app/routes/rental_route.py @@ -1,6 +1,79 @@ from app import db from app.models.rental import Rental +from app.models.customer import Customer +from app.models.video import Video +from app.routes.customer_route import validate_model from flask import Blueprint, jsonify, abort, make_response, request from datetime import datetime -rentals_bp = Blueprint("rentals_bp",__name__, url_prefix="/rentals") \ No newline at end of file +rentals_bp = Blueprint("rentals_bp",__name__, url_prefix="/rentals") + +#============================== rentals_bp.route ============================= +#============================================================================ + +## `POST /rentals/check-out` +@rentals_bp.route("/check-out", methods=["POST"]) +def create_rental(): + request_body = request.get_json() + + customer = validate_model(Customer, request_body["customer_id"]) + video = validate_model(Video, request_body["video_id"]) + + if video.total_inventory == 0: + abort(make_response({"message":f"Could not perform checkout"}, 400)) + + videos_checked_out = Rental.query.filter_by(video_id=video.id).count() # look into count method + + available_inventory = video.total_inventory - videos_checked_out + if available_inventory == 0: + abort(make_response({"message":f"Could not perform checkout"}, 400)) + + new_rental = Rental.from_dict(request_body) + + customer.videos_checked_out_count += 1 + db.session.add(new_rental) + db.session.add(customer) + db.session.commit() + + rental_response = new_rental.to_dict() + rental_response["videos_checked_out_count"] = customer.videos_checked_out_count + available_inventory -= 1 + rental_response["available_inventory"] = available_inventory + + return jsonify(rental_response), 200 + + + + +## `POST /rentals/check-in` +@rentals_bp.route("/check-in", methods=["POST"]) +def check_in_rental(): + request_body = request.get_json() + + customer = validate_model(Customer, request_body["customer_id"]) + video = validate_model(Video, request_body["video_id"]) + + rental = Rental.filter_by(video_id=video.id, customer_id=customer.id).first() + + if not rental: + abort(make_response({"message": f"No outstanding rentals for customer {customer.id} and video {video.id}"}, 400)) + + if rental.check_out_status == False: + abort(make_response({"message":f"Could not perform checkout bc already checked out"}, 400)) + videos_checked_out = db.session.query(Rental).filter_by(video_id=video.id).all() # look into count method, refactor duplicate code + available_inventory = video.total_inventory - len(videos_checked_out) + + customer.videos_checked_out_count -= 1 + available_inventory += 1 + rental.check_out_status = False + + rental = Rental.from_dict(request_body) + + db.session.add(rental) + db.session.commit() + + rental_response = rental.to_dict() + rental_response["videos_checked_out_count"] = customer.videos_checked_out_count + rental_response["available_inventory"] = available_inventory + + return jsonify(rental_response), 200 \ No newline at end of file From 352ab045af47d416a54d4dfec7edf19c1fa8dbd2 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 15:36:14 -0800 Subject: [PATCH 32/46] import rental model --- app/routes/video_route.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index 53c49cf3d..d08770872 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -1,5 +1,6 @@ from app import db from app.models.video import Video +from app.models.rental import Rental from flask import Blueprint, jsonify, abort, make_response, request from app.routes.customer_route import validate_model videos_bp = Blueprint("videos_bp",__name__, url_prefix="/videos") @@ -63,4 +64,16 @@ def put_videos_by_id(id): db.session.commit() - return jsonify(video.to_dict()),200 \ No newline at end of file + return jsonify(video.to_dict()),200 + +## `GET /videos//rentals` +@videos_bp.route("//rentals", methods=["GET"]) +def get_rentals_by_video_id(id): + video = validate_model(Video, id) + # rentals = Rental.query.filter(video_id=id).all() + response_body = [] + for rental in video.rentals: + response_body.append(rental.to_dict()) + + + return jsonify(response_body) \ No newline at end of file From b0b45a8f5553d467ff30763619b477f3891d5803 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 16:03:46 -0800 Subject: [PATCH 33/46] add GET /videos//rentals test pass --- app/routes/customer_route.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index 21aa3f062..c6868aff6 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -81,4 +81,13 @@ def delete_customers_by_id(id): return jsonify(customer.to_dict()),200 -# `GET /customers//rentals` \ No newline at end of file +# `GET /customers//rentals` +@customers_bp.route("//rentals", methods=["GET"]) +def get_rentals_by_customer_id(id): + customer = validate_model(Customer, id) + response_body = [] + for rental in customer.rentals: + response_body.append({"title":rental.video.title}) + + + return jsonify(response_body) \ No newline at end of file From 20dae48b0e378a43ff554e2d871a42dfe2019255 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 16:05:19 -0800 Subject: [PATCH 34/46] GET /videos//rentals` test done --- app/routes/video_route.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index d08770872..77078ab51 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -70,10 +70,9 @@ def put_videos_by_id(id): @videos_bp.route("//rentals", methods=["GET"]) def get_rentals_by_video_id(id): video = validate_model(Video, id) - # rentals = Rental.query.filter(video_id=id).all() response_body = [] for rental in video.rentals: - response_body.append(rental.to_dict()) + response_body.append({"name":rental.customer.name}) return jsonify(response_body) \ No newline at end of file From a4f462d93cc1b51148e63bcda37f70d43c14b59a Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 16:14:49 -0800 Subject: [PATCH 35/46] wave 2 not finish --- app/models/rental.py | 2 +- app/routes/customer_route.py | 1 + app/routes/rental_route.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/rental.py b/app/models/rental.py index 28f4db7e1..148fd7245 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -17,7 +17,7 @@ def to_dict(self): rental_dict["customer_id"] = self.customer_id rental_dict["video_id"] = self.video_id rental_dict["due_date"] = self.due_date - #rental_dict["check_out_status"] = self.check_out_status + return rental_dict @classmethod diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index c6868aff6..c8c244e2c 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -24,6 +24,7 @@ def validate_model(cls, model_id): #GET /customers @customers_bp.route("", methods=["GET"]) def get_customers(): + customer_query = Customer.query.all() response_body = [] diff --git a/app/routes/rental_route.py b/app/routes/rental_route.py index b075c4f00..0d08ea725 100644 --- a/app/routes/rental_route.py +++ b/app/routes/rental_route.py @@ -15,7 +15,7 @@ @rentals_bp.route("/check-out", methods=["POST"]) def create_rental(): request_body = request.get_json() - + new_rental = Rental.from_dict(request_body) customer = validate_model(Customer, request_body["customer_id"]) video = validate_model(Video, request_body["video_id"]) @@ -28,7 +28,7 @@ def create_rental(): if available_inventory == 0: abort(make_response({"message":f"Could not perform checkout"}, 400)) - new_rental = Rental.from_dict(request_body) + # new_rental = Rental.from_dict(request_body) customer.videos_checked_out_count += 1 db.session.add(new_rental) @@ -66,7 +66,7 @@ def check_in_rental(): customer.videos_checked_out_count -= 1 available_inventory += 1 rental.check_out_status = False - + rental = Rental.from_dict(request_body) db.session.add(rental) From fbdfff3aef24571d4ecaf3e0ba43a8cd35a9b904 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 16:48:09 -0800 Subject: [PATCH 36/46] get sort args --- app/routes/customer_route.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index c8c244e2c..dc8ca91bb 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -24,8 +24,15 @@ def validate_model(cls, model_id): #GET /customers @customers_bp.route("", methods=["GET"]) def get_customers(): - - customer_query = Customer.query.all() + sort_query = request.args.get("sort") + if sort_query == "name": + customer_query = Customer.query.order_by(Customer.name) + elif sort_query == "registered_at": + customer_query = Customer.query.order_by(Customer.registered_at) + elif sort_query == "postal_code": + customer_query = Customer.query.order_by(Customer.postal_code) + else: + customer_query = Customer.query.order_by(Customer.id) response_body = [] for customer in customer_query: From f275f01db063f36468334cf297fcf7b73bf65684 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 17:12:48 -0800 Subject: [PATCH 37/46] add sort --- app/routes/video_route.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index 77078ab51..70af92ff1 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -10,10 +10,19 @@ #GET /videos @videos_bp.route("", methods=["GET"]) def get_videos(): - videos = Video.query.all() + sort_query = request.args.get("sort") + if sort_query == "title": + video_query = Video.query.order_by(Video.name) + elif sort_query == "release_date": + video_query = Video.query.order_by(Video.release_date) + + else: + video_query = Video.query.order_by(Video.id) + + response_body = [] - for video in videos: + for video in video_query: response_body.append(video.to_dict()) return jsonify(response_body) From 9596ea21131eedc94a6ff54a87bbac0cd472610a Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 20:47:48 -0800 Subject: [PATCH 38/46] modify model --- app/models/rental.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/rental.py b/app/models/rental.py index 148fd7245..1d09a8a1e 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -3,8 +3,8 @@ class Rental(db.Model): id = db.Column(db.Integer, primary_key=True) - customer_id = db.Column(db.Integer, db.ForeignKey("customer.id")) - video_id = db.Column(db.Integer, db.ForeignKey("video.id")) + customer_id = db.Column(db.Integer, db.ForeignKey("customer.id"),nullable = False) + video_id = db.Column(db.Integer, db.ForeignKey("video.id"),nullable = False) due_date = db.Column(db.DateTime, default=(datetime.date.today()+datetime.timedelta(days=7))) #videos_checked_out_count = db.Column(db.Integer) available_inventory = db.Column(db.Integer) From 60881e08cdef2f06a7d9cf388c7846a5d260cfe6 Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 20:48:39 -0800 Subject: [PATCH 39/46] finish customer --- app/routes/customer_route.py | 62 ++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/app/routes/customer_route.py b/app/routes/customer_route.py index dc8ca91bb..2d038f036 100644 --- a/app/routes/customer_route.py +++ b/app/routes/customer_route.py @@ -1,5 +1,7 @@ from app import db from app.models.customer import Customer +from app.models.video import Video +from app.models.rental import Rental from flask import Blueprint, jsonify, abort, make_response, request from datetime import datetime @@ -19,6 +21,13 @@ def validate_model(cls, model_id): return model +def validate_num_queries(query_param): + try: + query_int = int(query_param) + except: + return False + return True + #============================== customers_bp.route ============================= #============================================================================ #GET /customers @@ -33,6 +42,27 @@ def get_customers(): customer_query = Customer.query.order_by(Customer.postal_code) else: customer_query = Customer.query.order_by(Customer.id) + + count_query = request.args.get("count") + page_num_query = request.args.get("page_num") + if validate_num_queries(count_query) and validate_num_queries(page_num_query): + page = customer_query.paginate(page=int(page_num_query), per_page=int(count_query), error_out=False) + customers_response = [] + + for items in page.items: + customers_response.append(items.to_dict()) + return jsonify(customers_response), 200 + + if validate_num_queries(count_query) and not validate_num_queries(page_num_query): + page = customer_query.paginate(per_page=int(count_query), error_out=False) + customers = customer_query.all() + customers_response = [] + + for items in page.items: + customers_response.append(items.to_dict()) + return jsonify(customers_response), 200 + + response_body = [] for customer in customer_query: @@ -93,9 +123,37 @@ def delete_customers_by_id(id): @customers_bp.route("//rentals", methods=["GET"]) def get_rentals_by_customer_id(id): customer = validate_model(Customer, id) + + video_query = Rental.query.filter_by(customer_id=customer.id).join(Video) + sort_query = request.args.get("sort") + if sort_query == "title": + rental_query = video_query.order_by(Video.title) + else: + rental_query = video_query.order_by(Video.id) + count_query = request.args.get("count") + page_num_query = request.args.get("page_num") + + if validate_num_queries(count_query) and validate_num_queries(page_num_query): + + page = video_query.paginate(page=int(page_num_query), per_page=int(count_query), error_out=False) + video_result = [] + + for items in page.items: + video_result.append(items.video.to_dict()) + return jsonify(video_result), 200 + if validate_num_queries(count_query) and not validate_num_queries(page_num_query): + page = video_query.paginate(per_page=int(count_query), error_out=False) + video_result = [] + + for items in page.items: + video_result.append(items.video.to_dict()) + return jsonify(video_result), 200 + response_body = [] - for rental in customer.rentals: - response_body.append({"title":rental.video.title}) + for rental in rental_query: + response_body.append({"title":rental.video.title, + "id":rental.video.id, + "total_inventory":rental.video.total_inventory}) return jsonify(response_body) \ No newline at end of file From b420483b4a3f65ad9335a14f2ca151d6beb9c78f Mon Sep 17 00:00:00 2001 From: annadu Date: Sun, 8 Jan 2023 21:00:27 -0800 Subject: [PATCH 40/46] wave 3 done --- app/routes/video_route.py | 40 +++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/app/routes/video_route.py b/app/routes/video_route.py index 70af92ff1..cd350c814 100644 --- a/app/routes/video_route.py +++ b/app/routes/video_route.py @@ -1,8 +1,10 @@ from app import db from app.models.video import Video from app.models.rental import Rental +from app.models.customer import Customer from flask import Blueprint, jsonify, abort, make_response, request from app.routes.customer_route import validate_model +from app.routes.customer_route import validate_num_queries videos_bp = Blueprint("videos_bp",__name__, url_prefix="/videos") #============================== videos_bp.route ============================= @@ -79,9 +81,39 @@ def put_videos_by_id(id): @videos_bp.route("//rentals", methods=["GET"]) def get_rentals_by_video_id(id): video = validate_model(Video, id) - response_body = [] - for rental in video.rentals: - response_body.append({"name":rental.customer.name}) + customer_query = Rental.query.filter_by(video_id=video.id).join(Customer) + sort_query = request.args.get("sort") + if sort_query == "name": + customer_query = customer_query.order_by(Customer.name) + elif sort_query == "postal_code": + customer_query = customer_query.order_by(Customer.postal_code) + elif sort_query == "registered_at": + customer_query = customer_query.order_by(Customer.registered_at) + else: + customer_query = customer_query.order_by(Customer.id) + + count_query = request.args.get("count") + page_num_query = request.args.get("page_num") + if validate_num_queries(count_query) and validate_num_queries(page_num_query): + page = customer_query.paginate(page=int(page_num_query), per_page=int(count_query), error_out=False) + customer_result = [] + + for items in page.items: + customer_result.append(items.customer.to_dict()) + return jsonify(customer_result), 200 + + + if validate_num_queries(count_query) and not validate_num_queries(page_num_query): + page = customer_query.paginate(per_page=int(count_query), error_out=False) + customer_query = customer_query.all() + customer_result = [] + for items in page.items: + customer_result.append(items.customer.to_dict()) + return jsonify(customer_result), 200 - return jsonify(response_body) \ No newline at end of file + customer_result = [] + customer_query = customer_query.all() + for customer in customer_query: + customer_result.append(customer.customer.to_dict()) + return jsonify(customer_result), 200 \ No newline at end of file From ad7cecb762e1eea9d8482ed47f4c8161ef363376 Mon Sep 17 00:00:00 2001 From: annadu Date: Mon, 9 Jan 2023 18:28:16 -0800 Subject: [PATCH 41/46] add rental status --- ...aa2099951a_adds_rental_check_out_status.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 migrations/versions/2daa2099951a_adds_rental_check_out_status.py diff --git a/migrations/versions/2daa2099951a_adds_rental_check_out_status.py b/migrations/versions/2daa2099951a_adds_rental_check_out_status.py new file mode 100644 index 000000000..3b6667bff --- /dev/null +++ b/migrations/versions/2daa2099951a_adds_rental_check_out_status.py @@ -0,0 +1,42 @@ +"""adds rental check out status + +Revision ID: 2daa2099951a +Revises: 362db262353e +Create Date: 2023-01-09 18:13:26.651231 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2daa2099951a' +down_revision = '362db262353e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('customer', sa.Column('videos_checked_out_count', sa.Integer(), nullable=True)) + op.add_column('rental', sa.Column('available_inventory', sa.Integer(), nullable=True)) + op.add_column('rental', sa.Column('check_out_status', sa.Boolean(), nullable=True)) + op.add_column('rental', sa.Column('customer_id', sa.Integer(), nullable=False)) + op.add_column('rental', sa.Column('due_date', sa.DateTime(), nullable=True)) + op.add_column('rental', sa.Column('video_id', sa.Integer(), nullable=False)) + op.create_foreign_key(None, 'rental', 'video', ['video_id'], ['id']) + op.create_foreign_key(None, 'rental', 'customer', ['customer_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'rental', type_='foreignkey') + op.drop_constraint(None, 'rental', type_='foreignkey') + op.drop_column('rental', 'video_id') + op.drop_column('rental', 'due_date') + op.drop_column('rental', 'customer_id') + op.drop_column('rental', 'check_out_status') + op.drop_column('rental', 'available_inventory') + op.drop_column('customer', 'videos_checked_out_count') + # ### end Alembic commands ### From 6a75a34b9d46cc3bfc1d80d4ae838a7ad1e75484 Mon Sep 17 00:00:00 2001 From: annadu Date: Mon, 9 Jan 2023 18:28:58 -0800 Subject: [PATCH 42/46] add validate for check out missing deta --- app/routes/rental_route.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/routes/rental_route.py b/app/routes/rental_route.py index 0d08ea725..83f5e9087 100644 --- a/app/routes/rental_route.py +++ b/app/routes/rental_route.py @@ -15,10 +15,14 @@ @rentals_bp.route("/check-out", methods=["POST"]) def create_rental(): request_body = request.get_json() - new_rental = Rental.from_dict(request_body) + try: + new_rental = Rental.from_dict(request_body) + except: + abort(make_response({"message":f"Missing data"}, 400)) + customer = validate_model(Customer, request_body["customer_id"]) video = validate_model(Video, request_body["video_id"]) - + if video.total_inventory == 0: abort(make_response({"message":f"Could not perform checkout"}, 400)) @@ -49,11 +53,14 @@ def create_rental(): @rentals_bp.route("/check-in", methods=["POST"]) def check_in_rental(): request_body = request.get_json() - + try: + rental = Rental.from_dict(request_body) + except: + abort(make_response({"message":f"Missing data"}, 400)) customer = validate_model(Customer, request_body["customer_id"]) video = validate_model(Video, request_body["video_id"]) - rental = Rental.filter_by(video_id=video.id, customer_id=customer.id).first() + rental = db.session.query(Rental).filter_by(video_id=video.id, customer_id=customer.id).first() if not rental: abort(make_response({"message": f"No outstanding rentals for customer {customer.id} and video {video.id}"}, 400)) @@ -67,7 +74,6 @@ def check_in_rental(): available_inventory += 1 rental.check_out_status = False - rental = Rental.from_dict(request_body) db.session.add(rental) db.session.commit() From f7ad568577929d25bc6330021e3466c10f085b57 Mon Sep 17 00:00:00 2001 From: annadu Date: Mon, 9 Jan 2023 18:29:19 -0800 Subject: [PATCH 43/46] add rental status --- app/models/rental.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/rental.py b/app/models/rental.py index 1d09a8a1e..a5e3302b2 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -3,10 +3,10 @@ class Rental(db.Model): id = db.Column(db.Integer, primary_key=True) - customer_id = db.Column(db.Integer, db.ForeignKey("customer.id"),nullable = False) - video_id = db.Column(db.Integer, db.ForeignKey("video.id"),nullable = False) + customer_id = db.Column(db.Integer, db.ForeignKey("customer.id"),nullable = True) + video_id = db.Column(db.Integer, db.ForeignKey("video.id"),nullable = True) due_date = db.Column(db.DateTime, default=(datetime.date.today()+datetime.timedelta(days=7))) - #videos_checked_out_count = db.Column(db.Integer) + check_out_status = db.Column(db.Boolean,default = True) available_inventory = db.Column(db.Integer) customer = db.relationship("Customer", back_populates="rentals") video = db.relationship("Video", back_populates="rentals") @@ -17,6 +17,7 @@ def to_dict(self): rental_dict["customer_id"] = self.customer_id rental_dict["video_id"] = self.video_id rental_dict["due_date"] = self.due_date + rental_dict["check_out_status"] = self.check_out_status return rental_dict From 29c264144ca5fcf5dce9d8fca74b5dd379160c0f Mon Sep 17 00:00:00 2001 From: annadu Date: Mon, 9 Jan 2023 18:32:29 -0800 Subject: [PATCH 44/46] change foreignkey to nullable true --- ...4_change_rental_foreignkey_to_nullable_.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 migrations/versions/ebd4f39e11f4_change_rental_foreignkey_to_nullable_.py diff --git a/migrations/versions/ebd4f39e11f4_change_rental_foreignkey_to_nullable_.py b/migrations/versions/ebd4f39e11f4_change_rental_foreignkey_to_nullable_.py new file mode 100644 index 000000000..a6a3e94ed --- /dev/null +++ b/migrations/versions/ebd4f39e11f4_change_rental_foreignkey_to_nullable_.py @@ -0,0 +1,38 @@ +"""change rental foreignkey to nullable true + +Revision ID: ebd4f39e11f4 +Revises: 2daa2099951a +Create Date: 2023-01-09 18:30:33.658956 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ebd4f39e11f4' +down_revision = '2daa2099951a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('rental', 'customer_id', + existing_type=sa.INTEGER(), + nullable=True) + op.alter_column('rental', 'video_id', + existing_type=sa.INTEGER(), + nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('rental', 'video_id', + existing_type=sa.INTEGER(), + nullable=False) + op.alter_column('rental', 'customer_id', + existing_type=sa.INTEGER(), + nullable=False) + # ### end Alembic commands ### From 4d247cf779d1dcd65c1ee2a56e65be2710593438 Mon Sep 17 00:00:00 2001 From: annadu Date: Mon, 9 Jan 2023 18:49:22 -0800 Subject: [PATCH 45/46] modify --- app/models/customer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/customer.py b/app/models/customer.py index de03def4e..ae179c1a3 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -11,7 +11,6 @@ class Customer(db.Model): rentals = db.relationship("Rental", back_populates="customer") - def to_dict(self): customer_dict = {} customer_dict["id"] = self.id From 3815b0daa650c99401f9237992a81b7337919b0d Mon Sep 17 00:00:00 2001 From: annadu Date: Mon, 9 Jan 2023 18:58:52 -0800 Subject: [PATCH 46/46] add gunicorn --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 89e00b497..971fcb7d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ click==7.1.2 Flask==1.1.2 Flask-Migrate==2.6.0 Flask-SQLAlchemy==2.4.4 +gunicorn==20.1.0 idna==2.10 iniconfig==1.1.1 itsdangerous==1.1.0 @@ -28,5 +29,6 @@ requests==2.25.1 six==1.15.0 SQLAlchemy==1.3.23 toml==0.10.2 +tomli==2.0.1 urllib3==1.26.5 Werkzeug==1.0.1