├── api ├── __init__.py └── v1 │ ├── __init__.py │ ├── views │ ├── documentation │ │ ├── city │ │ │ ├── delete_city.yml │ │ │ ├── put_city.yml │ │ │ ├── post_city.yml │ │ │ ├── get_city.yml │ │ │ └── cities_by_state.yml │ │ ├── user │ │ │ ├── delete_user.yml │ │ │ ├── put_user.yml │ │ │ ├── post_user.yml │ │ │ ├── all_users.yml │ │ │ └── get_user.yml │ │ ├── place │ │ │ ├── delete_place.yml │ │ │ ├── put_place.yml │ │ │ ├── post_place.yml │ │ │ ├── post_search.yml │ │ │ ├── get_place.yml │ │ │ └── get_places.yml │ │ ├── state │ │ │ ├── delete_state.yml │ │ │ ├── post_state.yml │ │ │ ├── put_state.yml │ │ │ ├── get_state.yml │ │ │ └── get_id_state.yml │ │ ├── reviews │ │ │ ├── delete_reviews.yml │ │ │ ├── put_reviews.yml │ │ │ ├── post_reviews.yml │ │ │ ├── get_review.yml │ │ │ └── get_reviews.yml │ │ ├── amenity │ │ │ ├── post_amenity.yml │ │ │ ├── put_amenity.yml │ │ │ ├── delete_amenity.yml │ │ │ ├── all_amenities.yml │ │ │ └── get_amenity.yml │ │ └── place_amenity │ │ │ ├── delete_place_amenities.yml │ │ │ ├── post_place_amenities.yml │ │ │ └── get_places_amenities.yml │ ├── __init__.py │ ├── index.py │ ├── states.py │ ├── users.py │ ├── amenities.py │ ├── places_amenities.py │ ├── cities.py │ └── places_reviews.py │ └── app.py ├── tests ├── __init__.py ├── test_models │ ├── __init__.py │ └── test_engine │ │ └── __init__.py └── test_console.py ├── web_dynamic ├── __init__.py ├── static │ ├── images │ │ ├── icon.png │ │ ├── logo.png │ │ ├── icon_bed.png │ │ ├── icon_bath.png │ │ └── icon_group.png │ ├── styles │ │ ├── 4-common.css │ │ ├── 3-footer.css │ │ ├── 3-header.css │ │ ├── 6-filters.css │ │ ├── 8-places.css │ │ └── w3c_validator.py │ └── scripts │ │ ├── 1-hbnb.js │ │ ├── 2-hbnb.js │ │ ├── 3-hbnb.js │ │ ├── 4-hbnb.js │ │ ├── 100-hbnb.js │ │ └── 101-hbnb.js ├── 0-hbnb.py ├── 1-hbnb.py ├── 2-hbnb.py ├── 3-hbnb.py ├── 4-hbnb.py ├── 100-hbnb.py ├── 101-hbnb.py └── templates │ ├── 0-hbnb.html │ ├── 1-hbnb.html │ ├── 2-hbnb.html │ ├── 3-hbnb.html │ ├── 4-hbnb.html │ ├── 100-hbnb.html │ └── 101-hbnb.html ├── web_flask ├── __init__.py ├── templates │ ├── 100-hbnb.html~ │ ├── 5-number.html │ ├── 6-number_odd_or_even.html │ ├── 7-states_list.html │ ├── 8-cities_by_states.html │ ├── 9-states.html │ ├── 10-hbnb_filters.html │ ├── 100-hbnb.html │ └── w3c_validator.py ├── README.md ├── static │ ├── images │ │ ├── icon.png │ │ ├── logo.png │ │ ├── icon_bath.png │ │ ├── icon_bed.png │ │ └── icon_group.png │ └── styles │ │ ├── 3-header.css │ │ ├── 4-common.css │ │ ├── 3-footer.css │ │ ├── 6-filters.css │ │ ├── 8-places.css │ │ └── w3c_validator.py ├── 0-hello_route.py ├── 1-hbnb_route.py ├── 2-c_route.py ├── 7-states_list.py ├── 3-python_route.py ├── 4-number_route.py ├── 10-hbnb_filters.py ├── 100-hbnb.py ├── 8-cities_by_states.py ├── 9-states.py ├── 5-number_template.py └── 6-number_odd_or_even.py ├── code_review.txt ├── models ├── engine │ ├── __init__.py │ ├── file_storage.py │ └── db_storage.py ├── __init__.py ├── amenity.py ├── review.py ├── city.py ├── state.py ├── user.py ├── base_model.py └── place.py ├── web_static ├── README.md ├── styles │ ├── 2-common.css │ ├── 2-header.css │ ├── 102-header.css │ ├── 103-header.css │ ├── 3-header.css │ ├── 2-footer.css │ ├── 3-footer.css │ ├── 102-footer.css │ ├── 103-footer.css │ ├── 3-common.css │ ├── 4-common.css │ ├── 102-common.css │ ├── 103-common.css │ ├── 7-places.css │ ├── 4-filters.css │ ├── 5-filters.css │ ├── 6-filters.css │ ├── 8-places.css │ ├── 100-places.css │ ├── 101-places.css │ ├── 102-places.css~ │ ├── 102-filters.css │ ├── 102-places.css │ └── 103-filters.css ├── images │ ├── icon.png │ ├── logo.png │ ├── icon_bed.png │ ├── icon_tv.png │ ├── icon_bath.png │ ├── icon_group.png │ ├── icon_pets.png │ └── icon_wifi.png ├── 2-index.html ├── 3-index.html ├── 0-index.html ├── 4-index.html ├── 1-index.html ├── 5-index.html ├── 6-index.html ├── 7-index.html └── w3c_validator.py ├── AUTHORS ├── setup_mysql_dev.sql ├── setup_mysql_test.sql ├── 0-setup_web_static.sh ├── 1-pack_web_static.py ├── 2-do_deploy_web_static.py └── 3-deploy_web_static.py /api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web_dynamic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web_flask/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code_review.txt: -------------------------------------------------------------------------------- 1 | jzamora5 2 | -------------------------------------------------------------------------------- /models/engine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web_flask/templates/100-hbnb.html~: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_models/test_engine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web_static/README.md: -------------------------------------------------------------------------------- 1 | ** Web Static AirBnB 2 | -------------------------------------------------------------------------------- /web_flask/README.md: -------------------------------------------------------------------------------- 1 | # Web Framework with Flask 2 | -------------------------------------------------------------------------------- /web_static/styles/2-common.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | -------------------------------------------------------------------------------- /web_static/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon.png -------------------------------------------------------------------------------- /web_static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/logo.png -------------------------------------------------------------------------------- /web_static/images/icon_bed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon_bed.png -------------------------------------------------------------------------------- /web_static/images/icon_tv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon_tv.png -------------------------------------------------------------------------------- /web_flask/static/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_flask/static/images/icon.png -------------------------------------------------------------------------------- /web_flask/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_flask/static/images/logo.png -------------------------------------------------------------------------------- /web_static/images/icon_bath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon_bath.png -------------------------------------------------------------------------------- /web_static/images/icon_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon_group.png -------------------------------------------------------------------------------- /web_static/images/icon_pets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon_pets.png -------------------------------------------------------------------------------- /web_static/images/icon_wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_static/images/icon_wifi.png -------------------------------------------------------------------------------- /web_dynamic/static/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_dynamic/static/images/icon.png -------------------------------------------------------------------------------- /web_dynamic/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_dynamic/static/images/logo.png -------------------------------------------------------------------------------- /web_dynamic/static/images/icon_bed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_dynamic/static/images/icon_bed.png -------------------------------------------------------------------------------- /web_flask/static/images/icon_bath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_flask/static/images/icon_bath.png -------------------------------------------------------------------------------- /web_flask/static/images/icon_bed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_flask/static/images/icon_bed.png -------------------------------------------------------------------------------- /web_flask/static/images/icon_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_flask/static/images/icon_group.png -------------------------------------------------------------------------------- /web_static/styles/2-header.css: -------------------------------------------------------------------------------- 1 | header { 2 | background-color: #FF0000; 3 | height: 70px; 4 | width: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /web_dynamic/static/images/icon_bath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_dynamic/static/images/icon_bath.png -------------------------------------------------------------------------------- /web_dynamic/static/images/icon_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor0089/AirBnB_clone_v4/HEAD/web_dynamic/static/images/icon_group.png -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This file lists all individuals having contributed content to the repository. 2 | 3 | 4 | Jennifer Huang <133@holbertonschool.com> 5 | Alexa Orrico <210@holbertonschool.com> 6 | Joann Vuong <130@holbertonschool.com> 7 | -------------------------------------------------------------------------------- /web_flask/templates/5-number.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HBNB 5 | 6 | 7 |

Number: {{ value }}

