diff --git a/1-pack_web_static.py b/1-pack_web_static.py old mode 100644 new mode 100755 diff --git a/2-do_deploy_web_static.py b/2-do_deploy_web_static.py old mode 100644 new mode 100755 diff --git a/3-deploy_web_static.py b/3-deploy_web_static.py old mode 100644 new mode 100755 diff --git a/README.md b/README.md index ae0c24c98f4..de1acfe512d 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,8 @@ Alexa Orrico - [Github](https://github.com/alexaorrico) / [Twitter](https://twit Jennifer Huang - [Github](https://github.com/jhuang10123) / [Twitter](https://twitter.com/earthtojhuang) Jhoan Zamora - [Github](https://github.com/jzamora5) / [Twitter](https://twitter.com/JhoanZamora10) David Ovalle - [Github](https://github.com/Nukemenonai) / [Twitter](https://twitter.com/disartDave) - +Elelta Alemu - [Github](https://github.com/Lillymuller) +Yared Kebede - [Github](https://github.com/Yared91) Second part of Airbnb: Joann Vuong ## License Public Domain. No copy write protection. diff --git a/api/v1/app.py b/api/v1/app.py index 2a74f26a1e5..1f8764f413f 100755 --- a/api/v1/app.py +++ b/api/v1/app.py @@ -11,7 +11,7 @@ app = Flask(__name__) app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True app.register_blueprint(app_views) -cors = CORS(app, resources={r"/*": {"origins": "0.0.0.0"}}) +cors = CORS(app, resources={r"/api/v1/*": {"origins": "*"}}) @app.teardown_appcontext diff --git a/code_review.txt b/code_review.txt old mode 100644 new mode 100755 diff --git a/models/__pycache__/__init__.cpython-38.pyc b/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000000..15a3b115c8e Binary files /dev/null and b/models/__pycache__/__init__.cpython-38.pyc differ diff --git a/models/__pycache__/amenity.cpython-38.pyc b/models/__pycache__/amenity.cpython-38.pyc new file mode 100644 index 00000000000..6b57c6ff541 Binary files /dev/null and b/models/__pycache__/amenity.cpython-38.pyc differ diff --git a/models/__pycache__/base_model.cpython-38.pyc b/models/__pycache__/base_model.cpython-38.pyc new file mode 100644 index 00000000000..22eedd03826 Binary files /dev/null and b/models/__pycache__/base_model.cpython-38.pyc differ diff --git a/models/__pycache__/city.cpython-38.pyc b/models/__pycache__/city.cpython-38.pyc new file mode 100644 index 00000000000..a4d2bb6132d Binary files /dev/null and b/models/__pycache__/city.cpython-38.pyc differ diff --git a/models/__pycache__/place.cpython-38.pyc b/models/__pycache__/place.cpython-38.pyc new file mode 100644 index 00000000000..6d9962c94df Binary files /dev/null and b/models/__pycache__/place.cpython-38.pyc differ diff --git a/models/__pycache__/review.cpython-38.pyc b/models/__pycache__/review.cpython-38.pyc new file mode 100644 index 00000000000..75eb56b650f Binary files /dev/null and b/models/__pycache__/review.cpython-38.pyc differ diff --git a/models/__pycache__/state.cpython-38.pyc b/models/__pycache__/state.cpython-38.pyc new file mode 100644 index 00000000000..b81ae1808cc Binary files /dev/null and b/models/__pycache__/state.cpython-38.pyc differ diff --git a/models/__pycache__/user.cpython-38.pyc b/models/__pycache__/user.cpython-38.pyc new file mode 100644 index 00000000000..17c3c3bd8f7 Binary files /dev/null and b/models/__pycache__/user.cpython-38.pyc differ diff --git a/models/engine/__pycache__/__init__.cpython-38.pyc b/models/engine/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000000..bcd71f9f42e Binary files /dev/null and b/models/engine/__pycache__/__init__.cpython-38.pyc differ diff --git a/models/engine/__pycache__/db_storage.cpython-38.pyc b/models/engine/__pycache__/db_storage.cpython-38.pyc new file mode 100644 index 00000000000..fbdd1856725 Binary files /dev/null and b/models/engine/__pycache__/db_storage.cpython-38.pyc differ diff --git a/setup_mysql_dev.sql b/setup_mysql_dev.sql old mode 100644 new mode 100755 diff --git a/setup_mysql_test.sql b/setup_mysql_test.sql old mode 100644 new mode 100755 diff --git a/web_dynamic/0-hbnb.py b/web_dynamic/0-hbnb.py new file mode 100755 index 00000000000..f1addd4abea --- /dev/null +++ b/web_dynamic/0-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/0-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('0-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/1-hbnb.py b/web_dynamic/1-hbnb.py new file mode 100755 index 00000000000..9984c81d317 --- /dev/null +++ b/web_dynamic/1-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/1-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('1-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/100-hbnb.py b/web_dynamic/100-hbnb.py new file mode 100755 index 00000000000..9f7754664fb --- /dev/null +++ b/web_dynamic/100-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/100-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('100-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/101-hbnb.py b/web_dynamic/101-hbnb.py new file mode 100755 index 00000000000..8e43d0b50bd --- /dev/null +++ b/web_dynamic/101-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/101-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('101-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/2-hbnb.py b/web_dynamic/2-hbnb.py new file mode 100755 index 00000000000..28721ca7b40 --- /dev/null +++ b/web_dynamic/2-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/2-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('2-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/3-hbnb.py b/web_dynamic/3-hbnb.py new file mode 100755 index 00000000000..62fc13bd04d --- /dev/null +++ b/web_dynamic/3-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/3-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('3-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/4-hbnb.py b/web_dynamic/4-hbnb.py new file mode 100755 index 00000000000..f41be1aa78c --- /dev/null +++ b/web_dynamic/4-hbnb.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" Starts a Flash Web Application """ +from models import storage +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from os import environ +from flask import Flask, render_template +import uuid + + +app = Flask(__name__) +# app.jinja_env.trim_blocks = True +# app.jinja_env.lstrip_blocks = True + + +@app.teardown_appcontext +def close_db(error): + """ Remove the current SQLAlchemy Session """ + storage.close() + + +@app.route('/4-hbnb/', strict_slashes=False) +def hbnb(): + """ HBNB is alive! """ + states = storage.all(State).values() + states = sorted(states, key=lambda k: k.name) + st_ct = [] + + for state in states: + st_ct.append([state, sorted(state.cities, key=lambda k: k.name)]) + + amenities = storage.all(Amenity).values() + amenities = sorted(amenities, key=lambda k: k.name) + + places = storage.all(Place).values() + places = sorted(places, key=lambda k: k.name) + + return render_template('4-hbnb.html', + states=st_ct, + amenities=amenities, + places=places + cache_id=(str(uuid.uuid4()))) + + +if __name__ == "__main__": + """ Main Function """ + app.run(host='0.0.0.0', port=5000) diff --git a/web_dynamic/__init__.py b/web_dynamic/__init__.py new file mode 100755 index 00000000000..e69de29bb2d diff --git a/web_dynamic/static/images/icon.png b/web_dynamic/static/images/icon.png new file mode 100644 index 00000000000..93492bb8df2 Binary files /dev/null and b/web_dynamic/static/images/icon.png differ diff --git a/web_dynamic/static/images/icon_bath.png b/web_dynamic/static/images/icon_bath.png new file mode 100644 index 00000000000..7a9bfed9d8d Binary files /dev/null and b/web_dynamic/static/images/icon_bath.png differ diff --git a/web_dynamic/static/images/icon_bed.png b/web_dynamic/static/images/icon_bed.png new file mode 100644 index 00000000000..2a632848770 Binary files /dev/null and b/web_dynamic/static/images/icon_bed.png differ diff --git a/web_dynamic/static/images/icon_group.png b/web_dynamic/static/images/icon_group.png new file mode 100644 index 00000000000..3e012ab4d5c Binary files /dev/null and b/web_dynamic/static/images/icon_group.png differ diff --git a/web_dynamic/static/images/logo.png b/web_dynamic/static/images/logo.png new file mode 100644 index 00000000000..9b255c95555 Binary files /dev/null and b/web_dynamic/static/images/logo.png differ diff --git a/web_dynamic/static/scripts/1-hbnb.js b/web_dynamic/static/scripts/1-hbnb.js new file mode 100755 index 00000000000..8bd0afe09f3 --- /dev/null +++ b/web_dynamic/static/scripts/1-hbnb.js @@ -0,0 +1,14 @@ +$('document').ready(function () { + let amenities = {}; + $('INPUT[type="checkbox"]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + amenities[dataId] = dataName; + } else { + delete amenities[$(this).attr('data-id')]; + } + const amenityList = Object.values(amenities).join(', ') + $('div.amenities H4').text(aminityList); + }); +}); diff --git a/web_dynamic/static/scripts/100-hbnb.js b/web_dynamic/static/scripts/100-hbnb.js new file mode 100755 index 00000000000..af8d8540954 --- /dev/null +++ b/web_dynamic/static/scripts/100-hbnb.js @@ -0,0 +1,203 @@ +$('document').ready(function () { + const url = 'http://0.0.0.0:5001/api/v1/status/'; + $.get(url, function (response) { + if (response.status === 'OK') { + $('div#api_status').addClass('available'); + } else { + $('div#api_status').removeClass('available'); + } + }); + +$.ajax({ + url: api + ':5001/api/v1/places_search/', + type: 'POST', + data: '{}', + contentType: 'application/json', + dataType: 'json', + success: appendPlaces + }); + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify({}), + contentType: 'application/json', + success: function (data) => { + data.forEach(place => { + const html = ` +
+
+

${place.name}

+
${place.price_by_night}
+
+
+
+ +
${place.max_guest} Guests +
+
+ +
${place.number_rooms} Bedrooms +
+
+ +
${place.number_bathrooms} Bathrooms +
+
+
${place.description}
+
+ `; + $('section.places').append(html); + }); + } +}); + +$('button').click(function () { + // Remove existing articles + $('article').remove(); + + // Prepare data for AJAX request + const amenitiesData = { + amenities: Object.keys(ls_amen) + }; + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify(amenitiesData), + contentType: 'application/json', + success: function (data) { + // Process and display search results + for (let i = 0; i < data.length; i++) { + const place = data[i]; + const html = ` +
+
+

${place.name}

+
${place.price_by_night}
+
+
+
+ +
${place.max_guest} Guests +
+
+ +
${place.number_rooms} Bedrooms +
+
+ +
${place.number_bathrooms} Bathrooms +
+
+
${place.description}
+
+ `; + $('section.places').append(html); + } + } + }); +}); + + +$('document').ready(function () { + let amenities = {}; + $('INPUT[type="checkbox"]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + amenities[dataId] = dataName; + } else { + delete amenities[$(this).attr('data-id')]; + } + const amenityList = Object.values(amenities).join(', ') + $('div.amenities H4').text(aminityList); + }); +}); + +$('document').ready(function () { + let states = {}; + $('.locations ul.popover LI.s input[type=checkbox]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + states[dataId] = dataName; + } else { + delete states[$(this).attr('data-id')]; + } + const stateList = Object.values(states).join(', ') + $('.locations h4').text(stateList); + }); +}); + +$('document').ready(function () { + let cities = {}; + $('.locations ul.popover LI.s input[type=checkbox]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + cities[dataId] = dataName; + } else { + delete cities[$(this).attr('data-id')]; + } + const cityList = Object.values(cities).join(', ') + $('.locations h4').text(cityList); + }); +}); + +$('button').click(function () { + // Remove existing articles + $('article').remove(); + + // Prepare data for AJAX request + const searchData = { + amenities: Object.keys(ls_amen), + cities: Object.keys(ls_cities), + states: Object.keys(ls_states) + }; + +$.ajax({ + type: 'POST', + url: 'http://127.0.0.1:5002/api/v1/places_search/', + data: JSON.stringify(searchData), + contentType: 'application/json', + success: function (data) { + // Process and display search results + for (let i = 0; i < data.length; i++) { + const place = data[i]; + + // Log data for debugging purposes + console.log(place); + console.log(ls_amen); + console.log('cities: ', ls_cities); + console.log('states: ', ls_states); + + // Build and append HTML content + const html = ` +
+
+

${place.name}

+
${place.price_by_night}
+
+
+
+ +
${place.max_guest} Guests +
+
+ +
${place.number_rooms} Bedrooms +
+
+ +
${place.number_bathrooms} Bathrooms +
+
+
${place.description}
+
+ `; + $('section.places').append(html); + } + } + }); +}); diff --git a/web_dynamic/static/scripts/101-hbnb.js b/web_dynamic/static/scripts/101-hbnb.js new file mode 100755 index 00000000000..476f8a74c48 --- /dev/null +++ b/web_dynamic/static/scripts/101-hbnb.js @@ -0,0 +1,225 @@ +$('document').ready(function () { + const url = 'http://0.0.0.0:5001/api/v1/status/'; + $.get(url, function (response) { + if (response.status === 'OK') { + $('div#api_status').addClass('available'); + } else { + $('div#api_status').removeClass('available'); + } + }); + +$.ajax({ + url: api + ':5001/api/v1/places_search/', + type: 'POST', + data: '{}', + contentType: 'application/json', + dataType: 'json', + success: appendPlaces + }); + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify({}), + contentType: 'application/json', + success: function (data) => { + data.forEach(place => { + const html = ` +
+
+

${place.name}

+
${place.price_by_night}
+
+
+
+ +
${place.max_guest} Guests +
+
+ +
${place.number_rooms} Bedrooms +
+
+ +
${place.number_bathrooms} Bathrooms +
+
+
${place.description}
+
+ `; + $('section.places').append(html); + }); + } +}); + +$('button').click(function () { + // Remove existing articles + $('article').remove(); + + // Prepare data for AJAX request + const amenitiesData = { + amenities: Object.keys(ls_amen) + }; + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify(amenitiesData), + contentType: 'application/json', + success: function (data) { + // Process and display search results + for (let i = 0; i < data.length; i++) { + const place = data[i]; + const html = ` +
+
+

${place.name}

+
${place.price_by_night}
+
+
+
+ +
${place.max_guest} Guests +
+
+ +
${place.number_rooms} Bedrooms +
+
+ +
${place.number_bathrooms} Bathrooms +
+
+
${place.description}
+
+ `; + $('section.places').append(html); + } + } + }); +}); + + +$('document').ready(function () { + let amenities = {}; + $('INPUT[type="checkbox"]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + amenities[dataId] = dataName; + } else { + delete amenities[$(this).attr('data-id')]; + } + const amenityList = Object.values(amenities).join(', ') + $('div.amenities H4').text(aminityList); + }); +}); + +$('document').ready(function () { + let states = {}; + $('.locations ul.popover LI.s input[type=checkbox]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + states[dataId] = dataName; + } else { + delete states[$(this).attr('data-id')]; + } + const stateList = Object.values(states).join(', ') + $('.locations h4').text(stateList); + }); +}); + +$('document').ready(function () { + let cities = {}; + $('.locations ul.popover LI.s input[type=checkbox]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + cities[dataId] = dataName; + } else { + delete cities[$(this).attr('data-id')]; + } + const cityList = Object.values(cities).join(', ') + $('.locations h4').text(cityList); + }); +}); + +$('button').click(function () { + // Remove existing articles + $('article').remove(); + + // Prepare data for AJAX request + const searchData = { + amenities: Object.keys(ls_amen), + cities: Object.keys(ls_cities), + states: Object.keys(ls_states) + }; + +$.ajax({ + type: 'POST', + url: 'http://127.0.0.1:5002/api/v1/places_search/', + data: JSON.stringify(searchData), + contentType: 'application/json', + success: function (data) { + // Process and display search results + for (let i = 0; i < data.length; i++) { + const place = data[i]; + + // Log data for debugging purposes + console.log(place); + console.log(ls_amen); + console.log('cities: ', ls_cities); + console.log('states: ', ls_states); + + // Build and append HTML content + const html = ` +
+
+

${place.name}

+
${place.price_by_night}
+
+
+
+ +
${place.max_guest} Guests +
+
+ +
${place.number_rooms} Bedrooms +
+
+ +
${place.number_bathrooms} Bathrooms +
+
+
${place.description}
+
+ `; + $('section.places').append(html); + } + } + }); +}); + +$('.reviewSpan').click(function (event) { + const placeId = $(this).attr('data-id'); + const reviewsContainer = $('.reviews ul'); + + $.ajax({ + url: `http://0.0.0.0:5001/api/v1/places/${placeId}/reviews`, + }) + .done(function (data) { + reviewsContainer.empty(); // Clear existing reviews + + if ($(this).text() === 'show') { + for (const review of data) { + reviewsContainer.append(`
  • ${review.text}
  • `); + } + $(this).text('hide'); // Update button text + } else { + reviewsContainer.empty(); + $(this).text('show'); // Update button text back + } + }); +}); diff --git a/web_dynamic/static/scripts/2-hbnb.js b/web_dynamic/static/scripts/2-hbnb.js new file mode 100755 index 00000000000..862c5e3cfa4 --- /dev/null +++ b/web_dynamic/static/scripts/2-hbnb.js @@ -0,0 +1,24 @@ +$('document').ready(function () { + const url = 'http://0.0.0.0:5001/api/v1/status/'; + $.get(url, function (response) { + if (response.status === 'OK') { + $('div#api_status').addClass('available'); + } else { + $('div#api_status').removeClass('available'); + } + }); + +$('document').ready(function () { + let amenities = {}; + $('INPUT[type="checkbox"]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + amenities[dataId] = dataName; + } else { + delete amenities[$(this).attr('data-id')]; + } + const amenityList = Object.values(amenities).join(', ') + $('div.amenities H4').text(aminityList); + }); +}); diff --git a/web_dynamic/static/scripts/3-hbnb.js b/web_dynamic/static/scripts/3-hbnb.js new file mode 100755 index 00000000000..26c1c22cfee --- /dev/null +++ b/web_dynamic/static/scripts/3-hbnb.js @@ -0,0 +1,59 @@ +$('document').ready(function () { + const url = 'http://0.0.0.0:5001/api/v1/status/'; + $.get(url, function (response) { + if (response.status === 'OK') { + $('div#api_status').addClass('available'); + } else { + $('div#api_status').removeClass('available'); + } + }); + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify({}), + contentType: 'application/json', + success: function (data) => { + data.forEach(place => { + const html = ` +
    +
    +

    ${place.name}

    +
    ${place.price_by_night}
    +
    +
    +
    + +
    ${place.max_guest} Guests +
    +
    + +
    ${place.number_rooms} Bedrooms +
    +
    + +
    ${place.number_bathrooms} Bathrooms +
    +
    +
    ${place.description}
    +
    + `; + $('section.places').append(html); + }); + } +}); + +$('document').ready(function () { + let amenities = {}; + $('INPUT[type="checkbox"]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + amenities[dataId] = dataName; + } else { + delete amenities[$(this).attr('data-id')]; + } + const amenityList = Object.values(amenities).join(', ') + $('div.amenities H4').text(aminityList); + }); +}); diff --git a/web_dynamic/static/scripts/4-hbnb.js b/web_dynamic/static/scripts/4-hbnb.js new file mode 100755 index 00000000000..50fae73dfcb --- /dev/null +++ b/web_dynamic/static/scripts/4-hbnb.js @@ -0,0 +1,116 @@ +$('document').ready(function () { + const url = 'http://0.0.0.0:5001/api/v1/status/'; + $.get(url, function (response) { + if (response.status === 'OK') { + $('div#api_status').addClass('available'); + } else { + $('div#api_status').removeClass('available'); + } + }); + +$.ajax({ + url: api + ':5001/api/v1/places_search/', + type: 'POST', + data: '{}', + contentType: 'application/json', + dataType: 'json', + success: appendPlaces + }); + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify({}), + contentType: 'application/json', + success: function (data) => { + data.forEach(place => { + const html = ` +
    +
    +

    ${place.name}

    +
    ${place.price_by_night}
    +
    +
    +
    + +
    ${place.max_guest} Guests +
    +
    + +
    ${place.number_rooms} Bedrooms +
    +
    + +
    ${place.number_bathrooms} Bathrooms +
    +
    +
    ${place.description}
    +
    + `; + $('section.places').append(html); + }); + } +}); + +$('button').click(function () { + // Remove existing articles + $('article').remove(); + + // Prepare data for AJAX request + const amenitiesData = { + amenities: Object.keys(ls_amen) + }; + +$.ajax({ + type: 'POST', + url: 'http://0.0.0.0:5001/api/v1/places_search/', + data: JSON.stringify(amenitiesData), + contentType: 'application/json', + success: function (data) { + // Process and display search results + for (let i = 0; i < data.length; i++) { + const place = data[i]; + const html = ` +
    +
    +

    ${place.name}

    +
    ${place.price_by_night}
    +
    +
    +
    + +
    ${place.max_guest} Guests +
    +
    + +
    ${place.number_rooms} Bedrooms +
    +
    + +
    ${place.number_bathrooms} Bathrooms +
    +
    +
    ${place.description}
    +
    + `; + $('section.places').append(html); + } + } + }); +}); + + +$('document').ready(function () { + let amenities = {}; + $('INPUT[type="checkbox"]').change(function () { + const dataId = $(this).attr('data-id') + const dataName = $(this).attr('data-name') + if ($(this).is(':checked')) { + amenities[dataId] = dataName; + } else { + delete amenities[$(this).attr('data-id')]; + } + const amenityList = Object.values(amenities).join(', ') + $('div.amenities H4').text(aminityList); + }); +}); diff --git a/web_dynamic/static/styles/3-footer.css b/web_dynamic/static/styles/3-footer.css new file mode 100755 index 00000000000..2cd4ff05d90 --- /dev/null +++ b/web_dynamic/static/styles/3-footer.css @@ -0,0 +1,16 @@ +footer { + position: fixed; + background: white; + height: 60px; + width: 100%; + bottom: 0; + border-top: 1px solid #CCCCCC; +} +footer p { + position: absolute; + text-align: center; + top: 10%; + bottom: 0; + right: 0; + left: 0; +} diff --git a/web_dynamic/static/styles/3-header.css b/web_dynamic/static/styles/3-header.css new file mode 100755 index 00000000000..dd19cfb2e62 --- /dev/null +++ b/web_dynamic/static/styles/3-header.css @@ -0,0 +1,23 @@ +header { + background: white; + height: 70px; + width: 100%; + border-bottom: 1px solid #CCCCCC; +} +header .logo { + background: url("../images/logo.png") no-repeat; + left: 20px; + height: 100%; +} + +header div#api_status { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 30px; + background-color: #cccccc; +} + +header div#api_status.available { + background-color: #ff545f; +} diff --git a/web_dynamic/static/styles/4-common.css b/web_dynamic/static/styles/4-common.css new file mode 100755 index 00000000000..46cfc19d1c1 --- /dev/null +++ b/web_dynamic/static/styles/4-common.css @@ -0,0 +1,11 @@ +body { + margin: 0; + padding: 0; + color: #484848; + font-size: 14px; + font-family: Circular,"Helvetica Neue",Helvetica,Arial,sans-serif; +} +body .container { + max-width: 1000px; + margin: 30px auto; +} diff --git a/web_dynamic/static/styles/6-filters.css b/web_dynamic/static/styles/6-filters.css new file mode 100755 index 00000000000..d47107732aa --- /dev/null +++ b/web_dynamic/static/styles/6-filters.css @@ -0,0 +1,103 @@ +.container .filters { + position: relative; + background: white; + height: 70px; + width: 100%; + border: 1px solid #DDDDDD; + border-radius: 4px; +} +button { + position: absolute; + font-size: 18px; + background: #FF5A5F; + color: #FFFFFF; + height: 48px; + width: 20%; + border-style: none; + border-radius: 4px; + top: 15%; + right: 30px; +} +button:hover { + opacity: 0.9; +} +.filters div { + display: inline-grid; +} +.filters h2 { + margin-left: 15%; + margin-top: 0; + margin-bottom: 0; + font-weight: 600; +} +.filters h3 { + margin-left: 15%; + margin-bottom: 0; + font-weight: 600; +} +.filters h4 { + margin-left: 15%; + margin-top: 0; + font-weight: 400; + font-size: 14px; +} +.locations { + height: 100%; + width: 25%; + border-right: 1px solid #DDDDDD; +} +.amenities { + height: 100%; + width: 25%; +} + +.popover { + visibility: hidden; + width: 100%; + border: 1px solid #DDDDDD; + border-radius: 4px; + background: #FAFAFA; + padding-bottom: 15px; + height: 300px; + overflow-y: scroll; + scrollbar-width: none; + max-height: 300px; +} + +.popover::-webkit-scrollbar{ + width: 0px; +} + +.amenities .popover { + padding: 10px 0; + margin-left: -5px; + margin-top: 0%; +} + +.amenities .popover ul{ + margin: 0px; +} + +.locations .popover { + margin-top: 0%; +} +.popover ul { + list-style-type: none; + padding-bottom: 10px; + padding-left: 10px; +} +.popover ul li{ + padding: 4px; + padding-left: 10px; +} + +.popover ul h2{ + margin-top: 1.5%; + margin-bottom: 5%; + margin-left: 0px; +} + +.amenities:hover .popover, +.locations:hover .popover { + visibility: visible; +} diff --git a/web_dynamic/static/styles/8-places.css b/web_dynamic/static/styles/8-places.css new file mode 100755 index 00000000000..9dac66c0d7f --- /dev/null +++ b/web_dynamic/static/styles/8-places.css @@ -0,0 +1,101 @@ +.places { + column-count: 2; + columns: 30em; + justify-content: center; + padding: 0 20px; + margin-top: 1%; + margin-bottom: 8%; +} +@media only screen and (max-width: 920px) +{ + .places { + display: flex; + flex-wrap: wrap; + } +} + +.placesh1 h1 { + width: 100%; + margin-right: 400px; + text-align: left; + font-size: 35px; +} + +.places article { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + display: inline-block; + width: 390px; + height: 100%; + padding: 20px; + margin: 20px; + border: 1px solid #FF5A5F; + border-radius: 4px; +} +.places h2 { + font-size: 30px; + text-align: center; + margin-top: 0; +} +.title_box { + display: flex; + justify-content: space-between; + margin-top: -2%; +} + +.title_box h2 { + text-align: left; + margin: 25px 3% 40px 2%; + max-width: 75%; + word-wrap: break-word; +} +.price_by_night { + display: flex; + height: 60px; + min-width: 60px; + font-size: 30px; + justify-content: center; + align-items: center; + color: #FF5A5F; + border: 4px solid #FF5A5F; + border-radius: 50%; + align-items: center; + padding: 2.3%; +} + +.information { + display: flex; + justify-content: center; + align-items: center; + height: 80px; + border-top: 1px solid #DDDDDD; + border-bottom: 1px solid #DDDDDD; + margin-bottom: 5%; +} + +.information div { + display: flex; + justify-content: flex-end; + align-items: center; + flex-direction: column; + height: 65px; +} + +.information .max_guest { + background: url("../images/icon_group.png") no-repeat top center; + width: 100px; +} + +.information .number_rooms { + background: url("../images/icon_bed.png") no-repeat top center; + width: 100px; +} + +.information .number_bathrooms { + background: url("../images/icon_bath.png") no-repeat top center; + width: 100px; +} + +.user { + margin-bottom: 1.5%; +} diff --git a/web_dynamic/static/styles/w3c_validator.py b/web_dynamic/static/styles/w3c_validator.py new file mode 100755 index 00000000000..ee9593fce40 --- /dev/null +++ b/web_dynamic/static/styles/w3c_validator.py @@ -0,0 +1,123 @@ +#!/usr/bin/python3 +""" +W3C validator for Holberton School + +For HTML and CSS files. + +Based on 2 APIs: + +- https://validator.w3.org/nu/ +- http://jigsaw.w3.org/css-validator/validator + + +Usage: + +Simple file: + +``` +./w3c_validator.py index.html +``` + +Multiple files: + +``` +./w3c_validator.py index.html header.html styles/common.css +``` + +All errors are printed in `STDERR` + +Return: +Exit status is the # of errors, 0 on Success + +References + +https://developer.mozilla.org/en-US/ + +""" +import sys +import requests + + +def __print_stdout(msg): + """Print message in STDOUT + """ + sys.stdout.write(msg) + + +def __print_stderr(msg): + """Print message in STDERR + """ + sys.stderr.write(msg) + + +def __analyse_html(file_path): + """Start analyse of HTML file + """ + h = {'Content-Type': "text/html; charset=utf-8"} + d = open(file_path, "rb").read() + u = "https://validator.w3.org/nu/?out=json" + r = requests.post(u, headers=h, data=d) + res = [] + messages = r.json().get('messages', []) + for m in messages: + res.append("[{}:{}] {}".format(file_path, m['lastLine'], m['message'])) + return res + + +def __analyse_css(file_path): + """Start analyse of CSS file + """ + d = {'output': "json"} + f = {'file': (file_path, open(file_path, 'rb'), 'text/css')} + u = "http://jigsaw.w3.org/css-validator/validator" + r = requests.post(u, data=d, files=f) + res = [] + errors = r.json().get('cssvalidation', {}).get('errors', []) + for e in errors: + res.append("[{}:{}] {}".format(file_path, e['line'], e['message'])) + return res + + +def __analyse(file_path): + """Start analyse of a file and print the result + """ + nb_errors = 0 + try: + result = None + if file_path.endswith('.css'): + result = __analyse_css(file_path) + else: + result = __analyse_html(file_path) + + if len(result) > 0: + for msg in result: + __print_stderr("{}\n".format(msg)) + nb_errors += 1 + else: + __print_stdout("{}: OK\n".format(file_path)) + + except Exception as e: + __print_stderr("[{}] {}\n".format(e.__class__.__name__, e)) + return nb_errors + + +def __files_loop(): + """Loop that analyses for each file from input arguments + """ + nb_errors = 0 + for file_path in sys.argv[1:]: + nb_errors += __analyse(file_path) + + return nb_errors + + +if __name__ == "__main__": + """Main + """ + if len(sys.argv) < 2: + __print_stderr("usage: w3c_validator.py file1 file2 ...\n") + exit(1) + + """execute tests, then exit. Exit status = # of errors (0 on success) + """ + sys.exit(__files_loop()) diff --git a/web_dynamic/templates/0-hbnb.html b/web_dynamic/templates/0-hbnb.html new file mode 100755 index 00000000000..03d3577c8b2 --- /dev/null +++ b/web_dynamic/templates/0-hbnb.html @@ -0,0 +1,78 @@ + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    + + {% for place in places %} +
    +
    +

    {{ place.name }}

    +
    ${{ place.price_by_night }}
    +
    +
    +
    {{ place.max_guest }} Guest{% if place.max_guest != 1 %}s{% endif %}
    +
    {{ place.number_rooms }} Bedroom{% if place.number_rooms != 1 %}s{% endif %}
    +
    {{ place.number_bathrooms }} Bathroom{% if place.number_bathrooms != 1 %}s{% endif %}
    +
    +
    + Owner: {{ place.user.first_name }} {{ place.user.last_name }} +
    +
    + {{ place.description | safe }} +
    +
    + {% endfor %} +
    +
    + + + diff --git a/web_dynamic/templates/1-hbnb.html b/web_dynamic/templates/1-hbnb.html new file mode 100755 index 00000000000..9fe1988624f --- /dev/null +++ b/web_dynamic/templates/1-hbnb.html @@ -0,0 +1,80 @@ + + + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    + + {% for place in places %} +
    +
    +

    {{ place.name }}

    +
    ${{ place.price_by_night }}
    +
    +
    +
    {{ place.max_guest }} Guest{% if place.max_guest != 1 %}s{% endif %}
    +
    {{ place.number_rooms }} Bedroom{% if place.number_rooms != 1 %}s{% endif %}
    +
    {{ place.number_bathrooms }} Bathroom{% if place.number_bathrooms != 1 %}s{% endif %}
    +
    +
    + Owner: {{ place.user.first_name }} {{ place.user.last_name }} +
    +
    + {{ place.description | safe }} +
    +
    + {% endfor %} +
    +
    + + + diff --git a/web_dynamic/templates/100-hbnb.html b/web_dynamic/templates/100-hbnb.html new file mode 100755 index 00000000000..945ff99b9ca --- /dev/null +++ b/web_dynamic/templates/100-hbnb.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    +
    +
    + + + diff --git a/web_dynamic/templates/101-hbnb.html b/web_dynamic/templates/101-hbnb.html new file mode 100755 index 00000000000..484af22f001 --- /dev/null +++ b/web_dynamic/templates/101-hbnb.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    +
    +
    + + + diff --git a/web_dynamic/templates/2-hbnb.html b/web_dynamic/templates/2-hbnb.html new file mode 100755 index 00000000000..898b7a88e19 --- /dev/null +++ b/web_dynamic/templates/2-hbnb.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    + + {% for place in places %} +
    +
    +

    {{ place.name }}

    +
    ${{ place.price_by_night }}
    +
    +
    +
    {{ place.max_guest }} Guest{% if place.max_guest != 1 %}s{% endif %}
    +
    {{ place.number_rooms }} Bedroom{% if place.number_rooms != 1 %}s{% endif %}
    +
    {{ place.number_bathrooms }} Bathroom{% if place.number_bathrooms != 1 %}s{% endif %}
    +
    +
    + Owner: {{ place.user.first_name }} {{ place.user.last_name }} +
    +
    + {{ place.description | safe }} +
    +
    + {% endfor %} +
    +
    + + + diff --git a/web_dynamic/templates/3-hbnb.html b/web_dynamic/templates/3-hbnb.html new file mode 100755 index 00000000000..b3d837e1e75 --- /dev/null +++ b/web_dynamic/templates/3-hbnb.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    +
    +
    + + + diff --git a/web_dynamic/templates/4-hbnb.html b/web_dynamic/templates/4-hbnb.html new file mode 100755 index 00000000000..e3af3785f40 --- /dev/null +++ b/web_dynamic/templates/4-hbnb.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + HBnB + + +
    + +
    +
    +
    +
    +
    +

    States

    +

     

    +
    +
      + {% for state in states %} +
    • +

      {{ state[0].name }}:

      +
        + {% for city in state[1] %} +
      • {{ city.name }}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    +
    +
    +

    Amenities

    +

     

    +
    +
      + {% for amenity in amenities %} +
    • {{ amenity.name }}
    • + {% endfor %} +
    +
    +
    + +
    +

    Places

    +
    +
    +
    + + +