From 10baced71e874f121bf64e18df9c8e3f3eac274d Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 11:40:25 +0300 Subject: [PATCH 01/42] README.md --- README.md | 950 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 813 insertions(+), 137 deletions(-) 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 + From 88bdcdaededbc05054bcdca8b12110f61ca37eb9 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 12:35:33 +0300 Subject: [PATCH 02/42] the --- console.py | 557 ++++++++++++++++++++++++++--------------------------- 1 file changed, 276 insertions(+), 281 deletions(-) diff --git a/console.py b/console.py index 13a8af68e930..80b4038f6399 100755 --- a/console.py +++ b/console.py @@ -1,324 +1,319 @@ #!/usr/bin/python3 -""" Console Module """ +""" +Module for console +""" import cmd -import sys +import re +import shlex +import ast +from models import storage from models.base_model import BaseModel -from models.__init__ import storage from models.user import User +from models.amenity import Amenity from models.place import Place +from models.review import Review from models.state import State from models.city import City -from models.amenity import Amenity -from models.review import Review + +def split_curly_braces(e_arg): + """ + Splits the curly braces for the update method + """ + curly_braces = re.search(r"\{(.*?)\}", e_arg) + + if curly_braces: + id_with_comma = shlex.split(e_arg[:curly_braces.span()[0]]) + id = [i.strip(",") for i in id_with_comma][0] + + str_data = curly_braces.group(1) + try: + arg_dict = ast.literal_eval("{" + str_data + "}") + except Exception: + print("** invalid dictionary format **") + return + return id, arg_dict + else: + commands = e_arg.split(",") + if commands: + try: + id = commands[0] + except Exception: + return "", "" + try: + attr_name = commands[1] + except Exception: + return id, "" + try: + attr_value = commands[2] + except Exception: + return id, attr_name + return f"{id}", f"{attr_name} {attr_value}" + 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 '' - - 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 preloop(self): - """Prints if isatty is false""" - if not sys.__stdin__.isatty(): - print('(hbnb)') - - def precmd(self, line): - """Reformat command line for advanced command syntax. - - Usage: .([ [<*args> or <**kwargs>]]) - (Brackets denote optional fields in usage example.) + """ + HBNBCommand console class + """ + prompt = "(hbnb) " + valid_classes = ["BaseModel", "User", "Amenity", + "Place", "Review", "State", "City"] + + def emptyline(self): + """ + Do nothing when an empty line is entered. """ - _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") + pass def do_EOF(self, arg): - """ Handles EOF to exit program """ - print() - exit() + """ + EOF (Ctrl+D) signal to exit the program. + """ + return True - def help_EOF(self): - """ Prints the help documentation for EOF """ - print("Exits the program without formatting\n") + def do_quit(self, arg): + """ + Quit command to exit the program. + """ + return True + - def emptyline(self): - """ Overrides the emptyline method of CMD """ - pass + def do_create(self, arg): + """ + Create a new instance of BaseModel and save it to the JSON file. + Usage: create + """ + try: + class_name = arg.split(" ")[0] + if len(class_name) == 0: + print("** class name missing **") + return + if class_name and class_name not in self.valid_classes: + print("** class doesn't exist **") + return - def do_create(self, args): - """ Create an object of any class""" - if not args: - print("** class name missing **") - return - elif args not in HBNBCommand.classes: - 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: - print("** class name missing **") + kwargs = {} + commands = arg.split(" ") + for i in range(1, len(commands)): + + key = commands[i].split("=")[0] + value = commands[i].split("=")[1] + #key, value = tuple(commands[i].split("=")) + if value.startswith('"'): + value = value.strip('"').replace("_", " ") + else: + try: + value = eval(value) + except (SyntaxError, NameError): + continue + kwargs[key] = value + + if kwargs == {}: + new_instance = eval(class_name)() + else: + new_instance = eval(class_name)(**kwargs) + storage.new(new_instance) + print(new_instance.id) + storage.save() + except ValueError: + print(ValueError) return - if c_name not in HBNBCommand.classes: - print("** class doesn't exist **") - return + def do_show(self, arg): + """ + Show the string representation of an instance. + Usage: show + """ + commands = shlex.split(arg) - if not c_id: + if len(commands) == 0: + print("** class name missing **") + elif commands[0] not in self.valid_classes: + print("** class doesn't exist **") + elif len(commands) < 2: print("** instance id missing **") - return + else: + objects = storage.all() - 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: - print("** class name missing **") - return + key = "{}.{}".format(commands[0], commands[1]) + if key in objects: + print(objects[key]) + else: + print("** no instance found **") - if c_name not in HBNBCommand.classes: - print("** class doesn't exist **") - return + def do_destroy(self, arg): + """ + Delete an instance based on the class name and id. + Usage: destroy + """ + commands = shlex.split(arg) - if not c_id: + if len(commands) == 0: + print("** class name missing **") + elif commands[0] not in self.valid_classes: + print("** class doesn't exist **") + elif len(commands) < 2: print("** instance id missing **") - return + else: + objects = storage.all() + key = "{}.{}".format(commands[0], commands[1]) + if key in objects: + del objects[key] + storage.save() + else: + print("** no instance found **") + + def do_all(self, arg): + """ + Print the string representation of all instances or a specific class. + Usage: .all() + .show() + """ + objects = storage.all() - key = c_name + "." + c_id + commands = shlex.split(arg) - try: - del(storage.all()[key]) - storage.save() - except KeyError: - print("** no instance found **") + if len(commands) == 0: + for key, value in objects.items(): + print(str(value)) + elif commands[0] not in self.valid_classes: + print("** class doesn't exist **") + else: + for key, value in objects.items(): + if key.split('.')[0] == commands[0]: + print(str(value)) + + def do_count(self, arg): + """ + Counts and retrieves the number of instances of a class + usage: .count() + """ + objects = storage.all() - def help_destroy(self): - """ Help information for the destroy command """ - print("Destroys an individual instance of a class") - print("[Usage]: destroy \n") + commands = shlex.split(arg) - def do_all(self, args): - """ Shows all objects, or all objects of a class""" - print_list = [] + if arg: + incoming_class_name = commands[0] + count = 0 - 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)) + if commands: + if incoming_class_name in self.valid_classes: + for obj in objects.values(): + if obj.__class__.__name__ == incoming_class_name: + count += 1 + print(count) + else: + print("** invalid class name **") else: - for k, v in storage._FileStorage__objects.items(): - print_list.append(str(v)) - - print(print_list) + print("** class name missing **") - 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_update(self, arg): + """ + Update an instance by adding or updating an attribute. + Usage: update "" + """ + commands = shlex.split(arg) - 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 help_count(self): - """ """ - print("Usage: count ") - - def do_update(self, args): - """ Updates a certain object with new info """ - c_name = c_id = att_name = att_val = kwargs = '' - - # isolate cls from id/args, ex: (, delim, ) - args = args.partition(" ") - if args[0]: - c_name = args[0] - else: # class name not present + if len(commands) == 0: print("** class name missing **") - return - if c_name not in HBNBCommand.classes: # class name invalid + elif commands[0] not in self.valid_classes: 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 + elif len(commands) < 2: print("** instance id missing **") - return + else: + objects = storage.all() + + key = "{}.{}".format(commands[0], commands[1]) + if key not in objects: + print("** no instance found **") + elif len(commands) < 3: + print("** attribute name missing **") + elif len(commands) < 4: + print("** value missing **") + else: + obj = objects[key] + curly_braces = re.search(r"\{(.*?)\}", arg) + + if curly_braces: + # added to catch errors + try: + str_data = curly_braces.group(1) + + arg_dict = ast.literal_eval("{" + str_data + "}") + + attribute_names = list(arg_dict.keys()) + attribute_values = list(arg_dict.values()) + # added to catch exception + try: + attr_name1 = attribute_names[0] + attr_value1 = attribute_values[0] + setattr(obj, attr_name1, attr_value1) + except Exception: + pass + try: + # added to catch exception + attr_name2 = attribute_names[1] + attr_value2 = attribute_values[1] + setattr(obj, attr_name2, attr_value2) + except Exception: + pass + except Exception: + pass + else: + + attr_name = commands[2] + attr_value = commands[3] + + try: + attr_value = eval(attr_value) + except Exception: + pass + setattr(obj, attr_name, attr_value) + + obj.save() + + def default(self, arg): + """ + Default behavior for cmd module when input is invalid + """ + arg_list = arg.split('.') - # generate key from class and id - key = c_name + "." + c_id + cls_nm = arg_list[0] # incoming class name - # determine if key is present - if key not in storage.all(): - print("** no instance found **") - return + command = arg_list[1].split('(') - # 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] - - 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) + cmd_met = command[0] # incoming command method - # update dictionary with name, value pair - new_dict.__dict__.update({att_name: att_val}) + e_arg = command[1].split(')')[0] # extra arguments - new_dict.save() # save updates to file + method_dict = { + 'all': self.do_all, + 'show': self.do_show, + 'destroy': self.do_destroy, + 'update': self.do_update, + 'count': self.do_count + } - def help_update(self): - """ Help information for the update class """ - print("Updates an object with new information") - print("Usage: update \n") + if cmd_met in method_dict.keys(): + if cmd_met != "update": + return method_dict[cmd_met]("{} {}".format(cls_nm, e_arg)) + else: + if not cls_nm: + print("** class name missing **") + return + try: + obj_id, arg_dict = split_curly_braces(e_arg) + except Exception: + pass + try: + call = method_dict[cmd_met] + return call("{} {} {}".format(cls_nm, obj_id, arg_dict)) + except Exception: + pass + else: + print("*** Unknown syntax: {}".format(arg)) + return False + -if __name__ == "__main__": +if __name__ == '__main__': HBNBCommand().cmdloop() From fbeb5d12ab18f676662b43cfad05de1f7cb5d67b Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 12:54:46 +0300 Subject: [PATCH 03/42] console --- console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console.py b/console.py index 80b4038f6399..4208595fc8d7 100755 --- a/console.py +++ b/console.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python """ Module for console """ From 1a121dbfa3bd215afe563ec52f6c5546fcad18ce Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 14:26:43 +0300 Subject: [PATCH 04/42] console.py --- console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console.py b/console.py index 4208595fc8d7..80b4038f6399 100755 --- a/console.py +++ b/console.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 """ Module for console """ From a5c074df9a4ff2f81b3b3ae916870195ecf57bf3 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 14:32:11 +0300 Subject: [PATCH 05/42] wils --- models/engine/file_storage.py | 54 ++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 6f5d7f8d4680..f3ea2bac49b5 100644 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,6 +1,13 @@ #!/usr/bin/python3 """This module defines a class to manage file storage for hbnb clone""" import json +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 class FileStorage: @@ -8,8 +15,23 @@ class FileStorage: __file_path = 'file.json' __objects = {} - def all(self): - """Returns a dictionary of models currently in storage""" + def all(self, cls=None): + """Returns a dictionary of models currently in storage. + + Args: + cls (class, optional): If specified, filters the result to include + only objects of the specified class. + + Returns: + dict: A dictionary containing objects in storage. + """ + if cls: + if isinstance(cls, str): + cls = globals().get(cls) + if cls and issubclass(cls, BaseModel): + cls_dict = {k: v for k, + v in self.__objects.items() if isinstance(v, cls)} + return cls_dict return FileStorage.__objects def new(self, obj): @@ -26,15 +48,7 @@ def save(self): json.dump(temp, 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 - + """Loads storage dictionary from file.""" classes = { 'BaseModel': BaseModel, 'User': User, 'Place': Place, 'State': State, 'City': City, 'Amenity': Amenity, @@ -48,3 +62,21 @@ def reload(self): self.all()[key] = classes[val['__class__']](**val) except FileNotFoundError: pass + except json.decoder.JSONDecodeError: + pass + + def delete(self, obj=None): + """ + Delete obj from __objects if it’s inside - if obj is equal to None, + the method should not do anything + """ + if obj is None: + return + obj_to_del = f"{obj.__class__.__name__}.{obj.id}" + + try: + del FileStorage.__objects[obj_to_del] + except AttributeError: + pass + except KeyboardInterrupt: + pass From ff73bcde8ba152b2e87acad433980df420c4e429 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 14:33:59 +0300 Subject: [PATCH 06/42] testconsole --- tests/test_console.py | 148 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tests/test_console.py diff --git a/tests/test_console.py b/tests/test_console.py new file mode 100644 index 000000000000..dcd6cf212950 --- /dev/null +++ b/tests/test_console.py @@ -0,0 +1,148 @@ +#!/usr/bin/python3 +"""Defines unittests for console.py.""" +import os +import unittest +from unittest.mock import patch +from io import StringIO +from console import HBNBCommand +from models.engine.file_storage import FileStorage + + +class TestHBNBCommand(unittest.TestCase): + """Unittests for testing the HBNB command interpreter.""" + + @classmethod + def setUpClass(cls): + """HBNBCommand testing setup. + + Temporarily rename any existing file.json. + Reset FileStorage objects dictionary. + Create an instance of the command interpreter. + """ + try: + os.rename("file.json", "tmp") + except IOError: + pass + # Create an instance of the HBNBCommand class. This allows the test + # methods within the class to access and use this instance during the + # testing process. + cls.HBNB = HBNBCommand() + + @classmethod + def tearDownClass(cls): + """HBNBCommand testing teardown. + + Restore original file.json. + Delete the test HBNBCommand instance. + """ + try: + os.rename("tmp", "file.json") + except IOError: + pass + del cls.HBNB + + def setUp(self): + """Reset FileStorage objects dictionary.""" + FileStorage._FileStorage__objects = {} + + def tearDown(self): + """Delete any created file.json.""" + try: + os.remove("file.json") + except IOError: + pass + + def test_create_for_errors(self): + """Test create command errors.""" + # Test if class name is missing + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create") + self.assertEqual( + "** class name missing **\n", f.getvalue()) + # Test if class doesn't exist + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create asdfsfsd") + self.assertEqual( + "** class doesn't exist **\n", f.getvalue()) + + def test_create_command_validity(self): + """Test create command.""" + # Create BaseModel instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create BaseModel") + bm = f.getvalue().strip() + + # Create User instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create User") + us = f.getvalue().strip() + + # Create State instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create State") + st = f.getvalue().strip() + + # Create Place instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create Place") + pl = f.getvalue().strip() + + # Create City instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create City") + ct = f.getvalue().strip() + + # Create Review instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create Review") + rv = f.getvalue().strip() + + # Create Amenity instance and capture its ID + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("create Amenity") + am = f.getvalue().strip() + # Test if the created instances are in the output of "all" command + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all BaseModel") + self.assertIn(bm, f.getvalue()) + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all User") + self.assertIn(us, f.getvalue()) + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all State") + self.assertIn(st, f.getvalue()) + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all Place") + self.assertIn(pl, f.getvalue()) + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all City") + self.assertIn(ct, f.getvalue()) + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all Review") + self.assertIn(rv, f.getvalue()) + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all Amenity") + self.assertIn(am, f.getvalue()) + + def test_create_command_with_kwargs(self): + """Test create command with kwargs.""" + # Test create command with additional key-value pairs + with patch("sys.stdout", new=StringIO()) as f: + call = (f'create Place city_id="0001" name="My_house" number_rooms=4 latitude=37.77 longitude=43.434') # noqa + self.HBNB.onecmd(call) + pl = f.getvalue().strip() + # Test if the created instance and kwargs are in the + # output of "all" command + with patch("sys.stdout", new=StringIO()) as f: + self.HBNB.onecmd("all Place") + output = f.getvalue() + self.assertIn(pl, output) + self.assertIn("'city_id': '0001'", output) + self.assertIn("'name': 'My house'", output) + self.assertIn("'number_rooms': 4", output) + self.assertIn("'latitude': 37.77", output) + self.assertIn("'longitude': 43.434", output) + + +if __name__ == "__main__": + unittest.main() From 63514a94693d45f61060b33cca37644b39491020 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 14:38:01 +0300 Subject: [PATCH 07/42] filetest --- tests/test_models/test_engine/test_file_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index e1de7198b697..fda4105f66a6 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -105,5 +105,5 @@ def test_key_format(self): def test_storage_var_created(self): """ FileStorage object storage created """ from models.engine.file_storage import FileStorage - print(type(storage)) + #print(type(storage)) self.assertEqual(type(storage), FileStorage) From 35047c283226f0f7f804cd12f718300acbf19d2d Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 14:40:09 +0300 Subject: [PATCH 08/42] setupsql --- setup_mysql_dev.sql | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 setup_mysql_dev.sql 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; From d95d7f943942e94a37810fabb107010d5a9aef34 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 25 Sep 2024 14:42:52 +0300 Subject: [PATCH 09/42] dgt --- setup_mysql_test.sql | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 setup_mysql_test.sql 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; From 27ac5f9d115c5b17492601975955e9b556cee563 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 08:13:11 +0300 Subject: [PATCH 10/42] test --- models/engine/db_storage.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 models/engine/db_storage.py diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py new file mode 100644 index 000000000000..e69de29bb2d1 From 268907f2254191733be4d1fac3154cc15d859a91 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 08:17:05 +0300 Subject: [PATCH 11/42] db_storage --- models/engine/db_storage.py | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index e69de29bb2d1..43e97b056d50 100644 --- a/models/engine/db_storage.py +++ 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() From 0f33217a5df1b1aff3acf1d14da50eee167dc626 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 08:30:17 +0300 Subject: [PATCH 12/42] state.py --- models/state.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/models/state.py b/models/state.py index 583f041f07e4..250e66a82f14 100644 --- 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) From 20073f324cb835d225c59475160deb960ca72f43 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 08:45:45 +0300 Subject: [PATCH 13/42] city.py --- models/city.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/models/city.py b/models/city.py index b9b4fe221502..041d5bf94e6b 100644 --- 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") From 07e5285bedc36cdc20468ccc0f0dd4a4badb9583 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 08:48:03 +0300 Subject: [PATCH 14/42] init --- models/__init__.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/models/__init__.py b/models/__init__.py index d3765c2bc603..e0912aa884e9 100644 --- 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() From 5677b7589282d5364443a8d208dc22b68eda91c0 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 08:53:15 +0300 Subject: [PATCH 15/42] base_model --- models/base_model.py | 97 +++++++++++++++++++++++++++++++------------- models/states.py | 0 2 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 models/states.py diff --git a/models/base_model.py b/models/base_model.py index 4856e9de421f..937901ff28e9 100644 --- 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/states.py b/models/states.py new file mode 100644 index 000000000000..e69de29bb2d1 From 7112dcfd7b1b5aa570bcc1027f73ec88c5a45a5b Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 09:12:21 +0300 Subject: [PATCH 16/42] console.py --- console.py | 461 ++++++++++++++++++++++++----------------------------- 1 file changed, 207 insertions(+), 254 deletions(-) diff --git a/console.py b/console.py index 80b4038f6399..6c4b5606f057 100755 --- a/console.py +++ b/console.py @@ -1,105 +1,58 @@ #!/usr/bin/python3 -""" -Module for console -""" +"""Defines the HBNB console.""" import cmd -import re -import shlex -import ast +from shlex import split from models import storage +from datetime import datetime 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 models.state import State -from models.city import City - - -def split_curly_braces(e_arg): - """ - Splits the curly braces for the update method - """ - curly_braces = re.search(r"\{(.*?)\}", e_arg) - - if curly_braces: - id_with_comma = shlex.split(e_arg[:curly_braces.span()[0]]) - id = [i.strip(",") for i in id_with_comma][0] - - str_data = curly_braces.group(1) - try: - arg_dict = ast.literal_eval("{" + str_data + "}") - except Exception: - print("** invalid dictionary format **") - return - return id, arg_dict - else: - commands = e_arg.split(",") - if commands: - try: - id = commands[0] - except Exception: - return "", "" - try: - attr_name = commands[1] - except Exception: - return id, "" - try: - attr_value = commands[2] - except Exception: - return id, attr_name - return f"{id}", f"{attr_name} {attr_value}" class HBNBCommand(cmd.Cmd): - """ - HBNBCommand console class - """ + """Defines the HolbertonBnB command interpreter.""" + prompt = "(hbnb) " - valid_classes = ["BaseModel", "User", "Amenity", - "Place", "Review", "State", "City"] + __classes = { + "BaseModel", + "User", + "State", + "City", + "Amenity", + "Place", + "Review" + } def emptyline(self): - """ - Do nothing when an empty line is entered. - """ + """Ignore empty spaces.""" pass - def do_EOF(self, arg): - """ - EOF (Ctrl+D) signal to exit the program. - """ + def do_quit(self, line): + """Quit command to exit the program.""" return True - def do_quit(self, arg): - """ - Quit command to exit the program. - """ + def do_EOF(self, line): + """EOF signal to exit the program.""" + print("") return True - - def do_create(self, arg): - """ - Create a new instance of BaseModel and save it to the JSON file. - Usage: create + def do_create(self, line): + """Usage: create = = ... + Create a new class instance with given keys/values and print its id. """ try: - class_name = arg.split(" ")[0] - if len(class_name) == 0: - print("** class name missing **") - return - if class_name and class_name not in self.valid_classes: - print("** class doesn't exist **") - return + if not line: + raise SyntaxError() + my_list = line.split(" ") kwargs = {} - commands = arg.split(" ") - for i in range(1, len(commands)): - - key = commands[i].split("=")[0] - value = commands[i].split("=")[1] - #key, value = tuple(commands[i].split("=")) - if value.startswith('"'): + for i in range(1, len(my_list)): + key, value = tuple(my_list[i].split("=")) + if value[0] == '"': value = value.strip('"').replace("_", " ") else: try: @@ -109,211 +62,211 @@ def do_create(self, arg): kwargs[key] = value if kwargs == {}: - new_instance = eval(class_name)() + obj = eval(my_list[0])() else: - new_instance = eval(class_name)(**kwargs) - storage.new(new_instance) - print(new_instance.id) - storage.save() - except ValueError: - print(ValueError) - return - - def do_show(self, arg): - """ - Show the string representation of an instance. - Usage: show - """ - commands = shlex.split(arg) + obj = eval(my_list[0])(**kwargs) + storage.new(obj) + print(obj.id) + obj.save() - if len(commands) == 0: + except SyntaxError: print("** class name missing **") - elif commands[0] not in self.valid_classes: + except NameError: print("** class doesn't exist **") - elif len(commands) < 2: - print("** instance id missing **") - else: - objects = storage.all() - key = "{}.{}".format(commands[0], commands[1]) + 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: - print("** no instance found **") - - def do_destroy(self, arg): - """ - Delete an instance based on the class name and id. - Usage: destroy - """ - commands = shlex.split(arg) - - if len(commands) == 0: + raise KeyError() + except SyntaxError: print("** class name missing **") - elif commands[0] not in self.valid_classes: + except NameError: print("** class doesn't exist **") - elif len(commands) < 2: + except IndexError: print("** instance id missing **") - else: + except KeyError: + print("** no instance found **") + + 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 = "{}.{}".format(commands[0], commands[1]) + key = my_list[0] + '.' + my_list[1] if key in objects: del objects[key] storage.save() else: - print("** no instance found **") - - def do_all(self, arg): - """ - Print the string representation of all instances or a specific class. - Usage: .all() - .show() - """ - objects = storage.all() - - commands = shlex.split(arg) - - if len(commands) == 0: - for key, value in objects.items(): - print(str(value)) - elif commands[0] not in self.valid_classes: + raise KeyError() + except SyntaxError: + print("** class name missing **") + except NameError: print("** class doesn't exist **") - else: - for key, value in objects.items(): - if key.split('.')[0] == commands[0]: - print(str(value)) - - def do_count(self, arg): - """ - Counts and retrieves the number of instances of a class - usage: .count() - """ - objects = storage.all() - - commands = shlex.split(arg) + except IndexError: + print("** instance id missing **") + except KeyError: + print("** no instance found **") + + 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() - if arg: - incoming_class_name = commands[0] - count = 0 + o = storage.all(eval(args[0])) + print([o[k].__str__() for k in o]) - if commands: - if incoming_class_name in self.valid_classes: - for obj in objects.values(): - if obj.__class__.__name__ == incoming_class_name: - count += 1 - print(count) - else: - print("** invalid class name **") - else: - print("** class name missing **") + except NameError: + print("** class doesn't exist **") - def do_update(self, arg): + 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 """ - Update an instance by adding or updating an attribute. - Usage: update "" - """ - commands = shlex.split(arg) - - if len(commands) == 0: + 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 **") - elif commands[0] not in self.valid_classes: + except NameError: print("** class doesn't exist **") - elif len(commands) < 2: + except IndexError: print("** instance id missing **") - else: - objects = storage.all() - - key = "{}.{}".format(commands[0], commands[1]) - if key not in objects: - print("** no instance found **") - elif len(commands) < 3: - print("** attribute name missing **") - elif len(commands) < 4: - print("** value missing **") - else: - obj = objects[key] - curly_braces = re.search(r"\{(.*?)\}", arg) - - if curly_braces: - # added to catch errors - try: - str_data = curly_braces.group(1) - - arg_dict = ast.literal_eval("{" + str_data + "}") - - attribute_names = list(arg_dict.keys()) - attribute_values = list(arg_dict.values()) - # added to catch exception - try: - attr_name1 = attribute_names[0] - attr_value1 = attribute_values[0] - setattr(obj, attr_name1, attr_value1) - except Exception: - pass - try: - # added to catch exception - attr_name2 = attribute_names[1] - attr_value2 = attribute_values[1] - setattr(obj, attr_name2, attr_value2) - except Exception: - pass - except Exception: - pass - else: - - attr_name = commands[2] - attr_value = commands[3] + except KeyError: + print("** no instance found **") + except AttributeError: + print("** attribute name missing **") + except ValueError: + print("** value missing **") - try: - attr_value = eval(attr_value) - except Exception: - pass - setattr(obj, attr_name, attr_value) + 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 **") - obj.save() - - def default(self, arg): + 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 """ - Default behavior for cmd module when input is invalid + 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 """ - arg_list = arg.split('.') - - cls_nm = arg_list[0] # incoming class name - - command = arg_list[1].split('(') - - cmd_met = command[0] # incoming command method - - e_arg = command[1].split(')')[0] # extra arguments - - method_dict = { - 'all': self.do_all, - 'show': self.do_show, - 'destroy': self.do_destroy, - 'update': self.do_update, - 'count': self.do_count - } - - if cmd_met in method_dict.keys(): - if cmd_met != "update": - return method_dict[cmd_met]("{} {}".format(cls_nm, e_arg)) - else: - if not cls_nm: - print("** class name missing **") - return - try: - obj_id, arg_dict = split_curly_braces(e_arg) - except Exception: - pass - try: - call = method_dict[cmd_met] - return call("{} {} {}".format(cls_nm, obj_id, arg_dict)) - except Exception: - pass + 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: - print("*** Unknown syntax: {}".format(arg)) - return False - + cmd.Cmd.default(self, line) + if __name__ == '__main__': HBNBCommand().cmdloop() From 44d24d92e573a124e3dc0db9f1a4150b4abdb423 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 09:15:29 +0300 Subject: [PATCH 17/42] console --- console.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/console.py b/console.py index 6c4b5606f057..954e6bf0205f 100755 --- a/console.py +++ b/console.py @@ -229,15 +229,15 @@ def strip_clean(self, args): new_list.append(args[0]) try: my_dict = eval( - args[1][args[1].find('{'):args[1].find('}')+1]) + 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_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_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) From 4710e674d61bfd154b09e727d42a8918cec0add8 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 09:19:43 +0300 Subject: [PATCH 18/42] cons --- console.py | 461 +++++++++++++++++++++++++++++------------------------ 1 file changed, 254 insertions(+), 207 deletions(-) diff --git a/console.py b/console.py index 954e6bf0205f..80b4038f6399 100755 --- a/console.py +++ b/console.py @@ -1,58 +1,105 @@ #!/usr/bin/python3 -"""Defines the HBNB console.""" +""" +Module for console +""" import cmd -from shlex import split +import re +import shlex +import ast from models import storage -from datetime import datetime 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 models.state import State +from models.city import City -class HBNBCommand(cmd.Cmd): - """Defines the HolbertonBnB command interpreter.""" +def split_curly_braces(e_arg): + """ + Splits the curly braces for the update method + """ + curly_braces = re.search(r"\{(.*?)\}", e_arg) + + if curly_braces: + id_with_comma = shlex.split(e_arg[:curly_braces.span()[0]]) + id = [i.strip(",") for i in id_with_comma][0] + + str_data = curly_braces.group(1) + try: + arg_dict = ast.literal_eval("{" + str_data + "}") + except Exception: + print("** invalid dictionary format **") + return + return id, arg_dict + else: + commands = e_arg.split(",") + if commands: + try: + id = commands[0] + except Exception: + return "", "" + try: + attr_name = commands[1] + except Exception: + return id, "" + try: + attr_value = commands[2] + except Exception: + return id, attr_name + return f"{id}", f"{attr_name} {attr_value}" + +class HBNBCommand(cmd.Cmd): + """ + HBNBCommand console class + """ prompt = "(hbnb) " - __classes = { - "BaseModel", - "User", - "State", - "City", - "Amenity", - "Place", - "Review" - } + valid_classes = ["BaseModel", "User", "Amenity", + "Place", "Review", "State", "City"] def emptyline(self): - """Ignore empty spaces.""" + """ + Do nothing when an empty line is entered. + """ pass - def do_quit(self, line): - """Quit command to exit the program.""" + def do_EOF(self, arg): + """ + EOF (Ctrl+D) signal to exit the program. + """ return True - def do_EOF(self, line): - """EOF signal to exit the program.""" - print("") + def do_quit(self, arg): + """ + Quit command to exit the program. + """ return True + - def do_create(self, line): - """Usage: create = = ... - Create a new class instance with given keys/values and print its id. + def do_create(self, arg): + """ + Create a new instance of BaseModel and save it to the JSON file. + Usage: create """ try: - if not line: - raise SyntaxError() - my_list = line.split(" ") + class_name = arg.split(" ")[0] + if len(class_name) == 0: + print("** class name missing **") + return + if class_name and class_name not in self.valid_classes: + print("** class doesn't exist **") + return kwargs = {} - for i in range(1, len(my_list)): - key, value = tuple(my_list[i].split("=")) - if value[0] == '"': + commands = arg.split(" ") + for i in range(1, len(commands)): + + key = commands[i].split("=")[0] + value = commands[i].split("=")[1] + #key, value = tuple(commands[i].split("=")) + if value.startswith('"'): value = value.strip('"').replace("_", " ") else: try: @@ -62,211 +109,211 @@ def do_create(self, line): kwargs[key] = value if kwargs == {}: - obj = eval(my_list[0])() + new_instance = eval(class_name)() else: - obj = eval(my_list[0])(**kwargs) - storage.new(obj) - print(obj.id) - obj.save() + new_instance = eval(class_name)(**kwargs) + storage.new(new_instance) + print(new_instance.id) + storage.save() + except ValueError: + print(ValueError) + return + + def do_show(self, arg): + """ + Show the string representation of an instance. + Usage: show + """ + commands = shlex.split(arg) - except SyntaxError: + if len(commands) == 0: print("** class name missing **") - except NameError: + elif commands[0] not in self.valid_classes: print("** class doesn't exist **") - - 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() + elif len(commands) < 2: + print("** instance id missing **") + else: objects = storage.all() - key = my_list[0] + '.' + my_list[1] + + key = "{}.{}".format(commands[0], commands[1]) if key in objects: print(objects[key]) else: - raise KeyError() - except SyntaxError: + print("** no instance found **") + + def do_destroy(self, arg): + """ + Delete an instance based on the class name and id. + Usage: destroy + """ + commands = shlex.split(arg) + + if len(commands) == 0: print("** class name missing **") - except NameError: + elif commands[0] not in self.valid_classes: print("** class doesn't exist **") - except IndexError: + elif len(commands) < 2: print("** instance id missing **") - except KeyError: - print("** no instance found **") - - 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() + else: objects = storage.all() - key = my_list[0] + '.' + my_list[1] + key = "{}.{}".format(commands[0], commands[1]) if key in objects: del objects[key] storage.save() else: - raise KeyError() - except SyntaxError: - print("** class name missing **") - except NameError: - print("** class doesn't exist **") - except IndexError: - print("** instance id missing **") - except KeyError: - print("** no instance found **") - - 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() + print("** no instance found **") - o = storage.all(eval(args[0])) - print([o[k].__str__() for k in o]) + def do_all(self, arg): + """ + Print the string representation of all instances or a specific class. + Usage: .all() + .show() + """ + objects = storage.all() + + commands = shlex.split(arg) - except NameError: + if len(commands) == 0: + for key, value in objects.items(): + print(str(value)) + elif commands[0] not in self.valid_classes: print("** class doesn't exist **") + else: + for key, value in objects.items(): + if key.split('.')[0] == commands[0]: + print(str(value)) + + def do_count(self, arg): + """ + Counts and retrieves the number of instances of a class + usage: .count() + """ + objects = storage.all() + + commands = shlex.split(arg) + + if arg: + incoming_class_name = commands[0] + count = 0 + + if commands: + if incoming_class_name in self.valid_classes: + for obj in objects.values(): + if obj.__class__.__name__ == incoming_class_name: + count += 1 + print(count) + else: + print("** invalid class name **") + else: + print("** class name missing **") - 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 + def do_update(self, arg): """ - 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: + Update an instance by adding or updating an attribute. + Usage: update "" + """ + commands = shlex.split(arg) + + if len(commands) == 0: print("** class name missing **") - except NameError: + elif commands[0] not in self.valid_classes: print("** class doesn't exist **") - except IndexError: + elif len(commands) < 2: print("** instance id missing **") - except KeyError: - print("** no instance found **") - except AttributeError: - print("** attribute name missing **") - except ValueError: - print("** value missing **") - - 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() + else: 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 **") - 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 + key = "{}.{}".format(commands[0], commands[1]) + if key not in objects: + print("** no instance found **") + elif len(commands) < 3: + print("** attribute name missing **") + elif len(commands) < 4: + print("** value missing **") + else: + obj = objects[key] + curly_braces = re.search(r"\{(.*?)\}", arg) + + if curly_braces: + # added to catch errors + try: + str_data = curly_braces.group(1) + + arg_dict = ast.literal_eval("{" + str_data + "}") + + attribute_names = list(arg_dict.keys()) + attribute_values = list(arg_dict.values()) + # added to catch exception + try: + attr_name1 = attribute_names[0] + attr_value1 = attribute_values[0] + setattr(obj, attr_name1, attr_value1) + except Exception: + pass + try: + # added to catch exception + attr_name2 = attribute_names[1] + attr_value2 = attribute_values[1] + setattr(obj, attr_name2, attr_value2) + except Exception: + pass + except Exception: + pass + else: + + attr_name = commands[2] + attr_value = commands[3] + + try: + attr_value = eval(attr_value) + except Exception: + pass + setattr(obj, attr_name, attr_value) + + obj.save() + + def default(self, arg): """ - 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 + Default behavior for cmd module when input is invalid """ - 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) + arg_list = arg.split('.') + + cls_nm = arg_list[0] # incoming class name + + command = arg_list[1].split('(') + cmd_met = command[0] # incoming command method + + e_arg = command[1].split(')')[0] # extra arguments + + method_dict = { + 'all': self.do_all, + 'show': self.do_show, + 'destroy': self.do_destroy, + 'update': self.do_update, + 'count': self.do_count + } + + if cmd_met in method_dict.keys(): + if cmd_met != "update": + return method_dict[cmd_met]("{} {}".format(cls_nm, e_arg)) + else: + if not cls_nm: + print("** class name missing **") + return + try: + obj_id, arg_dict = split_curly_braces(e_arg) + except Exception: + pass + try: + call = method_dict[cmd_met] + return call("{} {} {}".format(cls_nm, obj_id, arg_dict)) + except Exception: + pass + else: + print("*** Unknown syntax: {}".format(arg)) + return False + if __name__ == '__main__': HBNBCommand().cmdloop() From 8b7ee148f00eec3a78979d4b8eae3e641c70a9b5 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 09:29:36 +0300 Subject: [PATCH 19/42] console --- tests/test_console.py | 185 +++++++++--------------------------------- 1 file changed, 39 insertions(+), 146 deletions(-) diff --git a/tests/test_console.py b/tests/test_console.py index dcd6cf212950..015e5c46886a 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -1,148 +1,41 @@ #!/usr/bin/python3 -"""Defines unittests for console.py.""" -import os -import unittest -from unittest.mock import patch -from io import StringIO -from console import HBNBCommand -from models.engine.file_storage import FileStorage - - -class TestHBNBCommand(unittest.TestCase): - """Unittests for testing the HBNB command interpreter.""" - - @classmethod - def setUpClass(cls): - """HBNBCommand testing setup. - - Temporarily rename any existing file.json. - Reset FileStorage objects dictionary. - Create an instance of the command interpreter. - """ - try: - os.rename("file.json", "tmp") - except IOError: - pass - # Create an instance of the HBNBCommand class. This allows the test - # methods within the class to access and use this instance during the - # testing process. - cls.HBNB = HBNBCommand() - - @classmethod - def tearDownClass(cls): - """HBNBCommand testing teardown. - - Restore original file.json. - Delete the test HBNBCommand instance. - """ - try: - os.rename("tmp", "file.json") - except IOError: - pass - del cls.HBNB - - def setUp(self): - """Reset FileStorage objects dictionary.""" - FileStorage._FileStorage__objects = {} - - def tearDown(self): - """Delete any created file.json.""" - try: - os.remove("file.json") - except IOError: - pass - - def test_create_for_errors(self): - """Test create command errors.""" - # Test if class name is missing - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create") - self.assertEqual( - "** class name missing **\n", f.getvalue()) - # Test if class doesn't exist - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create asdfsfsd") - self.assertEqual( - "** class doesn't exist **\n", f.getvalue()) +""" +Contains the class TestConsoleDocs +""" - def test_create_command_validity(self): - """Test create command.""" - # Create BaseModel instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create BaseModel") - bm = f.getvalue().strip() - - # Create User instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create User") - us = f.getvalue().strip() - - # Create State instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create State") - st = f.getvalue().strip() - - # Create Place instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create Place") - pl = f.getvalue().strip() - - # Create City instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create City") - ct = f.getvalue().strip() - - # Create Review instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create Review") - rv = f.getvalue().strip() - - # Create Amenity instance and capture its ID - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("create Amenity") - am = f.getvalue().strip() - # Test if the created instances are in the output of "all" command - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all BaseModel") - self.assertIn(bm, f.getvalue()) - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all User") - self.assertIn(us, f.getvalue()) - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all State") - self.assertIn(st, f.getvalue()) - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all Place") - self.assertIn(pl, f.getvalue()) - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all City") - self.assertIn(ct, f.getvalue()) - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all Review") - self.assertIn(rv, f.getvalue()) - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all Amenity") - self.assertIn(am, f.getvalue()) - - def test_create_command_with_kwargs(self): - """Test create command with kwargs.""" - # Test create command with additional key-value pairs - with patch("sys.stdout", new=StringIO()) as f: - call = (f'create Place city_id="0001" name="My_house" number_rooms=4 latitude=37.77 longitude=43.434') # noqa - self.HBNB.onecmd(call) - pl = f.getvalue().strip() - # Test if the created instance and kwargs are in the - # output of "all" command - with patch("sys.stdout", new=StringIO()) as f: - self.HBNB.onecmd("all Place") - output = f.getvalue() - self.assertIn(pl, output) - self.assertIn("'city_id': '0001'", output) - self.assertIn("'name': 'My house'", output) - self.assertIn("'number_rooms': 4", output) - self.assertIn("'latitude': 37.77", output) - self.assertIn("'longitude': 43.434", output) - - -if __name__ == "__main__": - unittest.main() +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") From 8262ac636c98688e4d7f4a9704fe3bbcd567fe84 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 09:30:37 +0300 Subject: [PATCH 20/42] console --- console.py | 461 ++++++++++++++++++++++++----------------------------- 1 file changed, 207 insertions(+), 254 deletions(-) diff --git a/console.py b/console.py index 80b4038f6399..6c4b5606f057 100755 --- a/console.py +++ b/console.py @@ -1,105 +1,58 @@ #!/usr/bin/python3 -""" -Module for console -""" +"""Defines the HBNB console.""" import cmd -import re -import shlex -import ast +from shlex import split from models import storage +from datetime import datetime 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 models.state import State -from models.city import City - - -def split_curly_braces(e_arg): - """ - Splits the curly braces for the update method - """ - curly_braces = re.search(r"\{(.*?)\}", e_arg) - - if curly_braces: - id_with_comma = shlex.split(e_arg[:curly_braces.span()[0]]) - id = [i.strip(",") for i in id_with_comma][0] - - str_data = curly_braces.group(1) - try: - arg_dict = ast.literal_eval("{" + str_data + "}") - except Exception: - print("** invalid dictionary format **") - return - return id, arg_dict - else: - commands = e_arg.split(",") - if commands: - try: - id = commands[0] - except Exception: - return "", "" - try: - attr_name = commands[1] - except Exception: - return id, "" - try: - attr_value = commands[2] - except Exception: - return id, attr_name - return f"{id}", f"{attr_name} {attr_value}" class HBNBCommand(cmd.Cmd): - """ - HBNBCommand console class - """ + """Defines the HolbertonBnB command interpreter.""" + prompt = "(hbnb) " - valid_classes = ["BaseModel", "User", "Amenity", - "Place", "Review", "State", "City"] + __classes = { + "BaseModel", + "User", + "State", + "City", + "Amenity", + "Place", + "Review" + } def emptyline(self): - """ - Do nothing when an empty line is entered. - """ + """Ignore empty spaces.""" pass - def do_EOF(self, arg): - """ - EOF (Ctrl+D) signal to exit the program. - """ + def do_quit(self, line): + """Quit command to exit the program.""" return True - def do_quit(self, arg): - """ - Quit command to exit the program. - """ + def do_EOF(self, line): + """EOF signal to exit the program.""" + print("") return True - - def do_create(self, arg): - """ - Create a new instance of BaseModel and save it to the JSON file. - Usage: create + def do_create(self, line): + """Usage: create = = ... + Create a new class instance with given keys/values and print its id. """ try: - class_name = arg.split(" ")[0] - if len(class_name) == 0: - print("** class name missing **") - return - if class_name and class_name not in self.valid_classes: - print("** class doesn't exist **") - return + if not line: + raise SyntaxError() + my_list = line.split(" ") kwargs = {} - commands = arg.split(" ") - for i in range(1, len(commands)): - - key = commands[i].split("=")[0] - value = commands[i].split("=")[1] - #key, value = tuple(commands[i].split("=")) - if value.startswith('"'): + for i in range(1, len(my_list)): + key, value = tuple(my_list[i].split("=")) + if value[0] == '"': value = value.strip('"').replace("_", " ") else: try: @@ -109,211 +62,211 @@ def do_create(self, arg): kwargs[key] = value if kwargs == {}: - new_instance = eval(class_name)() + obj = eval(my_list[0])() else: - new_instance = eval(class_name)(**kwargs) - storage.new(new_instance) - print(new_instance.id) - storage.save() - except ValueError: - print(ValueError) - return - - def do_show(self, arg): - """ - Show the string representation of an instance. - Usage: show - """ - commands = shlex.split(arg) + obj = eval(my_list[0])(**kwargs) + storage.new(obj) + print(obj.id) + obj.save() - if len(commands) == 0: + except SyntaxError: print("** class name missing **") - elif commands[0] not in self.valid_classes: + except NameError: print("** class doesn't exist **") - elif len(commands) < 2: - print("** instance id missing **") - else: - objects = storage.all() - key = "{}.{}".format(commands[0], commands[1]) + 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: - print("** no instance found **") - - def do_destroy(self, arg): - """ - Delete an instance based on the class name and id. - Usage: destroy - """ - commands = shlex.split(arg) - - if len(commands) == 0: + raise KeyError() + except SyntaxError: print("** class name missing **") - elif commands[0] not in self.valid_classes: + except NameError: print("** class doesn't exist **") - elif len(commands) < 2: + except IndexError: print("** instance id missing **") - else: + except KeyError: + print("** no instance found **") + + 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 = "{}.{}".format(commands[0], commands[1]) + key = my_list[0] + '.' + my_list[1] if key in objects: del objects[key] storage.save() else: - print("** no instance found **") - - def do_all(self, arg): - """ - Print the string representation of all instances or a specific class. - Usage: .all() - .show() - """ - objects = storage.all() - - commands = shlex.split(arg) - - if len(commands) == 0: - for key, value in objects.items(): - print(str(value)) - elif commands[0] not in self.valid_classes: + raise KeyError() + except SyntaxError: + print("** class name missing **") + except NameError: print("** class doesn't exist **") - else: - for key, value in objects.items(): - if key.split('.')[0] == commands[0]: - print(str(value)) - - def do_count(self, arg): - """ - Counts and retrieves the number of instances of a class - usage: .count() - """ - objects = storage.all() - - commands = shlex.split(arg) + except IndexError: + print("** instance id missing **") + except KeyError: + print("** no instance found **") + + 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() - if arg: - incoming_class_name = commands[0] - count = 0 + o = storage.all(eval(args[0])) + print([o[k].__str__() for k in o]) - if commands: - if incoming_class_name in self.valid_classes: - for obj in objects.values(): - if obj.__class__.__name__ == incoming_class_name: - count += 1 - print(count) - else: - print("** invalid class name **") - else: - print("** class name missing **") + except NameError: + print("** class doesn't exist **") - def do_update(self, arg): + 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 """ - Update an instance by adding or updating an attribute. - Usage: update "" - """ - commands = shlex.split(arg) - - if len(commands) == 0: + 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 **") - elif commands[0] not in self.valid_classes: + except NameError: print("** class doesn't exist **") - elif len(commands) < 2: + except IndexError: print("** instance id missing **") - else: - objects = storage.all() - - key = "{}.{}".format(commands[0], commands[1]) - if key not in objects: - print("** no instance found **") - elif len(commands) < 3: - print("** attribute name missing **") - elif len(commands) < 4: - print("** value missing **") - else: - obj = objects[key] - curly_braces = re.search(r"\{(.*?)\}", arg) - - if curly_braces: - # added to catch errors - try: - str_data = curly_braces.group(1) - - arg_dict = ast.literal_eval("{" + str_data + "}") - - attribute_names = list(arg_dict.keys()) - attribute_values = list(arg_dict.values()) - # added to catch exception - try: - attr_name1 = attribute_names[0] - attr_value1 = attribute_values[0] - setattr(obj, attr_name1, attr_value1) - except Exception: - pass - try: - # added to catch exception - attr_name2 = attribute_names[1] - attr_value2 = attribute_values[1] - setattr(obj, attr_name2, attr_value2) - except Exception: - pass - except Exception: - pass - else: - - attr_name = commands[2] - attr_value = commands[3] + except KeyError: + print("** no instance found **") + except AttributeError: + print("** attribute name missing **") + except ValueError: + print("** value missing **") - try: - attr_value = eval(attr_value) - except Exception: - pass - setattr(obj, attr_name, attr_value) + 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 **") - obj.save() - - def default(self, arg): + 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 """ - Default behavior for cmd module when input is invalid + 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 """ - arg_list = arg.split('.') - - cls_nm = arg_list[0] # incoming class name - - command = arg_list[1].split('(') - - cmd_met = command[0] # incoming command method - - e_arg = command[1].split(')')[0] # extra arguments - - method_dict = { - 'all': self.do_all, - 'show': self.do_show, - 'destroy': self.do_destroy, - 'update': self.do_update, - 'count': self.do_count - } - - if cmd_met in method_dict.keys(): - if cmd_met != "update": - return method_dict[cmd_met]("{} {}".format(cls_nm, e_arg)) - else: - if not cls_nm: - print("** class name missing **") - return - try: - obj_id, arg_dict = split_curly_braces(e_arg) - except Exception: - pass - try: - call = method_dict[cmd_met] - return call("{} {} {}".format(cls_nm, obj_id, arg_dict)) - except Exception: - pass + 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: - print("*** Unknown syntax: {}".format(arg)) - return False - + cmd.Cmd.default(self, line) + if __name__ == '__main__': HBNBCommand().cmdloop() From c420dac651220f898c60c4171a3c205189ce92db Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 27 Sep 2024 09:37:13 +0300 Subject: [PATCH 21/42] task-user --- models/user.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/models/user.py b/models/user.py index 4b54a6d24120..d15bdfbecffb 100644 --- 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") From 8218df6b8fddc92aece83cc7cfc95edc059cc2a8 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 05:42:36 -0700 Subject: [PATCH 22/42] place --- models/__init__.py | 0 models/amenity.py | 0 models/base_model.py | 0 models/city.py | 0 models/place.py | 77 +++++++++++++++++++++++++++++++++++++------- models/review.py | 0 models/state.py | 0 models/states.py | 0 models/user.py | 0 9 files changed, 65 insertions(+), 12 deletions(-) mode change 100644 => 100755 models/__init__.py mode change 100644 => 100755 models/amenity.py mode change 100644 => 100755 models/base_model.py mode change 100644 => 100755 models/city.py mode change 100644 => 100755 models/place.py mode change 100644 => 100755 models/review.py mode change 100644 => 100755 models/state.py mode change 100644 => 100755 models/states.py mode change 100644 => 100755 models/user.py diff --git a/models/__init__.py b/models/__init__.py old mode 100644 new mode 100755 diff --git a/models/amenity.py b/models/amenity.py old mode 100644 new mode 100755 diff --git a/models/base_model.py b/models/base_model.py old mode 100644 new mode 100755 diff --git a/models/city.py b/models/city.py old mode 100644 new mode 100755 diff --git a/models/place.py b/models/place.py old mode 100644 new mode 100755 index 5221e8210d17..91f7f4e16698 --- a/models/place.py +++ b/models/place.py @@ -1,18 +1,71 @@ #!/usr/bin/python3 """ Place Module for HBNB project """ -from models.base_model import BaseModel +import os +from models.base_model import BaseModel, Base +from sqlalchemy import Column, String, Integer, Float, ForeignKey, Table +from sqlalchemy.orm import relationship +from models.review import Review +from models.amenity import Amenity +import models -class Place(BaseModel): +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): """ 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 + __tablename__ = 'places' + + city_id = Column(String(60), ForeignKey("cities.id", ondelete="CASCADE"), + nullable=False) + user_id = Column(String(60), ForeignKey("users.id", ondelete="CASCADE"), + nullable=False) + name = Column(String(128), nullable=False) + description = Column(String(1024), nullable=True) + 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, nullable=True) + longitude = Column(Float, nullable=True) amenity_ids = [] + + if os.getenv("HBNB_TYPE_STORAGE") == "db": + reviews = relationship("Review", backref="place") + amenities = relationship("Amenity", secondary="place_amenity", + viewonly=False, + back_populates="place_amenities") + + if os.getenv("HBNB_TYPE_STORAGE") != "db": + @property + def reviews(self): + """Returns the list of Review instances with place_id equals + to the current Place.id.""" + + reviews = list(models.storage.all(Review).values()) + + return list( + filter(lambda review: (review.place_id == self.id), reviews)) + + @property + def amenities(self): + """Returns the list of Amenity instances based on + the attribute amenity_ids that contains all Amenity.id.""" + + amenities = list(models.storage.all(Amenity).values()) + + return list( + filter(lambda amenity: (amenity.place_id in self.amenity_ids), + amenities)) + + @amenities.setter + def amenities(self, value=None): + """Adds ids in amenity_ids .""" + if type(value) == type(Amenity): + self.amenity_ids.append(value.id) diff --git a/models/review.py b/models/review.py old mode 100644 new mode 100755 diff --git a/models/state.py b/models/state.py old mode 100644 new mode 100755 diff --git a/models/states.py b/models/states.py old mode 100644 new mode 100755 diff --git a/models/user.py b/models/user.py old mode 100644 new mode 100755 From 566cd7a8c2df66af12059b2fe32611fa984b3a33 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 05:51:54 -0700 Subject: [PATCH 23/42] users --- models/user.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/models/user.py b/models/user.py index d15bdfbecffb..c7b8aa12ddca 100755 --- a/models/user.py +++ b/models/user.py @@ -1,27 +1,16 @@ #!/usr/bin/python3 -"""This is the user class""" -from sqlalchemy.ext.declarative import declarative_base +"""This module defines a class User""" from models.base_model import BaseModel, Base -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, String from sqlalchemy.orm import relationship -from models.place import Place -from models.review import Review 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) + """This class defines a user by various attributes""" + __tablename__ = 'users' + email = Column(String(128), nullable=False, unique=True) 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") + places = relationship("Place", backref="user", cascade="delete") + reviews = relationship("Review", backref="user", cascade="delete") From dc67f449faf2351e6bb419cdb878ad5f2af27c10 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 06:15:12 -0700 Subject: [PATCH 24/42] places --- models/place.py | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/models/place.py b/models/place.py index 91f7f4e16698..18aa56f25993 100755 --- a/models/place.py +++ b/models/place.py @@ -35,37 +35,3 @@ class Place(BaseModel, Base): latitude = Column(Float, nullable=True) longitude = Column(Float, nullable=True) amenity_ids = [] - - if os.getenv("HBNB_TYPE_STORAGE") == "db": - reviews = relationship("Review", backref="place") - amenities = relationship("Amenity", secondary="place_amenity", - viewonly=False, - back_populates="place_amenities") - - if os.getenv("HBNB_TYPE_STORAGE") != "db": - @property - def reviews(self): - """Returns the list of Review instances with place_id equals - to the current Place.id.""" - - reviews = list(models.storage.all(Review).values()) - - return list( - filter(lambda review: (review.place_id == self.id), reviews)) - - @property - def amenities(self): - """Returns the list of Amenity instances based on - the attribute amenity_ids that contains all Amenity.id.""" - - amenities = list(models.storage.all(Amenity).values()) - - return list( - filter(lambda amenity: (amenity.place_id in self.amenity_ids), - amenities)) - - @amenities.setter - def amenities(self, value=None): - """Adds ids in amenity_ids .""" - if type(value) == type(Amenity): - self.amenity_ids.append(value.id) From e0a31f38fad0564941e3a2ed5a771dce22ff6ea3 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 06:21:09 -0700 Subject: [PATCH 25/42] places --- models/place.py | 92 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/models/place.py b/models/place.py index 18aa56f25993..1b795adda7c5 100755 --- a/models/place.py +++ b/models/place.py @@ -1,37 +1,83 @@ #!/usr/bin/python3 -""" Place Module for HBNB project """ -import os - +"""This is the place class""" +from sqlalchemy.ext.declarative import declarative_base from models.base_model import BaseModel, Base -from sqlalchemy import Column, String, Integer, Float, ForeignKey, Table +from sqlalchemy import Column, Table, String, Integer, Float, ForeignKey from sqlalchemy.orm import relationship -from models.review import Review -from models.amenity import Amenity +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)) +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): - """ A place to stay """ - __tablename__ = 'places' - city_id = Column(String(60), ForeignKey("cities.id", ondelete="CASCADE"), - nullable=False) - user_id = Column(String(60), ForeignKey("users.id", ondelete="CASCADE"), - 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), nullable=True) + 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, nullable=True) - longitude = Column(Float, nullable=True) + 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) From 2bd53439c920273bdcb5a03d9c24fb9fc18b33c4 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 07:03:57 -0700 Subject: [PATCH 26/42] clone --- models/place.py | 96 +++++++++---------------------------------------- models/user.py | 25 +++++++++---- 2 files changed, 34 insertions(+), 87 deletions(-) diff --git a/models/place.py b/models/place.py index 1b795adda7c5..4b8f64e93b27 100755 --- a/models/place.py +++ b/models/place.py @@ -1,83 +1,19 @@ #!/usr/bin/python3 -"""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) +""" 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 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/user.py b/models/user.py index c7b8aa12ddca..d15bdfbecffb 100755 --- a/models/user.py +++ b/models/user.py @@ -1,16 +1,27 @@ #!/usr/bin/python3 -"""This module defines a class User""" +"""This is the user class""" +from sqlalchemy.ext.declarative import declarative_base from models.base_model import BaseModel, Base -from sqlalchemy import Column, String +from sqlalchemy import Column, Integer, String from sqlalchemy.orm import relationship +from models.place import Place +from models.review import Review class User(BaseModel, Base): - """This class defines a user by various attributes""" - __tablename__ = 'users' - email = Column(String(128), nullable=False, unique=True) + """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", backref="user", cascade="delete") - reviews = relationship("Review", backref="user", cascade="delete") + places = relationship("Place", cascade='all, delete, delete-orphan', + backref="user") + reviews = relationship("Review", cascade='all, delete, delete-orphan', + backref="user") From 6f37023b87b05cf2d6c5f66ec737a156f49e21e8 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 13:37:15 -0700 Subject: [PATCH 27/42] DBStorage - Place --- models/place.py | 96 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 16 deletions(-) diff --git a/models/place.py b/models/place.py index 4b8f64e93b27..1b795adda7c5 100755 --- a/models/place.py +++ b/models/place.py @@ -1,19 +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) From 02aa88cc767d03cf3a3c0743904e8f09bdbe7348 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 13:41:10 -0700 Subject: [PATCH 28/42] DBStorage - Review --- models/review.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/models/review.py b/models/review.py index c487d90d34f0..3fcebb30bc26 100755 --- 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) From ddc3c13a28d17490c7055d0a6f00b761478106bb Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 13:44:05 -0700 Subject: [PATCH 29/42] DBStorage - Amenity... and BOOM! --- models/amenity.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/models/amenity.py b/models/amenity.py index a181095e4170..dcf66a55a39e 100755 --- 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) From ad35a4f29d0509cc52a6e7c5828c7ee949696f1c Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 14:01:38 -0700 Subject: [PATCH 30/42] basemodel --- tests/test_models/test_base_model.py | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) 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) From 73f269088214dc162823be1ecee1a496dcedc92a Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 14:11:16 -0700 Subject: [PATCH 31/42] test engine --- .../test_engine/test_db_storage.py | 103 ++++++++++++++++++ .../test_engine/test_file_storage.py | 47 ++++---- 2 files changed, 129 insertions(+), 21 deletions(-) create mode 100755 tests/test_models/test_engine/test_db_storage.py 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 fda4105f66a6..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) From 96740a39b26a01eb51117ae5ac33b766210dc9cb Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 14:17:46 -0700 Subject: [PATCH 32/42] test uers --- tests/test_models/test_user.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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) From f6cb1dbb3d8752a6ac29f018310f122c94e67292 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 14:26:39 -0700 Subject: [PATCH 33/42] tests --- tests/test_models/test_amenity.py | 7 ++++--- tests/test_models/test_city.py | 10 +++++++--- tests/test_models/test_place.py | 22 ++++++++++++++++++---- tests/test_models/test_review.py | 13 ++++++++++--- tests/test_models/test_state.py | 9 +++++---- 5 files changed, 44 insertions(+), 17 deletions(-) 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_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_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) From d283c5d4beda5aac765a9e4badcf4367868fe613 Mon Sep 17 00:00:00 2001 From: usanas7 Date: Fri, 27 Sep 2024 14:38:04 -0700 Subject: [PATCH 34/42] file --- models/engine/__init__.py | 0 models/engine/db_storage.py | 0 models/engine/file_storage.py | 104 +++++++++++++++++----------------- 3 files changed, 51 insertions(+), 53 deletions(-) mode change 100644 => 100755 models/engine/__init__.py mode change 100644 => 100755 models/engine/db_storage.py mode change 100644 => 100755 models/engine/file_storage.py 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 old mode 100644 new mode 100755 diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py old mode 100644 new mode 100755 index f3ea2bac49b5..60196ed0bb36 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,82 +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.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 +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, cls=None): - """Returns a dictionary of models currently in storage. - - Args: - cls (class, optional): If specified, filters the result to include - only objects of the specified class. - - Returns: - dict: A dictionary containing objects in storage. + """returns a dictionary + Return: + returns a dictionary of __object """ + dic = {} if cls: - if isinstance(cls, str): - cls = globals().get(cls) - if cls and issubclass(cls, BaseModel): - cls_dict = {k: v for k, - v in self.__objects.items() if isinstance(v, cls)} - return cls_dict - return FileStorage.__objects + 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.""" - 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 - except json.decoder.JSONDecodeError: - pass def delete(self, obj=None): + """ delete an existing element """ - Delete obj from __objects if it’s inside - if obj is equal to None, - the method should not do anything - """ - if obj is None: - return - obj_to_del = f"{obj.__class__.__name__}.{obj.id}" + if obj: + key = "{}.{}".format(type(obj).__name__, obj.id) + del self.__objects[key] - try: - del FileStorage.__objects[obj_to_del] - except AttributeError: - pass - except KeyboardInterrupt: - pass + def close(self): + """ calls reload() + """ + self.reload() From dfb7b124f6e718a189be5da94443f772309328d8 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 15:17:13 +0000 Subject: [PATCH 35/42] webstatic.sh --- 0-setup_web_static.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100755 0-setup_web_static.sh diff --git a/0-setup_web_static.sh b/0-setup_web_static.sh new file mode 100755 index 000000000000..d1aff47f357e --- /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 'Mediqueueless' | 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 From 983169099738462866c89ec1e01a7891ff6fb0ea Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 15:21:20 +0000 Subject: [PATCH 36/42] web_stat.sh --- 0-setup_web_static.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/0-setup_web_static.sh b/0-setup_web_static.sh index d1aff47f357e..57691d7312c4 100755 --- a/0-setup_web_static.sh +++ b/0-setup_web_static.sh @@ -7,7 +7,7 @@ sudo service nginx start sudo mkdir -p /data/web_static/shared/ sudo mkdir -p /data/web_static/releases/test/ # Creating a fake HTML file with simple content -echo 'Mediqueueless' | sudo tee /data/web_static/releases/test/index.html +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 From 08f24669237297d6c7240598bac211b38cd18b20 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 15:25:35 +0000 Subject: [PATCH 37/42] pack_web --- 1-pack_web_static.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100755 1-pack_web_static.py 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 From 296d85baf8c9648842ac74c1cef8d322c0f7e21a Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 15:29:57 +0000 Subject: [PATCH 38/42] deploy --- 2-do_deploy_web_static.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 2-do_deploy_web_static.py diff --git a/2-do_deploy_web_static.py b/2-do_deploy_web_static.py new file mode 100755 index 000000000000..51fd4ccb0cc3 --- /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 = ["54.210.17.29", "54.234.24.157"] +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 From e3a4908363aaea19b64d7f871665d99c7a963906 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 15:34:49 +0000 Subject: [PATCH 39/42] 3deploy --- 3-deploy_web_static.py | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 3-deploy_web_static.py diff --git a/3-deploy_web_static.py b/3-deploy_web_static.py new file mode 100755 index 000000000000..aeba2f90b65b --- /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 = ["54.210.17.29", "54.234.24.157"] +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) From 71801ccf8bbaa0f241018868d27ea1be67ef9bf1 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 16:17:00 +0000 Subject: [PATCH 40/42] wilsdep --- 2-do_deploy_web_static.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-do_deploy_web_static.py b/2-do_deploy_web_static.py index 51fd4ccb0cc3..e5983fd42f09 100755 --- a/2-do_deploy_web_static.py +++ b/2-do_deploy_web_static.py @@ -3,7 +3,7 @@ from fabric.api import env, put, run from os.path import exists -env.hosts = ["54.210.17.29", "54.234.24.157"] +env.hosts = ["98.84.116.96", "3.90.179.12"] env.user = "ubuntu" env.key = "~/.ssh/id_rsa" From ba12203812c907162d87d10c2853b4bfa551e60b Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Wed, 2 Oct 2024 16:24:26 +0000 Subject: [PATCH 41/42] debug --- 3-deploy_web_static.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3-deploy_web_static.py b/3-deploy_web_static.py index aeba2f90b65b..af166e3e7e53 100755 --- a/3-deploy_web_static.py +++ b/3-deploy_web_static.py @@ -6,7 +6,7 @@ from datetime import datetime from os.path import exists -env.hosts = ["54.210.17.29", "54.234.24.157"] +env.hosts = ["98.84.116.96", "3.90.179.12"] env.user = "ubuntu" env.key = "~/.ssh/id_rsa" From dea60338f07f9be66dabea37f21037b8526589e7 Mon Sep 17 00:00:00 2001 From: Wilsons-Navid Date: Fri, 4 Oct 2024 07:27:01 +0300 Subject: [PATCH 42/42] gh --- 2-do_deploy_web_static.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-do_deploy_web_static.py b/2-do_deploy_web_static.py index e5983fd42f09..2526b6da036d 100755 --- a/2-do_deploy_web_static.py +++ b/2-do_deploy_web_static.py @@ -3,7 +3,7 @@ from fabric.api import env, put, run from os.path import exists -env.hosts = ["98.84.116.96", "3.90.179.12"] +env.hosts = ["3.88.201.199", "3.87.90.59"] env.user = "ubuntu" env.key = "~/.ssh/id_rsa"