8 | 9 | 10 | -------------------------------------------------------------------------------- /web_flask/static/styles/3-header.css: -------------------------------------------------------------------------------- 1 | header { 2 | background: white; 3 | height: 70px; 4 | width: 100%; 5 | border-bottom: 1px solid #CCCCCC; 6 | } 7 | header .logo { 8 | background: url("../images/logo.png") no-repeat; 9 | left: 20px; 10 | height: 100%; 11 | } 12 | -------------------------------------------------------------------------------- /web_dynamic/static/styles/4-common.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | color: #484848; 5 | font-size: 14px; 6 | font-family: Circular,"Helvetica Neue",Helvetica,Arial,sans-serif; 7 | } 8 | body .container { 9 | max-width: 1000px; 10 | margin: 30px auto; 11 | } 12 | -------------------------------------------------------------------------------- /web_flask/static/styles/4-common.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | color: #484848; 5 | font-size: 14px; 6 | font-family: Circular,"Helvetica Neue",Helvetica,Arial,sans-serif; 7 | } 8 | body .container { 9 | max-width: 1000px; 10 | margin: 30px auto; 11 | } 12 | -------------------------------------------------------------------------------- /web_static/styles/102-header.css: -------------------------------------------------------------------------------- 1 | header { 2 | height: 70px; 3 | width: 100%; 4 | border-bottom: 1px solid #CCCCCC; 5 | background: white url(../images/logo.png); 6 | background-repeat: no-repeat; 7 | background-position: 20px center; 8 | flex-shrink: 0; 9 | } 10 | -------------------------------------------------------------------------------- /web_static/styles/103-header.css: -------------------------------------------------------------------------------- 1 | header { 2 | height: 70px; 3 | width: 100%; 4 | border-bottom: 1px solid #575757; 5 | background: white url(../images/logo.png); 6 | background-repeat: no-repeat; 7 | background-position: 20px center; 8 | flex-shrink: 0; 9 | } 10 | -------------------------------------------------------------------------------- /web_static/styles/3-header.css: -------------------------------------------------------------------------------- 1 | header { 2 | height: 70px; 3 | width: 100%; 4 | border-bottom: 1px solid #CCCCCC; 5 | background: white url(../images/logo.png); 6 | background-repeat: no-repeat; 7 | background-position: 20px center; 8 | flex-shrink: 0; 9 | } 10 | -------------------------------------------------------------------------------- /web_static/styles/2-footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | 3 | background-color: #00FF00; 4 | height: 60px; 5 | width: 100%; 6 | position: fixed; 7 | display:flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | align-items: center; 11 | bottom: 0; 12 | } 13 | -------------------------------------------------------------------------------- /web_static/styles/3-footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | 3 | background-color: white; 4 | height: 60px; 5 | width: 100%; 6 | border-top: 1px solid #CCCCCC; 7 | display:flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | align-items: center; 11 | bottom: 0; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web_static/styles/102-footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | 3 | background-color: white; 4 | height: 60px; 5 | width: 100%; 6 | border-top: 1px solid #CCCCCC; 7 | display:flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | align-items: center; 11 | bottom: 0; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web_static/styles/103-footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | 3 | background-color: white; 4 | height: 60px; 5 | width: 100%; 6 | border-top: 1px solid #575757; 7 | display:flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | align-items: center; 11 | bottom: 0; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /setup_mysql_dev.sql: -------------------------------------------------------------------------------- 1 | -- prepares a MySQL server for the project 2 | 3 | CREATE DATABASE IF NOT EXISTS hbnb_dev_db; 4 | CREATE USER IF NOT EXISTS 'hbnb_dev'@'localhost' IDENTIFIED BY 'hbnb_dev_pwd'; 5 | GRANT ALL PRIVILEGES ON `hbnb_dev_db`.* TO 'hbnb_dev'@'localhost'; 6 | GRANT SELECT ON `performance_schema`.* TO 'hbnb_dev'@'localhost'; 7 | FLUSH PRIVILEGES; 8 | -------------------------------------------------------------------------------- /web_flask/templates/6-number_odd_or_even.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HBNB 5 | 6 | 7 | {% if (value % 2 == 0) %} 8 |

Number: {{ value }} is even

9 | {% else %} 10 |

Number: {{ value }} is odd

11 | {% endif %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /setup_mysql_test.sql: -------------------------------------------------------------------------------- 1 | -- prepares a MySQL server for the project 2 | 3 | CREATE DATABASE IF NOT EXISTS hbnb_test_db; 4 | CREATE USER IF NOT EXISTS 'hbnb_test'@'localhost' IDENTIFIED BY 'hbnb_test_pwd'; 5 | GRANT ALL PRIVILEGES ON `hbnb_test_db`.* TO 'hbnb_test'@'localhost'; 6 | GRANT SELECT ON `performance_schema`.* TO 'hbnb_test'@'localhost'; 7 | FLUSH PRIVILEGES; 8 | -------------------------------------------------------------------------------- /web_dynamic/static/styles/3-footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | position: fixed; 3 | background: white; 4 | height: 60px; 5 | width: 100%; 6 | bottom: 0; 7 | border-top: 1px solid #CCCCCC; 8 | } 9 | footer p { 10 | position: absolute; 11 | text-align: center; 12 | top: 10%; 13 | bottom: 0; 14 | right: 0; 15 | left: 0; 16 | } 17 | -------------------------------------------------------------------------------- /web_flask/static/styles/3-footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | position: fixed; 3 | background: white; 4 | height: 60px; 5 | width: 100%; 6 | bottom: 0; 7 | border-top: 1px solid #CCCCCC; 8 | } 9 | footer p { 10 | position: absolute; 11 | text-align: center; 12 | top: 10%; 13 | bottom: 0; 14 | right: 0; 15 | left: 0; 16 | } 17 | -------------------------------------------------------------------------------- /web_flask/templates/7-states_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HBNB 5 | 6 | 7 |

States

8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /web_flask/0-hello_route.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from flask import Flask 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/', strict_slashes=False) 8 | def hello_hbnb(): 9 | """ Prints a Message when / is called """ 10 | return 'Hello HBNB!' 11 | 12 | if __name__ == "__main__": 13 | """ Main Function """ 14 | app.run(host='0.0.0.0', port=5000) 15 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | initialize the models package 4 | """ 5 | 6 | from os import getenv 7 | 8 | 9 | storage_t = getenv("HBNB_TYPE_STORAGE") 10 | 11 | if storage_t == "db": 12 | from models.engine.db_storage import DBStorage 13 | storage = DBStorage() 14 | else: 15 | from models.engine.file_storage import FileStorage 16 | storage = FileStorage() 17 | storage.reload() 18 | -------------------------------------------------------------------------------- /api/v1/views/documentation/city/delete_city.yml: -------------------------------------------------------------------------------- 1 | deletes a city based on id 2 | --- 3 | tags: 4 | - Cities 5 | parameters: 6 | - name: city_id 7 | in: path 8 | type: string 9 | required: true 10 | description: id of the city 11 | responses: 12 | 404: 13 | description: City not found! 14 | 200: 15 | description: Request completed successfully -------------------------------------------------------------------------------- /api/v1/views/documentation/user/delete_user.yml: -------------------------------------------------------------------------------- 1 | Deletes a user 2 | --- 3 | tags: 4 | - Users 5 | parameters: 6 | - name: user_id 7 | in: path 8 | type: string 9 | required: false 10 | description: The unique id of the user 11 | responses: 12 | 404: 13 | description: user not found! 14 | 200: 15 | description: request executed successfully -------------------------------------------------------------------------------- /web_static/styles/3-common.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | body { 5 | margin: 0px; 6 | padding: 0px; 7 | color: #484848; 8 | font-size: 14px; 9 | font-family: Circular, "Helvetica Neue",Helvetica,Arial,sans-serif; 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | .container { 14 | width: 100%; 15 | max-width: 1000px; 16 | flex: 1 0 auto; 17 | } 18 | -------------------------------------------------------------------------------- /web_static/styles/4-common.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | body { 5 | margin: 0px; 6 | padding: 0px; 7 | color: #484848; 8 | font-size: 14px; 9 | font-family: Circular, "Helvetica Neue",Helvetica,Arial,sans-serif; 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | .container { 14 | max-width: 1000px; 15 | margin: 30px auto; 16 | width: 100%; 17 | flex: 1 0 auto; 18 | } -------------------------------------------------------------------------------- /api/v1/views/documentation/place/delete_place.yml: -------------------------------------------------------------------------------- 1 | Deletes a place based on the id provided 2 | --- 3 | tags: 4 | - Places 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The uuid of the place to delete 11 | responses: 12 | 404: 13 | description: Place not found! 14 | 200: 15 | description: Deletion request sucessful 16 | -------------------------------------------------------------------------------- /api/v1/views/documentation/state/delete_state.yml: -------------------------------------------------------------------------------- 1 | Deletes a state based on the id provided 2 | --- 3 | tags: 4 | - States 5 | parameters: 6 | - name: state_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The uuid of the state to delete 11 | responses: 12 | 404: 13 | description: State not found! 14 | 200: 15 | description: Deletion request sucessful 16 | -------------------------------------------------------------------------------- /api/v1/views/documentation/reviews/delete_reviews.yml: -------------------------------------------------------------------------------- 1 | Deletes a review based on the id provided 2 | --- 3 | tags: 4 | - Reviews 5 | parameters: 6 | - name: review_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The uuid of the review to delete 11 | responses: 12 | 404: 13 | description: Review not found! 14 | 200: 15 | description: Deletion request sucessful 16 | -------------------------------------------------------------------------------- /web_static/styles/102-common.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | body { 5 | margin: 0px; 6 | padding: 0px; 7 | color: #484848; 8 | font-size: 14px; 9 | font-family: Circular, "Helvetica Neue",Helvetica,Arial,sans-serif; 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | .container { 14 | max-width: 1000px; 15 | margin: 30px auto; 16 | width: 100%; 17 | flex: 1 0 auto; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /web_static/styles/103-common.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | body { 5 | margin: 0px; 6 | padding: 0px; 7 | color: #181818; 8 | font-size: 16px; 9 | font-family: Circular, "Helvetica Neue",Helvetica,Arial,sans-serif; 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | .container { 14 | max-width: 1000px; 15 | margin: 30px auto; 16 | width: 100%; 17 | flex: 1 0 auto; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /api/v1/views/documentation/amenity/post_amenity.yml: -------------------------------------------------------------------------------- 1 | Links an amenity object to a place 2 | --- 3 | tags: 4 | - Amenities 5 | parameters: 6 | - name: amenity_id 7 | in: body 8 | required: true 9 | requires: 10 | - name 11 | properties: 12 | name: 13 | type: string 14 | 15 | responses: 16 | 400: 17 | description: Not a JSON 18 | 201: 19 | description: request executed successfully -------------------------------------------------------------------------------- /api/v1/views/documentation/state/post_state.yml: -------------------------------------------------------------------------------- 1 | Posts a new state. 2 | --- 3 | tags: 4 | - States 5 | parameters: 6 | - name: request 7 | in: body 8 | required: true 9 | requires: 10 | - name 11 | properties: 12 | name: 13 | type: string 14 | 15 | responses: 16 | 400: 17 | description: Missing name or Not Valid JSON 18 | 201: 19 | description: Request completed successfully 20 | -------------------------------------------------------------------------------- /api/v1/views/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Blueprint for API """ 3 | from flask import Blueprint 4 | 5 | app_views = Blueprint('app_views', __name__, url_prefix='/api/v1') 6 | 7 | from api.v1.views.index import * 8 | from api.v1.views.states import * 9 | from api.v1.views.places import * 10 | from api.v1.views.places_reviews import * 11 | from api.v1.views.cities import * 12 | from api.v1.views.amenities import * 13 | from api.v1.views.users import * 14 | from api.v1.views.places_amenities import * 15 | -------------------------------------------------------------------------------- /api/v1/views/documentation/user/put_user.yml: -------------------------------------------------------------------------------- 1 | updates user information 2 | --- 3 | tags: 4 | - Users 5 | parameters: 6 | - name: user_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the user to update 11 | - name: update_request 12 | in: body 13 | required: true 14 | 15 | responses: 16 | 404: 17 | description: user not found! 18 | 200: 19 | description: request executed successfully -------------------------------------------------------------------------------- /web_static/styles/7-places.css: -------------------------------------------------------------------------------- 1 | .places { 2 | font-size: 30px; 3 | display: flex; 4 | flex-wrap: wrap; 5 | justify-content: center; 6 | } 7 | 8 | .places h1 { 9 | width: 100%; 10 | font-size: 30px; 11 | } 12 | 13 | article { 14 | width: 390px; 15 | padding: 20px 20px 20px 20px; 16 | margin: 20px 20px 20px 20px; 17 | border: 1px solid #FF5A5F; 18 | border-radius: 4px; 19 | } 20 | 21 | article h2 { 22 | font-size: 30px; 23 | text-align: center; 24 | } 25 | -------------------------------------------------------------------------------- /api/v1/views/documentation/state/put_state.yml: -------------------------------------------------------------------------------- 1 | Updates the state info based on ID 2 | --- 3 | tags: 4 | - States 5 | parameters: 6 | - name: state_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the state 11 | - name: request 12 | in: body 13 | required: true 14 | 15 | responses: 16 | 404: 17 | description: State not found 18 | 200: 19 | description: Request executed successfully 20 | -------------------------------------------------------------------------------- /web_flask/1-hbnb_route.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application HBNB""" 3 | from flask import Flask 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/', strict_slashes=False) 8 | def hello_hbnb(): 9 | """ Prints a Message when / is called """ 10 | return 'Hello HBNB!' 11 | 12 | 13 | @app.route('/hbnb', strict_slashes=False) 14 | def hbnb(): 15 | """ Prints a Message when /hbnb is called """ 16 | return 'HBNB' 17 | 18 | if __name__ == "__main__": 19 | """ Main Function """ 20 | app.run(host='0.0.0.0', port=5000) 21 | -------------------------------------------------------------------------------- /web_flask/templates/8-cities_by_states.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HBNB 5 | 6 | 7 |

{{ h_1 }}

8 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /api/v1/views/documentation/city/put_city.yml: -------------------------------------------------------------------------------- 1 | Updates a city based on id 2 | --- 3 | tags: 4 | - Cities 5 | parameters: 6 | - name: city_id 7 | in: path 8 | type: string 9 | required: true 10 | description: id of the city 11 | - name: update_request 12 | in: body 13 | required: true 14 | 15 | responses: 16 | 404: 17 | description: city not found! 18 | 400: 19 | description: Not a valid JSON 20 | 200: 21 | description: Request completed successfully -------------------------------------------------------------------------------- /api/v1/views/documentation/amenity/put_amenity.yml: -------------------------------------------------------------------------------- 1 | Updates an amenity 2 | --- 3 | tags: 4 | - Amenities 5 | parameters: 6 | - name: amenity_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the amenity 11 | - name: update_request 12 | in: body 13 | required: true 14 | responses: 15 | 404: 16 | description: resource not found! 17 | 400: 18 | description: Not a JSON 19 | 200: 20 | description: request executed successfully -------------------------------------------------------------------------------- /api/v1/views/documentation/user/post_user.yml: -------------------------------------------------------------------------------- 1 | posts a new user 2 | --- 3 | tags: 4 | - Users 5 | parameters: 6 | - name: user_and_password 7 | in: body 8 | required: true 9 | requires: 10 | - email: 11 | - password: 12 | properties: 13 | email: 14 | type: string 15 | password: 16 | type: string 17 | 18 | responses: 19 | 400: 20 | description: Missing email/password or not a JSON 21 | 201: 22 | description: successfully created user -------------------------------------------------------------------------------- /web_static/2-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 | 9 | 10 | 11 |
12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web_static/styles/4-filters.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | background-color: white; 3 | height: 70px; 4 | border: 1px solid #DDDDDD; 5 | border-radius: 4px; 6 | display: flex; 7 | align-items: center; 8 | position: relative; 9 | } 10 | 11 | .filters button { 12 | font-size: 18px; 13 | background-color: #FF5A5F; 14 | color: #FFFFFF; 15 | height: 48px; 16 | width: 20%; 17 | border: none; 18 | border-radius: 4px; 19 | position: absolute; 20 | right: 30px; 21 | 22 | } 23 | .filters button:hover { 24 | opacity: 0.9; 25 | } 26 | -------------------------------------------------------------------------------- /0-setup_web_static.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # sets up the web servers for the deployment of web_static 3 | 4 | sudo apt-get -y update 5 | sudo apt-get -y upgrade 6 | sudo apt-get -y install nginx 7 | sudo mkdir -p /data/web_static/releases/test /data/web_static/shared 8 | echo "This is a test" | sudo tee /data/web_static/releases/test/index.html 9 | sudo ln -sf /data/web_static/releases/test/ /data/web_static/current 10 | sudo chown -hR ubuntu:ubuntu /data/ 11 | sudo sed -i '38i\\tlocation /hbnb_static/ {\n\t\talias /data/web_static/current/;\n\t}\n' /etc/nginx/sites-available/default 12 | sudo service nginx start 13 | -------------------------------------------------------------------------------- /web_dynamic/static/styles/3-header.css: -------------------------------------------------------------------------------- 1 | header { 2 | background: #FFFFFF; 3 | height: 70px; 4 | width: 100%; 5 | border-bottom: 1px solid #CCCCCC; 6 | background-image: url("../images/logo.png"); 7 | background-repeat: no-repeat; 8 | background-position: 20px 50%; 9 | } 10 | 11 | #api_status.available{ 12 | background-color: #ff545f; 13 | } 14 | 15 | #api_status { 16 | background-color: #cccccc; 17 | height: 40px; 18 | width: 40px; 19 | float: right; 20 | border-radius: 50%; 21 | margin-right: 30px; 22 | transform: translateY(-50%); 23 | top: 50%; 24 | } 25 | -------------------------------------------------------------------------------- /models/amenity.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ holds class Amenity""" 3 | import models 4 | from models.base_model import BaseModel, Base 5 | from os import getenv 6 | import sqlalchemy 7 | from sqlalchemy import Column, String 8 | from sqlalchemy.orm import relationship 9 | 10 | 11 | class Amenity(BaseModel, Base): 12 | """Representation of Amenity """ 13 | if models.storage_t == 'db': 14 | __tablename__ = 'amenities' 15 | name = Column(String(128), nullable=False) 16 | else: 17 | name = "" 18 | 19 | def __init__(self, *args, **kwargs): 20 | """initializes Amenity""" 21 | super().__init__(*args, **kwargs) 22 | -------------------------------------------------------------------------------- /api/v1/views/documentation/amenity/delete_amenity.yml: -------------------------------------------------------------------------------- 1 | Removes an amenity of the specified location 2 | --- 3 | tags: 4 | - Amenities 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the place 11 | - name: amenity_id 12 | in: path 13 | type: string 14 | required: true 15 | description: the id of the amenity to remove 16 | responses: 17 | 404: 18 | description: place not found / amenity not found / amenity not found in place 19 | 200: 20 | description: request executed successfully -------------------------------------------------------------------------------- /api/v1/views/documentation/place_amenity/delete_place_amenities.yml: -------------------------------------------------------------------------------- 1 | Deletes an amenity from a place based on the ids provided 2 | --- 3 | tags: 4 | - Place_Amenities 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The uuid of the place with the amenity 11 | - name: amenity_id 12 | in: path 13 | type: string 14 | required: true 15 | description: The uuid of the amenity to delete 16 | responses: 17 | 404: 18 | description: Amenity or Place not found! 19 | 200: 20 | description: Deletion request sucessful 21 | -------------------------------------------------------------------------------- /1-pack_web_static.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Fabric script that generates a tgz archive from the contents of the web_static 4 | folder of the AirBnB Clone repo 5 | """ 6 | 7 | from datetime import datetime 8 | from fabric.api import local 9 | from os.path import isdir 10 | 11 | 12 | def do_pack(): 13 | """generates a tgz archive""" 14 | try: 15 | date = datetime.now().strftime("%Y%m%d%H%M%S") 16 | if isdir("versions") is False: 17 | local("mkdir versions") 18 | file_name = "versions/web_static_{}.tgz".format(date) 19 | local("tar -cvzf {} web_static".format(file_name)) 20 | return file_name 21 | except: 22 | return None 23 | -------------------------------------------------------------------------------- /api/v1/views/documentation/place_amenity/post_place_amenities.yml: -------------------------------------------------------------------------------- 1 | Links a Review to a Place 2 | --- 3 | tags: 4 | - Place_Amenities 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the Amenity 11 | - name: amenity_id 12 | in: path 13 | type: string 14 | required: true 15 | description: The id of the Place to link to 16 | 17 | 18 | responses: 19 | 404: 20 | description: resource not found! 21 | 201: 22 | description: Successful request 23 | 200: 24 | description: Amenity already linked to Place 25 | -------------------------------------------------------------------------------- /api/v1/views/documentation/city/post_city.yml: -------------------------------------------------------------------------------- 1 | Post a new city 2 | --- 3 | tags: 4 | - Cities 5 | parameters: 6 | - state_id: 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the city state 11 | - name: body_request 12 | in: body 13 | required: true 14 | requires: 15 | - name 16 | properties: 17 | name: 18 | type: string 19 | 20 | responses: 21 | 404: 22 | description: State not found! 23 | 400: 24 | description: Not a valid JSON or missing name 25 | 201: 26 | description: Request completed successfully -------------------------------------------------------------------------------- /web_flask/templates/9-states.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HBNB 5 | 6 | 7 | {% if (found == 1) %} 8 |

State: {{ state }}

9 |

Cities:

10 | 15 | {% elif found == 0 %} 16 |

States

17 | 22 | {% else %} 23 |

Not found!

24 | {% endif %} 25 | 26 | 27 | -------------------------------------------------------------------------------- /web_static/3-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /api/v1/views/documentation/place/put_place.yml: -------------------------------------------------------------------------------- 1 | Updates the place info based on ID 2 | --- 3 | tags: 4 | - Places 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The unique id of the place 11 | - name: parameters to update 12 | in: body 13 | required: true 14 | requires: 15 | - parameter 16 | properties: 17 | parameter: 18 | type: string 19 | responses: 20 | 404: 21 | description: resource not found! 22 | 400: 23 | description: Not a Valid JSON 24 | 200: 25 | description: Successful request 26 | -------------------------------------------------------------------------------- /api/v1/views/documentation/reviews/put_reviews.yml: -------------------------------------------------------------------------------- 1 | Updates the review info based on ID 2 | --- 3 | tags: 4 | - Reviews 5 | parameters: 6 | - name: review_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The unique id of the review 11 | - name: parameters to update 12 | in: body 13 | required: true 14 | requires: 15 | - parameter 16 | properties: 17 | parameter: 18 | type: string 19 | responses: 20 | 404: 21 | description: resource not found! 22 | 400: 23 | description: Not a Valid JSON 24 | 200: 25 | description: Successful request 26 | -------------------------------------------------------------------------------- /api/v1/views/documentation/state/get_state.yml: -------------------------------------------------------------------------------- 1 | Gets the list of all states 2 | --- 3 | tags: 4 | - States 5 | responses: 6 | 200: 7 | description: Successful request 8 | schema: 9 | type: array 10 | items: 11 | properties: 12 | __class__: 13 | type: string 14 | created_at: 15 | type: string 16 | description: time of creation of the instance 17 | updated_at: 18 | type: string 19 | description: time of last update of the instance 20 | id: 21 | type: string 22 | description: The uuid of the state instance 23 | name: 24 | type: string 25 | description: State name -------------------------------------------------------------------------------- /web_flask/2-c_route.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application C is FUN""" 3 | from flask import Flask 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/', strict_slashes=False) 8 | def hello_hbnb(): 9 | """ Prints a Message when / is called """ 10 | return 'Hello HBNB!' 11 | 12 | 13 | @app.route('/hbnb', strict_slashes=False) 14 | def hbnb(): 15 | """ Prints a Message when /hbnb is called """ 16 | return 'HBNB' 17 | 18 | 19 | @app.route('/c/', strict_slashes=False) 20 | def c_is_fun(text): 21 | """ Prints a Message when /c is called """ 22 | return "C " + text.replace('_', ' ') 23 | 24 | if __name__ == "__main__": 25 | """ Main Function """ 26 | app.run(host='0.0.0.0', port=5000) 27 | -------------------------------------------------------------------------------- /web_dynamic/static/scripts/1-hbnb.js: -------------------------------------------------------------------------------- 1 | // JavaScript script that is executed only when DOM is loaded 2 | // Uses JQuery 3 | 4 | let checked_box = {}; 5 | $(document).ready(function () { 6 | $('input:checkbox').change(function () { 7 | if ($(this).is(':checked_box')) { 8 | checked_box[$(this).data('id')] = $(this).data('name'); 9 | } 10 | else { 11 | delete checked_box[$(this).data('id')]; 12 | } 13 | $('div.amenities h4').html(function () { 14 | let amenities = []; 15 | Object.keys(checked_box).forEach(function (key) { 16 | amenities.push(checked_box[key]); 17 | }); 18 | if (amenities.length === 0) { 19 | return (' '); 20 | } 21 | return (amenities.join(', ')); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /api/v1/views/documentation/amenity/all_amenities.yml: -------------------------------------------------------------------------------- 1 | gets all amenities of a place 2 | --- 3 | tags: 4 | - Amenities 5 | responses: 6 | 200: 7 | description: request executed successfully 8 | schema: 9 | type: array 10 | items: 11 | properties: 12 | __class__: 13 | type: string 14 | created_at: 15 | type: string 16 | description: time of creation of the instance 17 | updated_at: 18 | type: string 19 | description: time of last update of the instance 20 | id: 21 | type: string 22 | description: The uuid of the instance 23 | name: 24 | type: string 25 | description: amenity name -------------------------------------------------------------------------------- /api/v1/views/documentation/place/post_place.yml: -------------------------------------------------------------------------------- 1 | Posts a new place. 2 | --- 3 | tags: 4 | - Places 5 | parameters: 6 | - name: city_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the City to link to 11 | - name: user_id and name 12 | in: body 13 | required: true 14 | requires: 15 | - user_id 16 | - name 17 | properties: 18 | user_id: 19 | type: string 20 | name: 21 | type: string 22 | 23 | responses: 24 | 404: 25 | description: resource not found! 26 | 400: 27 | description: missing user_id, name or invalid JSON 28 | 201: 29 | description: Successful request 30 | -------------------------------------------------------------------------------- /api/v1/views/documentation/reviews/post_reviews.yml: -------------------------------------------------------------------------------- 1 | Posts a new review 2 | --- 3 | tags: 4 | - Reviews 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the Place to link to 11 | - name: user_id and text 12 | in: body 13 | required: true 14 | requires: 15 | - user_id 16 | - text 17 | properties: 18 | user_id: 19 | type: string 20 | text: 21 | type: string 22 | 23 | responses: 24 | 404: 25 | description: resource not found! 26 | 400: 27 | description: missing user_id, text, or Invalid JSON 28 | 201: 29 | description: Successful request 30 | -------------------------------------------------------------------------------- /models/review.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ holds class Review""" 3 | import models 4 | from models.base_model import BaseModel, Base 5 | from os import getenv 6 | import sqlalchemy 7 | from sqlalchemy import Column, String, ForeignKey 8 | 9 | 10 | class Review(BaseModel, Base): 11 | """Representation of Review """ 12 | if models.storage_t == 'db': 13 | __tablename__ = 'reviews' 14 | place_id = Column(String(60), ForeignKey('places.id'), nullable=False) 15 | user_id = Column(String(60), ForeignKey('users.id'), nullable=False) 16 | text = Column(String(1024), nullable=False) 17 | else: 18 | place_id = "" 19 | user_id = "" 20 | text = "" 21 | 22 | def __init__(self, *args, **kwargs): 23 | """initializes Review""" 24 | super().__init__(*args, **kwargs) 25 | -------------------------------------------------------------------------------- /web_static/0-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 |
9 |
10 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /api/v1/views/documentation/amenity/get_amenity.yml: -------------------------------------------------------------------------------- 1 | gets amenity by ID 2 | --- 3 | tags: 4 | - Amenities 5 | parameters: 6 | - name: amenity_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the amenity 11 | responses: 12 | 404: 13 | description: amenity not found. 14 | 200: 15 | description: request executed successfully 16 | schema: 17 | properties: 18 | __class__: 19 | type: string 20 | created_at: 21 | type: string 22 | description: time of creation of the instance 23 | updated_at: 24 | type: string 25 | description: time of last update of the instance 26 | id: 27 | type: string 28 | description: The uuid of the instance 29 | name: 30 | type: string 31 | description: amenity name -------------------------------------------------------------------------------- /api/v1/views/documentation/place/post_search.yml: -------------------------------------------------------------------------------- 1 | Search Places with a POST request. 2 | --- 3 | tags: 4 | - Places 5 | parameters: 6 | - name: user_id and name 7 | in: body 8 | required: false 9 | requires: 10 | - states 11 | - cities 12 | - amenities 13 | properties: 14 | states: 15 | type: array 16 | items: 17 | type: string 18 | cities: 19 | type: array 20 | items: 21 | type: string 22 | amenities: 23 | type: array 24 | items: 25 | type: string 26 | 27 | responses: 28 | 404: 29 | description: resource not found! 30 | 400: 31 | description: Not a valid JSON 32 | 200: 33 | description: Successful request 34 | -------------------------------------------------------------------------------- /web_flask/7-states_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from models import storage 4 | from models.state import State 5 | from flask import Flask, render_template 6 | app = Flask(__name__) 7 | # app.jinja_env.trim_blocks = True 8 | # app.jinja_env.lstrip_blocks = True 9 | 10 | 11 | @app.teardown_appcontext 12 | def close_db(error): 13 | """ Remove the current SQLAlchemy Session """ 14 | storage.close() 15 | 16 | 17 | @app.route('/states_list', strict_slashes=False) 18 | def states_list(): 19 | """ displays a HTML page with a list of states """ 20 | states = storage.all(State).values() 21 | states = sorted(states, key=lambda k: k.name) 22 | return render_template('7-states_list.html', states=states) 23 | 24 | 25 | if __name__ == "__main__": 26 | """ Main Function """ 27 | app.run(host='0.0.0.0', port=5000) 28 | -------------------------------------------------------------------------------- /api/v1/views/documentation/state/get_id_state.yml: -------------------------------------------------------------------------------- 1 | Gets a specific state by ID or the list of all states if no ID is specied 2 | --- 3 | tags: 4 | - States 5 | parameters: 6 | - name: state_id 7 | in: path 8 | type: string 9 | required: false 10 | description: the unique id of the state 11 | responses: 12 | 404: 13 | description: State not found 14 | 200: 15 | description: Successful request 16 | schema: 17 | properties: 18 | __class__: 19 | type: string 20 | created_at: 21 | type: string 22 | description: time of creation of the instance 23 | updated_at: 24 | type: string 25 | description: time of last update of the instance 26 | id: 27 | type: string 28 | description: The uuid of the state instance 29 | name: 30 | type: string 31 | description: State name -------------------------------------------------------------------------------- /models/city.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ holds class City""" 3 | import models 4 | from models.base_model import BaseModel, Base 5 | from os import getenv 6 | import sqlalchemy 7 | from sqlalchemy import Column, String, ForeignKey 8 | from sqlalchemy.orm import relationship 9 | 10 | 11 | class City(BaseModel, Base): 12 | """Representation of city """ 13 | if models.storage_t == "db": 14 | __tablename__ = 'cities' 15 | state_id = Column(String(60), ForeignKey('states.id'), nullable=False) 16 | name = Column(String(128), nullable=False) 17 | places = relationship("Place", 18 | backref="cities", 19 | cascade="all, delete, delete-orphan") 20 | else: 21 | state_id = "" 22 | name = "" 23 | 24 | def __init__(self, *args, **kwargs): 25 | """initializes city""" 26 | super().__init__(*args, **kwargs) 27 | -------------------------------------------------------------------------------- /api/v1/views/documentation/city/get_city.yml: -------------------------------------------------------------------------------- 1 | get a specific city based on the state_id 2 | --- 3 | tags: 4 | - Cities 5 | parameters: 6 | - name: city_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The uniqe id of the city 11 | responses: 12 | 200: 13 | description: Successful request 14 | schema: 15 | properties: 16 | __class__: 17 | type: string 18 | created_at: 19 | type: string 20 | description: time of creation of the instance 21 | updated_at: 22 | type: string 23 | description: time of last update of the instance 24 | id: 25 | type: string 26 | description: The uuid of the instance 27 | state_id: 28 | type: string 29 | description: uuid of the city's state 30 | name: 31 | type: string 32 | description: city name -------------------------------------------------------------------------------- /web_static/4-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 19 | 20 |
21 |
22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /api/v1/views/documentation/reviews/get_review.yml: -------------------------------------------------------------------------------- 1 | Retrieves a Review 2 | --- 3 | tags: 4 | - Reviews 5 | parameters: 6 | - name: review_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the review 11 | responses: 12 | 200: 13 | description: Successful request 14 | schema: 15 | properties: 16 | __class__: 17 | type: string 18 | created_at: 19 | type: string 20 | description: time of creation of the instance 21 | updated_at: 22 | type: string 23 | description: time of last update of the instance 24 | id: 25 | type: string 26 | description: The uuid of the state instance 27 | text: 28 | type: string 29 | description: Description of the place 30 | place_id: 31 | type: string 32 | description: uuid of the place 33 | 404: 34 | description: Place not found 35 | -------------------------------------------------------------------------------- /api/v1/views/documentation/place_amenity/get_places_amenities.yml: -------------------------------------------------------------------------------- 1 | Gets the list of all amenities of a place 2 | --- 3 | tags: 4 | - Place_Amenities 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the place 11 | 12 | responses: 13 | 200: 14 | description: Successful request 15 | schema: 16 | type: array 17 | items: 18 | properties: 19 | __class__: 20 | type: string 21 | created_at: 22 | type: string 23 | description: time of creation of the instance 24 | updated_at: 25 | type: string 26 | description: time of last update of the instance 27 | id: 28 | type: string 29 | description: The uuid of the state instance 30 | name: 31 | type: string 32 | description: name of the amenity 33 | 404: 34 | description: Place not found 35 | -------------------------------------------------------------------------------- /api/v1/views/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Index """ 3 | from models.amenity import Amenity 4 | from models.city import City 5 | from models.place import Place 6 | from models.review import Review 7 | from models.state import State 8 | from models.user import User 9 | from models import storage 10 | from api.v1.views import app_views 11 | from flask import jsonify 12 | 13 | 14 | @app_views.route('/status', methods=['GET'], strict_slashes=False) 15 | def status(): 16 | """ Status of API """ 17 | return jsonify({"status": "OK"}) 18 | 19 | 20 | @app_views.route('/stats', methods=['GET'], strict_slashes=False) 21 | def number_objects(): 22 | """ Retrieves the number of each objects by type """ 23 | classes = [Amenity, City, Place, Review, State, User] 24 | names = ["amenities", "cities", "places", "reviews", "states", "users"] 25 | 26 | num_objs = {} 27 | for i in range(len(classes)): 28 | num_objs[names[i]] = storage.count(classes[i]) 29 | 30 | return jsonify(num_objs) 31 | -------------------------------------------------------------------------------- /web_flask/3-python_route.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application Python is Cool""" 3 | from flask import Flask 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/', strict_slashes=False) 8 | def hello_hbnb(): 9 | """ Prints a Message when / is called """ 10 | return 'Hello HBNB!' 11 | 12 | 13 | @app.route('/hbnb', strict_slashes=False) 14 | def hbnb(): 15 | """ Prints a Message when /hbnb is called """ 16 | return 'HBNB' 17 | 18 | 19 | @app.route('/c/', strict_slashes=False) 20 | def c_is_fun(text): 21 | """ Prints a Message when /c is called """ 22 | return "C " + text.replace('_', ' ') 23 | 24 | 25 | @app.route('/python', strict_slashes=False) 26 | @app.route('/python/', strict_slashes=False) 27 | def python_is_cool(text='is_cool'): 28 | """ Prints a Message when /python is called """ 29 | return "Python " + text.replace('_', ' ') 30 | 31 | if __name__ == "__main__": 32 | """ Main Function """ 33 | app.run(host='0.0.0.0', port=5000) 34 | -------------------------------------------------------------------------------- /web_dynamic/static/scripts/2-hbnb.js: -------------------------------------------------------------------------------- 1 | // JavaScript script that is executed only when DOM is loaded 2 | // Uses JQuery 3 | 4 | let checked_box = {}; 5 | $(document).ready(function () { 6 | $('input:checkbox').change(function () { 7 | if ($(this).is(':checked_box')) { 8 | checked_box[$(this).data('id')] = $(this).data('name'); 9 | } 10 | else { 11 | delete checked_box[$(this).data('id')]; 12 | } 13 | $('div.amenities h4').html(function () { 14 | let amenities = []; 15 | Object.keys(checked_box).forEach(function (key) { 16 | amenities.push(checked_box[key]); 17 | }); 18 | if (amenities.length === 0) { 19 | return (' '); 20 | } 21 | return (amenities.join(', ')); 22 | }); 23 | }); 24 | 25 | 26 | const apiStatus = $('DIV#api_status'); 27 | $.ajax('http://0.0.0.0:5001/api/v1/status/').done(function (data) { 28 | if (data.status === 'OK') { 29 | apiStatus.addClass('available'); 30 | } else { 31 | apiStatus.removeClass('available'); 32 | } 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /api/v1/views/documentation/user/all_users.yml: -------------------------------------------------------------------------------- 1 | retrieves a list of all users. 2 | --- 3 | tags: 4 | - Users 5 | 6 | responses: 7 | 200: 8 | description: request executed successfully 9 | schema: 10 | type: array 11 | items: 12 | properties: 13 | __class__: 14 | type: string 15 | created_at: 16 | type: string 17 | description: time of creation of the instance 18 | updated_at: 19 | type: string 20 | description: time of last update of the instance 21 | id: 22 | type: string 23 | description: The uuid of the instance 24 | email: 25 | type: string 26 | description: user's email 27 | password: 28 | type: string 29 | description: user's password 30 | first_name: 31 | type: string 32 | description: user's first name 33 | last_name: 34 | type: string 35 | description: user's last name -------------------------------------------------------------------------------- /api/v1/views/documentation/city/cities_by_state.yml: -------------------------------------------------------------------------------- 1 | get a list of cities based on the state_id 2 | --- 3 | tags: 4 | - Cities 5 | parameters: 6 | - name: state_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The uniqe id of the state 11 | responses: 12 | 404: 13 | description: No state is linked to the ID! 14 | 200: 15 | description: Request completed successfully 16 | schema: 17 | type: array 18 | items: 19 | properties: 20 | __class__: 21 | type: string 22 | created_at: 23 | type: string 24 | description: time of creation of the instance 25 | updated_at: 26 | type: string 27 | description: time of last update of the instance 28 | id: 29 | type: string 30 | description: The uuid of the instance 31 | state_id: 32 | type: string 33 | description: uuid of the city's state 34 | name: 35 | type: string 36 | description: city name -------------------------------------------------------------------------------- /api/v1/views/documentation/reviews/get_reviews.yml: -------------------------------------------------------------------------------- 1 | Gets the list of all reviews of place 2 | --- 3 | tags: 4 | - Reviews 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the place 11 | 12 | responses: 13 | 200: 14 | description: Successful request 15 | schema: 16 | type: array 17 | items: 18 | properties: 19 | __class__: 20 | type: string 21 | created_at: 22 | type: string 23 | description: time of creation of the instance 24 | updated_at: 25 | type: string 26 | description: time of last update of the instance 27 | id: 28 | type: string 29 | description: The uuid of the state instance 30 | text: 31 | type: string 32 | description: Description of the place 33 | place_id: 34 | type: string 35 | description: uuid of the place 36 | 37 | 404: 38 | description: Place not found 39 | -------------------------------------------------------------------------------- /web_static/1-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 31 | 32 | 33 |
34 |
35 |

Holberton School

36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /web_static/styles/5-filters.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | background-color: white; 3 | height: 70px; 4 | border: 1px solid #DDDDDD; 5 | border-radius: 4px; 6 | display: flex; 7 | align-items: center; 8 | position: relative; 9 | } 10 | 11 | .filters button { 12 | font-size: 18px; 13 | background-color: #FF5A5F; 14 | color: #FFFFFF; 15 | height: 48px; 16 | width: 20%; 17 | border: none; 18 | border-radius: 4px; 19 | position: absolute; 20 | right: 30px; 21 | 22 | } 23 | .filters button:hover { 24 | opacity: 0.9; 25 | } 26 | 27 | .locations, .amenities { 28 | 29 | height: 100%; 30 | width: 25%; 31 | position: relative; 32 | display: flex; 33 | justify-content: center; 34 | flex-direction: column; 35 | 36 | } 37 | 38 | .locations { 39 | border-right: 1px solid #DDDDDD ; 40 | } 41 | 42 | .locations h3, .amenities h3{ 43 | font-weight: 600; 44 | margin: 2px 20px; 45 | } 46 | .locations h4, .amenities h4{ 47 | font-weight: 400; 48 | font-size: 14px; 49 | margin: 2px 20px; 50 | } 51 | -------------------------------------------------------------------------------- /2-do_deploy_web_static.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Fabric script based on the file 1-pack_web_static.py that distributes an 4 | archive to the web servers 5 | """ 6 | 7 | from fabric.api import put, run, env 8 | from os.path import exists 9 | env.hosts = ['142.44.167.228', '144.217.246.195'] 10 | 11 | 12 | def do_deploy(archive_path): 13 | """distributes an archive to the web servers""" 14 | if exists(archive_path) is False: 15 | return False 16 | try: 17 | file_n = archive_path.split("/")[-1] 18 | no_ext = file_n.split(".")[0] 19 | path = "/data/web_static/releases/" 20 | put(archive_path, '/tmp/') 21 | run('mkdir -p {}{}/'.format(path, no_ext)) 22 | run('tar -xzf /tmp/{} -C {}{}/'.format(file_n, path, no_ext)) 23 | run('rm /tmp/{}'.format(file_n)) 24 | run('mv {0}{1}/web_static/* {0}{1}/'.format(path, no_ext)) 25 | run('rm -rf {}{}/web_static'.format(path, no_ext)) 26 | run('rm -rf /data/web_static/current') 27 | run('ln -s {}{}/ /data/web_static/current'.format(path, no_ext)) 28 | return True 29 | except: 30 | return False 31 | -------------------------------------------------------------------------------- /api/v1/views/documentation/user/get_user.yml: -------------------------------------------------------------------------------- 1 | retrieves a specific user. 2 | --- 3 | tags: 4 | - Users 5 | parameters: 6 | - name: user_id 7 | in: path 8 | type: string 9 | required: true 10 | description: The id of the user to retrieve 11 | responses: 12 | 404: 13 | description: user not found! 14 | 200: 15 | description: request executed successfully 16 | schema: 17 | properties: 18 | __class__: 19 | type: string 20 | created_at: 21 | type: string 22 | description: time of creation of the instance 23 | updated_at: 24 | type: string 25 | description: time of last update of the instance 26 | id: 27 | type: string 28 | description: The uuid of the instance 29 | email: 30 | type: string 31 | description: user's email 32 | password: 33 | type: string 34 | description: user's password 35 | first_name: 36 | type: string 37 | description: user's first name 38 | last_name: 39 | type: string 40 | description: user's last name -------------------------------------------------------------------------------- /web_flask/4-number_route.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application Python is Cool""" 3 | from flask import Flask 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/', strict_slashes=False) 8 | def hello_hbnb(): 9 | """ Prints a Message when / is called """ 10 | return 'Hello HBNB!' 11 | 12 | 13 | @app.route('/hbnb', strict_slashes=False) 14 | def hbnb(): 15 | """ Prints a Message when /hbnb is called """ 16 | return 'HBNB' 17 | 18 | 19 | @app.route('/c/', strict_slashes=False) 20 | def c_is_fun(text): 21 | """ Prints a Message when /c is called """ 22 | return "C " + text.replace('_', ' ') 23 | 24 | 25 | @app.route('/python', strict_slashes=False) 26 | @app.route('/python/', strict_slashes=False) 27 | def python_is_cool(text='is_cool'): 28 | """ Prints a Message when /python is called """ 29 | return "Python " + text.replace('_', ' ') 30 | 31 | 32 | @app.route('/number/', strict_slashes=False) 33 | def is_n_number(n): 34 | """ Prints a Message when /number is called only if n is an int""" 35 | return "{:d} is a number".format(n) 36 | 37 | if __name__ == "__main__": 38 | """ Main Function """ 39 | app.run(host='0.0.0.0', port=5000) 40 | -------------------------------------------------------------------------------- /web_flask/10-hbnb_filters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from models import storage 4 | from models.state import State 5 | from models.city import City 6 | from models.amenity import Amenity 7 | from os import environ 8 | from flask import Flask, render_template 9 | app = Flask(__name__) 10 | # app.jinja_env.trim_blocks = True 11 | # app.jinja_env.lstrip_blocks = True 12 | 13 | 14 | @app.teardown_appcontext 15 | def close_db(error): 16 | """ Remove the current SQLAlchemy Session """ 17 | storage.close() 18 | 19 | 20 | @app.route('/hbnb_filters', strict_slashes=False) 21 | def hbnb_filter(): 22 | """ HBNB filters """ 23 | states = storage.all(State).values() 24 | states = sorted(states, key=lambda k: k.name) 25 | st_ct = [] 26 | 27 | for state in states: 28 | st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) 29 | 30 | amenities = storage.all(Amenity).values() 31 | amenities = sorted(amenities, key=lambda k: k.name) 32 | 33 | return render_template('10-hbnb_filters.html', 34 | states=st_ct, 35 | amenities=amenities) 36 | 37 | 38 | if __name__ == "__main__": 39 | """ Main Function """ 40 | app.run(host='0.0.0.0', port=5000) 41 | -------------------------------------------------------------------------------- /models/state.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ holds class State""" 3 | import models 4 | from models.base_model import BaseModel, Base 5 | from models.city import City 6 | from os import getenv 7 | import sqlalchemy 8 | from sqlalchemy import Column, String, ForeignKey 9 | from sqlalchemy.orm import relationship 10 | 11 | 12 | class State(BaseModel, Base): 13 | """Representation of state """ 14 | if models.storage_t == "db": 15 | __tablename__ = 'states' 16 | name = Column(String(128), nullable=False) 17 | cities = relationship("City", 18 | backref="state", 19 | cascade="all, delete, delete-orphan") 20 | else: 21 | name = "" 22 | 23 | def __init__(self, *args, **kwargs): 24 | """initializes state""" 25 | super().__init__(*args, **kwargs) 26 | 27 | if models.storage_t != "db": 28 | @property 29 | def cities(self): 30 | """getter for list of city instances related to the state""" 31 | city_list = [] 32 | all_cities = models.storage.all(City) 33 | for city in all_cities.values(): 34 | if city.state_id == self.id: 35 | city_list.append(city) 36 | return city_list 37 | -------------------------------------------------------------------------------- /models/user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ holds class User""" 3 | 4 | import models 5 | from models.base_model import BaseModel, Base 6 | from os import getenv 7 | import sqlalchemy 8 | from sqlalchemy import Column, String 9 | from sqlalchemy.orm import relationship 10 | from hashlib import md5 11 | 12 | 13 | class User(BaseModel, Base): 14 | """Representation of a user """ 15 | if models.storage_t == 'db': 16 | __tablename__ = 'users' 17 | email = Column(String(128), nullable=False) 18 | password = Column(String(128), nullable=False) 19 | first_name = Column(String(128), nullable=True) 20 | last_name = Column(String(128), nullable=True) 21 | places = relationship("Place", backref="user") 22 | reviews = relationship("Review", backref="user") 23 | else: 24 | email = "" 25 | password = "" 26 | first_name = "" 27 | last_name = "" 28 | 29 | def __init__(self, *args, **kwargs): 30 | """initializes user""" 31 | super().__init__(*args, **kwargs) 32 | 33 | def __setattr__(self, name, value): 34 | """sets a password with md5 encryption""" 35 | if name == "password": 36 | value = md5(value.encode()).hexdigest() 37 | super().__setattr__(name, value) 38 | -------------------------------------------------------------------------------- /api/v1/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Flask Application """ 3 | from models import storage 4 | from api.v1.views import app_views 5 | from os import environ 6 | from flask import Flask, render_template, make_response, jsonify 7 | from flask_cors import CORS 8 | from flasgger import Swagger 9 | from flasgger.utils import swag_from 10 | 11 | app = Flask(__name__) 12 | app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True 13 | app.register_blueprint(app_views) 14 | cors = CORS(app, resources={r"/*": {"origins": "0.0.0.0"}}) 15 | 16 | 17 | @app.teardown_appcontext 18 | def close_db(error): 19 | """ Close Storage """ 20 | storage.close() 21 | 22 | 23 | @app.errorhandler(404) 24 | def not_found(error): 25 | """ 404 Error 26 | --- 27 | responses: 28 | 404: 29 | description: a resource was not found 30 | """ 31 | return make_response(jsonify({'error': "Not found"}), 404) 32 | 33 | app.config['SWAGGER'] = { 34 | 'title': 'AirBnB clone Restful API', 35 | 'uiversion': 3 36 | } 37 | 38 | Swagger(app) 39 | 40 | 41 | if __name__ == "__main__": 42 | """ Main Function """ 43 | host = environ.get('HBNB_API_HOST') 44 | port = environ.get('HBNB_API_PORT') 45 | if not host: 46 | host = '0.0.0.0' 47 | if not port: 48 | port = '5000' 49 | app.run(host=host, port=port, threaded=True) 50 | -------------------------------------------------------------------------------- /web_flask/100-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from models import storage 4 | from models.state import State 5 | from models.city import City 6 | from models.amenity import Amenity 7 | from models.place import Place 8 | from os import environ 9 | from flask import Flask, render_template 10 | app = Flask(__name__) 11 | # app.jinja_env.trim_blocks = True 12 | # app.jinja_env.lstrip_blocks = True 13 | 14 | 15 | @app.teardown_appcontext 16 | def close_db(error): 17 | """ Remove the current SQLAlchemy Session """ 18 | storage.close() 19 | 20 | 21 | @app.route('/hbnb', strict_slashes=False) 22 | def hbnb(): 23 | """ HBNB is alive! """ 24 | states = storage.all(State).values() 25 | states = sorted(states, key=lambda k: k.name) 26 | st_ct = [] 27 | 28 | for state in states: 29 | st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) 30 | 31 | amenities = storage.all(Amenity).values() 32 | amenities = sorted(amenities, key=lambda k: k.name) 33 | 34 | places = storage.all(Place).values() 35 | places = sorted(places, key=lambda k: k.name) 36 | 37 | return render_template('100-hbnb.html', 38 | states=st_ct, 39 | amenities=amenities, 40 | places=places) 41 | 42 | 43 | if __name__ == "__main__": 44 | """ Main Function """ 45 | app.run(host='0.0.0.0', port=5000) 46 | -------------------------------------------------------------------------------- /web_flask/8-cities_by_states.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from models import storage 4 | from models.state import State 5 | from os import environ 6 | from flask import Flask, render_template 7 | app = Flask(__name__) 8 | # app.jinja_env.trim_blocks = True 9 | # app.jinja_env.lstrip_blocks = True 10 | 11 | 12 | @app.teardown_appcontext 13 | def close_db(error): 14 | """ Remove the current SQLAlchemy Session """ 15 | storage.close() 16 | 17 | 18 | @app.route('/states_list', strict_slashes=False) 19 | def states_list(): 20 | """ displays a HTML page with a list of states """ 21 | states = storage.all(State).values() 22 | states = sorted(states, key=lambda k: k.name) 23 | return render_template('7-states_list.html', states=states) 24 | 25 | 26 | @app.route('/cities_by_states', strict_slashes=False) 27 | def cities_list(): 28 | """ displays a HTML page with a list of cities by states """ 29 | states = storage.all(State).values() 30 | states = sorted(states, key=lambda k: k.name) 31 | st_ct = [] 32 | for state in states: 33 | st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) 34 | return render_template('8-cities_by_states.html', 35 | states=st_ct, 36 | h_1="States") 37 | 38 | 39 | if __name__ == "__main__": 40 | """ Main Function """ 41 | app.run(host='0.0.0.0', port=5000) 42 | -------------------------------------------------------------------------------- /web_flask/9-states.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from models import storage 4 | from models.state import State 5 | from os import environ 6 | from flask import Flask, render_template 7 | app = Flask(__name__) 8 | # app.jinja_env.trim_blocks = True 9 | # app.jinja_env.lstrip_blocks = True 10 | 11 | 12 | @app.teardown_appcontext 13 | def close_db(error): 14 | """ Remove the current SQLAlchemy Session """ 15 | storage.close() 16 | 17 | 18 | @app.route('/states', strict_slashes=False) 19 | @app.route('/states/', strict_slashes=False) 20 | def states_state(id=""): 21 | """ displays a HTML page with a list of cities by states """ 22 | states = storage.all(State).values() 23 | states = sorted(states, key=lambda k: k.name) 24 | found = 0 25 | state = "" 26 | cities = [] 27 | 28 | for i in states: 29 | if id == i.id: 30 | state = i 31 | found = 1 32 | break 33 | if found: 34 | states = sorted(state.cities, key=lambda k: k.name) 35 | state = state.name 36 | 37 | if id and not found: 38 | found = 2 39 | 40 | return render_template('9-states.html', 41 | state=state, 42 | array=states, 43 | found=found) 44 | 45 | 46 | if __name__ == "__main__": 47 | """ Main Function """ 48 | app.run(host='0.0.0.0', port=5000) 49 | -------------------------------------------------------------------------------- /web_flask/5-number_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from flask import Flask, render_template 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/', strict_slashes=False) 8 | def hello_hbnb(): 9 | """ Prints a Message when / is called """ 10 | return 'Hello HBNB!' 11 | 12 | 13 | @app.route('/hbnb', strict_slashes=False) 14 | def hbnb(): 15 | """ Prints a Message when /hbnb is called """ 16 | return 'HBNB' 17 | 18 | 19 | @app.route('/c/', strict_slashes=False) 20 | def c_is_fun(text): 21 | """ Prints a Message when /c is called """ 22 | return "C " + text.replace('_', ' ') 23 | 24 | 25 | @app.route('/python', strict_slashes=False) 26 | @app.route('/python/', strict_slashes=False) 27 | def python_is_cool(text='is_cool'): 28 | """ Prints a Message when /python is called """ 29 | return "Python " + text.replace('_', ' ') 30 | 31 | 32 | @app.route('/number/', strict_slashes=False) 33 | def is_n_number(n): 34 | """ Prints a Message when /number is called only if n is an int""" 35 | return "{:d} is a number".format(n) 36 | 37 | 38 | @app.route('/number_template/', strict_slashes=False) 39 | def number_template(n): 40 | """ display a HTML page only if n is an integer """ 41 | return render_template('5-number.html', value=n) 42 | 43 | 44 | if __name__ == "__main__": 45 | """ Main Function """ 46 | app.run(host='0.0.0.0', port=5000) 47 | -------------------------------------------------------------------------------- /web_dynamic/0-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, render_template 11 | import uuid 12 | app = Flask(__name__) 13 | # app.jinja_env.trim_blocks = True 14 | # app.jinja_env.lstrip_blocks = True 15 | 16 | 17 | @app.teardown_appcontext 18 | def close_db(error): 19 | """ Remove the current SQLAlchemy Session """ 20 | storage.close() 21 | 22 | 23 | @app.route('/0-hbnb/', strict_slashes=False) 24 | def hbnb(): 25 | """ HBNB is alive! """ 26 | states = storage.all(State).values() 27 | states = sorted(states, key=lambda k: k.name) 28 | st_ct = [] 29 | 30 | for state in states: 31 | st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) 32 | 33 | amenities = storage.all(Amenity).values() 34 | amenities = sorted(amenities, key=lambda k: k.name) 35 | 36 | places = storage.all(Place).values() 37 | places = sorted(places, key=lambda k: k.name) 38 | 39 | return render_template('0-hbnb.html', 40 | states=st_ct, 41 | amenities=amenities, 42 | places=places, 43 | cache_id=uuid.uuid4()) 44 | 45 | 46 | if __name__ == "__main__": 47 | """ Main Function """ 48 | app.run(host='0.0.0.0', port=5000) 49 | -------------------------------------------------------------------------------- /web_static/5-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 19 |
20 |

21 | States 22 |

23 |

24 | California, Arizona... 25 |

26 |
27 |
28 |

29 | Amenities 30 |

31 |

32 | Internet, Kitchen... 33 |

34 |
35 |
36 |
37 |
38 |

Holberton School

39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /web_flask/templates/10-hbnb_filters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | HBnB 10 | 11 | 12 |
13 | 14 |
15 |
16 |
17 |
18 |

States

19 |

 

20 |
21 |
    22 | {% for state in states %} 23 |
  • 24 |

    {{ state[0].name }}:

    25 |
      26 | {% for city in state[1] %} 27 |
    • {{ city.name }}
    • 28 | {% endfor %} 29 |
    30 |
  • 31 | {% endfor %} 32 |
33 |
34 |
35 |
36 |

Amenities

37 |

 

38 |
39 |
    40 | {% for amenity in amenities %} 41 |
  • {{ amenity.name }}
  • 42 | {% endfor %} 43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 |

Holberton School

51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /web_dynamic/1-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Script that Starts a Flash Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, render_template 11 | import uuid 12 | app = Flask(__name__) 13 | # app.jinja_env.trim_blocks = True 14 | # app.jinja_env.lstrip_blocks = True 15 | 16 | 17 | @app.teardown_appcontext 18 | def close_db(error): 19 | """ Remove the current SQLAlchemy Session """ 20 | storage.close() 21 | 22 | 23 | @app.route('/1-hbnb/', strict_slashes=False) 24 | def hbnb(): 25 | """ HBNB is alive! """ 26 | states = storage.all(State).values() 27 | states = sorted(states, key=lambda k: k.name) 28 | st_ct = [] 29 | 30 | for state in states: 31 | st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) 32 | 33 | amenities = storage.all(Amenity).values() 34 | amenities = sorted(amenities, key=lambda k: k.name) 35 | 36 | places = storage.all(Place).values() 37 | places = sorted(places, key=lambda k: k.name) 38 | 39 | return render_template('1-hbnb.html', 40 | states=st_ct, 41 | amenities=amenities, 42 | places=places, 43 | cache_id=uuid.uuid4()) 44 | 45 | 46 | if __name__ == "__main__": 47 | """ Main Function """ 48 | app.run(host='0.0.0.0', port=5000) 49 | -------------------------------------------------------------------------------- /web_dynamic/2-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Script that Starts a Flash Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, render_template 11 | import uuid 12 | app = Flask(__name__) 13 | # app.jinja_env.trim_blocks = True 14 | # app.jinja_env.lstrip_blocks = True 15 | 16 | 17 | @app.teardown_appcontext 18 | def close_db(error): 19 | """ Remove the current SQLAlchemy Session """ 20 | storage.close() 21 | 22 | 23 | @app.route('/2-hbnb/', strict_slashes=False) 24 | def hbnb(): 25 | """ HBNB is alive! """ 26 | states = storage.all(State).values() 27 | states = sorted(states, key=lambda k: k.name) 28 | st_ct = [] 29 | 30 | for state in states: 31 | st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) 32 | 33 | amenities = storage.all(Amenity).values() 34 | amenities = sorted(amenities, key=lambda k: k.name) 35 | 36 | places = storage.all(Place).values() 37 | places = sorted(places, key=lambda k: k.name) 38 | 39 | return render_template('2-hbnb.html', 40 | states=st_ct, 41 | amenities=amenities, 42 | places=places, 43 | cache_id=uuid.uuid4()) 44 | 45 | 46 | if __name__ == "__main__": 47 | """ Main Function """ 48 | app.run(host='0.0.0.0', port=5000) 49 | -------------------------------------------------------------------------------- /web_dynamic/3-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flask Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, url_for, render_template 11 | import uuid 12 | 13 | 14 | app = Flask(__name__) 15 | # app.jinja_env.trim_blocks = True 16 | # app.jinja_env.lstrip_blocks = True 17 | app.url_map.strict_slashes = False 18 | port = 5000 19 | host = '0.0.0.0' 20 | 21 | 22 | @app.teardown_appcontext 23 | def teardown_db(exception): 24 | """ Remove the current SQLAlchemy Session """ 25 | storage.close() 26 | 27 | 28 | @app.route('/3-hbnb') 29 | def hbnb_filters(the_id=None): 30 | """ request to custom template with states, cities & amentities """ 31 | state_objs = storage.all('State').values() 32 | states = dict([state.name, state] for state in state_objs) 33 | amenities = storage.all('Amenity').values() 34 | places = storage.all('Place').values() 35 | users = dict([user.id, "{} {}".format(user.first_name, user.last_name)] 36 | for user in storage.all('User').values()) 37 | return render_template('3-hbnb.html', 38 | states=states, 39 | amenities=amenities, 40 | places=places, 41 | users=users, 42 | cache_id=uuid.uuid4()) 43 | 44 | 45 | if __name__ == "__main__": 46 | """ Main Function """ 47 | app.run(host=host, port=port) 48 | -------------------------------------------------------------------------------- /web_dynamic/4-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flask Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, url_for, render_template 11 | import uuid 12 | 13 | 14 | app = Flask(__name__) 15 | # app.jinja_env.trim_blocks = True 16 | # app.jinja_env.lstrip_blocks = True 17 | app.url_map.strict_slashes = False 18 | port = 5000 19 | host = '0.0.0.0' 20 | 21 | 22 | @app.teardown_appcontext 23 | def teardown_db(exception): 24 | """ Remove the current SQLAlchemy Session """ 25 | storage.close() 26 | 27 | 28 | @app.route('/4-hbnb') 29 | def hbnb_filters(the_id=None): 30 | """ request to custom template with states, cities & amentities """ 31 | state_objs = storage.all('State').values() 32 | states = dict([state.name, state] for state in state_objs) 33 | amenities = storage.all('Amenity').values() 34 | places = storage.all('Place').values() 35 | users = dict([user.id, "{} {}".format(user.first_name, user.last_name)] 36 | for user in storage.all('User').values()) 37 | return render_template('4-hbnb.html', 38 | states=states, 39 | amenities=amenities, 40 | places=places, 41 | users=users, 42 | cache_id=uuid.uuid4()) 43 | 44 | 45 | if __name__ == "__main__": 46 | """ Main Function """ 47 | app.run(host=host, port=port) 48 | -------------------------------------------------------------------------------- /web_dynamic/100-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flask Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, url_for, render_template 11 | import uuid 12 | 13 | 14 | app = Flask(__name__) 15 | # app.jinja_env.trim_blocks = True 16 | # app.jinja_env.lstrip_blocks = True 17 | app.url_map.strict_slashes = False 18 | port = 5000 19 | host = '0.0.0.0' 20 | 21 | 22 | @app.teardown_appcontext 23 | def teardown_db(exception): 24 | """ Remove the current SQLAlchemy Session """ 25 | storage.close() 26 | 27 | 28 | @app.route('/100-hbnb') 29 | def hbnb_filters(the_id=None): 30 | """ request to custom template with states, cities & amentities """ 31 | state_objs = storage.all('State').values() 32 | states = dict([state.name, state] for state in state_objs) 33 | amenities = storage.all('Amenity').values() 34 | places = storage.all('Place').values() 35 | users = dict([user.id, "{} {}".format(user.first_name, user.last_name)] 36 | for user in storage.all('User').values()) 37 | return render_template('100-hbnb.html', 38 | states=states, 39 | amenities=amenities, 40 | places=places, 41 | users=users, 42 | cache_id=uuid.uuid4()) 43 | 44 | 45 | if __name__ == "__main__": 46 | """ Main Function """ 47 | app.run(host=host, port=port) 48 | -------------------------------------------------------------------------------- /web_dynamic/101-hbnb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flask Web Application """ 3 | 4 | from models import storage 5 | from models.state import State 6 | from models.city import City 7 | from models.amenity import Amenity 8 | from models.place import Place 9 | from os import environ 10 | from flask import Flask, url_for, render_template 11 | import uuid 12 | 13 | 14 | app = Flask(__name__) 15 | # app.jinja_env.trim_blocks = True 16 | # app.jinja_env.lstrip_blocks = True 17 | app.url_map.strict_slashes = False 18 | port = 5000 19 | host = '0.0.0.0' 20 | 21 | 22 | @app.teardown_appcontext 23 | def teardown_db(exception): 24 | """ Remove the current SQLAlchemy Session """ 25 | storage.close() 26 | 27 | 28 | @app.route('/101-hbnb') 29 | def hbnb_filters(the_id=None): 30 | """ request to custom template with states, cities & amentities """ 31 | state_objs = storage.all('State').values() 32 | states = dict([state.name, state] for state in state_objs) 33 | amenities = storage.all('Amenity').values() 34 | places = storage.all('Place').values() 35 | users = dict([user.id, "{} {}".format(user.first_name, user.last_name)] 36 | for user in storage.all('User').values()) 37 | return render_template('101-hbnb.html', 38 | states=states, 39 | amenities=amenities, 40 | places=places, 41 | users=users, 42 | cache_id=uuid.uuid4()) 43 | 44 | 45 | if __name__ == "__main__": 46 | """ Main Function """ 47 | app.run(host=host, port=port) 48 | -------------------------------------------------------------------------------- /api/v1/views/documentation/place/get_place.yml: -------------------------------------------------------------------------------- 1 | Retrieves a place 2 | --- 3 | tags: 4 | - Places 5 | parameters: 6 | - name: place_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the place 11 | responses: 12 | 200: 13 | description: Successful request 14 | schema: 15 | properties: 16 | __class__: 17 | type: string 18 | created_at: 19 | type: string 20 | description: time of creation of the instance 21 | updated_at: 22 | type: string 23 | description: time of last update of the instance 24 | id: 25 | type: string 26 | description: The uuid of the state instance 27 | description: 28 | type: string 29 | description: Description of the place 30 | latitude: 31 | type: number 32 | description: Latitude of the place 33 | longitude: 34 | type: number 35 | description: Longitude of the place 36 | max_guest: 37 | type: integer 38 | description: Number of guests possible 39 | name: 40 | type: string 41 | description: name of the place 42 | number_bathrooms: 43 | type: integer 44 | description: Number of bathrooms 45 | number_rooms: 46 | type: integer 47 | description: Number of rooms 48 | price_by_night: 49 | type: number 50 | description: Price of the night 51 | user_id: 52 | type: string 53 | description: uuid of the owner 54 | 55 | 404: 56 | description: Place not found 57 | -------------------------------------------------------------------------------- /tests/test_console.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Contains the class TestConsoleDocs 4 | """ 5 | 6 | import console 7 | import inspect 8 | import pep8 9 | import unittest 10 | HBNBCommand = console.HBNBCommand 11 | 12 | 13 | class TestConsoleDocs(unittest.TestCase): 14 | """Class for testing documentation of the console""" 15 | def test_pep8_conformance_console(self): 16 | """Test that console.py conforms to PEP8.""" 17 | pep8s = pep8.StyleGuide(quiet=True) 18 | result = pep8s.check_files(['console.py']) 19 | self.assertEqual(result.total_errors, 0, 20 | "Found code style errors (and warnings).") 21 | 22 | def test_pep8_conformance_test_console(self): 23 | """Test that tests/test_console.py conforms to PEP8.""" 24 | pep8s = pep8.StyleGuide(quiet=True) 25 | result = pep8s.check_files(['tests/test_console.py']) 26 | self.assertEqual(result.total_errors, 0, 27 | "Found code style errors (and warnings).") 28 | 29 | def test_console_module_docstring(self): 30 | """Test for the console.py module docstring""" 31 | self.assertIsNot(console.__doc__, None, 32 | "console.py needs a docstring") 33 | self.assertTrue(len(console.__doc__) >= 1, 34 | "console.py needs a docstring") 35 | 36 | def test_HBNBCommand_class_docstring(self): 37 | """Test for the HBNBCommand class docstring""" 38 | self.assertIsNot(HBNBCommand.__doc__, None, 39 | "HBNBCommand class needs a docstring") 40 | self.assertTrue(len(HBNBCommand.__doc__) >= 1, 41 | "HBNBCommand class needs a docstring") 42 | -------------------------------------------------------------------------------- /web_flask/6-number_odd_or_even.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ Starts a Flash Web Application """ 3 | from flask import Flask, render_template 4 | app = Flask(__name__) 5 | app.jinja_env.trim_blocks = True 6 | app.jinja_env.lstrip_blocks = True 7 | 8 | 9 | @app.route('/', strict_slashes=False) 10 | def hello_hbnb(): 11 | """ Prints a Message when / is called """ 12 | return 'Hello HBNB!' 13 | 14 | 15 | @app.route('/hbnb', strict_slashes=False) 16 | def hbnb(): 17 | """ Prints a Message when /hbnb is called """ 18 | return 'HBNB' 19 | 20 | 21 | @app.route('/c/', strict_slashes=False) 22 | def c_is_fun(text): 23 | """ Prints a Message when /c is called """ 24 | return "C " + text.replace('_', ' ') 25 | 26 | 27 | @app.route('/python', strict_slashes=False) 28 | @app.route('/python/', strict_slashes=False) 29 | def python_is_cool(text='is_cool'): 30 | """ Prints a Message when /python is called """ 31 | return "Python " + text.replace('_', ' ') 32 | 33 | 34 | @app.route('/number/', strict_slashes=False) 35 | def is_n_number(n): 36 | """ Prints a Message when /number is called only if n is an int""" 37 | return "{:d} is a number".format(n) 38 | 39 | 40 | @app.route('/number_template/', strict_slashes=False) 41 | def number_template(n): 42 | """ display a HTML page only if n is an integer """ 43 | return render_template('5-number.html', value=n) 44 | 45 | 46 | @app.route('/number_odd_or_even/', strict_slashes=False) 47 | def odd_or_even(n): 48 | """ display a HTML page only if n is an integer """ 49 | return render_template('6-number_odd_or_even.html', value=n) 50 | 51 | 52 | if __name__ == "__main__": 53 | """ Main Function """ 54 | app.run(host='0.0.0.0', port=5000) 55 | -------------------------------------------------------------------------------- /api/v1/views/documentation/place/get_places.yml: -------------------------------------------------------------------------------- 1 | Gets the list of all places of a city 2 | --- 3 | tags: 4 | - Places 5 | parameters: 6 | - name: city_id 7 | in: path 8 | type: string 9 | required: true 10 | description: the unique id of the city 11 | 12 | responses: 13 | 200: 14 | description: Successful request 15 | schema: 16 | type: array 17 | items: 18 | properties: 19 | __class__: 20 | type: string 21 | created_at: 22 | type: string 23 | description: time of creation of the instance 24 | updated_at: 25 | type: string 26 | description: time of last update of the instance 27 | id: 28 | type: string 29 | description: The uuid of the state instance 30 | description: 31 | type: string 32 | description: Description of the place 33 | latitude: 34 | type: number 35 | description: Latitude of the place 36 | longitude: 37 | type: number 38 | description: Longitude of the place 39 | max_guest: 40 | type: integer 41 | description: Number of guests possible 42 | name: 43 | type: string 44 | description: name of the place 45 | number_bathrooms: 46 | type: integer 47 | description: Number of bathrooms 48 | number_rooms: 49 | type: integer 50 | description: Number of rooms 51 | price_by_night: 52 | type: number 53 | description: Price of the night 54 | user_id: 55 | type: string 56 | description: uuid of the owner 57 | 58 | 404: 59 | description: City not found 60 | -------------------------------------------------------------------------------- /3-deploy_web_static.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Fabric script based on the file 2-do_deploy_web_static.py that creates and 4 | distributes an archive to the web servers 5 | """ 6 | 7 | from fabric.api import env, local, put, run 8 | from datetime import datetime 9 | from os.path import exists, isdir 10 | env.hosts = ['142.44.167.228', '144.217.246.195'] 11 | 12 | 13 | def do_pack(): 14 | """generates a tgz archive""" 15 | try: 16 | date = datetime.now().strftime("%Y%m%d%H%M%S") 17 | if isdir("versions") is False: 18 | local("mkdir versions") 19 | file_name = "versions/web_static_{}.tgz".format(date) 20 | local("tar -cvzf {} web_static".format(file_name)) 21 | return file_name 22 | except: 23 | return None 24 | 25 | 26 | def do_deploy(archive_path): 27 | """distributes an archive to the web servers""" 28 | if exists(archive_path) is False: 29 | return False 30 | try: 31 | file_n = archive_path.split("/")[-1] 32 | no_ext = file_n.split(".")[0] 33 | path = "/data/web_static/releases/" 34 | put(archive_path, '/tmp/') 35 | run('mkdir -p {}{}/'.format(path, no_ext)) 36 | run('tar -xzf /tmp/{} -C {}{}/'.format(file_n, path, no_ext)) 37 | run('rm /tmp/{}'.format(file_n)) 38 | run('mv {0}{1}/web_static/* {0}{1}/'.format(path, no_ext)) 39 | run('rm -rf {}{}/web_static'.format(path, no_ext)) 40 | run('rm -rf /data/web_static/current') 41 | run('ln -s {}{}/ /data/web_static/current'.format(path, no_ext)) 42 | return True 43 | except: 44 | return False 45 | 46 | 47 | def deploy(): 48 | """creates and distributes an archive to the web servers""" 49 | archive_path = do_pack() 50 | if archive_path is None: 51 | return False 52 | return do_deploy(archive_path) 53 | -------------------------------------------------------------------------------- /web_dynamic/static/scripts/3-hbnb.js: -------------------------------------------------------------------------------- 1 | // Script that is executed only when DOM is loaded with jQuery 2 | 3 | let checked_box = {}; 4 | $(document).ready(function () { 5 | $('input:checkbox').change(function () { 6 | if ($(this).is(':checked_box')) { 7 | checked_box[$(this).data('id')] = $(this).data('name'); 8 | } 9 | else { 10 | delete checked_box[$(this).data('id')]; 11 | } 12 | $('div.amenities h4').html(function () { 13 | let amenities = []; 14 | Object.keys(checked_box).forEach(function (key) { 15 | amenities.push(checked_box[key]); 16 | }); 17 | if (amenities.length === 0) { 18 | return (' '); 19 | } 20 | return (amenities.join(', ')); 21 | }); 22 | }); 23 | 24 | 25 | const apiStatus = $('DIV#api_status'); 26 | $.ajax('http://0.0.0.0:5001/api/v1/status/').done(function (data) { 27 | if (data.status === 'OK') { 28 | apiStatus.addClass('available'); 29 | } else { 30 | apiStatus.removeClass('available'); 31 | } 32 | }); 33 | }); 34 | 35 | 36 | $.ajax({ 37 | type: 'POST', 38 | url: 'http://0.0.0.0:5001/api/v1/places_search/', 39 | contentType: 'application/json', 40 | data: '{}', 41 | success: function (data) { 42 | for (let currentPlace of data) { 43 | $('.places').append('

' + currentPlace.name + '

' + '$' + currentPlace.price_by_night + '

' + currentPlace.max_guest + ' Guests

' + currentPlace.number_rooms + ' Bedrooms

' + currentPlace.number_bathrooms + ' Bathroom
' + '$' + currentPlace.description + '
'); 44 | } 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /web_dynamic/static/scripts/4-hbnb.js: -------------------------------------------------------------------------------- 1 | // Script that is executed only when DOM is loaded with jQuery 2 | 3 | let checked_box = {}; 4 | $(document).ready(function () { 5 | $('input:checkbox').change(function () { 6 | if ($(this).is(':checked_box')) { 7 | checked_box[$(this).data('id')] = $(this).data('name'); 8 | } 9 | else { 10 | delete checked_box[$(this).data('id')]; 11 | } 12 | $('div.amenities h4').html(function () { 13 | let amenities = []; 14 | Object.keys(checked_box).forEach(function (key) { 15 | amenities.push(checked_box[key]); 16 | }); 17 | if (amenities.length === 0) { 18 | return (' '); 19 | } 20 | return (amenities.join(', ')); 21 | }); 22 | }); 23 | 24 | 25 | const apiStatus = $('DIV#api_status'); 26 | $.ajax('http://0.0.0.0:5001/api/v1/status/').done(function (data) { 27 | if (data.status === 'OK') { 28 | apiStatus.addClass('available'); 29 | } else { 30 | apiStatus.removeClass('available'); 31 | } 32 | }); 33 | 34 | 35 | 36 | $('button').click(function(){ 37 | 38 | $.ajax({ 39 | type: 'POST', 40 | url: 'http://0.0.0.0:5001/api/v1/places_search/', 41 | contentType: 'application/json', 42 | data: '{}', 43 | success: function (data) { 44 | for (let currentPlace of data) { 45 | $('.places').append('

' + currentPlace.name + '

' + '$' + currentPlace.price_by_night + '

' + currentPlace.max_guest + ' Guests

' + currentPlace.number_rooms + ' Bedrooms

' + currentPlace.number_bathrooms + ' Bathroom
' + '$' + currentPlace.description + '
'); 46 | } 47 | } 48 | }); 49 | 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /web_dynamic/static/scripts/100-hbnb.js: -------------------------------------------------------------------------------- 1 | // Script that is executed only when DOM is loaded with jQuery 2 | 3 | let checked_box = {}; 4 | $(document).ready(function () { 5 | $('input:checkbox').change(function () { 6 | if ($(this).is(':checked_box')) { 7 | checked_box[$(this).data('id')] = $(this).data('name'); 8 | } 9 | else { 10 | delete checked_box[$(this).data('id')]; 11 | } 12 | $('div.amenities h4').html(function () { 13 | let amenities = []; 14 | Object.keys(checked_box).forEach(function (key) { 15 | amenities.push(checked_box[key]); 16 | }); 17 | if (amenities.length === 0) { 18 | return (' '); 19 | } 20 | return (amenities.join(', ')); 21 | }); 22 | }); 23 | 24 | 25 | const apiStatus = $('DIV#api_status'); 26 | $.ajax('http://0.0.0.0:5001/api/v1/status/').done(function (data) { 27 | if (data.status === 'OK') { 28 | apiStatus.addClass('available'); 29 | } else { 30 | apiStatus.removeClass('available'); 31 | } 32 | }); 33 | 34 | 35 | 36 | $('button').click(function(){ 37 | 38 | $.ajax({ 39 | type: 'POST', 40 | url: 'http://0.0.0.0:5001/api/v1/places_search/', 41 | contentType: 'application/json', 42 | data: '{}', 43 | success: function (data) { 44 | for (let currentPlace of data) { 45 | $('.places').append('

' + currentPlace.name + '

' + '$' + currentPlace.price_by_night + '

' + currentPlace.max_guest + ' Guests

' + currentPlace.number_rooms + ' Bedrooms

' + currentPlace.number_bathrooms + ' Bathroom
' + '$' + currentPlace.description + '
'); 46 | } 47 | } 48 | }); 49 | 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /web_dynamic/static/scripts/101-hbnb.js: -------------------------------------------------------------------------------- 1 | // Script that is executed only when DOM is loaded with jQuery 2 | 3 | let checked_box = {}; 4 | $(document).ready(function () { 5 | $('input:checkbox').change(function () { 6 | if ($(this).is(':checked_box')) { 7 | checked_box[$(this).data('id')] = $(this).data('name'); 8 | } 9 | else { 10 | delete checked_box[$(this).data('id')]; 11 | } 12 | $('div.amenities h4').html(function () { 13 | let amenities = []; 14 | Object.keys(checked_box).forEach(function (key) { 15 | amenities.push(checked_box[key]); 16 | }); 17 | if (amenities.length === 0) { 18 | return (' '); 19 | } 20 | return (amenities.join(', ')); 21 | }); 22 | }); 23 | 24 | 25 | const apiStatus = $('DIV#api_status'); 26 | $.ajax('http://0.0.0.0:5001/api/v1/status/').done(function (data) { 27 | if (data.status === 'OK') { 28 | apiStatus.addClass('available'); 29 | } else { 30 | apiStatus.removeClass('available'); 31 | } 32 | }); 33 | 34 | 35 | 36 | $('button').click(function(){ 37 | 38 | $.ajax({ 39 | type: 'POST', 40 | url: 'http://0.0.0.0:5001/api/v1/places_search/', 41 | contentType: 'application/json', 42 | data: '{}', 43 | success: function (data) { 44 | for (let currentPlace of data) { 45 | $('.places').append('

' + currentPlace.name + '

' + '$' + currentPlace.price_by_night + '

' + currentPlace.max_guest + ' Guests

' + currentPlace.number_rooms + ' Bedrooms

' + currentPlace.number_bathrooms + ' Bathroom
' + '$' + currentPlace.description + '
'); 46 | } 47 | } 48 | }); 49 | 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /web_static/styles/6-filters.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | background-color: white; 3 | height: 70px; 4 | border: 1px solid #DDDDDD; 5 | border-radius: 4px; 6 | display: flex; 7 | align-items: center; 8 | position: relative; 9 | 10 | } 11 | 12 | .filters button { 13 | font-size: 18px; 14 | background-color: #FF5A5F; 15 | color: #FFFFFF; 16 | height: 48px; 17 | width: 20%; 18 | border: none; 19 | border-radius: 4px; 20 | position: absolute; 21 | right: 30px; 22 | box-sizing: border-box; 23 | } 24 | .filters button:hover { 25 | opacity: 0.9; 26 | } 27 | 28 | .filters .locations,.filters .amenities { 29 | 30 | height: 100%; 31 | width: 25%; 32 | position: relative; 33 | display: flex; 34 | justify-content: center; 35 | flex-direction: column; 36 | box-sizing: border-box; 37 | } 38 | 39 | .locations { 40 | border-right: 1px solid #DDDDDD ; 41 | } 42 | 43 | .filters .locations h3, .filters .amenities h3{ 44 | font-weight: 600; 45 | margin: 2px 20px; 46 | 47 | } 48 | .locations h4, .amenities h4{ 49 | font-weight: 400; 50 | font-size: 14px; 51 | margin: 2px 20px; 52 | 53 | } 54 | .popover { 55 | display: none; 56 | background-color: #FAFAFA; 57 | border: 1px solid #DDDDDD; 58 | border-radius: 4px; 59 | position: absolute; 60 | box-sizing: border-box; 61 | width: 100%; 62 | top: 100%; 63 | margin-top: 0; 64 | } 65 | 66 | div:hover > .popover { 67 | display: block; 68 | cursor: pointer; 69 | 70 | } 71 | .popover h2 { 72 | font-size: 16px; 73 | } 74 | .popover li { 75 | margin: 10px 0px 5px 0px; 76 | list-style: none; 77 | } 78 | 79 | .filters .locations ul { 80 | padding: 0.5em 0 1em 1em; 81 | } 82 | 83 | .filters .locations li { 84 | margin: 0 0 0 5%; 85 | padding: 0.2em; 86 | } 87 | .filters .amenities ul { 88 | padding: 2em 0 2em 3em; 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /web_static/styles/8-places.css: -------------------------------------------------------------------------------- 1 | .places{ 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | } 6 | 7 | .places h1 { 8 | width: 100%; 9 | font-size: 30px; 10 | } 11 | 12 | article { 13 | width: 390px; 14 | padding: 20px 20px 20px 20px; 15 | margin: 20px 20px 20px 20px; 16 | border: 1px solid #FF5A5F; 17 | border-radius: 4px; 18 | 19 | position: relative; 20 | z-index: -1; 21 | } 22 | .information { 23 | display: flex; 24 | flex-wrap: wrap; 25 | } 26 | 27 | article h2 { 28 | font-size: 30px; 29 | display: flex; 30 | text-align: center; 31 | flex-direction: column; 32 | margin: 0; 33 | padding-bottom: 10% 34 | 35 | } 36 | 37 | .price_by_night { 38 | color: #FF5A5F; 39 | border: 4px solid #FF5A5F; 40 | border-radius: 100%; 41 | min-width: 60px; 42 | height: 60px; 43 | font-size: 30px; 44 | position: absolute; 45 | display: flex; 46 | justify-content: center; 47 | align-items: center; 48 | top: 5%; 49 | right: 7%; 50 | } 51 | 52 | .information { 53 | height: 80px; 54 | border-top: 1px solid #DDDDDD; 55 | border-bottom: 1px solid #DDDDDD; 56 | display: flex; 57 | justify-content: center; 58 | } 59 | 60 | 61 | .max_guest, .number_rooms, .number_bathrooms { 62 | background-repeat: no-repeat; 63 | width: 100px; 64 | height: 100px; 65 | background-position: center top; 66 | line-height: 9em; 67 | text-align: center; 68 | } 69 | .max_guest { 70 | background-image: url(../images/icon_group.png); 71 | 72 | } 73 | .number_rooms { 74 | background-image: url(../images/icon_bed.png); 75 | } 76 | .number_bathrooms { 77 | background-image: url(../images/icon_bath.png); 78 | 79 | } 80 | 81 | .user { 82 | margin: 10px auto; 83 | } 84 | 85 | /* 86 | justify-content 87 | align-items 88 | flex-direction 89 | order 90 | align-self 91 | flex-wrap 92 | flex-flow 93 | align-content 94 | */ 95 | -------------------------------------------------------------------------------- /web_dynamic/static/styles/6-filters.css: -------------------------------------------------------------------------------- 1 | .container .filters { 2 | position: relative; 3 | background: white; 4 | height: 70px; 5 | width: 100%; 6 | border: 1px solid #DDDDDD; 7 | border-radius: 4px; 8 | } 9 | button { 10 | position: absolute; 11 | font-size: 18px; 12 | background: #FF5A5F; 13 | color: #FFFFFF; 14 | height: 48px; 15 | width: 20%; 16 | border-style: none; 17 | border-radius: 4px; 18 | top: 15%; 19 | right: 30px; 20 | } 21 | button:hover { 22 | opacity: 0.9; 23 | } 24 | .filters div { 25 | display: inline-grid; 26 | } 27 | .filters h2 { 28 | margin-left: 15%; 29 | margin-top: 0; 30 | margin-bottom: 0; 31 | font-weight: 600; 32 | } 33 | .filters h3 { 34 | margin-left: 15%; 35 | margin-bottom: 0; 36 | font-weight: 600; 37 | } 38 | .filters h4 { 39 | margin-left: 15%; 40 | margin-top: 0; 41 | font-weight: 400; 42 | font-size: 14px; 43 | } 44 | .locations { 45 | height: 100%; 46 | width: 25%; 47 | border-right: 1px solid #DDDDDD; 48 | } 49 | .amenities { 50 | height: 100%; 51 | width: 25%; 52 | } 53 | 54 | .popover { 55 | visibility: hidden; 56 | width: 100%; 57 | border: 1px solid #DDDDDD; 58 | border-radius: 4px; 59 | background: #FAFAFA; 60 | padding-bottom: 15px; 61 | height: 300px; 62 | overflow-y: scroll; 63 | scrollbar-width: none; 64 | max-height: 300px; 65 | } 66 | 67 | .popover::-webkit-scrollbar{ 68 | width: 0px; 69 | } 70 | 71 | .amenities .popover { 72 | padding: 10px 0; 73 | margin-left: -5px; 74 | margin-top: 0%; 75 | } 76 | 77 | .amenities .popover ul{ 78 | margin: 0px; 79 | } 80 | 81 | .locations .popover { 82 | margin-top: 0%; 83 | } 84 | .popover ul { 85 | list-style-type: none; 86 | padding-bottom: 10px; 87 | padding-left: 10px; 88 | } 89 | .popover ul li{ 90 | padding: 4px; 91 | padding-left: 10px; 92 | } 93 | 94 | .popover ul h2{ 95 | margin-top: 1.5%; 96 | margin-bottom: 5%; 97 | margin-left: 0px; 98 | } 99 | 100 | .amenities:hover .popover, 101 | .locations:hover .popover { 102 | visibility: visible; 103 | } 104 | -------------------------------------------------------------------------------- /web_flask/static/styles/6-filters.css: -------------------------------------------------------------------------------- 1 | .container .filters { 2 | position: relative; 3 | background: white; 4 | height: 70px; 5 | width: 100%; 6 | border: 1px solid #DDDDDD; 7 | border-radius: 4px; 8 | } 9 | button { 10 | position: absolute; 11 | font-size: 18px; 12 | background: #FF5A5F; 13 | color: #FFFFFF; 14 | height: 48px; 15 | width: 20%; 16 | border-style: none; 17 | border-radius: 4px; 18 | top: 15%; 19 | right: 30px; 20 | } 21 | button:hover { 22 | opacity: 0.9; 23 | } 24 | .filters div { 25 | display: inline-grid; 26 | } 27 | .filters h2 { 28 | margin-left: 15%; 29 | margin-top: 0; 30 | margin-bottom: 0; 31 | font-weight: 600; 32 | } 33 | .filters h3 { 34 | margin-left: 15%; 35 | margin-bottom: 0; 36 | font-weight: 600; 37 | } 38 | .filters h4 { 39 | margin-left: 15%; 40 | margin-top: 0; 41 | font-weight: 400; 42 | font-size: 14px; 43 | } 44 | .locations { 45 | height: 100%; 46 | width: 25%; 47 | border-right: 1px solid #DDDDDD; 48 | } 49 | .amenities { 50 | height: 100%; 51 | width: 25%; 52 | } 53 | 54 | .popover { 55 | visibility: hidden; 56 | width: 100%; 57 | border: 1px solid #DDDDDD; 58 | border-radius: 4px; 59 | background: #FAFAFA; 60 | padding-bottom: 15px; 61 | height: 300px; 62 | overflow-y: scroll; 63 | scrollbar-width: none; 64 | max-height: 300px; 65 | } 66 | 67 | .popover::-webkit-scrollbar{ 68 | width: 0px; 69 | } 70 | 71 | .amenities .popover { 72 | padding: 10px 0; 73 | margin-left: -5px; 74 | margin-top: 0%; 75 | } 76 | 77 | .amenities .popover ul{ 78 | margin: 0px; 79 | } 80 | 81 | .locations .popover { 82 | margin-top: 0%; 83 | } 84 | .popover ul { 85 | list-style-type: none; 86 | padding-bottom: 10px; 87 | padding-left: 10px; 88 | } 89 | .popover ul li{ 90 | padding: 4px; 91 | padding-left: 10px; 92 | } 93 | 94 | .popover ul h2{ 95 | margin-top: 1.5%; 96 | margin-bottom: 5%; 97 | margin-left: 0px; 98 | } 99 | 100 | .amenities:hover .popover, 101 | .locations:hover .popover { 102 | visibility: visible; 103 | } 104 | -------------------------------------------------------------------------------- /web_flask/static/styles/8-places.css: -------------------------------------------------------------------------------- 1 | .places { 2 | column-count: 2; 3 | columns: 30em; 4 | justify-content: center; 5 | padding: 0 20px; 6 | margin-top: 1%; 7 | margin-bottom: 8%; 8 | } 9 | @media only screen and (max-width: 920px) 10 | { 11 | .places { 12 | display: flex; 13 | flex-wrap: wrap; 14 | } 15 | } 16 | 17 | .placesh1 h1 { 18 | width: 100%; 19 | margin-right: 400px; 20 | text-align: left; 21 | font-size: 35px; 22 | } 23 | 24 | .places article { 25 | -webkit-column-break-inside: avoid; 26 | page-break-inside: avoid; 27 | display: inline-block; 28 | width: 390px; 29 | height: 100%; 30 | padding: 20px; 31 | margin: 20px; 32 | border: 1px solid #FF5A5F; 33 | border-radius: 4px; 34 | } 35 | .places h2 { 36 | font-size: 30px; 37 | text-align: center; 38 | margin-top: 0; 39 | } 40 | .title_box { 41 | display: flex; 42 | justify-content: space-between; 43 | margin-top: -2%; 44 | } 45 | 46 | .title_box h2 { 47 | text-align: left; 48 | margin: 25px 3% 40px 2%; 49 | max-width: 75%; 50 | word-wrap: break-word; 51 | } 52 | .price_by_night { 53 | display: flex; 54 | height: 60px; 55 | min-width: 60px; 56 | font-size: 30px; 57 | justify-content: center; 58 | align-items: center; 59 | color: #FF5A5F; 60 | border: 4px solid #FF5A5F; 61 | border-radius: 50%; 62 | align-items: center; 63 | padding: 2.3%; 64 | } 65 | 66 | .information { 67 | display: flex; 68 | justify-content: center; 69 | align-items: center; 70 | height: 80px; 71 | border-top: 1px solid #DDDDDD; 72 | border-bottom: 1px solid #DDDDDD; 73 | margin-bottom: 5%; 74 | } 75 | 76 | .information div { 77 | display: flex; 78 | justify-content: flex-end; 79 | align-items: center; 80 | flex-direction: column; 81 | height: 65px; 82 | } 83 | 84 | .information .max_guest { 85 | background: url("../images/icon_group.png") no-repeat top center; 86 | width: 100px; 87 | } 88 | 89 | .information .number_rooms { 90 | background: url("../images/icon_bed.png") no-repeat top center; 91 | width: 100px; 92 | } 93 | 94 | .information .number_bathrooms { 95 | background: url("../images/icon_bath.png") no-repeat top center; 96 | width: 100px; 97 | } 98 | 99 | .user { 100 | margin-bottom: 1.5%; 101 | } 102 | -------------------------------------------------------------------------------- /web_dynamic/static/styles/8-places.css: -------------------------------------------------------------------------------- 1 | .places { 2 | column-count: 2; 3 | columns: 30em; 4 | justify-content: center; 5 | padding: 0 20px; 6 | margin-top: 1%; 7 | margin-bottom: 8%; 8 | } 9 | @media only screen and (max-width: 920px) 10 | { 11 | .places { 12 | display: flex; 13 | flex-wrap: wrap; 14 | } 15 | } 16 | 17 | .placesh1 h1 { 18 | width: 100%; 19 | margin-right: 400px; 20 | text-align: left; 21 | font-size: 35px; 22 | } 23 | 24 | .places article { 25 | -webkit-column-break-inside: avoid; 26 | page-break-inside: avoid; 27 | display: inline-block; 28 | width: 390px; 29 | height: 100%; 30 | padding: 20px; 31 | margin: 20px; 32 | border: 1px solid #FF5A5F; 33 | border-radius: 4px; 34 | } 35 | .places h2 { 36 | font-size: 30px; 37 | text-align: center; 38 | margin-top: 0; 39 | } 40 | .title_box { 41 | display: flex; 42 | justify-content: space-between; 43 | margin-top: -2%; 44 | } 45 | 46 | .title_box h2 { 47 | text-align: left; 48 | margin: 25px 3% 40px 2%; 49 | max-width: 75%; 50 | word-wrap: break-word; 51 | } 52 | .price_by_night { 53 | display: flex; 54 | height: 60px; 55 | min-width: 60px; 56 | font-size: 30px; 57 | justify-content: center; 58 | align-items: center; 59 | color: #FF5A5F; 60 | border: 4px solid #FF5A5F; 61 | border-radius: 50%; 62 | align-items: center; 63 | padding: 2.3%; 64 | } 65 | 66 | .information { 67 | display: flex; 68 | justify-content: center; 69 | align-items: center; 70 | height: 80px; 71 | border-top: 1px solid #DDDDDD; 72 | border-bottom: 1px solid #DDDDDD; 73 | margin-bottom: 5%; 74 | } 75 | 76 | .information div { 77 | display: flex; 78 | justify-content: flex-end; 79 | align-items: center; 80 | flex-direction: column; 81 | height: 65px; 82 | } 83 | 84 | .information .max_guest { 85 | background: url("../images/icon_group.png") no-repeat top center; 86 | width: 100px; 87 | } 88 | 89 | .information .number_rooms { 90 | background: url("../images/icon_bed.png") no-repeat top center; 91 | width: 100px; 92 | } 93 | 94 | .information .number_bathrooms { 95 | background: url("../images/icon_bath.png") no-repeat top center; 96 | width: 100px; 97 | } 98 | 99 | .user { 100 | margin-bottom: 1.5%; 101 | } 102 | -------------------------------------------------------------------------------- /web_flask/templates/100-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | HBnB 12 | 13 | 14 |
15 | 16 |
17 |
18 |
19 |
20 |

States

21 |

 

22 |
23 |
    24 | {% for state in states %} 25 |
  • 26 |

    {{ state[0].name }}:

    27 |
      28 | {% for city in state[1] %} 29 |
    • {{ city.name }}
    • 30 | {% endfor %} 31 |
    32 |
  • 33 | {% endfor %} 34 |
35 |
36 |
37 |
38 |

Amenities

39 |

 

40 |
41 |
    42 | {% for amenity in amenities %} 43 |
  • {{ amenity.name }}
  • 44 | {% endfor %} 45 |
46 |
47 |
48 | 49 |
50 |

Places

51 |
52 | 53 | {% for place in places %} 54 |
55 |
56 |

{{ place.name }}

57 |
${{ place.price_by_night }}
58 |
59 |
60 |
{{ place.max_guest }} Guest{% if place.max_guest != 1 %}s{% endif %}
61 |
{{ place.number_rooms }} Bedroom{% if place.number_rooms != 1 %}s{% endif %}
62 |
{{ place.number_bathrooms }} Bathroom{% if place.number_bathrooms != 1 %}s{% endif %}
63 |
64 |
65 | Owner: {{ place.user.first_name }} {{ place.user.last_name }} 66 |
67 |
68 | {{ place.description | safe }} 69 |
70 |
71 | {% endfor %} 72 |
73 |
74 |
75 |

Holberton School

76 |
77 | 78 | 79 | -------------------------------------------------------------------------------- /web_static/6-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 19 |
20 |

21 | States 22 |

23 |

24 | California, Arizona... 25 |

26 |
    27 |
  • 28 |

    Arizona:

    29 |
  • 30 |
  • 31 |
      32 |
    • Page
    • 33 |
    • Page2
    • 34 |
    35 |
  • 36 |
  • 37 |

    California:

    38 |
  • 39 |
  • 40 |
      41 |
    • San Francisco
    • 42 |
    • Los Altos
    • 43 |
    44 |
  • 45 |
46 | 47 |
48 |
49 |

50 | Amenities 51 |

52 |

53 | Internet, Kitchen... 54 |

55 |
    56 |
  • Internet
  • 57 |
  • TV
  • 58 |
  • Kitchen
  • 59 |
  • Iron
  • 60 |
61 |
62 |
63 |
64 |
65 |

Holberton School

66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /web_dynamic/templates/0-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | HBnB 12 | 13 | 14 |
15 | 16 |
17 |
18 |
19 |
20 |

States

21 |

 

22 |
23 |
    24 | {% for state in states %} 25 |
  • 26 |

    {{ state[0].name }}:

    27 |
      28 | {% for city in state[1] %} 29 |
    • {{ city.name }}
    • 30 | {% endfor %} 31 |
    32 |
  • 33 | {% endfor %} 34 |
35 |
36 |
37 |
38 |

Amenities

39 |

 

40 |
41 |
    42 | {% for amenity in amenities %} 43 |
  • {{ amenity.name }}
  • 44 | {% endfor %} 45 |
46 |
47 |
48 | 49 |
50 |

Places

51 |
52 | 53 | {% for place in places %} 54 |
55 |
56 |

{{ place.name }}

57 |
${{ place.price_by_night }}
58 |
59 |
60 |
{{ place.max_guest }} Guest{% if place.max_guest != 1 %}s{% endif %}
61 |
{{ place.number_rooms }} Bedroom{% if place.number_rooms != 1 %}s{% endif %}
62 |
{{ place.number_bathrooms }} Bathroom{% if place.number_bathrooms != 1 %}s{% endif %}
63 |
64 |
65 | Owner: {{ place.user.first_name }} {{ place.user.last_name }} 66 |
67 |
68 | {{ place.description | safe }} 69 |
70 |
71 | {% endfor %} 72 |
73 |
74 |
75 |

Holberton School

76 |
77 | 78 | 79 | -------------------------------------------------------------------------------- /web_dynamic/templates/1-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | HBnB 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 |
24 |

States

25 |

 

26 |
27 |
    28 | {% for state in states %} 29 |
  • 30 |

    {{ state[0].name }}:

    31 |
      32 | {% for city in state[1] %} 33 |
    • {{ city.name }}
    • 34 | {% endfor %} 35 |
    36 |
  • 37 | {% endfor %} 38 |
39 |
40 |
41 |
42 |

Amenities

43 |

 

44 |
45 |
    46 | {% for amenity in amens %} 47 |
  • {{ amenity.name }}
  • 48 | {% endfor %} 49 |
50 |
51 |
52 | 53 |
54 |

Places

55 |
56 | 57 | {% for place in places %} 58 |
59 |
60 |

{{ place.name }}

61 |
${{ place.price_by_night }}
62 |
63 |
64 |
{{ place.max_guest }} Guest{% if place.max_guest != 1 %}s{% endif %}
65 |
{{ place.number_rooms }} Bedroom{% if place.number_rooms != 1 %}s{% endif %}
66 |
{{ place.number_bathrooms }} Bathroom{% if place.number_bathrooms != 1 %}s{% endif %}
67 |
68 |
69 | Owner: {{ place.user.first_name }} {{ place.user.last_name }} 70 |
71 |
72 | {{ place.description | safe }} 73 |
74 |
75 | {% endfor %} 76 |
77 |
78 |
79 |

Holberton School

80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /api/v1/views/states.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ objects that handle all default RestFul API actions for States """ 3 | from models.state import State 4 | from models import storage 5 | from api.v1.views import app_views 6 | from flask import abort, jsonify, make_response, request 7 | from flasgger.utils import swag_from 8 | 9 | 10 | @app_views.route('/states', methods=['GET'], strict_slashes=False) 11 | @swag_from('documentation/state/get_state.yml', methods=['GET']) 12 | def get_states(): 13 | """ 14 | Retrieves the list of all State objects 15 | """ 16 | all_states = storage.all(State).values() 17 | list_states = [] 18 | for state in all_states: 19 | list_states.append(state.to_dict()) 20 | return jsonify(list_states) 21 | 22 | 23 | @app_views.route('/states/', methods=['GET'], strict_slashes=False) 24 | @swag_from('documentation/state/get_id_state.yml', methods=['get']) 25 | def get_state(state_id): 26 | """ Retrieves a specific State """ 27 | state = storage.get(State, state_id) 28 | if not state: 29 | abort(404) 30 | 31 | return jsonify(state.to_dict()) 32 | 33 | 34 | @app_views.route('/states/', methods=['DELETE'], 35 | strict_slashes=False) 36 | @swag_from('documentation/state/delete_state.yml', methods=['DELETE']) 37 | def delete_state(state_id): 38 | """ 39 | Deletes a State Object 40 | """ 41 | 42 | state = storage.get(State, state_id) 43 | 44 | if not state: 45 | abort(404) 46 | 47 | storage.delete(state) 48 | storage.save() 49 | 50 | return make_response(jsonify({}), 200) 51 | 52 | 53 | @app_views.route('/states', methods=['POST'], strict_slashes=False) 54 | @swag_from('documentation/state/post_state.yml', methods=['POST']) 55 | def post_state(): 56 | """ 57 | Creates a State 58 | """ 59 | if not request.get_json(): 60 | abort(400, description="Not a JSON") 61 | 62 | if 'name' not in request.get_json(): 63 | abort(400, description="Missing name") 64 | 65 | data = request.get_json() 66 | instance = State(**data) 67 | instance.save() 68 | return make_response(jsonify(instance.to_dict()), 201) 69 | 70 | 71 | @app_views.route('/states/', methods=['PUT'], strict_slashes=False) 72 | @swag_from('documentation/state/put_state.yml', methods=['PUT']) 73 | def put_state(state_id): 74 | """ 75 | Updates a State 76 | """ 77 | state = storage.get(State, state_id) 78 | 79 | if not state: 80 | abort(404) 81 | 82 | if not request.get_json(): 83 | abort(400, description="Not a JSON") 84 | 85 | ignore = ['id', 'created_at', 'updated_at'] 86 | 87 | data = request.get_json() 88 | for key, value in data.items(): 89 | if key not in ignore: 90 | setattr(state, key, value) 91 | storage.save() 92 | return make_response(jsonify(state.to_dict()), 200) 93 | -------------------------------------------------------------------------------- /api/v1/views/users.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ objects that handle all default RestFul API actions for Users """ 3 | from models.user import User 4 | from models import storage 5 | from api.v1.views import app_views 6 | from flask import abort, jsonify, make_response, request 7 | from flasgger.utils import swag_from 8 | 9 | 10 | @app_views.route('/users', methods=['GET'], strict_slashes=False) 11 | @swag_from('documentation/user/all_users.yml') 12 | def get_users(): 13 | """ 14 | Retrieves the list of all user objects 15 | or a specific user 16 | """ 17 | all_users = storage.all(User).values() 18 | list_users = [] 19 | for user in all_users: 20 | list_users.append(user.to_dict()) 21 | return jsonify(list_users) 22 | 23 | 24 | @app_views.route('/users/', methods=['GET'], strict_slashes=False) 25 | @swag_from('documentation/user/get_user.yml', methods=['GET']) 26 | def get_user(user_id): 27 | """ Retrieves an user """ 28 | user = storage.get(User, user_id) 29 | if not user: 30 | abort(404) 31 | 32 | return jsonify(user.to_dict()) 33 | 34 | 35 | @app_views.route('/users/', methods=['DELETE'], 36 | strict_slashes=False) 37 | @swag_from('documentation/user/delete_user.yml', methods=['DELETE']) 38 | def delete_user(user_id): 39 | """ 40 | Deletes a user Object 41 | """ 42 | 43 | user = storage.get(User, user_id) 44 | 45 | if not user: 46 | abort(404) 47 | 48 | storage.delete(user) 49 | storage.save() 50 | 51 | return make_response(jsonify({}), 200) 52 | 53 | 54 | @app_views.route('/users', methods=['POST'], strict_slashes=False) 55 | @swag_from('documentation/user/post_user.yml', methods=['POST']) 56 | def post_user(): 57 | """ 58 | Creates a user 59 | """ 60 | if not request.get_json(): 61 | abort(400, description="Not a JSON") 62 | 63 | if 'email' not in request.get_json(): 64 | abort(400, description="Missing email") 65 | if 'password' not in request.get_json(): 66 | abort(400, description="Missing password") 67 | 68 | data = request.get_json() 69 | instance = User(**data) 70 | instance.save() 71 | return make_response(jsonify(instance.to_dict()), 201) 72 | 73 | 74 | @app_views.route('/users/', methods=['PUT'], strict_slashes=False) 75 | @swag_from('documentation/user/put_user.yml', methods=['PUT']) 76 | def put_user(user_id): 77 | """ 78 | Updates a user 79 | """ 80 | user = storage.get(User, user_id) 81 | 82 | if not user: 83 | abort(404) 84 | 85 | if not request.get_json(): 86 | abort(400, description="Not a JSON") 87 | 88 | ignore = ['id', 'email', 'created_at', 'updated_at'] 89 | 90 | data = request.get_json() 91 | for key, value in data.items(): 92 | if key not in ignore: 93 | setattr(user, key, value) 94 | storage.save() 95 | return make_response(jsonify(user.to_dict()), 200) 96 | -------------------------------------------------------------------------------- /api/v1/views/amenities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ objects that handles all default RestFul API actions for Amenities""" 3 | from models.amenity import Amenity 4 | from models import storage 5 | from api.v1.views import app_views 6 | from flask import abort, jsonify, make_response, request 7 | from flasgger.utils import swag_from 8 | 9 | 10 | @app_views.route('/amenities', methods=['GET'], strict_slashes=False) 11 | @swag_from('documentation/amenity/all_amenities.yml') 12 | def get_amenities(): 13 | """ 14 | Retrieves a list of all amenities 15 | """ 16 | all_amenities = storage.all(Amenity).values() 17 | list_amenities = [] 18 | for amenity in all_amenities: 19 | list_amenities.append(amenity.to_dict()) 20 | return jsonify(list_amenities) 21 | 22 | 23 | @app_views.route('/amenities//', methods=['GET'], 24 | strict_slashes=False) 25 | @swag_from('documentation/amenity/get_amenity.yml', methods=['GET']) 26 | def get_amenity(amenity_id): 27 | """ Retrieves an amenity """ 28 | amenity = storage.get(Amenity, amenity_id) 29 | if not amenity: 30 | abort(404) 31 | 32 | return jsonify(amenity.to_dict()) 33 | 34 | 35 | @app_views.route('/amenities/', methods=['DELETE'], 36 | strict_slashes=False) 37 | @swag_from('documentation/amenity/delete_amenity.yml', methods=['DELETE']) 38 | def delete_amenity(amenity_id): 39 | """ 40 | Deletes an amenity Object 41 | """ 42 | 43 | amenity = storage.get(Amenity, amenity_id) 44 | 45 | if not amenity: 46 | abort(404) 47 | 48 | storage.delete(amenity) 49 | storage.save() 50 | 51 | return make_response(jsonify({}), 200) 52 | 53 | 54 | @app_views.route('/amenities', methods=['POST'], strict_slashes=False) 55 | @swag_from('documentation/amenity/post_amenity.yml', methods=['POST']) 56 | def post_amenity(): 57 | """ 58 | Creates an amenity 59 | """ 60 | if not request.get_json(): 61 | abort(400, description="Not a JSON") 62 | 63 | if 'name' not in request.get_json(): 64 | abort(400, description="Missing name") 65 | 66 | data = request.get_json() 67 | instance = Amenity(**data) 68 | instance.save() 69 | return make_response(jsonify(instance.to_dict()), 201) 70 | 71 | 72 | @app_views.route('/amenities/', methods=['PUT'], 73 | strict_slashes=False) 74 | @swag_from('documentation/amenity/put_amenity.yml', methods=['PUT']) 75 | def put_amenity(amenity_id): 76 | """ 77 | Updates an amenity 78 | """ 79 | if not request.get_json(): 80 | abort(400, description="Not a JSON") 81 | 82 | ignore = ['id', 'created_at', 'updated_at'] 83 | 84 | amenity = storage.get(Amenity, amenity_id) 85 | 86 | if not amenity: 87 | abort(404) 88 | 89 | data = request.get_json() 90 | for key, value in data.items(): 91 | if key not in ignore: 92 | setattr(amenity, key, value) 93 | storage.save() 94 | return make_response(jsonify(amenity.to_dict()), 200) 95 | -------------------------------------------------------------------------------- /models/base_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Contains class BaseModel 4 | """ 5 | 6 | from datetime import datetime 7 | import models 8 | from os import getenv 9 | import sqlalchemy 10 | from sqlalchemy import Column, String, DateTime 11 | from sqlalchemy.ext.declarative import declarative_base 12 | import uuid 13 | 14 | time = "%Y-%m-%dT%H:%M:%S.%f" 15 | 16 | if models.storage_t == "db": 17 | Base = declarative_base() 18 | else: 19 | Base = object 20 | 21 | 22 | class BaseModel: 23 | """The BaseModel class from which future classes will be derived""" 24 | if models.storage_t == "db": 25 | id = Column(String(60), primary_key=True) 26 | created_at = Column(DateTime, default=datetime.utcnow) 27 | updated_at = Column(DateTime, default=datetime.utcnow) 28 | 29 | def __init__(self, *args, **kwargs): 30 | """Initialization of the base model""" 31 | if kwargs: 32 | for key, value in kwargs.items(): 33 | if key != "__class__": 34 | setattr(self, key, value) 35 | if kwargs.get("created_at", None) and type(self.created_at) is str: 36 | self.created_at = datetime.strptime(kwargs["created_at"], time) 37 | else: 38 | self.created_at = datetime.utcnow() 39 | if kwargs.get("updated_at", None) and type(self.updated_at) is str: 40 | self.updated_at = datetime.strptime(kwargs["updated_at"], time) 41 | else: 42 | self.updated_at = datetime.utcnow() 43 | if kwargs.get("id", None) is None: 44 | self.id = str(uuid.uuid4()) 45 | else: 46 | self.id = str(uuid.uuid4()) 47 | self.created_at = datetime.utcnow() 48 | self.updated_at = self.created_at 49 | 50 | def __str__(self): 51 | """String representation of the BaseModel class""" 52 | return "[{:s}] ({:s}) {}".format(self.__class__.__name__, self.id, 53 | self.__dict__) 54 | 55 | def save(self): 56 | """updates the attribute 'updated_at' with the current datetime""" 57 | self.updated_at = datetime.utcnow() 58 | models.storage.new(self) 59 | models.storage.save() 60 | 61 | def to_dict(self, save_fs=None): 62 | """returns a dictionary containing all keys/values of the instance""" 63 | new_dict = self.__dict__.copy() 64 | if "created_at" in new_dict: 65 | new_dict["created_at"] = new_dict["created_at"].strftime(time) 66 | if "updated_at" in new_dict: 67 | new_dict["updated_at"] = new_dict["updated_at"].strftime(time) 68 | new_dict["__class__"] = self.__class__.__name__ 69 | if "_sa_instance_state" in new_dict: 70 | del new_dict["_sa_instance_state"] 71 | if save_fs is None: 72 | if "password" in new_dict: 73 | del new_dict["password"] 74 | return new_dict 75 | 76 | def delete(self): 77 | """delete the current instance from the storage""" 78 | models.storage.delete(self) 79 | -------------------------------------------------------------------------------- /web_static/7-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AirBnB clone 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 20 |
21 |

22 | States 23 |

24 |

25 | California, Arizona... 26 |

27 |
    28 |
  • 29 |

    Arizona:

    30 |
  • 31 |
  • 32 |
      33 |
    • Page
    • 34 |
    • Page2
    • 35 |
    36 |
  • 37 |
  • 38 |

    California:

    39 |
  • 40 |
  • 41 |
      42 |
    • San Francisco
    • 43 |
    • Los Altos
    • 44 |
    45 |
  • 46 |
47 | 48 |
49 |
50 |

51 | Amenities 52 |

53 |

54 | Internet, Kitchen... 55 |

56 |
    57 |
  • Internet
  • 58 |
  • TV
  • 59 |
  • Kitchen
  • 60 |
  • Iron
  • 61 |
62 |
63 |
64 |
65 |

Places

66 |
67 |

My Home

68 |
69 |
70 |

Tiny House

71 |
72 |
73 |

A suite

74 |
75 |
76 |
77 |
78 |

Holberton School

79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /api/v1/views/places_amenities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ objects that handle all default RestFul API actions for Place - Amenity """ 3 | from models.place import Place 4 | from models.amenity import Amenity 5 | from models import storage 6 | from api.v1.views import app_views 7 | from os import environ 8 | from flask import abort, jsonify, make_response, request 9 | from flasgger.utils import swag_from 10 | 11 | 12 | @app_views.route('places//amenities', methods=['GET'], 13 | strict_slashes=False) 14 | @swag_from('documentation/place_amenity/get_places_amenities.yml', 15 | methods=['GET']) 16 | def get_place_amenities(place_id): 17 | """ 18 | Retrieves the list of all Amenity objects of a Place 19 | """ 20 | place = storage.get(Place, place_id) 21 | 22 | if not place: 23 | abort(404) 24 | 25 | if environ.get('HBNB_TYPE_STORAGE') == "db": 26 | amenities = [amenity.to_dict() for amenity in place.amenities] 27 | else: 28 | amenities = [storage.get(Amenity, amenity_id).to_dict() 29 | for amenity_id in place.amenity_ids] 30 | 31 | return jsonify(amenities) 32 | 33 | 34 | @app_views.route('/places//amenities/', 35 | methods=['DELETE'], strict_slashes=False) 36 | @swag_from('documentation/place_amenity/delete_place_amenities.yml', 37 | methods=['DELETE']) 38 | def delete_place_amenity(place_id, amenity_id): 39 | """ 40 | Deletes a Amenity object of a Place 41 | """ 42 | place = storage.get(Place, place_id) 43 | 44 | if not place: 45 | abort(404) 46 | 47 | amenity = storage.get(Amenity, amenity_id) 48 | 49 | if not amenity: 50 | abort(404) 51 | 52 | if environ.get('HBNB_TYPE_STORAGE') == "db": 53 | if amenity not in place.amenities: 54 | abort(404) 55 | place.amenities.remove(amenity) 56 | else: 57 | if amenity_id not in place.amenity_ids: 58 | abort(404) 59 | place.amenity_ids.remove(amenity_id) 60 | 61 | storage.save() 62 | return make_response(jsonify({}), 200) 63 | 64 | 65 | @app_views.route('/places//amenities/', methods=['POST'], 66 | strict_slashes=False) 67 | @swag_from('documentation/place_amenity/post_place_amenities.yml', 68 | methods=['POST']) 69 | def post_place_amenity(place_id, amenity_id): 70 | """ 71 | Link a Amenity object to a Place 72 | """ 73 | place = storage.get(Place, place_id) 74 | 75 | if not place: 76 | abort(404) 77 | 78 | amenity = storage.get(Amenity, amenity_id) 79 | 80 | if not amenity: 81 | abort(404) 82 | 83 | if environ.get('HBNB_TYPE_STORAGE') == "db": 84 | if amenity in place.amenities: 85 | return make_response(jsonify(amenity.to_dict()), 200) 86 | else: 87 | place.amenities.append(amenity) 88 | else: 89 | if amenity_id in place.amenity_ids: 90 | return make_response(jsonify(amenity.to_dict()), 200) 91 | else: 92 | place.amenity_ids.append(amenity_id) 93 | 94 | storage.save() 95 | return make_response(jsonify(amenity.to_dict()), 201) 96 | -------------------------------------------------------------------------------- /api/v1/views/cities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ objects that handles all default RestFul API actions for cities """ 3 | from models.city import City 4 | from models.state import State 5 | from models import storage 6 | from api.v1.views import app_views 7 | from flask import abort, jsonify, make_response, request 8 | from flasgger.utils import swag_from 9 | 10 | 11 | @app_views.route('/states//cities', methods=['GET'], 12 | strict_slashes=False) 13 | @swag_from('documentation/city/cities_by_state.yml', methods=['GET']) 14 | def get_cities(state_id): 15 | """ 16 | Retrieves the list of all cities objects 17 | of a specific State, or a specific city 18 | """ 19 | list_cities = [] 20 | state = storage.get(State, state_id) 21 | if not state: 22 | abort(404) 23 | for city in state.cities: 24 | list_cities.append(city.to_dict()) 25 | 26 | return jsonify(list_cities) 27 | 28 | 29 | @app_views.route('/cities//', methods=['GET'], strict_slashes=False) 30 | @swag_from('documentation/city/get_city.yml', methods=['GET']) 31 | def get_city(city_id): 32 | """ 33 | Retrieves a specific city based on id 34 | """ 35 | city = storage.get(City, city_id) 36 | if not city: 37 | abort(404) 38 | return jsonify(city.to_dict()) 39 | 40 | 41 | @app_views.route('/cities/', methods=['DELETE'], strict_slashes=False) 42 | @swag_from('documentation/city/delete_city.yml', methods=['DELETE']) 43 | def delete_city(city_id): 44 | """ 45 | Deletes a city based on id provided 46 | """ 47 | city = storage.get(City, city_id) 48 | 49 | if not city: 50 | abort(404) 51 | storage.delete(city) 52 | storage.save() 53 | 54 | return make_response(jsonify({}), 200) 55 | 56 | 57 | @app_views.route('/states//cities', methods=['POST'], 58 | strict_slashes=False) 59 | @swag_from('documentation/city/post_city.yml', methods=['POST']) 60 | def post_city(state_id): 61 | """ 62 | Creates a City 63 | """ 64 | state = storage.get(State, state_id) 65 | if not state: 66 | abort(404) 67 | if not request.get_json(): 68 | abort(400, description="Not a JSON") 69 | if 'name' not in request.get_json(): 70 | abort(400, description="Missing name") 71 | 72 | data = request.get_json() 73 | instance = City(**data) 74 | instance.state_id = state.id 75 | instance.save() 76 | return make_response(jsonify(instance.to_dict()), 201) 77 | 78 | 79 | @app_views.route('/cities/', methods=['PUT'], strict_slashes=False) 80 | @swag_from('documentation/city/put_city.yml', methods=['PUT']) 81 | def put_city(city_id): 82 | """ 83 | Updates a City 84 | """ 85 | city = storage.get(City, city_id) 86 | if not city: 87 | abort(404) 88 | 89 | if not request.get_json(): 90 | abort(400, description="Not a JSON") 91 | 92 | ignore = ['id', 'state_id', 'created_at', 'updated_at'] 93 | 94 | data = request.get_json() 95 | for key, value in data.items(): 96 | if key not in ignore: 97 | setattr(city, key, value) 98 | storage.save() 99 | return make_response(jsonify(city.to_dict()), 200) 100 | -------------------------------------------------------------------------------- /web_static/w3c_validator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | W3C validator for Holberton School 4 | 5 | For HTML and CSS files. 6 | 7 | Based on 2 APIs: 8 | 9 | - https://validator.w3.org/nu/ 10 | - http://jigsaw.w3.org/css-validator/validator 11 | 12 | 13 | Usage: 14 | 15 | Simple file: 16 | 17 | ``` 18 | ./w3c_validator.py index.html 19 | ``` 20 | 21 | Multiple files: 22 | 23 | ``` 24 | ./w3c_validator.py index.html header.html styles/common.css 25 | ``` 26 | 27 | All errors are printed in `STDERR` 28 | 29 | Return: 30 | Exit status is the # of errors, 0 on Success 31 | 32 | References 33 | 34 | https://developer.mozilla.org/en-US/ 35 | 36 | """ 37 | import sys 38 | import requests 39 | 40 | 41 | def __print_stdout(msg): 42 | """Print message in STDOUT 43 | """ 44 | sys.stdout.write(msg) 45 | 46 | 47 | def __print_stderr(msg): 48 | """Print message in STDERR 49 | """ 50 | sys.stderr.write(msg) 51 | 52 | 53 | def __analyse_html(file_path): 54 | """Start analyse of HTML file 55 | """ 56 | h = {'Content-Type': "text/html; charset=utf-8"} 57 | d = open(file_path, "rb").read() 58 | u = "https://validator.w3.org/nu/?out=json" 59 | r = requests.post(u, headers=h, data=d) 60 | res = [] 61 | messages = r.json().get('messages', []) 62 | for m in messages: 63 | res.append("[{}:{}] {}".format(file_path, m['lastLine'], m['message'])) 64 | return res 65 | 66 | 67 | def __analyse_css(file_path): 68 | """Start analyse of CSS file 69 | """ 70 | d = {'output': "json"} 71 | f = {'file': (file_path, open(file_path, 'rb'), 'text/css')} 72 | u = "http://jigsaw.w3.org/css-validator/validator" 73 | r = requests.post(u, data=d, files=f) 74 | res = [] 75 | errors = r.json().get('cssvalidation', {}).get('errors', []) 76 | for e in errors: 77 | res.append("[{}:{}] {}".format(file_path, e['line'], e['message'])) 78 | return res 79 | 80 | 81 | def __analyse(file_path): 82 | """Start analyse of a file and print the result 83 | """ 84 | nb_errors = 0 85 | try: 86 | result = None 87 | if file_path.endswith('.css'): 88 | result = __analyse_css(file_path) 89 | else: 90 | result = __analyse_html(file_path) 91 | 92 | if len(result) > 0: 93 | for msg in result: 94 | __print_stderr("{}\n".format(msg)) 95 | nb_errors += 1 96 | else: 97 | __print_stdout("{}: OK\n".format(file_path)) 98 | 99 | except Exception as e: 100 | __print_stderr("[{}] {}\n".format(e.__class__.__name__, e)) 101 | return nb_errors 102 | 103 | 104 | def __files_loop(): 105 | """Loop that analyses for each file from input arguments 106 | """ 107 | nb_errors = 0 108 | for file_path in sys.argv[1:]: 109 | nb_errors += __analyse(file_path) 110 | 111 | return nb_errors 112 | 113 | 114 | if __name__ == "__main__": 115 | """Main 116 | """ 117 | if len(sys.argv) < 2: 118 | __print_stderr("usage: w3c_validator.py file1 file2 ...\n") 119 | exit(1) 120 | 121 | """execute tests, then exit. Exit status = # of errors (0 on success) 122 | """ 123 | sys.exit(__files_loop()) 124 | -------------------------------------------------------------------------------- /web_flask/templates/w3c_validator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | W3C validator for Holberton School 4 | 5 | For HTML and CSS files. 6 | 7 | Based on 2 APIs: 8 | 9 | - https://validator.w3.org/nu/ 10 | - http://jigsaw.w3.org/css-validator/validator 11 | 12 | 13 | Usage: 14 | 15 | Simple file: 16 | 17 | ``` 18 | ./w3c_validator.py index.html 19 | ``` 20 | 21 | Multiple files: 22 | 23 | ``` 24 | ./w3c_validator.py index.html header.html styles/common.css 25 | ``` 26 | 27 | All errors are printed in `STDERR` 28 | 29 | Return: 30 | Exit status is the # of errors, 0 on Success 31 | 32 | References 33 | 34 | https://developer.mozilla.org/en-US/ 35 | 36 | """ 37 | import sys 38 | import requests 39 | 40 | 41 | def __print_stdout(msg): 42 | """Print message in STDOUT 43 | """ 44 | sys.stdout.write(msg) 45 | 46 | 47 | def __print_stderr(msg): 48 | """Print message in STDERR 49 | """ 50 | sys.stderr.write(msg) 51 | 52 | 53 | def __analyse_html(file_path): 54 | """Start analyse of HTML file 55 | """ 56 | h = {'Content-Type': "text/html; charset=utf-8"} 57 | d = open(file_path, "rb").read() 58 | u = "https://validator.w3.org/nu/?out=json" 59 | r = requests.post(u, headers=h, data=d) 60 | res = [] 61 | messages = r.json().get('messages', []) 62 | for m in messages: 63 | res.append("[{}:{}] {}".format(file_path, m['lastLine'], m['message'])) 64 | return res 65 | 66 | 67 | def __analyse_css(file_path): 68 | """Start analyse of CSS file 69 | """ 70 | d = {'output': "json"} 71 | f = {'file': (file_path, open(file_path, 'rb'), 'text/css')} 72 | u = "http://jigsaw.w3.org/css-validator/validator" 73 | r = requests.post(u, data=d, files=f) 74 | res = [] 75 | errors = r.json().get('cssvalidation', {}).get('errors', []) 76 | for e in errors: 77 | res.append("[{}:{}] {}".format(file_path, e['line'], e['message'])) 78 | return res 79 | 80 | 81 | def __analyse(file_path): 82 | """Start analyse of a file and print the result 83 | """ 84 | nb_errors = 0 85 | try: 86 | result = None 87 | if file_path.endswith('.css'): 88 | result = __analyse_css(file_path) 89 | else: 90 | result = __analyse_html(file_path) 91 | 92 | if len(result) > 0: 93 | for msg in result: 94 | __print_stderr("{}\n".format(msg)) 95 | nb_errors += 1 96 | else: 97 | __print_stdout("{}: OK\n".format(file_path)) 98 | 99 | except Exception as e: 100 | __print_stderr("[{}] {}\n".format(e.__class__.__name__, e)) 101 | return nb_errors 102 | 103 | 104 | def __files_loop(): 105 | """Loop that analyses for each file from input arguments 106 | """ 107 | nb_errors = 0 108 | for file_path in sys.argv[1:]: 109 | nb_errors += __analyse(file_path) 110 | 111 | return nb_errors 112 | 113 | 114 | if __name__ == "__main__": 115 | """Main 116 | """ 117 | if len(sys.argv) < 2: 118 | __print_stderr("usage: w3c_validator.py file1 file2 ...\n") 119 | exit(1) 120 | 121 | """execute tests, then exit. Exit status = # of errors (0 on success) 122 | """ 123 | sys.exit(__files_loop()) 124 | -------------------------------------------------------------------------------- /web_dynamic/static/styles/w3c_validator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | W3C validator for Holberton School 4 | 5 | For HTML and CSS files. 6 | 7 | Based on 2 APIs: 8 | 9 | - https://validator.w3.org/nu/ 10 | - http://jigsaw.w3.org/css-validator/validator 11 | 12 | 13 | Usage: 14 | 15 | Simple file: 16 | 17 | ``` 18 | ./w3c_validator.py index.html 19 | ``` 20 | 21 | Multiple files: 22 | 23 | ``` 24 | ./w3c_validator.py index.html header.html styles/common.css 25 | ``` 26 | 27 | All errors are printed in `STDERR` 28 | 29 | Return: 30 | Exit status is the # of errors, 0 on Success 31 | 32 | References 33 | 34 | https://developer.mozilla.org/en-US/ 35 | 36 | """ 37 | import sys 38 | import requests 39 | 40 | 41 | def __print_stdout(msg): 42 | """Print message in STDOUT 43 | """ 44 | sys.stdout.write(msg) 45 | 46 | 47 | def __print_stderr(msg): 48 | """Print message in STDERR 49 | """ 50 | sys.stderr.write(msg) 51 | 52 | 53 | def __analyse_html(file_path): 54 | """Start analyse of HTML file 55 | """ 56 | h = {'Content-Type': "text/html; charset=utf-8"} 57 | d = open(file_path, "rb").read() 58 | u = "https://validator.w3.org/nu/?out=json" 59 | r = requests.post(u, headers=h, data=d) 60 | res = [] 61 | messages = r.json().get('messages', []) 62 | for m in messages: 63 | res.append("[{}:{}] {}".format(file_path, m['lastLine'], m['message'])) 64 | return res 65 | 66 | 67 | def __analyse_css(file_path): 68 | """Start analyse of CSS file 69 | """ 70 | d = {'output': "json"} 71 | f = {'file': (file_path, open(file_path, 'rb'), 'text/css')} 72 | u = "http://jigsaw.w3.org/css-validator/validator" 73 | r = requests.post(u, data=d, files=f) 74 | res = [] 75 | errors = r.json().get('cssvalidation', {}).get('errors', []) 76 | for e in errors: 77 | res.append("[{}:{}] {}".format(file_path, e['line'], e['message'])) 78 | return res 79 | 80 | 81 | def __analyse(file_path): 82 | """Start analyse of a file and print the result 83 | """ 84 | nb_errors = 0 85 | try: 86 | result = None 87 | if file_path.endswith('.css'): 88 | result = __analyse_css(file_path) 89 | else: 90 | result = __analyse_html(file_path) 91 | 92 | if len(result) > 0: 93 | for msg in result: 94 | __print_stderr("{}\n".format(msg)) 95 | nb_errors += 1 96 | else: 97 | __print_stdout("{}: OK\n".format(file_path)) 98 | 99 | except Exception as e: 100 | __print_stderr("[{}] {}\n".format(e.__class__.__name__, e)) 101 | return nb_errors 102 | 103 | 104 | def __files_loop(): 105 | """Loop that analyses for each file from input arguments 106 | """ 107 | nb_errors = 0 108 | for file_path in sys.argv[1:]: 109 | nb_errors += __analyse(file_path) 110 | 111 | return nb_errors 112 | 113 | 114 | if __name__ == "__main__": 115 | """Main 116 | """ 117 | if len(sys.argv) < 2: 118 | __print_stderr("usage: w3c_validator.py file1 file2 ...\n") 119 | exit(1) 120 | 121 | """execute tests, then exit. Exit status = # of errors (0 on success) 122 | """ 123 | sys.exit(__files_loop()) 124 | -------------------------------------------------------------------------------- /web_flask/static/styles/w3c_validator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | W3C validator for Holberton School 4 | 5 | For HTML and CSS files. 6 | 7 | Based on 2 APIs: 8 | 9 | - https://validator.w3.org/nu/ 10 | - http://jigsaw.w3.org/css-validator/validator 11 | 12 | 13 | Usage: 14 | 15 | Simple file: 16 | 17 | ``` 18 | ./w3c_validator.py index.html 19 | ``` 20 | 21 | Multiple files: 22 | 23 | ``` 24 | ./w3c_validator.py index.html header.html styles/common.css 25 | ``` 26 | 27 | All errors are printed in `STDERR` 28 | 29 | Return: 30 | Exit status is the # of errors, 0 on Success 31 | 32 | References 33 | 34 | https://developer.mozilla.org/en-US/ 35 | 36 | """ 37 | import sys 38 | import requests 39 | 40 | 41 | def __print_stdout(msg): 42 | """Print message in STDOUT 43 | """ 44 | sys.stdout.write(msg) 45 | 46 | 47 | def __print_stderr(msg): 48 | """Print message in STDERR 49 | """ 50 | sys.stderr.write(msg) 51 | 52 | 53 | def __analyse_html(file_path): 54 | """Start analyse of HTML file 55 | """ 56 | h = {'Content-Type': "text/html; charset=utf-8"} 57 | d = open(file_path, "rb").read() 58 | u = "https://validator.w3.org/nu/?out=json" 59 | r = requests.post(u, headers=h, data=d) 60 | res = [] 61 | messages = r.json().get('messages', []) 62 | for m in messages: 63 | res.append("[{}:{}] {}".format(file_path, m['lastLine'], m['message'])) 64 | return res 65 | 66 | 67 | def __analyse_css(file_path): 68 | """Start analyse of CSS file 69 | """ 70 | d = {'output': "json"} 71 | f = {'file': (file_path, open(file_path, 'rb'), 'text/css')} 72 | u = "http://jigsaw.w3.org/css-validator/validator" 73 | r = requests.post(u, data=d, files=f) 74 | res = [] 75 | errors = r.json().get('cssvalidation', {}).get('errors', []) 76 | for e in errors: 77 | res.append("[{}:{}] {}".format(file_path, e['line'], e['message'])) 78 | return res 79 | 80 | 81 | def __analyse(file_path): 82 | """Start analyse of a file and print the result 83 | """ 84 | nb_errors = 0 85 | try: 86 | result = None 87 | if file_path.endswith('.css'): 88 | result = __analyse_css(file_path) 89 | else: 90 | result = __analyse_html(file_path) 91 | 92 | if len(result) > 0: 93 | for msg in result: 94 | __print_stderr("{}\n".format(msg)) 95 | nb_errors += 1 96 | else: 97 | __print_stdout("{}: OK\n".format(file_path)) 98 | 99 | except Exception as e: 100 | __print_stderr("[{}] {}\n".format(e.__class__.__name__, e)) 101 | return nb_errors 102 | 103 | 104 | def __files_loop(): 105 | """Loop that analyses for each file from input arguments 106 | """ 107 | nb_errors = 0 108 | for file_path in sys.argv[1:]: 109 | nb_errors += __analyse(file_path) 110 | 111 | return nb_errors 112 | 113 | 114 | if __name__ == "__main__": 115 | """Main 116 | """ 117 | if len(sys.argv) < 2: 118 | __print_stderr("usage: w3c_validator.py file1 file2 ...\n") 119 | exit(1) 120 | 121 | """execute tests, then exit. Exit status = # of errors (0 on success) 122 | """ 123 | sys.exit(__files_loop()) 124 | -------------------------------------------------------------------------------- /web_static/styles/100-places.css: -------------------------------------------------------------------------------- 1 | .places { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | } 6 | 7 | .places h1 { 8 | width: 100%; 9 | font-size: 30px; 10 | } 11 | 12 | article { 13 | width: 390px; 14 | padding: 20px; 15 | margin: 20px 20px 20px 20px; 16 | border: 1px solid #FF5A5F; 17 | border-radius: 4px; 18 | position: relative; 19 | z-index: -1; 20 | } 21 | 22 | article>h2 { 23 | font-size: 30px; 24 | display: flex; 25 | text-align: center; 26 | flex-direction: column; 27 | margin: 0; 28 | padding-bottom: 10% 29 | } 30 | 31 | .price_by_night { 32 | color: #FF5A5F; 33 | border: 4px solid #FF5A5F; 34 | border-radius: 100%; 35 | min-width: 60px; 36 | height: 60px; 37 | font-size: 30px; 38 | position: absolute; 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | top: 2%; 43 | right: 7%; 44 | } 45 | 46 | .information { 47 | height: 80px; 48 | border-top: 1px solid #DDDDDD; 49 | border-bottom: 1px solid #DDDDDD; 50 | display: flex; 51 | flex-wrap: wrap; 52 | justify-content: center; 53 | } 54 | 55 | .max_guest, .number_rooms, .number_bathrooms { 56 | background-repeat: no-repeat; 57 | width: 100px; 58 | height: 100px; 59 | background-position: center top; 60 | line-height: 9em; 61 | text-align: center; 62 | } 63 | 64 | .max_guest { 65 | background-image: url(../images/icon_group.png); 66 | } 67 | 68 | .number_rooms { 69 | background-image: url(../images/icon_bed.png); 70 | } 71 | 72 | .number_bathrooms { 73 | background-image: url(../images/icon_bath.png); 74 | } 75 | 76 | .user { 77 | margin: 10px auto; 78 | } 79 | 80 | .places .amenities h2 { 81 | padding: 5% 0 5% 0; 82 | padding-bottom: 2%; 83 | border-bottom: 1px solid #DDDDDD; 84 | } 85 | 86 | .places .amenities>ul { 87 | list-style: none; 88 | display: flex; 89 | flex-direction: column; 90 | justify-content: center; 91 | padding: 0; 92 | } 93 | 94 | .places .amenities>ul li { 95 | background-size: 20px; 96 | padding-left: 25px; 97 | padding-bottom: 25px; 98 | background-repeat: no-repeat; 99 | line-height: 25px; 100 | } 101 | 102 | .tv { 103 | background-image: url(../images/icon_tv.png); 104 | } 105 | 106 | .wifi { 107 | background-image: url(../images/icon_wifi.png); 108 | } 109 | 110 | .pets { 111 | background-image: url(../images/icon_pets.png); 112 | } 113 | 114 | .reviews { 115 | margin-bottom: 5%; 116 | } 117 | 118 | .places .reviews>ul { 119 | list-style: none; 120 | display: flex; 121 | flex-direction: column; 122 | justify-content: center; 123 | padding: 0; 124 | } 125 | 126 | .places .reviews ul li h3 { 127 | margin-top: 0; 128 | padding-bottom: 0px; 129 | margin-bottom: 10px; 130 | } 131 | 132 | .places .reviews ul li p { 133 | padding-bottom: 5%; 134 | margin-top: 0px; 135 | } 136 | 137 | /* 138 | justify-content 139 | align-items 140 | flex-direction 141 | order 142 | align-self 143 | flex-wrap 144 | flex-flow 145 | align-content 146 | */ -------------------------------------------------------------------------------- /web_static/styles/101-places.css: -------------------------------------------------------------------------------- 1 | .places { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | } 6 | 7 | .places h1 { 8 | width: 100%; 9 | font-size: 30px; 10 | } 11 | 12 | article { 13 | width: 390px; 14 | padding: 20px; 15 | margin: 20px 20px 20px 20px; 16 | border: 1px solid #FF5A5F; 17 | border-radius: 4px; 18 | position: relative; 19 | z-index: -1; 20 | } 21 | 22 | article>h2 { 23 | font-size: 30px; 24 | display: flex; 25 | text-align: center; 26 | flex-direction: column; 27 | margin: 0; 28 | padding-bottom: 10% 29 | } 30 | 31 | .price_by_night { 32 | color: #FF5A5F; 33 | border: 4px solid #FF5A5F; 34 | border-radius: 100%; 35 | min-width: 60px; 36 | height: 60px; 37 | font-size: 30px; 38 | position: absolute; 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | top: 2%; 43 | right: 7%; 44 | } 45 | 46 | .information { 47 | height: 80px; 48 | border-top: 1px solid #DDDDDD; 49 | border-bottom: 1px solid #DDDDDD; 50 | display: flex; 51 | flex-wrap: wrap; 52 | justify-content: center; 53 | } 54 | 55 | .max_guest, .number_rooms, .number_bathrooms { 56 | background-repeat: no-repeat; 57 | width: 100px; 58 | height: 100px; 59 | background-position: center top; 60 | line-height: 9em; 61 | text-align: center; 62 | } 63 | 64 | .max_guest { 65 | background-image: url(../images/icon_group.png); 66 | } 67 | 68 | .number_rooms { 69 | background-image: url(../images/icon_bed.png); 70 | } 71 | 72 | .number_bathrooms { 73 | background-image: url(../images/icon_bath.png); 74 | } 75 | 76 | .user { 77 | margin: 10px auto; 78 | } 79 | 80 | .places .amenities h2 { 81 | padding: 5% 0 5% 0; 82 | padding-bottom: 2%; 83 | border-bottom: 1px solid #DDDDDD; 84 | } 85 | 86 | .places .amenities>ul { 87 | list-style: none; 88 | display: flex; 89 | flex-direction: column; 90 | justify-content: center; 91 | padding: 0; 92 | } 93 | 94 | .places .amenities>ul li { 95 | background-size: 20px; 96 | padding-left: 25px; 97 | padding-bottom: 25px; 98 | background-repeat: no-repeat; 99 | line-height: 25px; 100 | } 101 | 102 | .tv { 103 | background-image: url(../images/icon_tv.png); 104 | } 105 | 106 | .wifi { 107 | background-image: url(../images/icon_wifi.png); 108 | } 109 | 110 | .pets { 111 | background-image: url(../images/icon_pets.png); 112 | } 113 | 114 | .reviews { 115 | margin-bottom: 5%; 116 | } 117 | 118 | .places .reviews>ul { 119 | list-style: none; 120 | display: flex; 121 | flex-direction: column; 122 | justify-content: center; 123 | padding: 0; 124 | } 125 | 126 | .places .reviews ul li h3 { 127 | margin-top: 0; 128 | padding-bottom: 0px; 129 | margin-bottom: 10px; 130 | } 131 | 132 | .places .reviews ul li p { 133 | padding-bottom: 5%; 134 | margin-top: 0px; 135 | } 136 | 137 | /* 138 | justify-content 139 | align-items 140 | flex-direction 141 | order 142 | align-self 143 | flex-wrap 144 | flex-flow 145 | align-content 146 | */ -------------------------------------------------------------------------------- /web_dynamic/templates/2-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | HBnB 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |

States

27 |

 

28 |
    29 | {% for state in states|dictsort %} 30 |

    {{ state[0] }}:

    31 |
      32 | {% for city in state[1].cities|sort(attribute='name') %} 33 |
    • {{ city.name }}
    • 34 | {% endfor %} 35 |
    36 | {% endfor %} 37 |
38 |
39 |
40 |

Amenities

41 |

 

42 |
43 |
    44 | {% for amenity in amenities|sort(attribute='name') %} 45 |
  • ame {{ amenity.name }}
  • 46 | {% endfor %} 47 |
48 |
49 | 50 | 51 |
52 |

Places

53 |
54 | 55 | {% for place in places|sort(attribute='name') %} 56 |
57 |
58 |

{{ place.name }}

59 |
60 | {{ place.price_by_night }} 61 |
62 |
63 |
64 |
65 | 66 |
67 | {{ place.max_guest }} Guests 68 | 69 |
70 |
71 | 72 |
73 | {{ place.number_rooms }} Bedroom 74 |
75 |
76 | 77 |
78 | {{ place.number_bathrooms }} Bathroom 79 |
80 |
81 |
82 | Owner: {{ users[place.user_id] }} 83 |
84 |
85 | {{ place.description }} 86 |
87 |
88 | {% endfor %} 89 |
90 |
91 | 92 | 93 |
94 |

Holberton School

95 |
96 | 97 | -------------------------------------------------------------------------------- /web_dynamic/templates/3-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | HBnB 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |

States

27 |

 

28 |
    29 | {% for state in states|dictsort %} 30 |

    {{ state[0] }}:

    31 |
      32 | {% for city in state[1].cities|sort(attribute='name') %} 33 |
    • {{ city.name }}
    • 34 | {% endfor %} 35 |
    36 | {% endfor %} 37 |
38 |
39 |
40 |

Amenities

41 |

 

42 |
43 |
    44 | {% for amenity in amenities|sort(attribute='name') %} 45 |
  • ame {{ amenity.name }}
  • 46 | {% endfor %} 47 |
48 |
49 | 50 | 51 |
52 |

Places

53 |
54 | 55 | {% for place in places|sort(attribute='name') %} 56 |
57 |
58 |

{{ place.name }}

59 |
60 | {{ place.price_by_night }} 61 |
62 |
63 |
64 |
65 | 66 |
67 | {{ place.max_guest }} Guests 68 | 69 |
70 |
71 | 72 |
73 | {{ place.number_rooms }} Bedroom 74 |
75 |
76 | 77 |
78 | {{ place.number_bathrooms }} Bathroom 79 |
80 |
81 |
82 | Owner: {{ users[place.user_id] }} 83 |
84 |
85 | {{ place.description }} 86 |
87 |
88 | {% endfor %} 89 |
90 |
91 | 92 | 93 |
94 |

Holberton School

95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /web_dynamic/templates/4-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | HBnB 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |

States

27 |

 

28 |
    29 | {% for state in states|dictsort %} 30 |

    {{ state[0] }}:

    31 |
      32 | {% for city in state[1].cities|sort(attribute='name') %} 33 |
    • {{ city.name }}
    • 34 | {% endfor %} 35 |
    36 | {% endfor %} 37 |
38 |
39 |
40 |

Amenities

41 |

 

42 |
43 |
    44 | {% for amenity in amenities|sort(attribute='name') %} 45 |
  • ame {{ amenity.name }}
  • 46 | {% endfor %} 47 |
48 |
49 | 50 | 51 |
52 |

Places

53 |
54 | 55 | {% for place in places|sort(attribute='name') %} 56 |
57 |
58 |

{{ place.name }}

59 |
60 | {{ place.price_by_night }} 61 |
62 |
63 |
64 |
65 | 66 |
67 | {{ place.max_guest }} Guests 68 | 69 |
70 |
71 | 72 |
73 | {{ place.number_rooms }} Bedroom 74 |
75 |
76 | 77 |
78 | {{ place.number_bathrooms }} Bathroom 79 |
80 |
81 |
82 | Owner: {{ users[place.user_id] }} 83 |
84 |
85 | {{ place.description }} 86 |
87 |
88 | {% endfor %} 89 |
90 |
91 | 92 | 93 |
94 |

Holberton School

95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /web_dynamic/templates/100-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | HBnB 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |

States

27 |

 

28 |
    29 | {% for state in states|dictsort %} 30 |

    {{ state[0] }}:

    31 |
      32 | {% for city in state[1].cities|sort(attribute='name') %} 33 |
    • {{ city.name }}
    • 34 | {% endfor %} 35 |
    36 | {% endfor %} 37 |
38 |
39 |
40 |

Amenities

41 |

 

42 |
43 |
    44 | {% for amenity in amenities|sort(attribute='name') %} 45 |
  • ame {{ amenity.name }}
  • 46 | {% endfor %} 47 |
48 |
49 | 50 | 51 |
52 |

Places

53 |
54 | 55 | {% for place in places|sort(attribute='name') %} 56 |
57 |
58 |

{{ place.name }}

59 |
60 | {{ place.price_by_night }} 61 |
62 |
63 |
64 |
65 | 66 |
67 | {{ place.max_guest }} Guests 68 | 69 |
70 |
71 | 72 |
73 | {{ place.number_rooms }} Bedroom 74 |
75 |
76 | 77 |
78 | {{ place.number_bathrooms }} Bathroom 79 |
80 |
81 |
82 | Owner: {{ users[place.user_id] }} 83 |
84 |
85 | {{ place.description }} 86 |
87 |
88 | {% endfor %} 89 |
90 |
91 | 92 | 93 |
94 |

Holberton School

95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /web_dynamic/templates/101-hbnb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | HBnB 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |

States

27 |

 

28 |
    29 | {% for state in states|dictsort %} 30 |

    {{ state[0] }}:

    31 |
      32 | {% for city in state[1].cities|sort(attribute='name') %} 33 |
    • {{ city.name }}
    • 34 | {% endfor %} 35 |
    36 | {% endfor %} 37 |
38 |
39 |
40 |

Amenities

41 |

 

42 |
43 |
    44 | {% for amenity in amenities|sort(attribute='name') %} 45 |
  • ame {{ amenity.name }}
  • 46 | {% endfor %} 47 |
48 |
49 | 50 | 51 |
52 |

Places

53 |
54 | 55 | {% for place in places|sort(attribute='name') %} 56 |
57 |
58 |

{{ place.name }}

59 |
60 | {{ place.price_by_night }} 61 |
62 |
63 |
64 |
65 | 66 |
67 | {{ place.max_guest }} Guests 68 | 69 |
70 |
71 | 72 |
73 | {{ place.number_rooms }} Bedroom 74 |
75 |
76 | 77 |
78 | {{ place.number_bathrooms }} Bathroom 79 |
80 |
81 |
82 | Owner: {{ users[place.user_id] }} 83 |
84 |
85 | {{ place.description }} 86 |
87 |
88 | {% endfor %} 89 |
90 |
91 | 92 | 93 |
94 |

Holberton School

95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /models/place.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ holds class Place""" 3 | import models 4 | from models.base_model import BaseModel, Base 5 | from os import getenv 6 | import sqlalchemy 7 | from sqlalchemy import Column, String, Integer, Float, ForeignKey, Table 8 | from sqlalchemy.orm import relationship 9 | 10 | if models.storage_t == 'db': 11 | place_amenity = Table('place_amenity', Base.metadata, 12 | Column('place_id', String(60), 13 | ForeignKey('places.id', onupdate='CASCADE', 14 | ondelete='CASCADE'), 15 | primary_key=True), 16 | Column('amenity_id', String(60), 17 | ForeignKey('amenities.id', onupdate='CASCADE', 18 | ondelete='CASCADE'), 19 | primary_key=True)) 20 | 21 | 22 | class Place(BaseModel, Base): 23 | """Representation of Place """ 24 | if models.storage_t == 'db': 25 | __tablename__ = 'places' 26 | city_id = Column(String(60), ForeignKey('cities.id'), nullable=False) 27 | user_id = Column(String(60), ForeignKey('users.id'), nullable=False) 28 | name = Column(String(128), nullable=False) 29 | description = Column(String(1024), nullable=True) 30 | number_rooms = Column(Integer, nullable=False, default=0) 31 | number_bathrooms = Column(Integer, nullable=False, default=0) 32 | max_guest = Column(Integer, nullable=False, default=0) 33 | price_by_night = Column(Integer, nullable=False, default=0) 34 | latitude = Column(Float, nullable=True) 35 | longitude = Column(Float, nullable=True) 36 | reviews = relationship("Review", 37 | backref="place", 38 | cascade="all, delete, delete-orphan") 39 | amenities = relationship("Amenity", 40 | secondary=place_amenity, 41 | viewonly=False) 42 | else: 43 | city_id = "" 44 | user_id = "" 45 | name = "" 46 | description = "" 47 | number_rooms = 0 48 | number_bathrooms = 0 49 | max_guest = 0 50 | price_by_night = 0 51 | latitude = 0.0 52 | longitude = 0.0 53 | amenity_ids = [] 54 | 55 | def __init__(self, *args, **kwargs): 56 | """initializes Place""" 57 | super().__init__(*args, **kwargs) 58 | 59 | if models.storage_t != 'db': 60 | @property 61 | def reviews(self): 62 | """getter attribute returns the list of Review instances""" 63 | from models.review import Review 64 | review_list = [] 65 | all_reviews = models.storage.all(Review) 66 | for review in all_reviews.values(): 67 | if review.place_id == self.id: 68 | review_list.append(review) 69 | return review_list 70 | 71 | @property 72 | def amenities(self): 73 | """getter attribute returns the list of Amenity instances""" 74 | from models.amenity import Amenity 75 | amenity_list = [] 76 | all_amenities = models.storage.all(Amenity) 77 | for amenity in all_amenities.values(): 78 | if amenity.place_id == self.id: 79 | amenity_list.append(amenity) 80 | return amenity_list 81 | -------------------------------------------------------------------------------- /api/v1/views/places_reviews.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ objects that handle all default RestFul API actions for Reviews """ 3 | from models.review import Review 4 | from models.place import Place 5 | from models.user import User 6 | from models import storage 7 | from api.v1.views import app_views 8 | from flask import abort, jsonify, make_response, request 9 | from flasgger.utils import swag_from 10 | 11 | 12 | @app_views.route('/places//reviews', methods=['GET'], 13 | strict_slashes=False) 14 | @swag_from('documentation/reviews/get_reviews.yml', methods=['GET']) 15 | def get_reviews(place_id): 16 | """ 17 | Retrieves the list of all Review objects of a Place 18 | """ 19 | place = storage.get(Place, place_id) 20 | 21 | if not place: 22 | abort(404) 23 | 24 | reviews = [review.to_dict() for review in place.reviews] 25 | 26 | return jsonify(reviews) 27 | 28 | 29 | @app_views.route('/reviews/', methods=['GET'], strict_slashes=False) 30 | @swag_from('documentation/reviews/get_review.yml', methods=['GET']) 31 | def get_review(review_id): 32 | """ 33 | Retrieves a Review object 34 | """ 35 | review = storage.get(Review, review_id) 36 | if not review: 37 | abort(404) 38 | 39 | return jsonify(review.to_dict()) 40 | 41 | 42 | @app_views.route('/reviews/', methods=['DELETE'], 43 | strict_slashes=False) 44 | @swag_from('documentation/reviews/delete_reviews.yml', methods=['DELETE']) 45 | def delete_review(review_id): 46 | """ 47 | Deletes a Review Object 48 | """ 49 | 50 | review = storage.get(Review, review_id) 51 | 52 | if not review: 53 | abort(404) 54 | 55 | storage.delete(review) 56 | storage.save() 57 | 58 | return make_response(jsonify({}), 200) 59 | 60 | 61 | @app_views.route('/places//reviews', methods=['POST'], 62 | strict_slashes=False) 63 | @swag_from('documentation/reviews/post_reviews.yml', methods=['POST']) 64 | def post_review(place_id): 65 | """ 66 | Creates a Review 67 | """ 68 | place = storage.get(Place, place_id) 69 | 70 | if not place: 71 | abort(404) 72 | 73 | if not request.get_json(): 74 | abort(400, description="Not a JSON") 75 | 76 | if 'user_id' not in request.get_json(): 77 | abort(400, description="Missing user_id") 78 | 79 | data = request.get_json() 80 | user = storage.get(User, data['user_id']) 81 | 82 | if not user: 83 | abort(404) 84 | 85 | if 'text' not in request.get_json(): 86 | abort(400, description="Missing text") 87 | 88 | data['place_id'] = place_id 89 | instance = Review(**data) 90 | instance.save() 91 | return make_response(jsonify(instance.to_dict()), 201) 92 | 93 | 94 | @app_views.route('/reviews/', methods=['PUT'], strict_slashes=False) 95 | @swag_from('documentation/reviews/put_reviews.yml', methods=['PUT']) 96 | def put_review(review_id): 97 | """ 98 | Updates a Review 99 | """ 100 | review = storage.get(Review, review_id) 101 | 102 | if not review: 103 | abort(404) 104 | 105 | if not request.get_json(): 106 | abort(400, description="Not a JSON") 107 | 108 | ignore = ['id', 'user_id', 'place_id', 'created_at', 'updated_at'] 109 | 110 | data = request.get_json() 111 | for key, value in data.items(): 112 | if key not in ignore: 113 | setattr(review, key, value) 114 | storage.save() 115 | return make_response(jsonify(review.to_dict()), 200) 116 | -------------------------------------------------------------------------------- /models/engine/file_storage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Contains the FileStorage class 4 | """ 5 | 6 | import json 7 | import models 8 | from models.amenity import Amenity 9 | from models.base_model import BaseModel 10 | from models.city import City 11 | from models.place import Place 12 | from models.review import Review 13 | from models.state import State 14 | from models.user import User 15 | from hashlib import md5 16 | 17 | classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, 18 | "Place": Place, "Review": Review, "State": State, "User": User} 19 | 20 | 21 | class FileStorage: 22 | """serializes instances to a JSON file & deserializes back to instances""" 23 | 24 | # string - path to the JSON file 25 | __file_path = "file.json" 26 | # dictionary - empty but will store all objects by .id 27 | __objects = {} 28 | 29 | def all(self, cls=None): 30 | """returns the dictionary __objects""" 31 | if cls is not None: 32 | new_dict = {} 33 | for key, value in self.__objects.items(): 34 | if cls == value.__class__ or cls == value.__class__.__name__: 35 | new_dict[key] = value 36 | return new_dict 37 | return self.__objects 38 | 39 | def new(self, obj): 40 | """sets in __objects the obj with key .id""" 41 | if obj is not None: 42 | key = obj.__class__.__name__ + "." + obj.id 43 | self.__objects[key] = obj 44 | 45 | def save(self): 46 | """serializes __objects to the JSON file (path: __file_path)""" 47 | json_objects = {} 48 | for key in self.__objects: 49 | if key == "password": 50 | json_objects[key].decode() 51 | json_objects[key] = self.__objects[key].to_dict(save_fs=1) 52 | with open(self.__file_path, 'w') as f: 53 | json.dump(json_objects, f) 54 | 55 | def reload(self): 56 | """deserializes the JSON file to __objects""" 57 | try: 58 | with open(self.__file_path, 'r') as f: 59 | jo = json.load(f) 60 | for key in jo: 61 | self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) 62 | except: 63 | pass 64 | 65 | def delete(self, obj=None): 66 | """delete obj from __objects if it’s inside""" 67 | if obj is not None: 68 | key = obj.__class__.__name__ + '.' + obj.id 69 | if key in self.__objects: 70 | del self.__objects[key] 71 | 72 | def close(self): 73 | """call reload() method for deserializing the JSON file to objects""" 74 | self.reload() 75 | 76 | def get(self, cls, id): 77 | """ 78 | Returns the object based on the class name and its ID, or 79 | None if not found 80 | """ 81 | if cls not in classes.values(): 82 | return None 83 | 84 | all_cls = models.storage.all(cls) 85 | for value in all_cls.values(): 86 | if (value.id == id): 87 | return value 88 | 89 | return None 90 | 91 | def count(self, cls=None): 92 | """ 93 | count the number of objects in storage 94 | """ 95 | all_class = classes.values() 96 | 97 | if not cls: 98 | count = 0 99 | for clas in all_class: 100 | count += len(models.storage.all(clas).values()) 101 | else: 102 | count = len(models.storage.all(cls).values()) 103 | 104 | return count 105 | -------------------------------------------------------------------------------- /models/engine/db_storage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Contains the class DBStorage 4 | """ 5 | 6 | import models 7 | from models.amenity import Amenity 8 | from models.base_model import BaseModel, Base 9 | from models.city import City 10 | from models.place import Place 11 | from models.review import Review 12 | from models.state import State 13 | from models.user import User 14 | from os import getenv 15 | import sqlalchemy 16 | from sqlalchemy import create_engine 17 | from sqlalchemy.orm import scoped_session, sessionmaker 18 | 19 | classes = {"Amenity": Amenity, "City": City, 20 | "Place": Place, "Review": Review, "State": State, "User": User} 21 | 22 | 23 | class DBStorage: 24 | """interaacts with the MySQL database""" 25 | __engine = None 26 | __session = None 27 | 28 | def __init__(self): 29 | """Instantiate a DBStorage object""" 30 | HBNB_MYSQL_USER = getenv('HBNB_MYSQL_USER') 31 | HBNB_MYSQL_PWD = getenv('HBNB_MYSQL_PWD') 32 | HBNB_MYSQL_HOST = getenv('HBNB_MYSQL_HOST') 33 | HBNB_MYSQL_DB = getenv('HBNB_MYSQL_DB') 34 | HBNB_ENV = getenv('HBNB_ENV') 35 | self.__engine = create_engine('mysql+mysqldb://{}:{}@{}/{}'. 36 | format(HBNB_MYSQL_USER, 37 | HBNB_MYSQL_PWD, 38 | HBNB_MYSQL_HOST, 39 | HBNB_MYSQL_DB)) 40 | if HBNB_ENV == "test": 41 | Base.metadata.drop_all(self.__engine) 42 | 43 | def all(self, cls=None): 44 | """query on the current database session""" 45 | new_dict = {} 46 | for clss in classes: 47 | if cls is None or cls is classes[clss] or cls is clss: 48 | objs = self.__session.query(classes[clss]).all() 49 | for obj in objs: 50 | key = obj.__class__.__name__ + '.' + obj.id 51 | new_dict[key] = obj 52 | return (new_dict) 53 | 54 | def new(self, obj): 55 | """add the object to the current database session""" 56 | self.__session.add(obj) 57 | 58 | def save(self): 59 | """commit all changes of the current database session""" 60 | self.__session.commit() 61 | 62 | def delete(self, obj=None): 63 | """delete from the current database session obj if not None""" 64 | if obj is not None: 65 | self.__session.delete(obj) 66 | 67 | def reload(self): 68 | """reloads data from the database""" 69 | Base.metadata.create_all(self.__engine) 70 | sess_factory = sessionmaker(bind=self.__engine, expire_on_commit=False) 71 | Session = scoped_session(sess_factory) 72 | self.__session = Session 73 | 74 | def close(self): 75 | """call remove() method on the private session attribute""" 76 | self.__session.remove() 77 | 78 | def get(self, cls, id): 79 | """ 80 | Returns the object based on the class name and its ID, or 81 | None if not found 82 | """ 83 | if cls not in classes.values(): 84 | return None 85 | 86 | all_cls = models.storage.all(cls) 87 | for value in all_cls.values(): 88 | if (value.id == id): 89 | return value 90 | 91 | return None 92 | 93 | def count(self, cls=None): 94 | """ 95 | count the number of objects in storage 96 | """ 97 | all_class = classes.values() 98 | 99 | if not cls: 100 | count = 0 101 | for clas in all_class: 102 | count += len(models.storage.all(clas).values()) 103 | else: 104 | count = len(models.storage.all(cls).values()) 105 | 106 | return count 107 | -------------------------------------------------------------------------------- /web_static/styles/102-places.css~: -------------------------------------------------------------------------------- 1 | .places { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | } 6 | 7 | .places h1 { 8 | width: 100%; 9 | font-size: 30px; 10 | } 11 | 12 | article { 13 | width: 390px; 14 | padding: 20px; 15 | margin: 20px 20px 20px 20px; 16 | border: 1px solid #FF5A5F; 17 | border-radius: 4px; 18 | position: relative; 19 | z-index: -1; 20 | } 21 | 22 | article>h2 { 23 | font-size: 30px; 24 | display: flex; 25 | text-align: center; 26 | flex-direction: column; 27 | margin: 0; 28 | padding-bottom: 10% 29 | } 30 | 31 | .price_by_night { 32 | color: #FF5A5F; 33 | border: 4px solid #FF5A5F; 34 | border-radius: 100%; 35 | min-width: 60px; 36 | height: 60px; 37 | font-size: 30px; 38 | position: absolute; 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | top: 2%; 43 | right: 7%; 44 | } 45 | 46 | .information { 47 | height: 80px; 48 | border-top: 1px solid #DDDDDD; 49 | border-bottom: 1px solid #DDDDDD; 50 | display: flex; 51 | flex-wrap: wrap; 52 | justify-content: center; 53 | } 54 | 55 | .max_guest, .number_rooms, .number_bathrooms { 56 | background-repeat: no-repeat; 57 | width: 100px; 58 | height: 100px; 59 | background-position: center top; 60 | line-height: 9em; 61 | text-align: center; 62 | flex: 0% 0% ; 63 | } 64 | 65 | .max_guest { 66 | background-image: url(../images/icon_group.png); 67 | } 68 | 69 | .number_rooms { 70 | background-image: url(../images/icon_bed.png); 71 | } 72 | 73 | .number_bathrooms { 74 | background-image: url(../images/icon_bath.png); 75 | } 76 | 77 | .user { 78 | margin: 10px auto; 79 | } 80 | 81 | .places .amenities h2 { 82 | padding: 5% 0 5% 0; 83 | padding-bottom: 2%; 84 | border-bottom: 1px solid #DDDDDD; 85 | } 86 | 87 | .places .amenities>ul { 88 | list-style: none; 89 | display: flex; 90 | flex-direction: column; 91 | justify-content: center; 92 | padding: 0; 93 | } 94 | 95 | .places .amenities>ul li { 96 | background-size: 20px; 97 | padding-left: 25px; 98 | padding-bottom: 25px; 99 | background-repeat: no-repeat; 100 | line-height: 25px; 101 | } 102 | 103 | .tv { 104 | background-image: url(../images/icon_tv.png); 105 | } 106 | 107 | .wifi { 108 | background-image: url(../images/icon_wifi.png); 109 | } 110 | 111 | .pets { 112 | background-image: url(../images/icon_pets.png); 113 | } 114 | 115 | .reviews { 116 | margin-bottom: 5%; 117 | } 118 | 119 | .places .reviews>ul { 120 | list-style: none; 121 | display: flex; 122 | flex-direction: column; 123 | justify-content: center; 124 | padding: 0; 125 | } 126 | 127 | .places .reviews ul li h3 { 128 | margin-top: 0; 129 | padding-bottom: 0px; 130 | margin-bottom: 10px; 131 | } 132 | 133 | .places .reviews ul li p { 134 | padding-bottom: 5%; 135 | margin-top: 0px; 136 | } 137 | 138 | 139 | @media only screen and (max-width: 1030px) 140 | { 141 | .places h1 { 142 | width: 90%; 143 | } 144 | } 145 | @media only screen and (max-width: 381px) 146 | { 147 | .information { 148 | height: 20%; 149 | padding-top: 5%; 150 | } 151 | .price_by_night { 152 | position: static; 153 | width: 20%; 154 | min-width: 65px; 155 | display: flex; 156 | justify-content: center; 157 | margin: 0 auto 5% auto; 158 | 159 | } 160 | } 161 | @media only screen and (max-width: 281px) 162 | { 163 | .information { 164 | height: 23%; 165 | } 166 | .reviews { 167 | margin-bottom: 100%; 168 | } 169 | } 170 | 171 | 172 | 173 | 174 | /* 175 | justify-content 176 | align-items 177 | flex-direction 178 | order 179 | align-self 180 | flex-wrap 181 | flex-flow 182 | align-content 183 | */ -------------------------------------------------------------------------------- /web_static/styles/102-filters.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | background-color: white; 3 | height: 70px; 4 | border: 1px solid #DDDDDD; 5 | border-radius: 4px; 6 | display: flex; 7 | align-items: center; 8 | position: relative; 9 | 10 | } 11 | 12 | .filters button { 13 | font-size: 18px; 14 | background-color: #FF5A5F; 15 | color: #FFFFFF; 16 | height: 48px; 17 | width: 20%; 18 | border: none; 19 | border-radius: 4px; 20 | position: absolute; 21 | right: 30px; 22 | box-sizing: border-box; 23 | } 24 | .filters button:hover { 25 | opacity: 0.9; 26 | } 27 | 28 | .filters .locations,.filters .amenities { 29 | 30 | height: 100%; 31 | width: 25%; 32 | position: relative; 33 | display: flex; 34 | justify-content: center; 35 | flex-direction: column; 36 | box-sizing: border-box; 37 | } 38 | 39 | .locations { 40 | border-right: 1px solid #DDDDDD ; 41 | } 42 | 43 | .filters .locations h3, .filters .amenities h3{ 44 | font-weight: 600; 45 | margin: 2px 20px; 46 | } 47 | .locations h4, .amenities h4{ 48 | font-weight: 400; 49 | font-size: 14px; 50 | margin: 2px 20px; 51 | 52 | } 53 | .popover { 54 | display: none; 55 | background-color: #FAFAFA; 56 | border: 1px solid #DDDDDD; 57 | border-radius: 4px; 58 | position: absolute; 59 | box-sizing: border-box; 60 | width: 100%; 61 | top: 100%; 62 | margin-top: 0; 63 | } 64 | 65 | div:hover > .popover { 66 | display: block; 67 | cursor: pointer; 68 | 69 | } 70 | .popover h2 { 71 | font-size: 16px; 72 | } 73 | .popover li { 74 | margin: 10px 0px 5px 0px; 75 | list-style: none; 76 | } 77 | 78 | .filters .locations ul { 79 | padding: 0.5em 0 1em 1em; 80 | } 81 | 82 | .filters .locations li { 83 | margin: 0 0 0 5%; 84 | padding: 0.2em; 85 | } 86 | .filters .amenities ul { 87 | padding: 2em 0 2em 3em; 88 | } 89 | 90 | 91 | @media only screen and (max-width: 1030px) 92 | { 93 | .filters { 94 | width: 90%; 95 | margin: 0 auto; 96 | } 97 | } 98 | @media only screen and (max-width: 600px) 99 | { 100 | .filters .locations,.filters .amenities { 101 | width: 35%; 102 | } 103 | .filters .locations ul, .filters .amenities ul { 104 | padding: 0.7em; 105 | } 106 | .filters .locations li { 107 | margin: 0 0 0 5%; 108 | padding: 0; 109 | } 110 | 111 | } 112 | @media only screen and (max-width: 360px) 113 | { 114 | .filters button { 115 | font-size: 16px; 116 | justify-content: center; 117 | right: 8px; 118 | width: 25%; 119 | padding: 0px; 120 | } 121 | } 122 | @media only screen and (max-width: 350px) 123 | { 124 | .filters .locations,.filters .amenities { 125 | width: 35%; 126 | font-size: 0.85em; 127 | } 128 | .filters .locations h3, .filters .amenities h3{ 129 | font-size: 1.1em; 130 | margin: 2px 7px; 131 | } 132 | .locations h4, .amenities h4{ 133 | font-weight: 400; 134 | font-size: 0.9em; 135 | margin: 2px 7px; 136 | } 137 | .filters .locations ul { 138 | padding: 0.1em; 139 | } 140 | .filters .amenities ul { 141 | padding: 0.8em; 142 | 143 | } 144 | } 145 | @media only screen and (max-width: 280px) 146 | { 147 | .filters button { 148 | font-size: 12px; 149 | width: 20%; 150 | padding: 0; 151 | } 152 | .popover h2 { 153 | font-size: 11px; 154 | } 155 | 156 | } 157 | @media only screen and (max-width: 250px) 158 | { 159 | .filters button { 160 | font-size: 11px; 161 | width: 20%; 162 | padding: 0; 163 | } 164 | .filters .locations ul{ 165 | padding: 0; 166 | } 167 | .filters .amenities ul { 168 | padding: 1em; 169 | } 170 | } 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /web_static/styles/102-places.css: -------------------------------------------------------------------------------- 1 | .places { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | } 6 | 7 | .places h1 { 8 | width: 100%; 9 | font-size: 30px; 10 | } 11 | 12 | article { 13 | width: 390px; 14 | padding: 20px; 15 | margin: 20px 20px 20px 20px; 16 | border: 1px solid #FF5A5F; 17 | border-radius: 4px; 18 | position: relative; 19 | z-index: -1; 20 | } 21 | 22 | article>h2 { 23 | font-size: 30px; 24 | display: flex; 25 | text-align: center; 26 | flex-direction: column; 27 | margin: 0; 28 | padding-bottom: 10% 29 | } 30 | 31 | .price_by_night { 32 | color: #FF5A5F; 33 | border: 4px solid #FF5A5F; 34 | border-radius: 100%; 35 | min-width: 60px; 36 | height: 60px; 37 | font-size: 30px; 38 | position: absolute; 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | top: 2%; 43 | right: 7%; 44 | } 45 | 46 | .information { 47 | height: 80px; 48 | border-top: 1px solid #DDDDDD; 49 | border-bottom: 1px solid #DDDDDD; 50 | display: flex; 51 | flex-wrap: wrap; 52 | justify-content: center; 53 | } 54 | 55 | .max_guest, .number_rooms, .number_bathrooms { 56 | background-repeat: no-repeat; 57 | width: 100px; 58 | height: 100px; 59 | background-position: center top; 60 | line-height: 9em; 61 | text-align: center; 62 | } 63 | 64 | .max_guest { 65 | background-image: url(../images/icon_group.png); 66 | } 67 | 68 | .number_rooms { 69 | background-image: url(../images/icon_bed.png); 70 | } 71 | 72 | .number_bathrooms { 73 | background-image: url(../images/icon_bath.png); 74 | } 75 | 76 | .user { 77 | margin: 10px auto; 78 | } 79 | 80 | .places .amenities h2 { 81 | padding: 5% 0 5% 0; 82 | padding-bottom: 2%; 83 | border-bottom: 1px solid #DDDDDD; 84 | } 85 | 86 | .places .amenities>ul { 87 | list-style: none; 88 | display: flex; 89 | flex-direction: column; 90 | justify-content: center; 91 | padding: 0; 92 | } 93 | 94 | .places .amenities>ul li { 95 | background-size: 20px; 96 | padding-left: 25px; 97 | padding-bottom: 25px; 98 | background-repeat: no-repeat; 99 | line-height: 25px; 100 | } 101 | 102 | .tv { 103 | background-image: url(../images/icon_tv.png); 104 | } 105 | 106 | .wifi { 107 | background-image: url(../images/icon_wifi.png); 108 | } 109 | 110 | .pets { 111 | background-image: url(../images/icon_pets.png); 112 | } 113 | 114 | .reviews { 115 | margin-bottom: 5%; 116 | } 117 | 118 | .places .reviews>ul { 119 | list-style: none; 120 | display: flex; 121 | flex-direction: column; 122 | justify-content: center; 123 | padding: 0; 124 | } 125 | 126 | .places .reviews ul li h3 { 127 | margin-top: 0; 128 | padding-bottom: 0px; 129 | margin-bottom: 10px; 130 | } 131 | 132 | .places .reviews ul li p { 133 | padding-bottom: 5%; 134 | margin-top: 0px; 135 | } 136 | 137 | 138 | @media only screen and (max-width: 1030px) 139 | { 140 | .places h1 { 141 | width: 90%; 142 | } 143 | } 144 | @media only screen and (max-width: 410px) 145 | { 146 | .information { 147 | 148 | margin-top: 2%; 149 | } 150 | } 151 | @media only screen and (max-width: 381px) 152 | { 153 | .information { 154 | height: 20%; 155 | padding-top: 5%; 156 | } 157 | .price_by_night { 158 | position: static; 159 | width: 20%; 160 | min-width: 65px; 161 | display: flex; 162 | justify-content: center; 163 | margin: 0 auto 5% auto; 164 | 165 | } 166 | } 167 | @media only screen and (max-width: 281px) 168 | { 169 | .information { 170 | height: 23%; 171 | } 172 | .reviews { 173 | margin-bottom: 100%; 174 | } 175 | } 176 | 177 | 178 | 179 | 180 | /* 181 | justify-content 182 | align-items 183 | flex-direction 184 | order 185 | align-self 186 | flex-wrap 187 | flex-flow 188 | align-content 189 | */ 190 | -------------------------------------------------------------------------------- /web_static/styles/103-filters.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | background-color: white; 3 | height: 70px; 4 | border: 1px solid #575757; 5 | border-radius: 4px; 6 | display: flex; 7 | align-items: center; 8 | position: relative; 9 | 10 | } 11 | 12 | .filters button { 13 | font-size: 18px; 14 | background-color: #AA0004; 15 | color: #FFFFFF; 16 | height: 48px; 17 | width: 20%; 18 | border: none; 19 | border-radius: 4px; 20 | position: absolute; 21 | right: 30px; 22 | box-sizing: border-box; 23 | } 24 | .filters button:hover { 25 | 26 | background-color: #223FB0; 27 | } 28 | 29 | .filters .locations,.filters .amenities { 30 | 31 | height: 100%; 32 | width: 25%; 33 | position: relative; 34 | display: flex; 35 | justify-content: center; 36 | flex-direction: column; 37 | box-sizing: border-box; 38 | } 39 | 40 | .locations { 41 | border-right: 1px solid #575757 ; 42 | } 43 | 44 | .filters .locations h3, .filters .amenities h3{ 45 | font-weight: 600; 46 | margin: 2px 20px; 47 | } 48 | .locations h4, .amenities h4{ 49 | font-weight: 400; 50 | font-size: 14px; 51 | margin: 2px 20px; 52 | 53 | } 54 | .popover { 55 | display: none; 56 | background-color: #FAFAFA; 57 | border: 1px solid #575757; 58 | border-radius: 4px; 59 | position: absolute; 60 | box-sizing: border-box; 61 | width: 100%; 62 | top: 100%; 63 | margin-top: 0; 64 | } 65 | 66 | div:hover > .popover { 67 | display: block; 68 | cursor: pointer; 69 | 70 | } 71 | .popover h2 { 72 | font-size: 16px; 73 | } 74 | .popover li { 75 | margin: 10px 0px 5px 0px; 76 | list-style: none; 77 | } 78 | 79 | .filters .locations ul { 80 | padding: 0.5em 0 1em 1em; 81 | } 82 | 83 | .filters .locations li { 84 | margin: 0 0 0 5%; 85 | padding: 0.2em; 86 | } 87 | .filters .amenities ul { 88 | padding: 2em 0 2em 3em; 89 | } 90 | 91 | 92 | @media only screen and (max-width: 1030px) 93 | { 94 | .filters { 95 | width: 90%; 96 | margin: 0 auto; 97 | } 98 | } 99 | @media only screen and (max-width: 600px) 100 | { 101 | .filters .locations,.filters .amenities { 102 | width: 35%; 103 | } 104 | .filters .locations ul, .filters .amenities ul { 105 | padding: 0.7em; 106 | } 107 | .filters .locations li { 108 | margin: 0 0 0 5%; 109 | padding: 0; 110 | } 111 | 112 | } 113 | @media only screen and (max-width: 360px) 114 | { 115 | .filters button { 116 | font-size: 16px; 117 | justify-content: center; 118 | right: 8px; 119 | width: 25%; 120 | padding: 0px; 121 | } 122 | } 123 | @media only screen and (max-width: 350px) 124 | { 125 | .filters .locations,.filters .amenities { 126 | width: 35%; 127 | font-size: 0.85em; 128 | } 129 | .filters .locations h3, .filters .amenities h3{ 130 | font-size: 1.1em; 131 | margin: 2px 7px; 132 | } 133 | .locations h4, .amenities h4{ 134 | font-weight: 400; 135 | font-size: 0.9em; 136 | margin: 2px 7px; 137 | } 138 | .filters .locations ul { 139 | padding: 0.1em; 140 | } 141 | .filters .amenities ul { 142 | padding: 0.8em; 143 | 144 | } 145 | } 146 | @media only screen and (max-width: 280px) 147 | { 148 | .filters button { 149 | font-size: 12px; 150 | width: 20%; 151 | padding: 0; 152 | } 153 | .popover h2 { 154 | font-size: 11px; 155 | } 156 | .filters .locations ul { 157 | font-size: 0.9em; 158 | } 159 | .filters .amenities ul { 160 | font-size: 0.9em; 161 | } 162 | 163 | } 164 | @media only screen and (max-width: 250px) 165 | { 166 | .filters button { 167 | font-size: 11px; 168 | width: 20%; 169 | padding: 0; 170 | } 171 | .filters .locations h3, .filters .amenities h3{ 172 | font-size: 0.9em; 173 | } 174 | 175 | .filters .locations ul{ 176 | padding: 0; 177 | } 178 | .filters .amenities ul { 179 | padding: 1em; 180 | } 181 | } --------------------------------------------------------------------------------