Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maple - Stephanie & Ayaka #42

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def create_app(test_config=None):
app = Flask(__name__)
app.url_map.strict_slashes = False
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ECHO"] = True

if test_config is None:
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
Expand All @@ -21,7 +22,7 @@ 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
Expand All @@ -32,5 +33,9 @@ def create_app(test_config=None):
migrate.init_app(app, db)

#Register Blueprints Here
from .routes import videos_bp, customers_bp, rentals_bp
app.register_blueprint(videos_bp)
app.register_blueprint(customers_bp)
app.register_blueprint(rentals_bp)

return app
return app
15 changes: 14 additions & 1 deletion app/models/customer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
from app import db

class Customer(db.Model):
id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True)

Choose a reason for hiding this comment

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

👍

name = db.Column(db.String(40))
postal_code = db.Column(db.String)
phone = db.Column(db.String)
register_at = db.Column(db.DateTime)
videos = db.relationship("Video", secondary="rental", backref="customers")

def to_dict(self):

Choose a reason for hiding this comment

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

👍 yay model methods!

return {
"id": self.id,
"name": self.name,
"phone": self.phone,
"postal_code": self.postal_code
}
17 changes: 16 additions & 1 deletion app/models/rental.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
from app import db
from app.models.video import Video

class Rental(db.Model):
id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True)

Choose a reason for hiding this comment

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

👍

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)
is_checked_in = db.Column(db.Boolean, default=False)

def to_dict(self):

Choose a reason for hiding this comment

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

👍

video = Video.query.get(self.video_id)
checked_out = Rental.query.filter_by(video_id = self.video_id, is_checked_in=False).all()

Choose a reason for hiding this comment

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

I like how you both used queries to dynamically calculate your JSON data!

return {
"video_id": self.video_id,
"customer_id": self.customer_id,
"videos_checked_out_count": len(checked_out),
"available_inventory": video.total_inventory - len(checked_out)
}
18 changes: 17 additions & 1 deletion app/models/video.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
from app import db

class Video(db.Model):
id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True)

Choose a reason for hiding this comment

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

👍

title = db.Column(db.String)
release_date = db.Column(db.DateTime)
total_inventory = db.Column(db.Integer)

def to_dict(self):
return {
"id": self.id,
"title": self.title,
"total_inventory": self.total_inventory
}

def to_dict_using_rentals(self):
return {
"title": self.title,
"release_date": self.release_date
}
Empty file removed app/routes.py
Empty file.
3 changes: 3 additions & 0 deletions app/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .video_routes import videos_bp
from .customer_routes import customers_bp
from .rental_routes import rentals_bp
99 changes: 99 additions & 0 deletions app/routes/customer_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from app import db
from app.models.customer import Customer
from app.models.rental import Rental
from flask import Blueprint, request, jsonify

customers_bp = Blueprint("customers", __name__, url_prefix="/customers")

@customers_bp.route("", methods=["GET"])
def get_all_customers():

Choose a reason for hiding this comment

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

👍

customers = Customer.query.all()
response_body = [customer.to_dict() for customer in customers]
return jsonify(response_body)

Choose a reason for hiding this comment

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

solely for consistency's sake between all routes, don't forget to add a status code here

Suggested change
return jsonify(response_body)
return jsonify(response_body), 200


@customers_bp.route("", methods=["POST"])

Choose a reason for hiding this comment

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

👍 I see you separated each method out into its own route! nice

def create_customer():
request_body = request.get_json()

error_message = validate_request(request_body)
if error_message:
return error_message

new_customer = Customer(
name= request_body["name"],
phone= request_body["phone"],
postal_code= request_body["postal_code"]
)

db.session.add(new_customer)
db.session.commit()

return new_customer.to_dict(), 201

@customers_bp.route("/<customer_id>", methods=["GET"])

Choose a reason for hiding this comment

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

👍

def get_customer(customer_id):
try:
customer = Customer.query.get(customer_id)
except:
return jsonify(None), 400

if customer is None:
return {"message": f"Customer {customer_id} was not found"}, 404

return customer.to_dict()

Choose a reason for hiding this comment

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

Suggested change
return customer.to_dict()
return customer.to_dict(), 200


@customers_bp.route("/<customer_id>", methods=["DELETE"])

Choose a reason for hiding this comment

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

👍

def delete_customer(customer_id):
customer = Customer.query.get(customer_id)

if customer is None:
return {"message": f"Customer {customer_id} was not found"}, 404

db.session.delete(customer)
db.session.commit()

return customer.to_dict()

Choose a reason for hiding this comment

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

Suggested change
return customer.to_dict()
return customer.to_dict(), 200


@customers_bp.route("/<customer_id>", methods=["PUT"])

Choose a reason for hiding this comment

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

👍

def update_customer(customer_id):
customer = Customer.query.get(customer_id)
request_body = request.get_json()

if customer is None:
return {"message": f"Customer {customer_id} was not found"}, 404

error_message = validate_request(request_body)
if error_message:
return error_message

Choose a reason for hiding this comment

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

