diff --git a/0-setup_web_static.sh b/0-setup_web_static.sh new file mode 100755 index 000000000000..57691d7312c4 --- /dev/null +++ b/0-setup_web_static.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Script that sets up my web servers for the deployment of web_static +sudo apt-get -y update +sudo apt-get -y install nginx +sudo service nginx start +# Creating parent directories +sudo mkdir -p /data/web_static/shared/ +sudo mkdir -p /data/web_static/releases/test/ +# Creating a fake HTML file with simple content +echo 'Rethics' | sudo tee /data/web_static/releases/test/index.html +#Creating a symbolic link +ln -sf /data/web_static/releases/test/ /data/web_static/current +# Giving ownership of the /data/ folder to the ubuntu user AND group +sudo chown -R ubuntu:ubuntu /data/ +# Updating the Nginx configuration to serve the content of /data/web_static/current/ to hbnb_static +sed -i "38i location /hbnb_static/ { alias /data/web_static/current/; }" /etc/nginx/sites-available/default +# Restarting Nginx +sudo service nginx restart diff --git a/1-pack_web_static.py b/1-pack_web_static.py new file mode 100755 index 000000000000..955947171b8a --- /dev/null +++ b/1-pack_web_static.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 +"""Fabric script that generates a .tgz archive from the contents of the""" + + +from fabric.api import local +from datetime import datetime +import os + + +def do_pack(): + """Generates a .tgz archive from the contents + of the web_static folder""" + now = datetime.now() + now = now.strftime('%Y%m%d%H%M%S') + archive_path = 'versions/web_static_' + now + '.tgz' + + local('mkdir -p versions/') + result = local('tar -cvzf {} web_static/'.format(archive_path)) + + if result.succeeded: + return archive_path + return None diff --git a/2-do_deploy_web_static.py b/2-do_deploy_web_static.py new file mode 100755 index 000000000000..2526b6da036d --- /dev/null +++ b/2-do_deploy_web_static.py @@ -0,0 +1,29 @@ +#!/usr/bin/python3 +"""Fabric script that distributes an archive to your web servers""" +from fabric.api import env, put, run +from os.path import exists + +env.hosts = ["3.88.201.199", "3.87.90.59"] +env.user = "ubuntu" +env.key = "~/.ssh/id_rsa" + + +def do_deploy(archive_path): + """Function to distribute an archive to your web servers""" + if not exists(archive_path): + return False + try: + file_name = archive_path.split("/")[-1] + name = file_name.split(".")[0] + path_name = "/data/web_static/releases/" + name + put(archive_path, "/tmp/") + run("mkdir -p {}/".format(path_name)) + run('tar -xzf /tmp/{} -C {}/'.format(file_name, path_name)) + run("rm /tmp/{}".format(file_name)) + run("mv {}/web_static/* {}".format(path_name, path_name)) + run("rm -rf {}/web_static".format(path_name)) + run('rm -rf /data/web_static/current') + run('ln -s {}/ /data/web_static/current'.format(path_name)) + return True + except Exception: + return False diff --git a/3-deploy_web_static.py b/3-deploy_web_static.py new file mode 100755 index 000000000000..af166e3e7e53 --- /dev/null +++ b/3-deploy_web_static.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +"""Fabric script that creates and distributes an archive to your web servers""" + +import os +from fabric.api import env, local, put, run +from datetime import datetime +from os.path import exists + +env.hosts = ["98.84.116.96", "3.90.179.12"] +env.user = "ubuntu" +env.key = "~/.ssh/id_rsa" + + +def do_pack(): + """Create a .tgz archive from the web_static folder.""" + time_stamp = datetime.now().strftime("%Y%m%d%H%M%S") + local("mkdir -p versions") + archive_path = "versions/web_static_{}.tgz".format(time_stamp) + local("tar -cvzf {} web_static".format(archive_path)) + if os.path.exists(archive_path): + return archive_path + else: + return None + + +def do_deploy(archive_path): + """Distribute the archive to web servers and deploy it.""" + if not exists(archive_path): + return False + try: + file_name = archive_path.split("/")[-1] + name = file_name.split(".")[0] + path_name = "/data/web_static/releases/" + name + put(archive_path, "/tmp/") + run("mkdir -p {}/".format(path_name)) + run('tar -xzf /tmp/{} -C {}/'.format(file_name, path_name)) + run("rm /tmp/{}".format(file_name)) + run("mv {}/web_static/* {}".format(path_name, path_name)) + run("rm -rf {}/web_static".format(path_name)) + run('rm -rf /data/web_static/current') + run('ln -s {}/ /data/web_static/current'.format(path_name)) + return True + except Exception: + return False + + +def deploy(): + """Create and distribute an archive to web servers.""" + archive_path = do_pack() + if not archive_path: + return False + + return do_deploy(archive_path) diff --git a/README.md b/README.md index 3ce462902d67..e99203d32913 100644 --- a/README.md +++ b/README.md @@ -1,142 +1,818 @@ -

HBNB - The Console

- -This repository contains the initial stage of a student project to build a clone of the AirBnB website. This stage implements a backend interface, or console, to manage program data. Console commands allow the user to create, update, and destroy objects, as well as manage file storage. Using a system of JSON serialization/deserialization, storage is persistent between sessions. - ---- - -

Repository Contents by Project Task

