forked from AdaGold/retro-video-store
-
Notifications
You must be signed in to change notification settings - Fork 15
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
Ocelots - Megan M & Xuan Hien Pham #11
Open
maple-megan333
wants to merge
32
commits into
ada-ac2:main
Choose a base branch
from
maple-megan333:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
e5e4a0c
Set up envoirnment and DB for first wave
maple-megan333 15e74ab
Updated to seperate routes
maple-megan333 8c993d0
First models complete.
maple-megan333 cac0747
Made dictionary methods for models!
maple-megan333 3443973
CRUD for video
HienXuanPham 1e54c81
working on customer
maple-megan333 b80a7c1
Merge branch 'main' of https://github.com/maple-megan333/retro-video-…
maple-megan333 4ffae47
Post and get complete
maple-megan333 30850f7
customer deleted completed
maple-megan333 d08fca4
Finished put method!
maple-megan333 3622132
Updated database and added Video Model.
maple-megan333 08ab095
modify video_routes to pass all tests in test_wave_01
HienXuanPham 1d76b94
Tweaked rentals and started rental Routes.
maple-megan333 6462e46
passed first 3 rental tests!
maple-megan333 75476aa
bacref > back_populates
maple-megan333 109796c
videos back_populate upgrade.
maple-megan333 01a8a85
passed all checkout tests.
maple-megan333 12840e7
dynamic calculation of available inventory added.
maple-megan333 879db46
rentals passes all checkin methods!
maple-megan333 f9d491c
finished rentals by video
maple-megan333 2b45c78
Wave 2 complete
maple-megan333 fc894c5
working through wave 3
maple-megan333 73105b0
trying to do current tests.
maple-megan333 53fa5b6
continuing ot try and debug.
maple-megan333 b406f87
cleaned up
maple-megan333 b11a185
still tryiing to debug videos_routes.
maple-megan333 c4471a2
morning debugging.
maple-megan333 894e2f0
passes wave 3 minus history!
maple-megan333 25e2c5f
created refactor branch and added routes history for customer and video
HienXuanPham a88658c
gunicorn on my system
maple-megan333 b05a5bf
finsihed deploying on personal heroku
maple-megan333 ae1a7e7
finsiehd deploying personal heroku
maple-megan333 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,31 @@ | ||
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) | ||
postal_code = db.Column(db.String) | ||
phone = db.Column(db.String) | ||
registered_at = db.Column(db.DateTime, default=datetime.datetime.now()) | ||
videos_checked_out_count = db.Column(db.Integer, default=0) | ||
videos = db.relationship("Video", secondary="rentals", back_populates="customers") | ||
|
||
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["postal_code"]=self.postal_code | ||
customer_dict["phone"] = self.phone | ||
|
||
return customer_dict | ||
|
||
|
||
@classmethod | ||
def from_dict(cls,customer_data): | ||
new_customer = Customer( | ||
name=customer_data["name"], | ||
postal_code=customer_data["postal_code"], | ||
phone=customer_data["phone"], | ||
) | ||
return new_customer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,40 @@ | ||
from app import db | ||
import datetime | ||
import enum | ||
from sqlalchemy import Enum | ||
|
||
|
||
class Rental(db.Model): | ||
id = db.Column(db.Integer, primary_key=True) | ||
|
||
class RentalStatus(enum.Enum): | ||
CHECKIN="checked_in" | ||
CHECKOUT="checked_out" | ||
|
||
|
||
__tablename__ = "rentals" | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
video_id = db.Column(db.Integer, db.ForeignKey('video.id'), primary_key=True,nullable=False) | ||
customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'), primary_key=True,nullable=False) | ||
status = db.Column(db.Enum(RentalStatus), default=RentalStatus.CHECKOUT) | ||
checkout_date = db.Column(db.DateTime, default=datetime.datetime.now()) | ||
due_date = db.Column(db.DateTime, default=(datetime.date.today()+datetime.timedelta(days=7))) | ||
|
||
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["checkout_date"] = self.checkout_date | ||
rental_dict["status"] = self.status | ||
rental_dict["due_date"] = self.due_date | ||
|
||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,28 @@ | ||
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.Date) | ||
total_inventory = db.Column(db.Integer) | ||
customers = db.relationship("Customer", secondary="rentals", back_populates="videos") | ||
|
||
|
||
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 | ||
|
||
return video_dict | ||
|
||
@classmethod | ||
def from_dict(cls, video_data): | ||
new_video = Video( | ||
title=video_data["title"], | ||
release_date=video_data["release_date"], | ||
total_inventory=video_data["total_inventory"] | ||
) | ||
|
||
return new_video |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
from app import db | ||
from app.models.customer import Customer | ||
from app.models.video import Video | ||
from app.models.rental import Rental | ||
from app.routes.rental_routes import query_rentals | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
from app.routes.helper_functions import validate_model, custom_query | ||
|
||
customers_bp = Blueprint("customers_bp", __name__, url_prefix="/customers") | ||
|
||
@customers_bp.route("", methods=["POST"]) | ||
def create_customer(): | ||
request_body = request.get_json() | ||
try: | ||
|
||
new_customer = Customer.from_dict(request_body) | ||
except KeyError as key_error: | ||
abort(make_response({"details":f"Request body must include {key_error.args[0]}."}, 400)) | ||
|
||
db.session.add(new_customer) | ||
db.session.commit() | ||
return make_response(new_customer.to_dict(), 201) | ||
|
||
#queries customers appends dictionaries, jysonifys list. | ||
|
||
@customers_bp.route('', methods=["GET"]) | ||
def get_all_customer(): | ||
customers=custom_query(Customer,['id','name','registered_at','postal_code']) | ||
customer_response = [] | ||
for customer in customers: | ||
customer_response.append(customer.to_dict()) | ||
return jsonify(customer_response) | ||
|
||
#get customer by id | ||
@customers_bp.route("/<customer_id>", methods=["GET"]) | ||
def get_one_customer(customer_id): | ||
customer = validate_model(Customer, customer_id) | ||
return customer.to_dict() | ||
|
||
#route to delete | ||
@customers_bp.route("/<customer_id>", methods=["DELETE"]) | ||
def delete_customer(customer_id): | ||
customer = validate_model(Customer, customer_id) | ||
|
||
db.session.delete(customer) | ||
db.session.commit() | ||
|
||
return make_response(jsonify(customer.to_dict()), 200) | ||
|
||
#update route | ||
@customers_bp.route("/<customer_id>", methods=["PUT"]) | ||
def update_customer(customer_id): | ||
customer=validate_model(Customer, customer_id) | ||
|
||
request_body = request.get_json() | ||
try: | ||
|
||
customer.name=request_body["name"] | ||
customer.phone=request_body["phone"] | ||
customer.postal_code=request_body["postal_code"] | ||
except KeyError as key_error: | ||
abort(make_response({"details":f"Request body must include {key_error.args[0]}."}, 400)) | ||
|
||
db.session.add(customer) | ||
db.session.commit() | ||
|
||
return make_response(customer.to_dict(), 200) | ||
|
||
@customers_bp.route("/<customer_id>/rentals", methods=["GET"]) | ||
def get_rentals_by_customer(customer_id): | ||
customer = validate_model(Customer, customer_id) | ||
query = custom_query(Rental,['id','title','release_date'],{"customer_id":customer.id, "status": Rental.RentalStatus.CHECKOUT}) | ||
|
||
response=[] | ||
for rental in query: | ||
video = validate_model(Video, rental.video_id) | ||
rental_info = video.to_dict() | ||
rental_info["due_date"] = rental.due_date | ||
response.append(rental_info) | ||
return jsonify(response) | ||
|
||
@customers_bp.route("/<customer_id>/history", methods=["GET"]) | ||
def get_video_had_been_checked_out(customer_id): | ||
customer = validate_model(Customer, customer_id) | ||
videos = custom_query(Rental,['id','title','release_date'],{"customer_id":customer.id, "status": Rental.RentalStatus.CHECKIN}) | ||
response = [] | ||
rental_info = {} | ||
for rental in videos: | ||
video = validate_model(Video, rental.video_id) | ||
rental_info["title"] = video.title | ||
rental_info["checkout_date"] = rental.checkout_date | ||
rental_info["due_date"] = rental.due_date | ||
response.append(rental_info) | ||
return jsonify(response) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
from app.models.rental import Rental | ||
from app.models.customer import Customer | ||
from app.models.video import Video | ||
|
||
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} was not found"}, 404)) | ||
|
||
return model | ||
|
||
def custom_query(cls, approvedsortinig, filters={}): | ||
#list of accepted sort paramas | ||
valid_sort=(approvedsortinig) | ||
custom_querys=None | ||
|
||
#getting sort and pagnation args, with defults and types | ||
sort=request.args.get('sort', 'id') | ||
page = None | ||
count=None | ||
|
||
if request.args.get('page_num'): | ||
page=request.args.get('page_num', 1, type=int) | ||
else: page=1 | ||
|
||
if request.args.get("count"): | ||
count=request.args.get("count", 100, type=int) | ||
else: count=100 | ||
|
||
#making id if not valid. | ||
if sort not in valid_sort: sort= 'id' | ||
#checking to see if class is the orderby attricute | ||
order_cls=cls | ||
|
||
if cls is Rental: | ||
join_class=None | ||
if filters.get("customer_id"): | ||
join_class=Video | ||
else: | ||
join_class=Customer | ||
|
||
if not hasattr(cls,sort): | ||
find_att=[Customer,Video,Rental] | ||
for object in find_att: | ||
if hasattr(object,sort): | ||
order_cls=object | ||
break | ||
|
||
custom_querys=cls.query.filter_by(**filters).join(join_class).order_by( | ||
getattr(order_cls,sort)).paginate(page=page,per_page=count,error_out=False) | ||
else: | ||
custom_querys=cls.query.order_by(getattr( | ||
order_cls,sort)).paginate(page,count,False) | ||
|
||
query=custom_querys.items | ||
return query |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from app import db | ||
from app.models.rental import Rental | ||
from app.models.video import Video | ||
from app.models.customer import Customer | ||
from flask import Blueprint, jsonify, abort, make_response, request | ||
|
||
rentals_bp = Blueprint("rentals_bp", __name__, url_prefix="/rentals") | ||
|
||
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} was not found"}, 404)) | ||
|
||
return model | ||
|
||
#helper query function: | ||
def query_rentals(filters): | ||
query_results = db.session.query(Rental).filter_by(**filters).all() | ||
return query_results | ||
|
||
#extract results | ||
def extract_query(query_results): | ||
response = [] | ||
for object in query_results: | ||
response.append(object.to_dict()) | ||
return response | ||
|
||
#calculate avail inventory | ||
def availabl_inventory(video): | ||
vids_out = query_rentals({"video_id":video.id, "status":Rental.RentalStatus.CHECKOUT}) | ||
return video.total_inventory-len(vids_out) | ||
|
||
#response helper function | ||
def rental_response(rental,customer,video): | ||
rental_response={} | ||
rental_response["video_id"]=video.id | ||
rental_response["customer_id"]=customer.id | ||
rental_response["videos_checked_out_count"]=customer.videos_checked_out_count | ||
rental_response["available_inventory"] = availabl_inventory(video) #here we need to subtract all rentals associated with video | ||
return rental_response | ||
|
||
#should make a query method that can take in video_id,customer_id and status | ||
|
||
@rentals_bp.route("/check-out", methods=["POST"]) | ||
def video_checkout(): | ||
request_body=request.get_json() | ||
#put into rental model as to_dictionary | ||
try: | ||
customer = validate_model(Customer, request_body["customer_id"]) | ||
video = validate_model(Video, request_body["video_id"]) | ||
if availabl_inventory(video)==0: | ||
abort(make_response({"message":"Could not perform checkout"}, 400)) | ||
new_rental=Rental.from_dict(request_body) | ||
customer.videos_checked_out_count +=1 | ||
|
||
except KeyError as key_error: | ||
abort(make_response({"details":f"Request body must include {key_error.args[0]}."}, 400)) | ||
|
||
db.session.add_all([new_rental, customer]) | ||
|
||
db.session.commit() | ||
|
||
return make_response(rental_response(new_rental,customer,video), 200) | ||
|
||
@rentals_bp.route("/check-in", methods=["POST"]) | ||
def checkin_video(): | ||
request_body = request.get_json() | ||
|
||
try: | ||
customer = validate_model(Customer, request_body["customer_id"]) | ||
video = validate_model(Video, request_body["video_id"]) | ||
rental_query = query_rentals({"video_id": video.id, "customer_id":customer.id, "status": Rental.RentalStatus.CHECKOUT}) | ||
if not rental_query: | ||
abort(make_response({"message":"No outstanding rentals for customer 1 and video 1"}, 400)) | ||
rental=rental_query[0] | ||
rental.status=Rental.RentalStatus.CHECKIN | ||
customer.videos_checked_out_count -=1 | ||
except KeyError as key_error: | ||
abort(make_response({"details":f"Request body must include {key_error.args[0]}."}, 400)) | ||
|
||
db.session.add_all([rental, customer]) | ||
|
||
db.session.commit() | ||
|
||
return make_response(rental_response(rental,customer,video), 200) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
really well done, adding these optional enhancements!