Suggested change
return error_message
return error_message
```, 400


customer.name = request_body["name"]
customer.phone = request_body["phone"]
customer.postal_code = request_body["postal_code"]

db.session.commit()

return customer.to_dict()

@customers_bp.route("/<customer_id>/rentals", methods=["GET"])

Choose a reason for hiding this comment

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

👍

def get_rentals_by_customer(customer_id):
customer = Customer.query.get(customer_id)

if not customer:
return {"message": f"Customer {customer_id} was not found"}, 404

videos= []
for video in customer.videos:
rental = Rental.query.filter_by(customer_id = customer.id, video_id = video.id).first()
video = video.to_dict_using_rentals()
video["due_date"] = rental.due_date
videos.append(video)

return jsonify(videos)

Choose a reason for hiding this comment

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

Suggested change
return jsonify(videos)
return jsonify(videos), 200


def validate_request(request_body):

Choose a reason for hiding this comment

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

👍 one thing we could do is create a separate file called utilities.py or helpers.py where we store any helper functions we might use outside the route file

attributes = ["postal_code", "name", "phone"]
for attribute in attributes:
if attribute not in request_body:
return {"details": f"Request body must include {attribute}."}, 400


51 changes: 51 additions & 0 deletions app/routes/rental_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from app import db
from app.models.rental import Rental
from app.models.customer import Customer
from app.models.video import Video
from flask import Blueprint, request, jsonify
from datetime import datetime, timedelta

rentals_bp = Blueprint("rentals", __name__, url_prefix="/rentals")

@rentals_bp.route("/check-out", methods=["POST"])

Choose a reason for hiding this comment

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

👍

def check_out_video():
request_body = request.get_json()

try:
customer = Customer.query.get_or_404(request_body["customer_id"])
video = Video.query.get_or_404(request_body["video_id"])
except KeyError:
return jsonify(None), 400
Comment on lines +14 to +18

Choose a reason for hiding this comment

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

since you are using get_or_404 here, technically you wouldn't need to use the try as well. get_or_404 will take care of any errors for you and return them in an HTML response body.

I think sticking with try/except and a basic get would be a better combo!


if video.total_inventory - len(video.customers) == 0:
return {"message": "Could not perform checkout"}, 400

new_rental = Rental(
customer_id = customer.id,
video_id = video.id,
due_date = datetime.now() + timedelta(days=7)
)

db.session.add(new_rental)
db.session.commit()

return jsonify(new_rental.to_dict())

Choose a reason for hiding this comment

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

Suggested change
return jsonify(new_rental.to_dict())
return jsonify(new_rental.to_dict()), 201


@rentals_bp.route("/check-in", methods=["POST"])

Choose a reason for hiding this comment

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

👍

def check_in_video():
request_body = request.get_json()

try:
customer = Customer.query.get_or_404(request_body["customer_id"])
video = Video.query.get_or_404(request_body["video_id"])
except KeyError:
return jsonify(None), 400
Comment on lines +38 to +42

Choose a reason for hiding this comment

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

same idea as above! let's go with try/except and get method


rental = Rental.query.filter_by(customer_id = customer.id, video_id = video.id).first()
if not rental:
return {"message": f"No outstanding rentals for customer {customer.id} and video {video.id}"}, 400

rental.is_checked_in = True
db.session.commit()

return jsonify(rental.to_dict())
99 changes: 99 additions & 0 deletions app/routes/video_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from app import db
from app.models.video import Video
from app.models.rental import Rental
from flask import Blueprint, request, jsonify

videos_bp = Blueprint("videos", __name__, url_prefix="/videos")

@videos_bp.route("", methods=["GET"])

Choose a reason for hiding this comment

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

👍

def get_all_videos():
videos = Video.query.all()
response_body = [video.to_dict() for video in videos]
return jsonify(response_body)

Choose a reason for hiding this comment

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

Suggested change
return jsonify(response_body)
return jsonify(response_body), 200


@videos_bp.route("", methods=["POST"])

Choose a reason for hiding this comment

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

👍

def create_video():
request_body = request.get_json()

error_message = validate_request(request_body)
if error_message:
return error_message

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()

return jsonify(new_video.to_dict()), 201


@videos_bp.route("/<video_id>", methods=["GET"])
def get_video(video_id):
try:
video = Video.query.get(video_id)
except:
return jsonify(None), 400

if video is None:
return jsonify({"message": f"Video {video_id} was not found"}), 404

return video.to_dict()

Choose a reason for hiding this comment

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

Suggested change
return video.to_dict()
return video.to_dict(), 200



@videos_bp.route("/<video_id>", methods=["DELETE"])

Choose a reason for hiding this comment

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

👍

def delete_video(video_id):
video = Video.query.get(video_id)

if video is None:
return {"message": f"Video {video_id} was not found"}, 404

db.session.delete(video)
db.session.commit()

return video.to_dict()

Choose a reason for hiding this comment

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

Suggested change
return video.to_dict()
return video.to_dict(), 200


@videos_bp.route("/<video_id>", methods=["PUT"])

Choose a reason for hiding this comment

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

👍

def update_video(video_id):
request_body = request.get_json()
video = Video.query.get(video_id)

if video is None:
return {"message": f"Video {video_id} was not found"}, 404

error_message = validate_request(request_body)
if error_message:
return error_message

video.title = request_body["title"]
video.total_inventory = request_body["total_inventory"]
video.release_date = request_body["release_date"]

db.session.commit()

return video.to_dict()

Choose a reason for hiding this comment

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

Suggested change
return video.to_dict()
return video.to_dict(), 200


@videos_bp.route("/<video_id>/rentals", methods=["GET"])

Choose a reason for hiding this comment

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

👍

def get_rentals_by_video(video_id):
video = Video.query.get(video_id)

if video is None:
return {"message": f"Video {video_id} was not found"}, 404

customers = []
for customer in video.customers:
rental = Rental.query.filter_by(customer_id = customer.id, video_id = video_id).first()
customer = customer.to_dict()
customer["due_date"] = rental.due_date
customers.append(customer)

return jsonify(customers)

Choose a reason for hiding this comment

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

Suggested change
return jsonify(customers)
return jsonify(customers), 200


def validate_request(request_body):
attributes = ["title", "release_date", "total_inventory"]

for attribute in attributes:
if attribute not in request_body:
return {"details": f"Request body must include {attribute}."}, 400
1 change: 1 addition & 0 deletions migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
Loading