click here to see the web static mvp
The AirBnB clone project starts now until… the end of the first year. The goal of the project is to deploy on your server a simple copy of the AirBnB website. After 4 months, you will have a complete web application composed by:
- A command interpreter to manipulate data without a visual interface, like in a Shell (perfect for development and debugging)
- A website (the front-end) that shows the final product to everybody: static and dynamic
- A database or files that store data (data = objects)
- An API that provides a communication interface between the front-end and your data (retrieve, create, delete, update them)
And the final data diagram looks like this:
This is the first step towards building the first full web application: the AirBnB clone. This first step is very important because we will use what we build during this project with all other following projects: HTML/CSS templating, database storage, API, front-end integration…
Do you remember the Shell? It’s exactly the same but limited to a specific use-case. In our case, we want to be able to manage the objects of our project:
- Create a new object (ex: a new User or a new Place)
- Retrieve an object from a file, a database etc…
- Do operations on objects (count, compute stats, etc…)
- Update attributes of an object
- Destroy an object
Read or watch:
- cmd module
- packages concept page
- uuid module
- datetime
- unittest module
- args/kwargs
- Python test cheatsheet
- How to create a Python package
- How to create a command interpreter in Python using the cmd module
- What is Unit testing and how to implement it in a large project
- How to serialize and deserialize a Class
- How to write and read a JSON file
- How to manage datetime
- What is an UUID
- What is *args and how to use it
- What is **kwargs and how to use it
- How to handle named arguments in a function
The hbnb command interpreter should work like this in interactive mode:
$ ./console.py
(hbnb) help
Documented commands (type help <topic>):
========================================
EOF help quit
(hbnb)
(hbnb)
(hbnb) quit
$
But also in non-interactive mode: (like the Shell project in C):
$ echo "help" | ./console.py
(hbnb)
Documented commands (type help <topic>):
========================================
EOF help quit
(hbnb)
$
$ cat test_help
help
$
$ cat test_help | ./console.py
(hbnb)
Documented commands (type help <topic>):
========================================
EOF help quit
(hbnb)
$
All tests should also pass in non-interactive mode: $ echo "python3 -m unittest discover tests" | bash
## | File | Description |
---|---|---|
0 | console.py | command interpreter to manage your AirBnB objects: Create a new object (ex: a new User or a new Place) ; Retrieve an; object from a file, a database etc… ; Do operations on objects (count, compute stats, etc…); Update attributes of an object; Destroy an object |
1 | models | directory of all the classes |
2 | tests | directory of console test and class tests |
you can run it writting ./console.py
in your terminal and you will enter to the command interpreter like you see in this example, after it you can use the commands allowed for the terminal.
guillaume@ubuntu:~/AirBnB$ ./console.py
(hbnb) help
Documented commands (type help <topic>):
========================================
EOF help quit
(hbnb)
(hbnb) help quit
Quit command to exit the program
(hbnb)
(hbnb)
(hbnb) quit
guillaume@ubuntu:~/AirBnB$
## | Commands | how to use it in the command interpreter | Instance form | Description |
---|---|---|---|---|
0.0 | quit | quit |
Exit the program | |
0.1 | EOF | EOF |
Exit the program | |
0.2 | empty line | |
not do nothing | |
0.3 | create | create <class name> |
create an instance of the class | |
0.4 | show | show <class name> <id number> |
<class name>.show(<id>) |
Prints the string representation of an instance based on the class name and id |
0.5 | destroy | destroy <class name> <id number> |
<class name>.destroy(<id>) |
Deletes an instance based on the class name and id (save the change into the JSON file) |
0.6 | all | all or all <class name> |
<class name>.all() |
Prints all string representation of all instances based or not on the class name |
0.7 | update | update <class name> <id number> <attribute to update> "<new value of attribute>" |
simple form:<class name>.update(<id>, <attribute name>, <attribute value>) update more than 1 attribute(using dictionaries): <class name>.update(<id>, <dictionary representation>) |
Updates an instance based on the class name and id by adding or updating attribute (save the change into the JSON file). If there are more commands, the command interpreter will only count the first attribute with its value |
0.8 | count | count <class name> |
<class name>.count() |
retrieve the number of instances of a class |
## | Allowed classes |
---|---|
a | BaseModel |
b | User |
c | Place |
d | State |
e | City |
f | Amenity |
g | Review |
h | User |
- help:
(hbnb) help
Documented commands (type help <topic>):
========================================
EOF help quit
- quit and EOF: quits
(hbnb) quit
vagrant@ubuntu:~/AirBnB$
(hbnb) quit
vagrant@ubuntu:~/AirBnB$
- create:
(hbnb) create BaseModel
2dd6ef5c-467c-4f82-9521-a772ea7d84e9
(hbnb) create User
35dd5991-c54f-4e33-a4c4-2be5219cc15e
- all:
(hbnb) all BaseModel
["[BaseModel] (49faff9a-6318-451f-87b6-910505c55907) {'created_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903293), 'id': '49faff9a-6318-451f-87b6-910505c55907', 'updated_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903300)}"]
- show:
(hbnb) show BaseModel 49faff9a-6318-451f-87b6-910505c55907
[BaseModel] (49faff9a-6318-451f-87b6-910505c55907) {'created_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903293), 'id': '49faff9a-6318-451f-87b6-910505c55907', 'updated_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903300)}
- destroy:
(hbnb) destroy
** class name missing **
- update:
(hbnb) show BaseModel 49faff9a-6318-451f-87b6-910505c55907
[BaseModel] (49faff9a-6318-451f-87b6-910505c55907) {'created_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903293), 'id': '49faff9a-6318-451f-87b6-910505c55907', 'updated_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903300)}
(hbnb) update BaseModel 49faff9a-6318-451f-87b6-910505c55907 first_name "Betty"
(hbnb) show BaseModel 49faff9a-6318-451f-87b6-910505c55907
[BaseModel] (49faff9a-6318-451f-87b6-910505c55907) {'first_name': 'Betty', 'id': '49faff9a-6318-451f-87b6-910505c55907', 'created_at': datetime.datetime(2017, 10, 2, 3, 10, 25, 903293), 'updated_at': datetime.datetime(2017, 10, 2, 3, 11, 3, 49401)}
- all or all BaseModel:
(hbnb) create User
35dd5991-c54f-4e33-a4c4-2be5219cc15e
(hbnb) create BaseModel
2c181221-b41f-47f9-bf2a-9e7bc53126a1
(hbnb) all BaseModel
["[BaseModel] (2c181221-b41f-47f9-bf2a-9e7bc53126a1) {'id': '2c181221-b41f-47f9-bf2a-9e7bc53126a1', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 34, 306736), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 34, 306804)}", "[User] (35dd5991-c54f-4e33-a4c4-2be5219cc15e) {'id': '35dd5991-c54f-4e33-a4c4-2be5219cc15e', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151113), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151209)}"]
(hbnb) all
["[BaseModel] (2c181221-b41f-47f9-bf2a-9e7bc53126a1) {'id': '2c181221-b41f-47f9-bf2a-9e7bc53126a1', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 34, 306736), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 34, 306804)}", "[User] (35dd5991-c54f-4e33-a4c4-2be5219cc15e) {'id': '35dd5991-c54f-4e33-a4c4-2be5219cc15e', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151113), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151209)}"]
(hbnb) all User
["[User] (35dd5991-c54f-4e33-a4c4-2be5219cc15e) {'id': '35dd5991-c54f-4e33-a4c4-2be5219cc15e', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151113), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151209)}"]
(hbnb)
<class name>.all()
:
hbnb) User.all()
["[User] (35dd5991-c54f-4e33-a4c4-2be5219cc15e) {'id': '35dd5991-c54f-4e33-a4c4-2be5219cc15e', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151113), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151209)}", "[User] (03be30e8-d686-4b4f-bdb0-66180bb76c62) {'id': '03be30e8-d686-4b4f-bdb0-66180bb76c62', 'created_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274519), 'updated_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274584)}"]
<class name>.count()
:
(hbnb) User.count()
2
<class name>.show(<id>)
:
(hbnb) User.show("35dd5991-c54f-4e33-a4c4-2be5219cc15e")
[User] (35dd5991-c54f-4e33-a4c4-2be5219cc15e) {'id': '35dd5991-c54f-4e33-a4c4-2be5219cc15e', 'created_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151113), 'updated_at': datetime.datetime(2021, 7, 1, 4, 46, 0, 151209)}
(hbnb)
<class name>.destroy(<id>)
:
(hbnb) User.count()
2
(hbnb) User.destroy("35dd5991-c54f-4e33-a4c4-2be5219cc15e")
(hbnb) User.count()
1
(hbnb)
- update
(hbnb) User.show("03be30e8-d686-4b4f-bdb0-66180bb76c62")[User] (03be30e8-d686-4b4f-bdb0-66180bb76c62) {'id': '03be30e8-d686-4b4f-bdb0-66180bb76c62', 'created_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274519), 'updated_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274584)}
(hbnb) User.update("03be30e8-d686-4b4f-bdb0-66180bb76c62", "first_name", "John")
(hbnb) User.show("03be30e8-d686-4b4f-bdb0-66180bb76c62")[User] (03be30e8-d686-4b4f-bdb0-66180bb76c62) {'updated_at': datetime.datetime(2021, 7, 1, 4, 53, 12, 110537), 'id': '03be30e8-d686-4b4f-bdb0-66180bb76c62', 'first_name': '"John"', 'created_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274519)}
More than one command:
(hbnb) User.show("03be30e8-d686-4b4f-bdb0-66180bb76c62")[User] (03be30e8-d686-4b4f-bdb0-66180bb76c62) {'updated_at': datetime.datetime(2021, 7, 1, 4, 53, 12, 110537), 'id': '03be30e8-d686-4b4f-bdb0-66180bb76c62', 'first_name': '"John"', 'created_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274519)}
(hbnb) User.update("03be30e8-d686-4b4f-bdb0-66180bb76c62", {'first_name': "Goku", "age": 89})
(hbnb) User.show("03be30e8-d686-4b4f-bdb0-66180bb76c62")[User] (03be30e8-d686-4b4f-bdb0-66180bb76c62) {'age': '"89"', 'updated_at': datetime.datetime(2021, 7, 1, 4, 54, 21, 791081), 'id': '03be30e8-d686-4b4f-bdb0-66180bb76c62', 'first_name': '"Goku"', 'created_at': datetime.datetime(2021, 7, 1, 4, 48, 2, 274519)}
## | File | Description | Recommendations |
---|---|---|---|
1.0 | engine | directory of Store first object | The first way you will see here is to save these objects to a file with dictionaries: <class 'BaseModel'> -> to_dict() -> <class 'dict'> -> <class 'BaseModel'> |
1.1 | init.py | initialization code for the package | files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name |
1.2 | amenity.py | Amenity class | Inherits from BaseModel and contains specific public attributes |
1.3 | base_model.py | Base Model class | Defines all common attributes/methods for other classes sach as id, datetime |
1.4 | city.py | City Class | Inherits from BaseModel and contains specific public attributes |
1.5 | place.py | Place Class | Inherits from BaseModel and contains specific public attributes |
1.6 | review.py | Review Class | Inherits from BaseModel and contains specific public attributes |
1.7 | state.py | State Class | Inherits from BaseModel and contains specific public attributes |
1.8 | user.py | User Class | Inherits from BaseModel and contains specific public attributes |
Every time that the program is launched, we will save these objects to a file: In this project, we converted the dictionary representation to a JSON string. JSON is a standard representation of a data structure. With this format, humans can read and all programming languages have a JSON reader and writer.
The flow of serialization-deserialization is:
<class 'BaseModel'> -> to_dict() -> <class 'dict'> -> JSON dump -> <class 'str'> -> FILE -> <class 'str'> -> JSON load -> <class 'dict'> -> <class 'BaseModel'>
## | File | Description | Recommendations |
---|---|---|---|
1.0.0 | init.py | initialization code for the package | files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name |
1.0.1 | file_storage.py |
All files, classes, functions must be tested with unit tests
guillaume@ubuntu:~/AirBnB$ python3 -m unittest discover tests
...................................................................................
...................................................................................
.......................
----------------------------------------------------------------------
Ran 189 tests in 13.135s
OK
guillaume@ubuntu:~/AirBnB$
Unit tests must also pass in non-interactive mode:
guillaume@ubuntu:~/AirBnB$ echo "python3 -m unittest discover tests" | bash
...................................................................................
...................................................................................
.......................
----------------------------------------------------------------------
Ran 189 tests in 13.135s
OK
guillaume@ubuntu:~/AirBnB$
## | File | Description |
---|---|---|
2.0 | test_models | Test of the models class |
2.1 | init.py | initialization code for the package: files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name |
2.2 | test_console.py | Test of the console |
## | File | Description |
---|---|---|
2.0.0 | test_engine | Directory where the project tests all the tests for the storage of the program |
2.0.1 | init.py | initialization code for the package: files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name |
2.0.2 | test_amenity.py | Testing Amenity class- Comproving expectect outputs and documentation |
2.0.3 | test_base_model.py | Testing BaseModel- Comproving expectect outputs and documentation |
2.0.4 | test_city.py | Test City Class - Comproving expectect outputs and documentation |
2.0.5 | test_place.py | Test place - Comproving expectect outputs and documentation |
2.0.6 | test_review.py | Test Review - Comproving expectect outputs and documentation |
2.0.7 | test_state.py | Test state - Comproving expectect outputs and documentation |
2.0.8 | test_user.py | Test User - Comproving expectect outputs and documentation |
## | File | Description |
---|---|---|
2.0.0.0 | init.py | initialization code for the package: files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name |
2.0.0.1 | test_file_storage.py | Test if the process and the allocation is correct(ouput and process) |