- -| Tasks | Files | Description | -| ----- | ----- | ------ | -| 0: Authors/README File | [AUTHORS](https://github.com/justinmajetich/AirBnB_clone/blob/dev/AUTHORS) | Project authors | -| 1: Pep8 | N/A | All code is pep8 compliant| -| 2: Unit Testing | [/tests](https://github.com/justinmajetich/AirBnB_clone/tree/dev/tests) | All class-defining modules are unittested | -| 3. Make BaseModel | [/models/base_model.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/base_model.py) | Defines a parent class to be inherited by all model classes| -| 4. Update BaseModel w/ kwargs | [/models/base_model.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/base_model.py) | Add functionality to recreate an instance of a class from a dictionary representation| -| 5. Create FileStorage class | [/models/engine/file_storage.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/engine/file_storage.py) [/models/_ _init_ _.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/__init__.py) [/models/base_model.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/base_model.py) | Defines a class to manage persistent file storage system| -| 6. Console 0.0.1 | [console.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/console.py) | Add basic functionality to console program, allowing it to quit, handle empty lines and ^D | -| 7. Console 0.1 | [console.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/console.py) | Update the console with methods allowing the user to create, destroy, show, and update stored data | -| 8. Create User class | [console.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/console.py) [/models/engine/file_storage.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/engine/file_storage.py) [/models/user.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/user.py) | Dynamically implements a user class | -| 9. More Classes | [/models/user.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/user.py) [/models/place.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/place.py) [/models/city.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/city.py) [/models/amenity.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/amenity.py) [/models/state.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/state.py) [/models/review.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/review.py) | Dynamically implements more classes | -| 10. Console 1.0 | [console.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/console.py) [/models/engine/file_storage.py](https://github.com/justinmajetich/AirBnB_clone/blob/dev/models/engine/file_storage.py) | Update the console and file storage system to work dynamically with all classes update file storage | -
-
-

General Use

- -1. First clone this repository. - -3. Once the repository is cloned locate the "console.py" file and run it as follows: -``` -/AirBnB_clone$ ./console.py -``` -4. When this command is run the following prompt should appear: -``` +Concepts +For this project, we expect you to look at this concept: + +AirBnB clone +Background Context +Environment variables will be your best friend for this project! + +HBNB_ENV: running environment. It can be “dev” or “test” for the moment (“production” soon!) +HBNB_MYSQL_USER: the username of your MySQL +HBNB_MYSQL_PWD: the password of your MySQL +HBNB_MYSQL_HOST: the hostname of your MySQL +HBNB_MYSQL_DB: the database name of your MySQL +HBNB_TYPE_STORAGE: the type of storage used. It can be “file” (using FileStorage) or db (using DBStorage) +Resources +Read or watch: + +cmd module +packages concept page +unittest module +args/kwargs +SQLAlchemy tutorial +How To Create a New User and Grant Permissions in MySQL +Python3 and environment variables +SQLAlchemy +MySQL 8.0 SQL Statement Syntax +AirBnB clone - ORM +Learning Objectives +At the end of this project, you are expected to be able to explain to anyone, without the help of Google: + +General +What is Unit testing and how to implement it in a large project +What is *args and how to use it +What is **kwargs and how to use it +How to handle named arguments in a function +How to create a MySQL database +How to create a MySQL user and grant it privileges +What ORM means +How to map a Python Class to a MySQL table +How to handle 2 different storage engines with the same codebase +How to use environment variables +Requirements +Python Scripts +Allowed editors: vi, vim, emacs +All your files will be interpreted/compiled on Ubuntu 20.04 LTS using python3 (version 3.8.5) +All your files should end with a new line +The first line of all your files should be exactly #!/usr/bin/python3 +A README.md file, at the root of the folder of the project, is mandatory +Your code should use the pycodestyle (version 2.7.*) +All your files must be executable +The length of your files will be tested using wc +All your modules should have documentation (python3 -c 'print(__import__("my_module").__doc__)') +All your classes should have documentation (python3 -c 'print(__import__("my_module").MyClass.__doc__)') +All your functions (inside and outside a class) should have documentation (python3 -c 'print(__import__("my_module").my_function.__doc__)' and python3 -c 'print(__import__("my_module").MyClass.my_function.__doc__)') +A documentation is not a simple word, it’s a real sentence explaining what’s the purpose of the module, class or method (the length of it will be verified) +Python Unit Tests +Allowed editors: vi, vim, emacs +All your files should end with a new line +All your test files should be inside a folder tests +You have to use the unittest module +All your test files should be python files (extension: .py) +All your test files and folders should start by test_ +Your file organization in the tests folder should be the same as your project: ex: for models/base_model.py, unit tests must be in: tests/test_models/test_base_model.py +All your tests should be executed by using this command: python3 -m unittest discover tests +You can also test file by file by using this command: python3 -m unittest tests/test_models/test_base_model.py +All your modules should have documentation (python3 -c 'print(__import__("my_module").__doc__)') +All your classes should have documentation (python3 -c 'print(__import__("my_module").MyClass.__doc__)') +All your functions (inside and outside a class) should have documentation (python3 -c 'print(__import__("my_module").my_function.__doc__)' and python3 -c 'print(__import__("my_module").MyClass.my_function.__doc__)') +We strongly encourage you to work together on test cases, so that you don’t miss any edge cases +SQL Scripts +Allowed editors: vi, vim, emacs +All your files will be executed on Ubuntu 20.04 LTS using MySQL 8.0 +Your files will be executed with SQLAlchemy version 1.4.x +All your files should end with a new line +All your SQL queries should have a comment just before (i.e. syntax above) +All your files should start by a comment describing the task +All SQL keywords should be in uppercase (SELECT, WHERE…) +A README.md file, at the root of the folder of the project, is mandatory +The length of your files will be tested using wc +GitHub +There should be one project repository per group. If you clone/fork/whatever a partner’s project repository with the same name before the second deadline, you risk a 0% score. + +More Info + + + +Comments for your SQL file: +$ cat my_script.sql +-- first 3 students in the Batch ID=3 +-- because Batch 3 is the best! +SELECT id, name FROM students WHERE batch_id = 3 ORDER BY created_at DESC LIMIT 3; +$ +Tasks +0. Fork me if you can! +mandatory +In the industry, you will work on an existing codebase 90% of the time. Your first thoughts upon looking at it might include: + +“Who did this code?” +“How it works?” +“Where are unittests?” +“Where is this?” +“Why did they do that like this?” +“I don’t understand anything.” +“… I will refactor everything…” +But the worst thing you could possibly do is to redo everything. Please don’t do that! Note: the existing codebase might be perfect, or it might have errors. Don’t always trust the existing codebase! + +For this project you will fork this codebase: + +update the repository name (specified in the section Repo) +update the README.md with your information but don’t delete the initial authors +Repo: + +GitHub repository: alu-AirBnB_clone_v2 + +0/12 pts +1. Bug free! +mandatory +Do you remember the unittest module? + +This codebase contains many test cases. Some are missing, but the ones included cover the basic functionality of the program. + +guillaume@ubuntu:~/AirBnB_v2$ python3 -m unittest discover tests 2>&1 /dev/null | tail -n 1 +OK +guillaume@ubuntu:~/AirBnB_v2$ +All your unittests must pass without any errors at anytime in this project, with each storage engine!. Same for PEP8! + +guillaume@ubuntu:~/AirBnB_v2$ HBNB_ENV=test HBNB_MYSQL_USER=hbnb_test HBNB_MYSQL_PWD=hbnb_test_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_test_db HBNB_TYPE_STORAGE=db python3 -m unittest discover tests 2>&1 /dev/null | tail -n 1 +OK +guillaume@ubuntu:~/AirBnB_v2$ +Some tests won’t be relevant for some type of storage, please skip them by using the skipIf feature of the Unittest module - 26.3.6. Skipping tests and expected failures. Of course, the number of tests must be higher than the current number of tests, so if you decide to skip a test, you should write a new test! + +How to test with MySQL? +First, you create a specific database for it (next tasks). After, you have to remember what the purpose of an unittest? + +“Assert a current state (objects/data/database), do an action, and validate this action changed (or not) the state of your objects/data/database” + +For example, “you want to validate that the create State name="California" command in the console will add a new record in your table states in your database”, here steps for your unittest: + +get the number of current records in the table states (my using a MySQLdb for example - but not SQLAlchemy (remember, you want to test if it works, so it’s better to isolate from the system)) +execute the console command +get (again) the number of current records in the table states (same method, with MySQLdb) +if the difference is +1 => test passed +Repo: + +GitHub repository: alu-AirBnB_clone_v2 + +0/36 pts +2. Console improvements +mandatory +Update the def do_create(self, arg): function of your command interpreter (console.py) to allow for object creation with given parameters: + +Command syntax: create ... +Param syntax: = +Value syntax: +String: "" => starts with a double quote +any double quote inside the value must be escaped with a backslash \ +all underscores _ must be replace by spaces . Example: You want to set the string My little house to the attribute name, your command line must be name="My_little_house" +Float: . => contains a dot . +Integer: => default case +If any parameter doesn’t fit with these requirements or can’t be recognized correctly by your program, it must be skipped +Don’t forget to add tests for this new feature! + +Also, this new feature will be tested here only with FileStorage engine. + +guillaume@ubuntu:~/AirBnB_v2$ cat test_params_create +create State name="California" +create State name="Arizona" +all State + +create Place city_id="0001" user_id="0001" name="My_little_house" number_rooms=4 number_bathrooms=2 max_guest=10 price_by_night=300 latitude=37.773972 longitude=-122.431297 +all Place +guillaume@ubuntu:~/AirBnB_v2$ cat test_params_create | ./console.py +(hbnb) d80e0344-63eb-434a-b1e0-07783522124e +(hbnb) 092c9e5d-6cc0-4eec-aab9-3c1d79cfc2d7 +(hbnb) [[State] (d80e0344-63eb-434a-b1e0-07783522124e) {'id': 'd80e0344-63eb-434a-b1e0-07783522124e', 'created_at': datetime.datetime(2017, 11, 10, 4, 41, 7, 842160), 'updated_at': datetime.datetime(2017, 11, 10, 4, 41, 7, 842235), 'name': 'California'}, [State] (092c9e5d-6cc0-4eec-aab9-3c1d79cfc2d7) {'id': '092c9e5d-6cc0-4eec-aab9-3c1d79cfc2d7', 'created_at': datetime.datetime(2017, 11, 10, 4, 41, 7, 842779), 'updated_at': datetime.datetime(2017, 11, 10, 4, 41, 7, 842792), 'name': 'Arizona'}] +(hbnb) (hbnb) 76b65327-9e94-4632-b688-aaa22ab8a124 +(hbnb) [[Place] (76b65327-9e94-4632-b688-aaa22ab8a124) {'number_bathrooms': 2, 'longitude': -122.431297, 'city_id': '0001', 'user_id': '0001', 'latitude': 37.773972, 'price_by_night': 300, 'name': 'My little house', 'id': '76b65327-9e94-4632-b688-aaa22ab8a124', 'max_guest': 10, 'number_rooms': 4, 'updated_at': datetime.datetime(2017, 11, 10, 4, 41, 7, 843774), 'created_at': datetime.datetime(2017, 11, 10, 4, 41, 7, 843747)}] (hbnb) -``` -5. This prompt designates you are in the "HBnB" console. There are a variety of commands available within the console program. - -##### Commands - * create - Creates an instance based on given class - - * destroy - Destroys an object based on class and UUID - - * show - Shows an object based on class and UUID - - * all - Shows all objects the program has access to, or all objects of a given class - - * update - Updates existing attributes an object based on class name and UUID - - * quit - Exits the program (EOF will as well) - - -##### Alternative Syntax -Users are able to issue a number of console command using an alternative syntax: - - Usage: .([[name_arg value_arg]|[kwargs]]) -Advanced syntax is implemented for the following commands: - - * all - Shows all objects the program has access to, or all objects of a given class - - * count - Return number of object instances by class - - * show - Shows an object based on class and UUID - - * destroy - Destroys an object based on class and UUID - - * update - Updates existing attributes an object based on class name and UUID - -
-
-

Examples

-

Primary Command Syntax

- -###### Example 0: Create an object -Usage: create -``` -(hbnb) create BaseModel -``` -``` -(hbnb) create BaseModel -3aa5babc-efb6-4041-bfe9-3cc9727588f8 -(hbnb) -``` -###### Example 1: Show an object -Usage: show <_id> - -``` -(hbnb) show BaseModel 3aa5babc-efb6-4041-bfe9-3cc9727588f8 -[BaseModel] (3aa5babc-efb6-4041-bfe9-3cc9727588f8) {'id': '3aa5babc-efb6-4041-bfe9-3cc9727588f8', 'created_at': datetime.datetime(2020, 2, 18, 14, 21, 12, 96959), -'updated_at': datetime.datetime(2020, 2, 18, 14, 21, 12, 96971)} -(hbnb) -``` -###### Example 2: Destroy an object -Usage: destroy <_id> -``` -(hbnb) destroy BaseModel 3aa5babc-efb6-4041-bfe9-3cc9727588f8 -(hbnb) show BaseModel 3aa5babc-efb6-4041-bfe9-3cc9727588f8 -** no instance found ** -(hbnb) -``` -###### Example 3: Update an object -Usage: update <_id> -``` -(hbnb) update BaseModel b405fc64-9724-498f-b405-e4071c3d857f first_name "person" -(hbnb) show BaseModel b405fc64-9724-498f-b405-e4071c3d857f -[BaseModel] (b405fc64-9724-498f-b405-e4071c3d857f) {'id': 'b405fc64-9724-498f-b405-e4071c3d857f', 'created_at': datetime.datetime(2020, 2, 18, 14, 33, 45, 729889), -'updated_at': datetime.datetime(2020, 2, 18, 14, 33, 45, 729907), 'first_name': 'person'} +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: console.py, models/, tests/ + +0/22 pts +3. MySQL setup development +mandatory +Write a script that prepares a MySQL server for the project: + +A database hbnb_dev_db +A new user hbnb_dev (in localhost) +The password of hbnb_dev should be set to hbnb_dev_pwd +hbnb_dev should have all privileges on the database hbnb_dev_db (and only this database) +hbnb_dev should have SELECT privilege on the database performance_schema (and only this database) +If the database hbnb_dev_db or the user hbnb_dev already exists, your script should not fail +guillaume@ubuntu:~/AirBnB_v2$ cat setup_mysql_dev.sql | mysql -hlocalhost -uroot -p +Enter password: +guillaume@ubuntu:~/AirBnB_v2$ echo "SHOW DATABASES;" | mysql -uhbnb_dev -p | grep hbnb_dev_db +Enter password: +hbnb_dev_db +guillaume@ubuntu:~/AirBnB_v2$ echo "SHOW GRANTS FOR 'hbnb_dev'@'localhost';" | mysql -uroot -p +Enter password: +Grants for hbnb_dev@localhost +GRANT USAGE ON *.* TO 'hbnb_dev'@'localhost' +GRANT SELECT ON `performance_schema`.* TO 'hbnb_dev'@'localhost' +GRANT ALL PRIVILEGES ON `hbnb_dev_db`.* TO 'hbnb_dev'@'localhost' +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: setup_mysql_dev.sql + +0/21 pts +4. MySQL setup test +mandatory +Write a script that prepares a MySQL server for the project: + +A database hbnb_test_db +A new user hbnb_test (in localhost) +The password of hbnb_test should be set to hbnb_test_pwd +hbnb_test should have all privileges on the database hbnb_test_db (and only this database) +hbnb_test should have SELECT privilege on the database performance_schema (and only this database) +If the database hbnb_test_db or the user hbnb_test already exists, your script should not fail +guillaume@ubuntu:~/AirBnB_v2$ cat setup_mysql_test.sql | mysql -hlocalhost -uroot -p +Enter password: +guillaume@ubuntu:~/AirBnB_v2$ echo "SHOW DATABASES;" | mysql -uhbnb_test -p | grep hbnb_test_db +Enter password: +hbnb_test_db +guillaume@ubuntu:~/AirBnB_v2$ echo "SHOW GRANTS FOR 'hbnb_test'@'localhost';" | mysql -uroot -p +Enter password: +Grants for hbnb_test@localhost +GRANT USAGE ON *.* TO 'hbnb_test'@'localhost' +GRANT SELECT ON `performance_schema`.* TO 'hbnb_test'@'localhost' +GRANT ALL PRIVILEGES ON `hbnb_test_db`.* TO 'hbnb_test'@'localhost' +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: setup_mysql_test.sql + +0/21 pts +5. Delete object +mandatory +Update FileStorage: (models/engine/file_storage.py) + +Add a new public instance method: def delete(self, obj=None): to delete obj from __objects if it’s inside - if obj is equal to None, the method should not do anything +Update the prototype of def all(self) to def all(self, cls=None) - that returns the list of objects of one type of class. Example below with State - it’s an optional filtering +guillaume@ubuntu:~/AirBnB_v2$ cat main_delete.py +#!/usr/bin/python3 +""" Test delete feature +""" +from models.engine.file_storage import FileStorage +from models.state import State + +fs = FileStorage() + +# All States +all_states = fs.all(State) +print("All States: {}".format(len(all_states.keys()))) +for state_key in all_states.keys(): + print(all_states[state_key]) + +# Create a new State +new_state = State() +new_state.name = "California" +fs.new(new_state) +fs.save() +print("New State: {}".format(new_state)) + +# All States +all_states = fs.all(State) +print("All States: {}".format(len(all_states.keys()))) +for state_key in all_states.keys(): + print(all_states[state_key]) + +# Create another State +another_state = State() +another_state.name = "Nevada" +fs.new(another_state) +fs.save() +print("Another State: {}".format(another_state)) + +# All States +all_states = fs.all(State) +print("All States: {}".format(len(all_states.keys()))) +for state_key in all_states.keys(): + print(all_states[state_key]) + +# Delete the new State +fs.delete(new_state) + +# All States +all_states = fs.all(State) +print("All States: {}".format(len(all_states.keys()))) +for state_key in all_states.keys(): + print(all_states[state_key]) + +guillaume@ubuntu:~/AirBnB_v2$ ./main_delete.py +All States: 0 +New State: [State] (b0026fc6-116f-4d1a-a9cb-6bb9b299f1ce) {'name': 'California', 'created_at': datetime.datetime(2017, 11, 10, 1, 13, 32, 561137), 'id': 'b0026fc6-116f-4d1a-a9cb-6bb9b299f1ce'} +All States: 1 +[State] (b0026fc6-116f-4d1a-a9cb-6bb9b299f1ce) {'name': 'California', 'created_at': datetime.datetime(2017, 11, 10, 1, 13, 32, 561137), 'id': 'b0026fc6-116f-4d1a-a9cb-6bb9b299f1ce'} +Another State: [State] (37705d25-8903-4318-9303-6d6d336a22c1) {'name': 'Nevada', 'created_at': datetime.datetime(2017, 11, 10, 1, 13, 34, 619133), 'id': '37705d25-8903-4318-9303-6d6d336a22c1'} +All States: 2 +[State] (b0026fc6-116f-4d1a-a9cb-6bb9b299f1ce) {'name': 'California', 'created_at': datetime.datetime(2017, 11, 10, 1, 13, 32, 561137), 'id': 'b0026fc6-116f-4d1a-a9cb-6bb9b299f1ce'} +[State] (37705d25-8903-4318-9303-6d6d336a22c1) {'name': 'Nevada', 'created_at': datetime.datetime(2017, 11, 10, 1, 13, 34, 619133), 'id': '37705d25-8903-4318-9303-6d6d336a22c1'} +All States: 1 +[State] (37705d25-8903-4318-9303-6d6d336a22c1) {'name': 'Nevada', 'created_at': datetime.datetime(2017, 11, 10, 1, 13, 34, 619133), 'id': '37705d25-8903-4318-9303-6d6d336a22c1'} +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: models/engine/file_storage.py + +0/17 pts +6. DBStorage - States and Cities +mandatory +SQLAlchemy will be your best friend! + +It’s time to change your storage engine and use SQLAlchemy + + + +In the following steps, you will make multiple changes: + +the biggest one is the transition between FileStorage and DBStorage: In the industry, you will never find a system who can work with both in the same time - but you will find a lot of services who can manage multiple storage systems. (for example, logs service: in memory, in disk, in database, in ElasticSearch etc…) - The main concept behind is the abstraction: Make your code running without knowing how it’s stored. +add attributes for SQLAlchemy: they will be class attributes, like previously, with a “weird” value. Don’t worry, these values are for description and mapping to the database. If you change one of these values, or add/remove one attribute of the a model, you will have to delete the database and recreate it in SQL. (Yes it’s not optimal, but for development purposes, it’s ok. In production, we will add “migration mechanism” - for the moment, don’t spend time on it.) +Please follow all these steps: + +Update BaseModel: (models/base_model.py) + +Create Base = declarative_base() before the class definition of BaseModel +Note! BaseModel does /not/ inherit from Base. All other classes will inherit from BaseModel to get common values (id, created_at, updated_at), where inheriting from Base will actually cause SQLAlchemy to attempt to map it to a table. +Add or replace in the class BaseModel: +class attribute id +represents a column containing a unique string (60 characters) +can’t be null +primary key +class attribute created_at +represents a column containing a datetime +can’t be null +default value is the current datetime (use datetime.utcnow()) +class attribute updated_at +represents a column containing a datetime +can’t be null +default value is the current datetime (use datetime.utcnow()) +Move the models.storage.new(self) from def __init__(self, *args, **kwargs): to def save(self): and call it just before models.storage.save() +In def __init__(self, *args, **kwargs):, manage kwargs to create instance attribute from this dictionary. Ex: kwargs={ 'name': "California" } => self.name = "California" if it’s not already the case +Update the to_dict() method of the class BaseModel: +remove the key _sa_instance_state from the dictionary returned by this method only if this key exists +Add a new public instance method: def delete(self): to delete the current instance from the storage (models.storage) by calling the method delete +Update City: (models/city.py) + +City inherits from BaseModel and Base (respect the order) +Add or replace in the class City: +class attribute __tablename__ - +represents the table name, cities +class attribute name +represents a column containing a string (128 characters) +can’t be null +class attribute state_id +represents a column containing a string (60 characters) +can’t be null +is a foreign key to states.id +Update State: (models/state.py) + +State inherits from BaseModel and Base (respect the order) +Add or replace in the class State: +class attribute __tablename__ +represents the table name, states +class attribute name +represents a column containing a string (128 characters) +can’t be null +for DBStorage: class attribute cities must represent a relationship with the class City. If the State object is deleted, all linked City objects must be automatically deleted. Also, the reference from a City object to his State should be named state +for FileStorage: getter attribute cities that returns the list of City instances with state_id equals to the current State.id => It will be the FileStorage relationship between State and City +New engine DBStorage: (models/engine/db_storage.py) + +Private class attributes: +__engine: set to None +__session: set to None +Public instance methods: +__init__(self): +create the engine (self.__engine) +the engine must be linked to the MySQL database and user created before (hbnb_dev and hbnb_dev_db): +dialect: mysql +driver: mysqldb +all of the following values must be retrieved via environment variables: +MySQL user: HBNB_MYSQL_USER +MySQL password: HBNB_MYSQL_PWD +MySQL host: HBNB_MYSQL_HOST (here = localhost) +MySQL database: HBNB_MYSQL_DB +don’t forget the option pool_pre_ping=True when you call create_engine +drop all tables if the environment variable HBNB_ENV is equal to test +all(self, cls=None): +query on the current database session (self.__session) all objects depending of the class name (argument cls) +if cls=None, query all types of objects (User, State, City, Amenity, Place and Review) +this method must return a dictionary: (like FileStorage) +key = . +value = object +new(self, obj): add the object to the current database session (self.__session) +save(self): commit all changes of the current database session (self.__session) +delete(self, obj=None): delete from the current database session obj if not None +reload(self): +create all tables in the database (feature of SQLAlchemy) (WARNING: all classes who inherit from Base must be imported before calling Base.metadata.create_all(engine)) +create the current database session (self.__session) from the engine (self.__engine) by using a sessionmaker - the option expire_on_commit must be set to False ; and scoped_session - to make sure your Session is thread-safe +Update __init__.py: (models/__init__.py) + +Add a conditional depending of the value of the environment variable HBNB_TYPE_STORAGE: +If equal to db: +Import DBStorage class in this file +Create an instance of DBStorage and store it in the variable storage (the line storage.reload() should be executed after this instantiation) +Else: +Import FileStorage class in this file +Create an instance of FileStorage and store it in the variable storage (the line storage.reload() should be executed after this instantiation) +This “switch” will allow you to change storage type directly by using an environment variable (example below) +State creation: + +guillaume@ubuntu:~/AirBnB_v2$ echo 'create State name="California"' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) 95a5abab-aa65-4861-9bc6-1da4a36069aa (hbnb) -``` -

Alternative Syntax

- -###### Example 0: Show all User objects -Usage: .all() -``` -(hbnb) User.all() -["[User] (99f45908-1d17-46d1-9dd2-b7571128115b) {'updated_at': datetime.datetime(2020, 2, 19, 21, 47, 34, 92071), 'id': '99f45908-1d17-46d1-9dd2-b7571128115b', 'created_at': datetime.datetime(2020, 2, 19, 21, 47, 34, 92056)}", "[User] (98bea5de-9cb0-4d78-8a9d-c4de03521c30) {'updated_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134362), 'id': '98bea5de-9cb0-4d78-8a9d-c4de03521c30', 'created_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134343)}"] -``` - -###### Example 1: Destroy a User -Usage: .destroy(<_id>) -``` -(hbnb) User.destroy("99f45908-1d17-46d1-9dd2-b7571128115b") +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'all State' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) [[State] (95a5abab-aa65-4861-9bc6-1da4a36069aa) {'name': 'California', 'id': '95a5abab-aa65-4861-9bc6-1da4a36069aa', 'updated_at': datetime.datetime(2017, 11, 10, 0, 49, 54), 'created_at': datetime.datetime(2017, 11, 10, 0, 49, 54)}] +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM states\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: 95a5abab-aa65-4861-9bc6-1da4a36069aa +created_at: 2017-11-10 00:49:54 +updated_at: 2017-11-10 00:49:54 + name: California +guillaume@ubuntu:~/AirBnB_v2$ +City creation: + +guillaume@ubuntu:~/AirBnB_v2$ echo 'create City state_id="95a5abab-aa65-4861-9bc6-1da4a36069aa" name="San_Francisco"' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) 4b457e66-c7c8-4f63-910f-fd91c3b7140b (hbnb) -(hbnb) User.all() -(hbnb) ["[User] (98bea5de-9cb0-4d78-8a9d-c4de03521c30) {'updated_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134362), 'id': '98bea5de-9cb0-4d78-8a9d-c4de03521c30', 'created_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134343)}"] -``` -###### Example 2: Update User (by attribute) -Usage: .update(<_id>, , ) -``` -(hbnb) User.update("98bea5de-9cb0-4d78-8a9d-c4de03521c30", name "Todd the Toad") +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'all City' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) [[City] (4b457e66-c7c8-4f63-910f-fd91c3b7140b) {'id': '4b457e66-c7c8-4f63-910f-fd91c3b7140b', 'updated_at': datetime.datetime(2017, 11, 10, 0, 52, 53), 'state_id': '95a5abab-aa65-4861-9bc6-1da4a36069aa', 'name': 'San Francisco', 'created_at': datetime.datetime(2017, 11, 10, 0, 52, 53)] (hbnb) -(hbnb) User.all() -(hbnb) ["[User] (98bea5de-9cb0-4d78-8a9d-c4de03521c30) {'updated_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134362), 'id': '98bea5de-9cb0-4d78-8a9d-c4de03521c30', 'name': 'Todd the Toad', 'created_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134343)}"] -``` -###### Example 3: Update User (by dictionary) -Usage: .update(<_id>, ) -``` -(hbnb) User.update("98bea5de-9cb0-4d78-8a9d-c4de03521c30", {'name': 'Fred the Frog', 'age': 9}) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'create City state_id="95a5abab-aa65-4861-9bc6-1da4a36069aa" name="San_Jose"' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) a7db3cdc-30e0-4d80-ad8c-679fe45343ba (hbnb) -(hbnb) User.all() -(hbnb) ["[User] (98bea5de-9cb0-4d78-8a9d-c4de03521c30) {'updated_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134362), 'name': 'Fred the Frog', 'age': 9, 'id': '98bea5de-9cb0-4d78-8a9d-c4de03521c30', 'created_at': datetime.datetime(2020, 2, 19, 21, 47, 29, 134343)}"] -``` -
\ No newline at end of file +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM cities\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: 4b457e66-c7c8-4f63-910f-fd91c3b7140b +created_at: 2017-11-10 00:52:53 +updated_at: 2017-11-10 00:52:53 + name: San Francisco + state_id: 95a5abab-aa65-4861-9bc6-1da4a36069aa +*************************** 2. row *************************** + id: a7db3cdc-30e0-4d80-ad8c-679fe45343ba +created_at: 2017-11-10 00:53:19 +updated_at: 2017-11-10 00:53:19 + name: San Jose + state_id: 95a5abab-aa65-4861-9bc6-1da4a36069aa +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: models/base_model.py, models/city.py, models/state.py, models/engine/db_storage.py, models/__init__.py + +0/16 pts +7. DBStorage - User +mandatory +Update User: (models/user.py) + +User inherits from BaseModel and Base (respect the order) +Add or replace in the class User: +class attribute __tablename__ +represents the table name, users +class attribute email +represents a column containing a string (128 characters) +can’t be null +class attribute password +represents a column containing a string (128 characters) +can’t be null +class attribute first_name +represents a column containing a string (128 characters) +can be null +class attribute last_name +represents a column containing a string (128 characters) +can be null +guillaume@ubuntu:~/AirBnB_v2$ echo 'create User email="gui@hbtn.io" password="guipwd" first_name="Guillaume" last_name="Snow"' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) 4f3f4b42-a4c3-4c20-a492-efff10d00c0b +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'all User' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) [[User] (4f3f4b42-a4c3-4c20-a492-efff10d00c0b) {'updated_at': datetime.datetime(2017, 11, 10, 1, 17, 26), 'id': '4f3f4b42-a4c3-4c20-a492-efff10d00c0b', 'last_name': 'Snow', 'first_name': 'Guillaume', 'email': 'gui@hbtn.io', 'created_at': datetime.datetime(2017, 11, 10, 1, 17, 26), 'password': 'f4ce007d8e84e0910fbdd7a06fa1692d'}] +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM users\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: 4f3f4b42-a4c3-4c20-a492-efff10d00c0b +created_at: 2017-11-10 01:17:26 +updated_at: 2017-11-10 01:17:26 + email: gui@hbtn.io + password: guipwd +first_name: Guillaume + last_name: Snow +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: models/user.py + +0/9 pts +8. DBStorage - Place +mandatory +Update Place: (models/place.py) + +Place inherits from BaseModel and Base (respect the order) +Add or replace in the class Place: +class attribute __tablename__ +represents the table name, places +class attribute city_id +represents a column containing a string (60 characters) +can’t be null +is a foreign key to cities.id +class attribute user_id +represents a column containing a string (60 characters) +can’t be null +is a foreign key to users.id +class attribute name +represents a column containing a string (128 characters) +can’t be null +class attribute description +represents a column containing a string (1024 characters) +can be null +class attribute number_rooms +represents a column containing an integer +can’t be null +default value: 0 +class attribute number_bathrooms +represents a column containing an integer +can’t be null +default value: 0 +class attribute max_guest +represents a column containing an integer +can’t be null +default value: 0 +class attribute price_by_night +represents a column containing an integer +can’t be null +default value: 0 +class attribute latitude +represents a column containing a float +can be null +class attribute longitude +represents a column containing a float +can be null +Update User: (models/user.py) + +Add or replace in the class User: +class attribute places must represent a relationship with the class Place. If the User object is deleted, all linked Place objects must be automatically deleted. Also, the reference from a Place object to his User should be named user +Update City: (models/city.py) + +Add or replace in the class City: +class attribute places must represent a relationship with the class Place. If the City object is deleted, all linked Place objects must be automatically deleted. Also, the reference from a Place object to his City should be named cities +guillaume@ubuntu:~/AirBnB_v2$ echo 'create Place city_id="4b457e66-c7c8-4f63-910f-fd91c3b7140b" user_id="4f3f4b42-a4c3-4c20-a492-efff10d00c0b" name="Lovely_place" number_rooms=3 number_bathrooms=1 max_guest=6 price_by_night=120 latitude=37.773972 longitude=-122.431297' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) ed72aa02-3286-4891-acbc-9d9fc80a1103 +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'all Place' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) [[Place] (ed72aa02-3286-4891-acbc-9d9fc80a1103) {'latitude': 37.774, 'city_id': '4b457e66-c7c8-4f63-910f-fd91c3b7140b', 'price_by_night': 120, 'id': 'ed72aa02-3286-4891-acbc-9d9fc80a1103', 'user_id': '4f3f4b42-a4c3-4c20-a492-efff10d00c0b', 'max_guest': 6, 'created_at': datetime.datetime(2017, 11, 10, 1, 22, 30), 'description': None, 'number_rooms': 3, 'longitude': -122.431, 'number_bathrooms': 1, 'name': '"Lovely place', 'updated_at': datetime.datetime(2017, 11, 10, 1, 22, 30)}] +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM places\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: ed72aa02-3286-4891-acbc-9d9fc80a1103 + created_at: 2017-11-10 01:22:30 + updated_at: 2017-11-10 01:22:30 + city_id: 4b457e66-c7c8-4f63-910f-fd91c3b7140b + user_id: 4f3f4b42-a4c3-4c20-a492-efff10d00c0b + name: "Lovely place" + description: NULL + number_rooms: 3 +number_bathrooms: 1 + max_guest: 6 + price_by_night: 120 + latitude: 37.774 + longitude: -122.431 +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: models/place.py, models/user.py, models/city.py + +0/16 pts +9. DBStorage - Review +mandatory +Update Review: (models/review.py) + +Review inherits from BaseModel and Base (respect the order) +Add or replace in the class Review: +class attribute __tablename__ +represents the table name, reviews +class attribute text +represents a column containing a string (1024 characters) +can’t be null +class attribute place_id +represents a column containing a string (60 characters) +can’t be null +is a foreign key to places.id +class attribute user_id +represents a column containing a string (60 characters) +can’t be null +is a foreign key to users.id +Update User: (models/user.py) + +Add or replace in the class User: +class attribute reviews must represent a relationship with the class Review. If the User object is deleted, all linked Review objects must be automatically deleted. Also, the reference from a Review object to his User should be named user +Update Place: (models/place.py) + +for DBStorage: class attribute reviews must represent a relationship with the class Review. If the Place object is deleted, all linked Review objects must be automatically deleted. Also, the reference from a Review object to his Place should be named place +for FileStorage: getter attribute reviews that returns the list of Review instances with place_id equals to the current Place.id => It will be the FileStorage relationship between Place and Review +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'create User email="bob@hbtn.io" password="bobpwd" first_name="Bob" last_name="Dylan"' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) d93638d9-8233-4124-8f4e-17786592908b +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'create Review place_id="ed72aa02-3286-4891-acbc-9d9fc80a1103" user_id="d93638d9-8233-4124-8f4e-17786592908b" text="Amazing_place,_huge_kitchen"' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) a2d163d3-1982-48ab-a06b-9dc71e68a791 +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'all Review' | HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./console.py +(hbnb) [[Review] (f2616ff2-f723-4d67-85dc-f050a38e0f2f) {'text': 'Amazing place, huge kitchen', 'place_id': 'ed72aa02-3286-4891-acbc-9d9fc80a1103', 'id': 'f2616ff2-f723-4d67-85dc-f050a38e0f2f', 'updated_at': datetime.datetime(2017, 11, 10, 4, 6, 25), 'created_at': datetime.datetime(2017, 11, 10, 4, 6, 25), 'user_id': 'd93638d9-8233-4124-8f4e-17786592908b'}] +(hbnb) +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM reviews\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: f2616ff2-f723-4d67-85dc-f050a38e0f2f +created_at: 2017-11-10 04:06:25 +updated_at: 2017-11-10 04:06:25 + text: Amazing place, huge kitchen + place_id: ed72aa02-3286-4891-acbc-9d9fc80a1103 + user_id: d93638d9-8233-4124-8f4e-17786592908b +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: models/review.py, models/user.py, models/place.py + +0/10 pts +10. DBStorage - Amenity... and BOOM! +mandatory +Update Amenity: (models/amenity.py) + +Amenity inherits from BaseModel and Base (respect the order) +Add or replace in the class Amenity: +class attribute __tablename__ +represents the table name, amenities +class attribute name +represents a column containing a string (128 characters) +can’t be null +class attribute place_amenities must represent a relationship Many-To-Many between the class Place and Amenity. Please see below more detail: place_amenity in the Place update +Update Place: (models/place.py) + +Add an instance of SQLAlchemy Table called place_amenity for creating the relationship Many-To-Many between Place and Amenity: +table name place_amenity +metadata = Base.metadata +2 columns: +place_id, a string of 60 characters foreign key of places.id, primary key in the table and never null +amenity_id, a string of 60 characters foreign key of amenities.id, primary key in the table and never null +Update Place class: +for DBStorage: class attribute amenities must represent a relationship with the class Amenity but also as secondary to place_amenity with option viewonly=False (place_amenity has been define previously) +for FileStorage: +Getter attribute amenities that returns the list of Amenity instances based on the attribute amenity_ids that contains all Amenity.id linked to the Place +Setter attribute amenities that handles append method for adding an Amenity.id to the attribute amenity_ids. This method should accept only Amenity object, otherwise, do nothing. +What’s a Many-to-Many relationship? +In our system, we don’t want to duplicate amenities (for example, having 10000 time the amenity Wifi), so they will be unique. But, at least 2 places can have the same amenity (like Wifi for example). We are in the case of: + +an amenity can be linked to multiple places +a place can have multiple amenities += Many-To-Many + +To make this link working, we will create a third table called place_amenity that will create these links. + +And you are good, you have a new engine! + +guillaume@ubuntu:~/AirBnB_v2$ cat main_place_amenities.py +#!/usr/bin/python3 +""" Test link Many-To-Many Place <> Amenity +""" +from models import * + +# creation of a State +state = State(name="California") +state.save() + +# creation of a City +city = City(state_id=state.id, name="San Francisco") +city.save() + +# creation of a User +user = User(email="john@snow.com", password="johnpwd") +user.save() + +# creation of 2 Places +place_1 = Place(user_id=user.id, city_id=city.id, name="House 1") +place_1.save() +place_2 = Place(user_id=user.id, city_id=city.id, name="House 2") +place_2.save() + +# creation of 3 various Amenity +amenity_1 = Amenity(name="Wifi") +amenity_1.save() +amenity_2 = Amenity(name="Cable") +amenity_2.save() +amenity_3 = Amenity(name="Oven") +amenity_3.save() + +# link place_1 with 2 amenities +place_1.amenities.append(amenity_1) +place_1.amenities.append(amenity_2) + +# link place_2 with 3 amenities +place_2.amenities.append(amenity_1) +place_2.amenities.append(amenity_2) +place_2.amenities.append(amenity_3) + +storage.save() + +print("OK") + +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ HBNB_MYSQL_USER=hbnb_dev HBNB_MYSQL_PWD=hbnb_dev_pwd HBNB_MYSQL_HOST=localhost HBNB_MYSQL_DB=hbnb_dev_db HBNB_TYPE_STORAGE=db ./main_place_amenities.py +OK +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM amenities\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: 47321eb8-152a-46df-969a-440aa67a6d59 +created_at: 2017-11-10 04:22:02 +updated_at: 2017-11-10 04:22:02 + name: Cable +*************************** 2. row *************************** + id: 4a307e7f-68f9-438f-81c0-8325898dda2a +created_at: 2017-11-10 04:22:02 +updated_at: 2017-11-10 04:22:02 + name: Oven +*************************** 3. row *************************** + id: b80aec52-d0c9-420a-8471-3254572954b6 +created_at: 2017-11-10 04:22:02 +updated_at: 2017-11-10 04:22:02 + name: Wifi +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM places\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + id: 497e3867-d6e9-4401-9c7c-9687c18d2ac7 + created_at: 2017-11-10 04:22:02 + updated_at: 2017-11-10 04:22:02 + city_id: 9d60df6e-31f7-430c-8162-69e89f4a17aa + user_id: 9b37bd51-6aef-485f-bf10-c7ab83fea2e9 + name: House 1 + description: NULL + number_rooms: 0 +number_bathrooms: 0 + max_guest: 0 + price_by_night: 0 + latitude: NULL + longitude: NULL +*************************** 2. row *************************** + id: db549ae1-4500-4d0c-9b50-4b4978ed229e + created_at: 2017-11-10 04:22:02 + updated_at: 2017-11-10 04:22:02 + city_id: 9d60df6e-31f7-430c-8162-69e89f4a17aa + user_id: 9b37bd51-6aef-485f-bf10-c7ab83fea2e9 + name: House 2 + description: NULL + number_rooms: 0 +number_bathrooms: 0 + max_guest: 0 + price_by_night: 0 + latitude: NULL + longitude: NULL +guillaume@ubuntu:~/AirBnB_v2$ +guillaume@ubuntu:~/AirBnB_v2$ echo 'SELECT * FROM place_amenity\G' | mysql -uhbnb_dev -p hbnb_dev_db +Enter password: +*************************** 1. row *************************** + place_id: 497e3867-d6e9-4401-9c7c-9687c18d2ac7 +amenity_id: 47321eb8-152a-46df-969a-440aa67a6d59 +*************************** 2. row *************************** + place_id: db549ae1-4500-4d0c-9b50-4b4978ed229e +amenity_id: 47321eb8-152a-46df-969a-440aa67a6d59 +*************************** 3. row *************************** + place_id: db549ae1-4500-4d0c-9b50-4b4978ed229e +amenity_id: 4a307e7f-68f9-438f-81c0-8325898dda2a +*************************** 4. row *************************** + place_id: 497e3867-d6e9-4401-9c7c-9687c18d2ac7 +amenity_id: b80aec52-d0c9-420a-8471-3254572954b6 +*************************** 5. row *************************** + place_id: db549ae1-4500-4d0c-9b50-4b4978ed229e +amenity_id: b80aec52-d0c9-420a-8471-3254572954b6 +guillaume@ubuntu:~/AirBnB_v2$ +Repo: + +GitHub repository: alu-AirBnB_clone_v2 +File: models/amenity.py, models/place.py + +0/11 pts +Score +Project badge +0% +Your score will be updated once you launch the project review. + +Please review all the tasks before you start the peer review. + +Previous project + diff --git a/console.py b/console.py index 13a8af68e930..6c4b5606f057 100755 --- a/console.py +++ b/console.py @@ -1,324 +1,272 @@ #!/usr/bin/python3 -""" Console Module """ +"""Defines the HBNB console.""" import cmd -import sys +from shlex import split +from models import storage +from datetime import datetime from models.base_model import BaseModel -from models.__init__ import storage from models.user import User -from models.place import Place from models.state import State from models.city import City from models.amenity import Amenity +from models.place import Place from models.review import Review class HBNBCommand(cmd.Cmd): - """ Contains the functionality for the HBNB console""" - - # determines prompt for interactive/non-interactive modes - prompt = '(hbnb) ' if sys.__stdin__.isatty() else '' + """Defines the HolbertonBnB command interpreter.""" + + prompt = "(hbnb) " + __classes = { + "BaseModel", + "User", + "State", + "City", + "Amenity", + "Place", + "Review" + } - classes = { - 'BaseModel': BaseModel, 'User': User, 'Place': Place, - 'State': State, 'City': City, 'Amenity': Amenity, - 'Review': Review - } - dot_cmds = ['all', 'count', 'show', 'destroy', 'update'] - types = { - 'number_rooms': int, 'number_bathrooms': int, - 'max_guest': int, 'price_by_night': int, - 'latitude': float, 'longitude': float - } + def emptyline(self): + """Ignore empty spaces.""" + pass - def preloop(self): - """Prints if isatty is false""" - if not sys.__stdin__.isatty(): - print('(hbnb)') + def do_quit(self, line): + """Quit command to exit the program.""" + return True - def precmd(self, line): - """Reformat command line for advanced command syntax. + def do_EOF(self, line): + """EOF signal to exit the program.""" + print("") + return True - Usage: .([ [<*args> or <**kwargs>]]) - (Brackets denote optional fields in usage example.) + def do_create(self, line): + """Usage: create = = ... + Create a new class instance with given keys/values and print its id. """ - _cmd = _cls = _id = _args = '' # initialize line elements - - # scan for general formating - i.e '.', '(', ')' - if not ('.' in line and '(' in line and ')' in line): - return line - - try: # parse line left to right - pline = line[:] # parsed line - - # isolate - _cls = pline[:pline.find('.')] - - # isolate and validate - _cmd = pline[pline.find('.') + 1:pline.find('(')] - if _cmd not in HBNBCommand.dot_cmds: - raise Exception - - # if parantheses contain arguments, parse them - pline = pline[pline.find('(') + 1:pline.find(')')] - if pline: - # partition args: (, [], [<*args>]) - pline = pline.partition(', ') # pline convert to tuple - - # isolate _id, stripping quotes - _id = pline[0].replace('\"', '') - # possible bug here: - # empty quotes register as empty _id when replaced - - # if arguments exist beyond _id - pline = pline[2].strip() # pline is now str - if pline: - # check for *args or **kwargs - if pline[0] is '{' and pline[-1] is'}'\ - and type(eval(pline)) is dict: - _args = pline - else: - _args = pline.replace(',', '') - # _args = _args.replace('\"', '') - line = ' '.join([_cmd, _cls, _id, _args]) - - except Exception as mess: - pass - finally: - return line - - def postcmd(self, stop, line): - """Prints if isatty is false""" - if not sys.__stdin__.isatty(): - print('(hbnb) ', end='') - return stop - - def do_quit(self, command): - """ Method to exit the HBNB console""" - exit() - - def help_quit(self): - """ Prints the help documentation for quit """ - print("Exits the program with formatting\n") - - def do_EOF(self, arg): - """ Handles EOF to exit program """ - print() - exit() - - def help_EOF(self): - """ Prints the help documentation for EOF """ - print("Exits the program without formatting\n") - - def emptyline(self): - """ Overrides the emptyline method of CMD """ - pass - - def do_create(self, args): - """ Create an object of any class""" - if not args: + try: + if not line: + raise SyntaxError() + my_list = line.split(" ") + + kwargs = {} + for i in range(1, len(my_list)): + key, value = tuple(my_list[i].split("=")) + if value[0] == '"': + value = value.strip('"').replace("_", " ") + else: + try: + value = eval(value) + except (SyntaxError, NameError): + continue + kwargs[key] = value + + if kwargs == {}: + obj = eval(my_list[0])() + else: + obj = eval(my_list[0])(**kwargs) + storage.new(obj) + print(obj.id) + obj.save() + + except SyntaxError: print("** class name missing **") - return - elif args not in HBNBCommand.classes: + except NameError: print("** class doesn't exist **") - return - new_instance = HBNBCommand.classes[args]() - storage.save() - print(new_instance.id) - storage.save() - - def help_create(self): - """ Help information for the create method """ - print("Creates a class of any type") - print("[Usage]: create \n") - def do_show(self, args): - """ Method to show an individual object """ - new = args.partition(" ") - c_name = new[0] - c_id = new[2] - - # guard against trailing args - if c_id and ' ' in c_id: - c_id = c_id.partition(' ')[0] - - if not c_name: + def do_show(self, line): + """Prints the string representation of an instance + Exceptions: + SyntaxError: when there is no args given + NameError: when there is no object taht has the name + IndexError: when there is no id given + KeyError: when there is no valid id given + """ + try: + if not line: + raise SyntaxError() + my_list = line.split(" ") + if my_list[0] not in self.__classes: + raise NameError() + if len(my_list) < 2: + raise IndexError() + objects = storage.all() + key = my_list[0] + '.' + my_list[1] + if key in objects: + print(objects[key]) + else: + raise KeyError() + except SyntaxError: print("** class name missing **") - return - - if c_name not in HBNBCommand.classes: + except NameError: print("** class doesn't exist **") - return - - if not c_id: + except IndexError: print("** instance id missing **") - return - - key = c_name + "." + c_id - try: - print(storage._FileStorage__objects[key]) except KeyError: print("** no instance found **") - def help_show(self): - """ Help information for the show command """ - print("Shows an individual instance of a class") - print("[Usage]: show \n") - - def do_destroy(self, args): - """ Destroys a specified object """ - new = args.partition(" ") - c_name = new[0] - c_id = new[2] - if c_id and ' ' in c_id: - c_id = c_id.partition(' ')[0] - - if not c_name: + def do_destroy(self, line): + """Deletes an instance based on the class name and id + Exceptions: + SyntaxError: when there is no args given + NameError: when there is no object taht has the name + IndexError: when there is no id given + KeyError: when there is no valid id given + """ + try: + if not line: + raise SyntaxError() + my_list = line.split(" ") + if my_list[0] not in self.__classes: + raise NameError() + if len(my_list) < 2: + raise IndexError() + objects = storage.all() + key = my_list[0] + '.' + my_list[1] + if key in objects: + del objects[key] + storage.save() + else: + raise KeyError() + except SyntaxError: print("** class name missing **") - return - - if c_name not in HBNBCommand.classes: + except NameError: print("** class doesn't exist **") - return - - if not c_id: + except IndexError: print("** instance id missing **") - return - - key = c_name + "." + c_id - - try: - del(storage.all()[key]) - storage.save() except KeyError: print("** no instance found **") - def help_destroy(self): - """ Help information for the destroy command """ - print("Destroys an individual instance of a class") - print("[Usage]: destroy \n") - - def do_all(self, args): - """ Shows all objects, or all objects of a class""" - print_list = [] - - if args: - args = args.split(' ')[0] # remove possible trailing args - if args not in HBNBCommand.classes: - print("** class doesn't exist **") - return - for k, v in storage._FileStorage__objects.items(): - if k.split('.')[0] == args: - print_list.append(str(v)) - else: - for k, v in storage._FileStorage__objects.items(): - print_list.append(str(v)) - - print(print_list) - - def help_all(self): - """ Help information for the all command """ - print("Shows all objects, or all of a class") - print("[Usage]: all \n") - - def do_count(self, args): - """Count current number of class instances""" - count = 0 - for k, v in storage._FileStorage__objects.items(): - if args == k.split('.')[0]: - count += 1 - print(count) + def do_all(self, line): + """Usage: all or all or .all() + Display string representations of all instances of a given class. + If no class is specified, displays all instantiated objects.""" + if not line: + o = storage.all() + print([o[k].__str__() for k in o]) + return + try: + args = line.split(" ") + if args[0] not in self.__classes: + raise NameError() - def help_count(self): - """ """ - print("Usage: count ") + o = storage.all(eval(args[0])) + print([o[k].__str__() for k in o]) - def do_update(self, args): - """ Updates a certain object with new info """ - c_name = c_id = att_name = att_val = kwargs = '' + except NameError: + print("** class doesn't exist **") - # isolate cls from id/args, ex: (, delim, ) - args = args.partition(" ") - if args[0]: - c_name = args[0] - else: # class name not present + def do_update(self, line): + """Updates an instanceby adding or updating attribute + Exceptions: + SyntaxError: when there is no args given + NameError: when there is no object taht has the name + IndexError: when there is no id given + KeyError: when there is no valid id given + AttributeError: when there is no attribute given + ValueError: when there is no value given + """ + try: + if not line: + raise SyntaxError() + my_list = split(line, " ") + if my_list[0] not in self.__classes: + raise NameError() + if len(my_list) < 2: + raise IndexError() + objects = storage.all() + key = my_list[0] + '.' + my_list[1] + if key not in objects: + raise KeyError() + if len(my_list) < 3: + raise AttributeError() + if len(my_list) < 4: + raise ValueError() + v = objects[key] + try: + v.__dict__[my_list[2]] = eval(my_list[3]) + except Exception: + v.__dict__[my_list[2]] = my_list[3] + v.save() + except SyntaxError: print("** class name missing **") - return - if c_name not in HBNBCommand.classes: # class name invalid + except NameError: print("** class doesn't exist **") - return - - # isolate id from args - args = args[2].partition(" ") - if args[0]: - c_id = args[0] - else: # id not present + except IndexError: print("** instance id missing **") - return - - # generate key from class and id - key = c_name + "." + c_id - - # determine if key is present - if key not in storage.all(): + except KeyError: print("** no instance found **") - return - - # first determine if kwargs or args - if '{' in args[2] and '}' in args[2] and type(eval(args[2])) is dict: - kwargs = eval(args[2]) - args = [] # reformat kwargs into list, ex: [, , ...] - for k, v in kwargs.items(): - args.append(k) - args.append(v) - else: # isolate args - args = args[2] - if args and args[0] is '\"': # check for quoted arg - second_quote = args.find('\"', 1) - att_name = args[1:second_quote] - args = args[second_quote + 1:] - - args = args.partition(' ') - - # if att_name was not quoted arg - if not att_name and args[0] is not ' ': - att_name = args[0] - # check for quoted val arg - if args[2] and args[2][0] is '\"': - att_val = args[2][1:args[2].find('\"', 1)] - - # if att_val was not quoted arg - if not att_val and args[2]: - att_val = args[2].partition(' ')[0] + except AttributeError: + print("** attribute name missing **") + except ValueError: + print("** value missing **") - args = [att_name, att_val] - - # retrieve dictionary of current objects - new_dict = storage.all()[key] - - # iterate through attr names and values - for i, att_name in enumerate(args): - # block only runs on even iterations - if (i % 2 == 0): - att_val = args[i + 1] # following item is value - if not att_name: # check for att_name - print("** attribute name missing **") - return - if not att_val: # check for att_value - print("** value missing **") - return - # type cast as necessary - if att_name in HBNBCommand.types: - att_val = HBNBCommand.types[att_name](att_val) - - # update dictionary with name, value pair - new_dict.__dict__.update({att_name: att_val}) + def count(self, line): + """count the number of instances of a class + """ + counter = 0 + try: + my_list = split(line, " ") + if my_list[0] not in self.__classes: + raise NameError() + objects = storage.all() + for key in objects: + name = key.split('.') + if name[0] == my_list[0]: + counter += 1 + print(counter) + except NameError: + print("** class doesn't exist **") - new_dict.save() # save updates to file + def strip_clean(self, args): + """strips the argument and return a string of command + Args: + args: input list of args + Return: + returns string of argumetns + """ + new_list = [] + new_list.append(args[0]) + try: + my_dict = eval( + args[1][args[1].find('{'):args[1].find('}')+1]) + except Exception: + my_dict = None + if isinstance(my_dict, dict): + new_str = args[1][args[1].find('(')+1:args[1].find(')')] + new_list.append(((new_str.split(", "))[0]).strip('"')) + new_list.append(my_dict) + return new_list + new_str = args[1][args[1].find('(')+1:args[1].find(')')] + new_list.append(" ".join(new_str.split(", "))) + return " ".join(i for i in new_list) + + def default(self, line): + """retrieve all instances of a class and + retrieve the number of instances + """ + my_list = line.split('.') + if len(my_list) >= 2: + if my_list[1] == "all()": + self.do_all(my_list[0]) + elif my_list[1] == "count()": + self.count(my_list[0]) + elif my_list[1][:4] == "show": + self.do_show(self.strip_clean(my_list)) + elif my_list[1][:7] == "destroy": + self.do_destroy(self.strip_clean(my_list)) + elif my_list[1][:6] == "update": + args = self.strip_clean(my_list) + if isinstance(args, list): + obj = storage.all() + key = args[0] + ' ' + args[1] + for k, v in args[2].items(): + self.do_update(key + ' "{}" "{}"'.format(k, v)) + else: + self.do_update(args) + else: + cmd.Cmd.default(self, line) - def help_update(self): - """ Help information for the update class """ - print("Updates an object with new information") - print("Usage: update \n") -if __name__ == "__main__": +if __name__ == '__main__': HBNBCommand().cmdloop() diff --git a/models/__init__.py b/models/__init__.py old mode 100644 new mode 100755 index d3765c2bc603..e0912aa884e9 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,7 +1,19 @@ #!/usr/bin/python3 -"""This module instantiates an object of class FileStorage""" +"""create a unique FileStorage instance for your application""" from models.engine.file_storage import FileStorage +from models.engine.db_storage import DBStorage +from models.base_model import BaseModel +from models.user import User +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from models.review import Review +from os import getenv -storage = FileStorage() +if getenv("HBNB_TYPE_STORAGE") == "db": + storage = DBStorage() +else: + storage = FileStorage() storage.reload() diff --git a/models/amenity.py b/models/amenity.py old mode 100644 new mode 100755 index a181095e4170..dcf66a55a39e --- a/models/amenity.py +++ b/models/amenity.py @@ -1,7 +1,16 @@ #!/usr/bin/python3 -""" State Module for HBNB project """ -from models.base_model import BaseModel +"""This is the amenity class""" +from models.base_model import BaseModel, Base +from sqlalchemy.orm import relationship +from sqlalchemy import Column, String +from models.place import place_amenity -class Amenity(BaseModel): - name = "" +class Amenity(BaseModel, Base): + """This is the class for Amenity + Attributes: + name: input name + """ + __tablename__ = "amenities" + name = Column(String(128), nullable=False) + place_amenities = relationship("Place", secondary=place_amenity) diff --git a/models/base_model.py b/models/base_model.py old mode 100644 new mode 100755 index 4856e9de421f..937901ff28e9 --- a/models/base_model.py +++ b/models/base_model.py @@ -1,44 +1,83 @@ #!/usr/bin/python3 -"""This module defines a base class for all models in our hbnb clone""" +"""This is the base model class for AirBnB""" +from sqlalchemy.ext.declarative import declarative_base import uuid +import models from datetime import datetime +from sqlalchemy import Column, Integer, String, DateTime + + +Base = declarative_base() class BaseModel: - """A base class for all hbnb models""" + """This class will defines all common attributes/methods + for other classes + """ + id = Column(String(60), unique=True, nullable=False, primary_key=True) + created_at = Column(DateTime, nullable=False, default=(datetime.utcnow())) + updated_at = Column(DateTime, nullable=False, default=(datetime.utcnow())) + def __init__(self, *args, **kwargs): - """Instatntiates a new model""" - if not kwargs: - from models import storage - self.id = str(uuid.uuid4()) - self.created_at = datetime.now() - self.updated_at = datetime.now() - storage.new(self) + """Instantiation of base model class + Args: + args: it won't be used + kwargs: arguments for the constructor of the BaseModel + Attributes: + id: unique id generated + created_at: creation date + updated_at: updated date + """ + if kwargs: + for key, value in kwargs.items(): + if key == "created_at" or key == "updated_at": + value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") + if key != "__class__": + setattr(self, key, value) + if "id" not in kwargs: + self.id = str(uuid.uuid4()) + if "created_at" not in kwargs: + self.created_at = datetime.now() + if "updated_at" not in kwargs: + self.updated_at = datetime.now() else: - kwargs['updated_at'] = datetime.strptime(kwargs['updated_at'], - '%Y-%m-%dT%H:%M:%S.%f') - kwargs['created_at'] = datetime.strptime(kwargs['created_at'], - '%Y-%m-%dT%H:%M:%S.%f') - del kwargs['__class__'] - self.__dict__.update(kwargs) + self.id = str(uuid.uuid4()) + self.created_at = self.updated_at = datetime.now() def __str__(self): - """Returns a string representation of the instance""" - cls = (str(type(self)).split('.')[-1]).split('\'')[0] - return '[{}] ({}) {}'.format(cls, self.id, self.__dict__) + """returns a string + Return: + returns a string of class name, id, and dictionary + """ + return "[{}] ({}) {}".format( + type(self).__name__, self.id, self.__dict__) + + def __repr__(self): + """return a string representaion + """ + return self.__str__() def save(self): - """Updates updated_at with current time when instance is changed""" - from models import storage + """updates the public instance attribute updated_at to current + """ self.updated_at = datetime.now() - storage.save() + models.storage.new(self) + models.storage.save() def to_dict(self): - """Convert instance into dict format""" - dictionary = {} - dictionary.update(self.__dict__) - dictionary.update({'__class__': - (str(type(self)).split('.')[-1]).split('\'')[0]}) - dictionary['created_at'] = self.created_at.isoformat() - dictionary['updated_at'] = self.updated_at.isoformat() - return dictionary + """creates dictionary of the class and returns + Return: + returns a dictionary of all the key values in __dict__ + """ + my_dict = dict(self.__dict__) + my_dict["__class__"] = str(type(self).__name__) + my_dict["created_at"] = self.created_at.isoformat() + my_dict["updated_at"] = self.updated_at.isoformat() + if '_sa_instance_state' in my_dict.keys(): + del my_dict['_sa_instance_state'] + return my_dict + + def delete(self): + """ delete object + """ + models.storage.delete(self) diff --git a/models/city.py b/models/city.py old mode 100644 new mode 100755 index b9b4fe221502..041d5bf94e6b --- a/models/city.py +++ b/models/city.py @@ -1,9 +1,21 @@ #!/usr/bin/python3 -""" City Module for HBNB project """ -from models.base_model import BaseModel +"""This is the city class""" +from sqlalchemy.ext.declarative import declarative_base +from models.base_model import BaseModel, Base +from sqlalchemy import Column, Integer, String +from sqlalchemy import ForeignKey +from sqlalchemy.orm import relationship +from models.place import Place -class City(BaseModel): - """ The city class, contains state ID and name """ - state_id = "" - name = "" +class City(BaseModel, Base): + """This is the class for City + Attributes: + state_id: The state id + name: input name + """ + __tablename__ = "cities" + name = Column(String(128), nullable=False) + state_id = Column(String(60), ForeignKey('states.id'), nullable=False) + places = relationship("Place", cascade='all, delete, delete-orphan', + backref="cities") diff --git a/models/engine/__init__.py b/models/engine/__init__.py old mode 100644 new mode 100755 diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py new file mode 100755 index 000000000000..43e97b056d50 --- /dev/null +++ b/models/engine/db_storage.py @@ -0,0 +1,84 @@ +#!/usr/bin/python3 +""" new class for sqlAlchemy """ +from os import getenv +from sqlalchemy.orm import sessionmaker, scoped_session +from sqlalchemy import (create_engine) +from sqlalchemy.ext.declarative import declarative_base +from models.base_model import Base +from models.state import State +from models.city import City +from models.user import User +from models.place import Place +from models.review import Review +from models.amenity import Amenity + + +class DBStorage: + """ create tables in environmental""" + __engine = None + __session = None + + def __init__(self): + user = getenv("HBNB_MYSQL_USER") + passwd = getenv("HBNB_MYSQL_PWD") + db = getenv("HBNB_MYSQL_DB") + host = getenv("HBNB_MYSQL_HOST") + env = getenv("HBNB_ENV") + + self.__engine = create_engine('mysql+mysqldb://{}:{}@{}/{}' + .format(user, passwd, host, db), + pool_pre_ping=True) + + if env == "test": + Base.metadata.drop_all(self.__engine) + + def all(self, cls=None): + """returns a dictionary + Return: + returns a dictionary of __object + """ + dic = {} + if cls: + if type(cls) is str: + cls = eval(cls) + query = self.__session.query(cls) + for elem in query: + key = "{}.{}".format(type(elem).__name__, elem.id) + dic[key] = elem + else: + lista = [State, City, User, Place, Review, Amenity] + for clase in lista: + query = self.__session.query(clase) + for elem in query: + key = "{}.{}".format(type(elem).__name__, elem.id) + dic[key] = elem + return (dic) + + def new(self, obj): + """add a new element in the table + """ + self.__session.add(obj) + + def save(self): + """save changes + """ + self.__session.commit() + + def delete(self, obj=None): + """delete an element in the table + """ + if obj: + self.session.delete(obj) + + def reload(self): + """configuration + """ + Base.metadata.create_all(self.__engine) + sec = sessionmaker(bind=self.__engine, expire_on_commit=False) + Session = scoped_session(sec) + self.__session = Session() + + def close(self): + """ calls remove() + """ + self.__session.close() diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py old mode 100644 new mode 100755 index 6f5d7f8d4680..60196ed0bb36 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,50 +1,80 @@ #!/usr/bin/python3 -"""This module defines a class to manage file storage for hbnb clone""" +"""This is the file storage class for AirBnB""" import json +from models.base_model import BaseModel +from models.user import User +from models.state import State +from models.city import City +from models.amenity import Amenity +from models.place import Place +from models.review import Review +import shlex class FileStorage: - """This class manages storage of hbnb models in JSON format""" - __file_path = 'file.json' + """This class serializes instances to a JSON file and + deserializes JSON file to instances + Attributes: + __file_path: path to the JSON file + __objects: objects will be stored + """ + __file_path = "file.json" __objects = {} - def all(self): - """Returns a dictionary of models currently in storage""" - return FileStorage.__objects + def all(self, cls=None): + """returns a dictionary + Return: + returns a dictionary of __object + """ + dic = {} + if cls: + dictionary = self.__objects + for key in dictionary: + partition = key.replace('.', ' ') + partition = shlex.split(partition) + if (partition[0] == cls.__name__): + dic[key] = self.__objects[key] + return (dic) + else: + return self.__objects def new(self, obj): - """Adds new object to storage dictionary""" - self.all().update({obj.to_dict()['__class__'] + '.' + obj.id: obj}) + """sets __object to given obj + Args: + obj: given object + """ + if obj: + key = "{}.{}".format(type(obj).__name__, obj.id) + self.__objects[key] = obj def save(self): - """Saves storage dictionary to file""" - with open(FileStorage.__file_path, 'w') as f: - temp = {} - temp.update(FileStorage.__objects) - for key, val in temp.items(): - temp[key] = val.to_dict() - json.dump(temp, f) + """serialize the file path to JSON file path + """ + my_dict = {} + for key, value in self.__objects.items(): + my_dict[key] = value.to_dict() + with open(self.__file_path, 'w', encoding="UTF-8") as f: + json.dump(my_dict, f) def reload(self): - """Loads storage dictionary from file""" - from models.base_model import BaseModel - from models.user import User - from models.place import Place - from models.state import State - from models.city import City - from models.amenity import Amenity - from models.review import Review - - classes = { - 'BaseModel': BaseModel, 'User': User, 'Place': Place, - 'State': State, 'City': City, 'Amenity': Amenity, - 'Review': Review - } + """serialize the file path to JSON file path + """ try: - temp = {} - with open(FileStorage.__file_path, 'r') as f: - temp = json.load(f) - for key, val in temp.items(): - self.all()[key] = classes[val['__class__']](**val) + with open(self.__file_path, 'r', encoding="UTF-8") as f: + for key, value in (json.load(f)).items(): + value = eval(value["__class__"])(**value) + self.__objects[key] = value except FileNotFoundError: pass + + def delete(self, obj=None): + """ delete an existing element + """ + if obj: + key = "{}.{}".format(type(obj).__name__, obj.id) + del self.__objects[key] + + def close(self): + """ calls reload() + """ + self.reload() diff --git a/models/place.py b/models/place.py old mode 100644 new mode 100755 index 5221e8210d17..1b795adda7c5 --- a/models/place.py +++ b/models/place.py @@ -1,18 +1,83 @@ #!/usr/bin/python3 -""" Place Module for HBNB project """ -from models.base_model import BaseModel - - -class Place(BaseModel): - """ A place to stay """ - city_id = "" - user_id = "" - name = "" - description = "" - number_rooms = 0 - number_bathrooms = 0 - max_guest = 0 - price_by_night = 0 - latitude = 0.0 - longitude = 0.0 +"""This is the place class""" +from sqlalchemy.ext.declarative import declarative_base +from models.base_model import BaseModel, Base +from sqlalchemy import Column, Table, String, Integer, Float, ForeignKey +from sqlalchemy.orm import relationship +from os import getenv +import models + + +place_amenity = Table("place_amenity", Base.metadata, + Column("place_id", String(60), + ForeignKey("places.id"), + primary_key=True, + nullable=False), + Column("amenity_id", String(60), + ForeignKey("amenities.id"), + primary_key=True, + nullable=False)) + + +class Place(BaseModel, Base): + """This is the class for Place + Attributes: + city_id: city id + user_id: user id + name: name input + description: string of description + number_rooms: number of room in int + number_bathrooms: number of bathrooms in int + max_guest: maximum guest in int + price_by_night:: pice for a staying in int + latitude: latitude in flaot + longitude: longitude in float + amenity_ids: list of Amenity ids + """ + __tablename__ = "places" + 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) + description = Column(String(1024)) + number_rooms = Column(Integer, nullable=False, default=0) + number_bathrooms = Column(Integer, nullable=False, default=0) + max_guest = Column(Integer, nullable=False, default=0) + price_by_night = Column(Integer, nullable=False, default=0) + latitude = Column(Float) + longitude = Column(Float) amenity_ids = [] + + if getenv("HBNB_TYPE_STORAGE") == "db": + reviews = relationship("Review", cascade='all, delete, delete-orphan', + backref="place") + + amenities = relationship("Amenity", secondary=place_amenity, + viewonly=False, + back_populates="place_amenities") + else: + @property + def reviews(self): + """ Returns list of reviews.id """ + var = models.storage.all() + lista = [] + result = [] + for key in var: + review = key.replace('.', ' ') + review = shlex.split(review) + if (review[0] == 'Review'): + lista.append(var[key]) + for elem in lista: + if (elem.place_id == self.id): + result.append(elem) + return (result) + + @property + def amenities(self): + """ Returns list of amenity ids """ + return self.amenity_ids + + @amenities.setter + def amenities(self, obj=None): + """ Appends amenity ids to the attribute """ + if type(obj) is Amenity and obj.id not in self.amenity_ids: + self.amenity_ids.append(obj.id) diff --git a/models/review.py b/models/review.py old mode 100644 new mode 100755 index c487d90d34f0..3fcebb30bc26 --- a/models/review.py +++ b/models/review.py @@ -1,10 +1,18 @@ #!/usr/bin/python3 -""" Review module for the HBNB project """ -from models.base_model import BaseModel +"""This is the review class""" +from sqlalchemy.ext.declarative import declarative_base +from models.base_model import BaseModel, Base +from sqlalchemy import Column, Integer, String, ForeignKey, Float -class Review(BaseModel): - """ Review classto store review information """ - place_id = "" - user_id = "" - text = "" +class Review(BaseModel, Base): + """This is the class for Review + Attributes: + place_id: place id + user_id: user id + text: review description + """ + __tablename__ = "reviews" + text = Column(String(1024), nullable=False) + place_id = Column(String(60), ForeignKey("places.id"), nullable=False) + user_id = Column(String(60), ForeignKey("users.id"), nullable=False) diff --git a/models/state.py b/models/state.py old mode 100644 new mode 100755 index 583f041f07e4..250e66a82f14 --- a/models/state.py +++ b/models/state.py @@ -1,8 +1,35 @@ #!/usr/bin/python3 -""" State Module for HBNB project """ -from models.base_model import BaseModel +"""This is the state class""" +from sqlalchemy.ext.declarative import declarative_base +from models.base_model import BaseModel, Base +from sqlalchemy.orm import relationship +from sqlalchemy import Column, Integer, String +import models +from models.city import City +import shlex -class State(BaseModel): - """ State class """ - name = "" +class State(BaseModel, Base): + """This is the class for State + Attributes: + name: input name + """ + __tablename__ = "states" + name = Column(String(128), nullable=False) + cities = relationship("City", cascade='all, delete, delete-orphan', + backref="state") + + @property + def cities(self): + var = models.storage.all() + lista = [] + result = [] + for key in var: + city = key.replace('.', ' ') + city = shlex.split(city) + if (city[0] == 'City'): + lista.append(var[key]) + for elem in lista: + if (elem.state_id == self.id): + result.append(elem) + return (result) diff --git a/models/states.py b/models/states.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/models/user.py b/models/user.py old mode 100644 new mode 100755 index 4b54a6d24120..d15bdfbecffb --- a/models/user.py +++ b/models/user.py @@ -1,11 +1,27 @@ #!/usr/bin/python3 -"""This module defines a class User""" -from models.base_model import BaseModel +"""This is the user class""" +from sqlalchemy.ext.declarative import declarative_base +from models.base_model import BaseModel, Base +from sqlalchemy import Column, Integer, String +from sqlalchemy.orm import relationship +from models.place import Place +from models.review import Review -class User(BaseModel): - """This class defines a user by various attributes""" - email = '' - password = '' - first_name = '' - last_name = '' +class User(BaseModel, Base): + """This is the class for user + Attributes: + email: email address + password: password for you login + first_name: first name + last_name: last name + """ + __tablename__ = "users" + email = Column(String(128), nullable=False) + password = Column(String(128), nullable=False) + first_name = Column(String(128)) + last_name = Column(String(128)) + places = relationship("Place", cascade='all, delete, delete-orphan', + backref="user") + reviews = relationship("Review", cascade='all, delete, delete-orphan', + backref="user") diff --git a/setup_mysql_dev.sql b/setup_mysql_dev.sql new file mode 100644 index 000000000000..1453a7a6432c --- /dev/null +++ b/setup_mysql_dev.sql @@ -0,0 +1,20 @@ +-- Creates a MySQL server with: +-- Database hbnb_dev_db. +-- User hbnb_dev with password hbnb_dev_pwd in localhost. +-- Grants all privileges for hbnb_dev on hbnb_dev_db. +-- Grants SELECT privilege for hbnb_dev on performance. + +-- Create the database if it doesn't exist +CREATE DATABASE IF NOT EXISTS hbnb_dev_db; + +-- Create the user if it doesn't exist +CREATE USER IF NOT EXISTS 'hbnb_dev'@'localhost' IDENTIFIED BY 'hbnb_dev_pwd'; + +-- Grant all privileges on hbnb_dev_db to hbnb_dev +GRANT ALL PRIVILEGES ON hbnb_dev_db.* TO 'hbnb_dev'@'localhost'; + +-- Grant SELECT privilege on performance_schema to hbnb_dev +GRANT SELECT ON performance_schema.* TO 'hbnb_dev'@'localhost'; + +-- Flush privileges to apply changes +FLUSH PRIVILEGES; diff --git a/setup_mysql_test.sql b/setup_mysql_test.sql new file mode 100644 index 000000000000..922e9ae8cfb1 --- /dev/null +++ b/setup_mysql_test.sql @@ -0,0 +1,21 @@ +-- Creates a MySQL server with: +-- Database hbnb_test_db. +-- User hbnb_test with password hbnb_test_pwd in localhost. +-- Grants all privileges for hbnb_test on hbnb_test_db. +-- Grants SELECT privilege for hbnb_test on performance_schema. + + +-- Create the database if it doesn't exist +CREATE DATABASE IF NOT EXISTS hbnb_test_db; + +-- Create the user if it doesn't exist +CREATE USER IF NOT EXISTS 'hbnb_test'@'localhost' IDENTIFIED BY 'hbnb_test_pwd'; + +-- Grant all privileges on hbnb_test_db to hbnb_test +GRANT ALL PRIVILEGES ON hbnb_test_db.* TO 'hbnb_test'@'localhost'; + +-- Grant SELECT privilege on performance_schema to hbnb_test +GRANT SELECT ON performance_schema.* TO 'hbnb_test'@'localhost'; + +-- Flush privileges to apply changes +FLUSH PRIVILEGES; diff --git a/tests/test_console.py b/tests/test_console.py new file mode 100644 index 000000000000..015e5c46886a --- /dev/null +++ b/tests/test_console.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +""" +Contains the class TestConsoleDocs +""" + +import console +import inspect +import pep8 +import unittest +HBNBCommand = console.HBNBCommand + + +class TestConsoleDocs(unittest.TestCase): + """Class for testing documentation of the console""" + def test_pep8_conformance_console(self): + """Test that console.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['console.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_pep8_conformance_test_console(self): + """Test that tests/test_console.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['tests/test_console.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_console_module_docstring(self): + """Test for the console.py module docstring""" + self.assertIsNot(console.__doc__, None, + "console.py needs a docstring") + self.assertTrue(len(console.__doc__) >= 1, + "console.py needs a docstring") + + def test_HBNBCommand_class_docstring(self): + """Test for the HBNBCommand class docstring""" + self.assertIsNot(HBNBCommand.__doc__, None, + "HBNBCommand class needs a docstring") + self.assertTrue(len(HBNBCommand.__doc__) >= 1, + "HBNBCommand class needs a docstring") diff --git a/tests/test_models/test_amenity.py b/tests/test_models/test_amenity.py index e47ab0d2e09a..6286b774aab3 100755 --- a/tests/test_models/test_amenity.py +++ b/tests/test_models/test_amenity.py @@ -1,11 +1,11 @@ #!/usr/bin/python3 """ """ -from tests.test_models.test_base_model import test_basemodel +from tests.test_models.test_base_model import TestBaseModel from models.amenity import Amenity -class test_Amenity(test_basemodel): - """ """ +class TestAmenity(TestBaseModel): + """ Test for amenity""" def __init__(self, *args, **kwargs): """ """ @@ -16,4 +16,5 @@ def __init__(self, *args, **kwargs): def test_name2(self): """ """ new = self.value() + new.name = "amenity" self.assertEqual(type(new.name), str) diff --git a/tests/test_models/test_base_model.py b/tests/test_models/test_base_model.py index b6fef535c595..468ea36dc1ab 100755 --- a/tests/test_models/test_base_model.py +++ b/tests/test_models/test_base_model.py @@ -8,8 +8,8 @@ import os -class test_basemodel(unittest.TestCase): - """ """ +class TestBaseModel(unittest.TestCase): + """ Test base model""" def __init__(self, *args, **kwargs): """ """ @@ -47,21 +47,6 @@ def test_kwargs_int(self): with self.assertRaises(TypeError): new = BaseModel(**copy) - def test_save(self): - """ Testing save """ - i = self.value() - i.save() - key = self.name + "." + i.id - with open('file.json', 'r') as f: - j = json.load(f) - self.assertEqual(j[key], i.to_dict()) - - def test_str(self): - """ """ - i = self.value() - self.assertEqual(str(i), '[{}] ({}) {}'.format(self.name, i.id, - i.__dict__)) - def test_todict(self): """ """ i = self.value() @@ -74,12 +59,6 @@ def test_kwargs_none(self): with self.assertRaises(TypeError): new = self.value(**n) - def test_kwargs_one(self): - """ """ - n = {'Name': 'test'} - with self.assertRaises(KeyError): - new = self.value(**n) - def test_id(self): """ """ new = self.value() @@ -96,4 +75,5 @@ def test_updated_at(self): self.assertEqual(type(new.updated_at), datetime.datetime) n = new.to_dict() new = BaseModel(**n) - self.assertFalse(new.created_at == new.updated_at) + self.assertAlmostEqual(new.created_at.timestamp(), + new.updated_at.timestamp(), delta=1) diff --git a/tests/test_models/test_city.py b/tests/test_models/test_city.py index 2673225808c0..394842db5d91 100755 --- a/tests/test_models/test_city.py +++ b/tests/test_models/test_city.py @@ -1,11 +1,12 @@ #!/usr/bin/python3 """ """ -from tests.test_models.test_base_model import test_basemodel +from models.state import State +from tests.test_models.test_base_model import TestBaseModel from models.city import City -class test_City(test_basemodel): - """ """ +class TestCity(TestBaseModel): + """ Test for city""" def __init__(self, *args, **kwargs): """ """ @@ -15,10 +16,13 @@ def __init__(self, *args, **kwargs): def test_state_id(self): """ """ + state = State() new = self.value() + new.state_id = state.id self.assertEqual(type(new.state_id), str) def test_name(self): """ """ new = self.value() + new.name = "Batch" self.assertEqual(type(new.name), str) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py new file mode 100755 index 000000000000..7d5c38cee4bc --- /dev/null +++ b/tests/test_models/test_engine/test_db_storage.py @@ -0,0 +1,103 @@ +#!/usr/bin/python3 +import unittest +import models +from models.user import User +from models.review import Review +from models.amenity import Amenity +from models.state import State +from models.place import Place +from models.city import City +import os + + +# skip these test if the storage is not db +@unittest.skipIf(os.getenv('HBNB_TYPE_STORAGE') != 'db', "skip if not fs") +class TestDBStorage(unittest.TestCase): + """DB Storage test""" + + def setUp(self): + """ Set up test environment """ + self.storage = models.storage + + def tearDown(self): + """ Remove storage file at end of tests """ + del self.storage + + def test_user(self): + """ Tests user """ + user = User(name="Chyna", email="chyna@gmail.com", password="Chyna12345") + user.save() + self.assertFalse(user.id in self.storage.all()) + self.assertEqual(user.name, "Chyna") + + def test_city(self): + """ test city """ + state = State(name="California") + state.save() + city = City(name="Batch") + city.state_id = state.id + city.save() + self.assertFalse(city.id in self.storage.all()) + self.assertEqual(city.name, "Batch") + + def test_state(self): + """ test state""" + state = State(name="California") + state.save() + self.assertFalse(state.id in self.storage.all()) + self.assertEqual(state.name, "California") + + def test_place(self): + """Test place""" + state = State(name="California") + state.save() + + city = City(name="Batch") + city.state_id = state.id + city.save() + + user = User(name="Chyna", email="chyna@gmail.com", password="Chyna12345") + user.save() + + place = Place(name="Palace", number_rooms=4) + place.city_id = city.id + place.user_id = user.id + place.save() + + self.assertFalse(place.id in self.storage.all()) + self.assertEqual(place.number_rooms, 4) + self.assertEqual(place.name, "Palace") + + def test_amenity(self): + """ test amenity """ + amenity = Amenity(name="Startlink") + amenity.save() + self.assertFalse(amenity.id in self.storage.all()) + self.assertTrue(amenity.name, "Startlink") + + def test_review(self): + """ test review """ + state = State(name="California") + state.save() + + city = City(name="Batch") + city.state_id = state.id + city.save() + + user = User(name="Chyna", email="chyna@gmail.com", password="Chyna12345") + user.save() + + place = Place(name="Palace", number_rooms=4) + place.city_id = city.id + place.user_id = user.id + place.save() + + review = Review(text="no comment", place_id=place.id, user_id=user.id) + review.save() + + self.assertFalse(review.id in self.storage.all()) + self.assertEqual(review.text, "no comment") + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index e1de7198b697..9132134cc7ac 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -1,21 +1,24 @@ #!/usr/bin/python3 """ Module for testing file storage""" import unittest + +import models from models.base_model import BaseModel -from models import storage import os -class test_fileStorage(unittest.TestCase): +@unittest.skipIf(os.getenv('HBNB_TYPE_STORAGE') == 'db', "skip if not db") +class TestFileStorage(unittest.TestCase): """ Class to test the file storage method """ def setUp(self): """ Set up test environment """ + self.storage = models.storage del_list = [] - for key in storage._FileStorage__objects.keys(): + for key in self.storage._FileStorage__objects.keys(): del_list.append(key) for key in del_list: - del storage._FileStorage__objects[key] + del self.storage._FileStorage__objects[key] def tearDown(self): """ Remove storage file at end of tests """ @@ -24,21 +27,23 @@ def tearDown(self): except: pass + del self.storage + def test_obj_list_empty(self): """ __objects is initially empty """ - self.assertEqual(len(storage.all()), 0) + self.assertEqual(len(self.storage.all()), 0) def test_new(self): """ New object is correctly added to __objects """ new = BaseModel() - for obj in storage.all().values(): + for obj in self.storage.all().values(): temp = obj - self.assertTrue(temp is obj) + self.assertTrue(temp is obj) def test_all(self): """ __objects is properly returned """ new = BaseModel() - temp = storage.all() + temp = self.storage.all() self.assertIsInstance(temp, dict) def test_base_model_instantiation(self): @@ -57,28 +62,28 @@ def test_empty(self): def test_save(self): """ FileStorage save method """ new = BaseModel() - storage.save() + self.storage.save() self.assertTrue(os.path.exists('file.json')) def test_reload(self): """ Storage file is successfully loaded to __objects """ new = BaseModel() - storage.save() - storage.reload() - for obj in storage.all().values(): + self.storage.save() + self.storage.reload() + for obj in self.storage.all().values(): loaded = obj - self.assertEqual(new.to_dict()['id'], loaded.to_dict()['id']) + self.assertEqual(new.to_dict()['id'], loaded.to_dict()['id']) def test_reload_empty(self): """ Load from an empty file """ with open('file.json', 'w') as f: pass with self.assertRaises(ValueError): - storage.reload() + self.storage.reload() def test_reload_from_nonexistent(self): """ Nothing happens if file does not exist """ - self.assertEqual(storage.reload(), None) + self.assertEqual(self.storage.reload(), None) def test_base_model_save(self): """ BaseModel save method calls storage save """ @@ -88,22 +93,22 @@ def test_base_model_save(self): def test_type_path(self): """ Confirm __file_path is string """ - self.assertEqual(type(storage._FileStorage__file_path), str) + self.assertEqual(type(self.storage._FileStorage__file_path), str) def test_type_objects(self): """ Confirm __objects is a dict """ - self.assertEqual(type(storage.all()), dict) + self.assertEqual(type(self.storage.all()), dict) def test_key_format(self): """ Key is properly formatted """ new = BaseModel() _id = new.to_dict()['id'] - for key in storage.all().keys(): + for key in self.storage.all().keys(): temp = key - self.assertEqual(temp, 'BaseModel' + '.' + _id) + self.assertEqual(temp, 'BaseModel' + '.' + _id) def test_storage_var_created(self): """ FileStorage object storage created """ from models.engine.file_storage import FileStorage - print(type(storage)) - self.assertEqual(type(storage), FileStorage) + print(type(self.storage)) + self.assertEqual(type(self.storage), FileStorage) diff --git a/tests/test_models/test_place.py b/tests/test_models/test_place.py index ec133d104ef5..1f7a7fcc1f87 100755 --- a/tests/test_models/test_place.py +++ b/tests/test_models/test_place.py @@ -1,11 +1,13 @@ #!/usr/bin/python3 """ """ -from tests.test_models.test_base_model import test_basemodel +from models.city import City +from models.user import User +from tests.test_models.test_base_model import TestBaseModel from models.place import Place -class test_Place(test_basemodel): - """ """ +class TestPlace(TestBaseModel): + """ Test for place""" def __init__(self, *args, **kwargs): """ """ @@ -16,52 +18,64 @@ def __init__(self, *args, **kwargs): def test_city_id(self): """ """ new = self.value() + city = City() + new.city_id = city.id self.assertEqual(type(new.city_id), str) def test_user_id(self): """ """ new = self.value() + user = User() + new.user_id = user.id self.assertEqual(type(new.user_id), str) def test_name(self): """ """ new = self.value() + new.name = "Place" self.assertEqual(type(new.name), str) def test_description(self): """ """ new = self.value() + new.description = "" self.assertEqual(type(new.description), str) def test_number_rooms(self): """ """ new = self.value() + new.number_rooms = 4 self.assertEqual(type(new.number_rooms), int) def test_number_bathrooms(self): """ """ new = self.value() + new.number_bathrooms = 2 self.assertEqual(type(new.number_bathrooms), int) def test_max_guest(self): """ """ new = self.value() + new.max_guest = 8 self.assertEqual(type(new.max_guest), int) def test_price_by_night(self): """ """ new = self.value() + new.price_by_night = 18 self.assertEqual(type(new.price_by_night), int) def test_latitude(self): """ """ new = self.value() + new.latitude = -123.085222 self.assertEqual(type(new.latitude), float) def test_longitude(self): """ """ new = self.value() - self.assertEqual(type(new.latitude), float) + new.longitude = -120.085222 + self.assertEqual(type(new.longitude), float) def test_amenity_ids(self): """ """ diff --git a/tests/test_models/test_review.py b/tests/test_models/test_review.py index 23fbc61529e8..6804c485d917 100755 --- a/tests/test_models/test_review.py +++ b/tests/test_models/test_review.py @@ -1,11 +1,13 @@ #!/usr/bin/python3 """ """ -from tests.test_models.test_base_model import test_basemodel +from models.place import Place +from models.user import User +from tests.test_models.test_base_model import TestBaseModel from models.review import Review -class test_review(test_basemodel): - """ """ +class TestReview(TestBaseModel): + """Test for review """ def __init__(self, *args, **kwargs): """ """ @@ -16,14 +18,19 @@ def __init__(self, *args, **kwargs): def test_place_id(self): """ """ new = self.value() + place = Place() + new.place_id = place.id self.assertEqual(type(new.place_id), str) def test_user_id(self): """ """ new = self.value() + user = User() + new.user_id = user.id self.assertEqual(type(new.user_id), str) def test_text(self): """ """ new = self.value() + new.text = "" self.assertEqual(type(new.text), str) diff --git a/tests/test_models/test_state.py b/tests/test_models/test_state.py index 719e096d8633..7149c879f7aa 100755 --- a/tests/test_models/test_state.py +++ b/tests/test_models/test_state.py @@ -1,19 +1,20 @@ #!/usr/bin/python3 """ """ -from tests.test_models.test_base_model import test_basemodel +from tests.test_models.test_base_model import TestBaseModel from models.state import State -class test_state(test_basemodel): - """ """ +class TestState(TestBaseModel): + """Test for state """ def __init__(self, *args, **kwargs): """ """ super().__init__(*args, **kwargs) - self.name = "State" + self.name = "California" self.value = State def test_name3(self): """ """ new = self.value() + new.name = "Arizona" self.assertEqual(type(new.name), str) diff --git a/tests/test_models/test_user.py b/tests/test_models/test_user.py index 8660300f8bbc..c26f6956dd12 100755 --- a/tests/test_models/test_user.py +++ b/tests/test_models/test_user.py @@ -1,11 +1,11 @@ #!/usr/bin/python3 """ """ -from tests.test_models.test_base_model import test_basemodel +from tests.test_models.test_base_model import TestBaseModel from models.user import User -class test_User(test_basemodel): - """ """ +class TestUser(TestBaseModel): + """ Test for user""" def __init__(self, *args, **kwargs): """ """ @@ -16,19 +16,23 @@ def __init__(self, *args, **kwargs): def test_first_name(self): """ """ new = self.value() + new.first_name = "Chyna" self.assertEqual(type(new.first_name), str) def test_last_name(self): """ """ new = self.value() + new.last_name = "Chyna" self.assertEqual(type(new.last_name), str) def test_email(self): """ """ new = self.value() + new.email = "angoyewally@gmail.com" self.assertEqual(type(new.email), str) def test_password(self): """ """ new = self.value() + new.password = "123aashja" self.assertEqual(type(new.password), str)