diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..32e493fe6f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +myvenv/* +__pycache__/ +file.json diff --git a/1-pack_web_static.py b/1-pack_web_static.py index f08a8aea659..3313f622f49 100644 --- a/1-pack_web_static.py +++ b/1-pack_web_static.py @@ -18,5 +18,5 @@ def do_pack(): file_name = "versions/web_static_{}.tgz".format(date) local("tar -cvzf {} web_static".format(file_name)) return file_name - except: + except Exception: return None diff --git a/2-do_deploy_web_static.py b/2-do_deploy_web_static.py index aa5ab7852c6..3d1a2b4acf5 100644 --- a/2-do_deploy_web_static.py +++ b/2-do_deploy_web_static.py @@ -26,5 +26,5 @@ def do_deploy(archive_path): run('rm -rf /data/web_static/current') run('ln -s {}{}/ /data/web_static/current'.format(path, no_ext)) return True - except: + except Exception: return False diff --git a/3-deploy_web_static.py b/3-deploy_web_static.py index f7e7e254b5e..6aa5becbb77 100644 --- a/3-deploy_web_static.py +++ b/3-deploy_web_static.py @@ -19,7 +19,7 @@ def do_pack(): file_name = "versions/web_static_{}.tgz".format(date) local("tar -cvzf {} web_static".format(file_name)) return file_name - except: + except Exception: return None @@ -40,7 +40,7 @@ def do_deploy(archive_path): run('rm -rf /data/web_static/current') run('ln -s {}{}/ /data/web_static/current'.format(path, no_ext)) return True - except: + except Exception: return False diff --git a/AUTHORS b/AUTHORS index 64b26acdc14..0a9a7207f54 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,3 +4,17 @@ Jennifer Huang <133@holbertonschool.com> Alexa Orrico <210@holbertonschool.com> Joann Vuong <130@holbertonschool.com> + +Patrick Odhiambo +Steve Murimi + +The following new features and improvements have been added to the project: + +- **Selector**: Methods to select and manipulate HTML elements using various selectors. +- **Get and set content**: Functions to retrieve and update the content of HTML elements. +- **Manipulate CSS classes**: Utilities to add, remove, and toggle CSS classes on HTML elements. +- **Manipulate DOM elements**: Functions to create, remove, and modify HTML elements in the DOM. +- **Document ready**: Ensures that the DOM is fully loaded before executing JavaScript code. +- **Introduction**: Added introductory documentation and examples for new users. +- **GET & POST request**: Methods to perform asynchronous HTTP GET and POST requests. +- **HTTP access control (CORS)**: Implemented Cross-Origin Resource Sharing (CORS) to allow secure communication between different domains. diff --git a/README2.md b/README2.md new file mode 100644 index 00000000000..6749daeaf27 --- /dev/null +++ b/README2.md @@ -0,0 +1 @@ +# 0x06. AirBnB clone - Web dynamic diff --git a/api/v1/app.py b/api/v1/app.py index 2a74f26a1e5..2b6ebcac454 100755 --- a/api/v1/app.py +++ b/api/v1/app.py @@ -30,6 +30,7 @@ def not_found(error): """ return make_response(jsonify({'error': "Not found"}), 404) + app.config['SWAGGER'] = { 'title': 'AirBnB clone Restful API', 'uiversion': 3 diff --git a/console.py b/console.py index 4798f9ac76b..7fa6694d5ae 100755 --- a/console.py +++ b/console.py @@ -46,10 +46,10 @@ def _key_value_parser(self, args): else: try: value = int(value) - except: + except ValueError: try: value = float(value) - except: + except ValueError: continue new_dict[key] = value return new_dict @@ -140,12 +140,12 @@ def do_update(self, arg): if args[2] in integers: try: args[3] = int(args[3]) - except: + except ValueError: args[3] = 0 elif args[2] in floats: try: args[3] = float(args[3]) - except: + except ValueError: args[3] = 0.0 setattr(models.storage.all()[k], args[2], args[3]) models.storage.all()[k].save() @@ -160,5 +160,6 @@ def do_update(self, arg): else: print("** class doesn't exist **") + if __name__ == '__main__': HBNBCommand().cmdloop() diff --git a/models/city.py b/models/city.py index 0db5033c7ab..b12be96d52b 100755 --- a/models/city.py +++ b/models/city.py @@ -12,6 +12,7 @@ class City(BaseModel, Base): """Representation of city """ if models.storage_t == "db": __tablename__ = 'cities' + id = Column(String(60), primary_key=True, nullable=False) state_id = Column(String(60), ForeignKey('states.id'), nullable=False) name = Column(String(128), nullable=False) places = relationship("Place", diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 305234655c1..5c0da291969 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -59,7 +59,7 @@ def reload(self): jo = json.load(f) for key in jo: self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) - except: + except FileNotFoundError: pass def delete(self, obj=None): diff --git a/models/place.py b/models/place.py index 306e7403906..b8e21db35f9 100755 --- a/models/place.py +++ b/models/place.py @@ -23,6 +23,7 @@ class Place(BaseModel, Base): """Representation of Place """ if models.storage_t == 'db': __tablename__ = 'places' + id = Column(String(60), primary_key=True, nullable=False) city_id = Column(String(60), ForeignKey('cities.id'), nullable=False) user_id = Column(String(60), ForeignKey('users.id'), nullable=False) name = Column(String(128), nullable=False) diff --git a/models/state.py b/models/state.py index 9e774494e86..b8c01d66619 100755 --- a/models/state.py +++ b/models/state.py @@ -13,6 +13,7 @@ class State(BaseModel, Base): """Representation of state """ if models.storage_t == "db": __tablename__ = 'states' + id = Column(String(60), primary_key=True, nullable=False) name = Column(String(128), nullable=False) cities = relationship("City", backref="state", diff --git a/myfile.txt b/myfile.txt new file mode 100755 index 00000000000..56adbef0dcc --- /dev/null +++ b/myfile.txt @@ -0,0 +1,26 @@ +Starting a python env named myvenv: +```sh +python -m venv myvenv +source myvenv/bin/activate +deactivate +``` + +To set an empty password for the MySQL root user, follow these steps: +Log in to MySQL as the root user using sudo: +sudo mysql -u root + +Switch to the mysql database: +USE mysql; + +Update the authentication method for the root user to use the mysql_native_password plugin and set an empty password: +ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY ''; + +Flush the privileges to ensure that the changes take effect: +FLUSH PRIVILEGES; + +Exit the MySQL shell: +EXIT; + +Run the cat command again with the updated root user credentials: +cat 7-dump.sql | mysql -u root --password="" +cat 7-dump.sql | mysql -u root -p \ No newline at end of file diff --git a/tests/test_models/test_base_model.py b/tests/test_models/test_base_model.py index 231dab48ccd..9a6acb2bb11 100644 --- a/tests/test_models/test_base_model.py +++ b/tests/test_models/test_base_model.py @@ -82,15 +82,20 @@ def test_datetime_attributes(self): """Test that two BaseModel instances have different datetime objects and that upon creation have identical updated_at and created_at value.""" - tic = datetime.now() + time.sleep(1e-4) # Small delay to ensure correct time capture + tic = datetime.utcnow() inst1 = BaseModel() - toc = datetime.now() - self.assertTrue(tic <= inst1.created_at <= toc) + toc = datetime.utcnow() + self.assertTrue( + tic <= inst1.created_at <= toc, + f"inst1.created_at {inst1.created_at} not in range {tic} - {toc}") time.sleep(1e-4) - tic = datetime.now() + tic = datetime.utcnow() inst2 = BaseModel() - toc = datetime.now() - self.assertTrue(tic <= inst2.created_at <= toc) + toc = datetime.utcnow() + self.assertTrue(tic <= inst2.created_at <= toc, + f"inst2.created_at {inst2.created_at} " + f"not in range {tic} - {toc}") self.assertEqual(inst1.created_at, inst1.updated_at) self.assertEqual(inst2.created_at, inst2.updated_at) self.assertNotEqual(inst1.created_at, inst2.created_at) diff --git a/web_dynamic/0-hbnb.py b/web_dynamic/0-hbnb.py new file mode 100755 index 00000000000..d42b6ebbf38 --- /dev/null +++ b/web_dynamic/0-hbnb.py @@ -0,0 +1,51 @@ +#!/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 +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('/', strict_slashes=False) +def index(): + """ Redirect to /hbnb """ + return hbnb() + + +@app.route('/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) + + +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/styles/3-footer.css b/web_dynamic/static/styles/3-footer.css new file mode 100644 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 100644 index 00000000000..245cac7c980 --- /dev/null +++ b/web_dynamic/static/styles/3-header.css @@ -0,0 +1,11 @@ +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%; +} diff --git a/web_dynamic/static/styles/4-common.css b/web_dynamic/static/styles/4-common.css new file mode 100644 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 100644 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 100644 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/10-hbnb_filters.html b/web_dynamic/templates/10-hbnb_filters.html new file mode 100644 index 00000000000..8d7165b33a2 --- /dev/null +++ b/web_dynamic/templates/10-hbnb_filters.html @@ -0,0 +1,53 @@ + + + + + + + + + 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 %} +
+
+
+ +
+
+
+

Holberton School

+
+ + diff --git a/web_dynamic/templates/100-hbnb.html b/web_dynamic/templates/100-hbnb.html new file mode 100644 index 00000000000..776bbb9c999 --- /dev/null +++ b/web_dynamic/templates/100-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 %} +
+
+
+

Holberton School

+
+ + diff --git a/web_dynamic/templates/5-number.html b/web_dynamic/templates/5-number.html new file mode 100644 index 00000000000..5667276f1f1 --- /dev/null +++ b/web_dynamic/templates/5-number.html @@ -0,0 +1,9 @@ + + + + HBNB + + +

Number: {{ value }}

+ + diff --git a/web_dynamic/templates/6-number_odd_or_even.html b/web_dynamic/templates/6-number_odd_or_even.html new file mode 100644 index 00000000000..7aafa5bdf49 --- /dev/null +++ b/web_dynamic/templates/6-number_odd_or_even.html @@ -0,0 +1,13 @@ + + + + HBNB + + + {% if (value % 2 == 0) %} +

Number: {{ value }} is even

+ {% else %} +

Number: {{ value }} is odd

+ {% endif %} + + diff --git a/web_dynamic/templates/7-states_list.html b/web_dynamic/templates/7-states_list.html new file mode 100644 index 00000000000..07d562b9f5e --- /dev/null +++ b/web_dynamic/templates/7-states_list.html @@ -0,0 +1,14 @@ + + + + HBNB + + +

States

+ + + diff --git a/web_dynamic/templates/8-cities_by_states.html b/web_dynamic/templates/8-cities_by_states.html new file mode 100644 index 00000000000..9d5e65446b8 --- /dev/null +++ b/web_dynamic/templates/8-cities_by_states.html @@ -0,0 +1,20 @@ + + + + HBNB + + +

{{ h_1 }}

+ + + diff --git a/web_dynamic/templates/9-states.html b/web_dynamic/templates/9-states.html new file mode 100644 index 00000000000..3bcbc8167f9 --- /dev/null +++ b/web_dynamic/templates/9-states.html @@ -0,0 +1,26 @@ + + + + HBNB + + + {% if (found == 1) %} +

State: {{ state }}

+

Cities:

+ + {% elif found == 0 %} +

States

+ + {% else %} +

Not found!

+ {% endif %} + + diff --git a/web_dynamic/templates/w3c_validator.py b/web_dynamic/templates/w3c_validator.py new file mode 100755 index 00000000000..ee9593fce40 --- /dev/null +++ b/web_dynamic/templates/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_flask/0-hello_route.py b/web_flask/0-hello_route.py index 5f7332267b9..b2581dfc899 100755 --- a/web_flask/0-hello_route.py +++ b/web_flask/0-hello_route.py @@ -9,6 +9,7 @@ def hello_hbnb(): """ Prints a Message when / is called """ return 'Hello HBNB!' + if __name__ == "__main__": """ Main Function """ app.run(host='0.0.0.0', port=5000) diff --git a/web_flask/1-hbnb_route.py b/web_flask/1-hbnb_route.py index 693acca5aed..20d48ce5374 100755 --- a/web_flask/1-hbnb_route.py +++ b/web_flask/1-hbnb_route.py @@ -15,6 +15,7 @@ def hbnb(): """ Prints a Message when /hbnb is called """ return 'HBNB' + if __name__ == "__main__": """ Main Function """ app.run(host='0.0.0.0', port=5000) diff --git a/web_flask/2-c_route.py b/web_flask/2-c_route.py index 4c39aa53b68..a1cefc2c4af 100755 --- a/web_flask/2-c_route.py +++ b/web_flask/2-c_route.py @@ -21,6 +21,7 @@ def c_is_fun(text): """ Prints a Message when /c is called """ return "C " + text.replace('_', ' ') + if __name__ == "__main__": """ Main Function """ app.run(host='0.0.0.0', port=5000) diff --git a/web_flask/3-python_route.py b/web_flask/3-python_route.py index fc915498096..41be9bcbda4 100755 --- a/web_flask/3-python_route.py +++ b/web_flask/3-python_route.py @@ -28,6 +28,7 @@ def python_is_cool(text='is_cool'): """ Prints a Message when /python is called """ return "Python " + text.replace('_', ' ') + if __name__ == "__main__": """ Main Function """ app.run(host='0.0.0.0', port=5000) diff --git a/web_flask/4-number_route.py b/web_flask/4-number_route.py index 7d8727c395b..ef686489892 100755 --- a/web_flask/4-number_route.py +++ b/web_flask/4-number_route.py @@ -34,6 +34,7 @@ def is_n_number(n): """ Prints a Message when /number is called only if n is an int""" return "{:d} is a number".format(n) + if __name__ == "__main__": """ Main Function """ app.run(host='0.0.0.0', port=5000) diff --git a/web_flask/templates/0-hbnb.html b/web_flask/templates/0-hbnb.html new file mode 100644 index 00000000000..776bbb9c999 --- /dev/null +++ b/web_flask/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 %} +
+
+ + +