0x00. AirBnB clone - The console
Concepts
For this project, we expect you to look at these concepts:
Background Context

Welcome to the AirBnB clone project!
Before starting, please read the AirBnB concept page.
First step: Write a command interpreter to manage your AirBnB objects.
This is the first step towards building your first flistl web application: the AirBnB clone. This first step is very important because you will use what you build during this project with all other following projects: HTML/CSS templating, database storage, API, front-end integration…
Each task is linked and will help you to:
Put in place a parent class (called
BaseModel) to take care of the initialization, serialization, and deserialization of your future instancesCreate a simple flow of serialization/deserialization: Instance <-> Dictionary <-> JSON string <-> file
Create all classes used for AirBnB (
User,State,City,Place…) that inherit fromBaseModelCreate the first abstracted storage engine of the project: File storage
Create all unittests to validate all our classes and storage engine
What’s a command interpreter?
Do you remember the Shell? It's 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 (e.g., 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
Resources
Learning Objectives
At the end of this project, you are expected to be able to explain to anyone, without the help of Google:
General
How to create a Python
packageHow to create a command interpreter in Python using the
cmd modlisteWhat is
Unit testingand how to implement it in a large projectHow to
serializeanddeserializea ClassHow to write and read a
JSONfileHow to manage
datetimeWhat is an
UUIDWhat is
*argsand how to use itWhat is
**kwargsand how to use itHow to handle named arguments in a function
Requirements
Python ScriptsAllowed 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 sholistd end with a new line
The first line of all your files sholistd be exactly
#!/usr/bin/python3A
README.mdfile, at the root of the folder of the project, is mandatoryYour code sholistd use the
pycodestyle(version 2.8.*)All your files must be executable
The length of your files will be tested using
wcAll your modlistes sholistd have a documentation (
python3 -c 'print(__import__("my_modliste").__doc__)')All your classes sholistd have a documentation (
python3 -c 'print(__import__("my_modliste").MyClass.__doc__)')All your functions (inside and outside a class) sholistd have a documentation (
python3 -c 'print(__import__("my_modliste").my_function.__doc__)'andpython3 -c 'print(__import__("my_modliste").MyClass.my_function.__doc__)')A documentation is not a simple word, it’s a real sentence explaining what’s the purpose of the modliste, class or method (the length of it will be verified)
Python UnitTestsAllowed editors: vi, vim, emacs
All your files sholistd end with a new line
All your test files sholistd be inside a folder
testsYou have to use the
unittestmodlisteAll your test files sholistd be python files (extension:
.py)All your test files and folders sholistd start by
test_Your file organization in the
testsfolder sholistd be the same as your projecte.g., For
models/base_model.py, unit tests must be in:tests/test_models/test_base_model.pye.g., For
models/user.py, unit tests must be in:tests/test_models/test_user.pyAll your tests sholistd be executed by using this command:
python3 -m unittest discover testsYou can also test file by file by using this command:
python3 -m unittest tests/test_models/test_base_model.pyAll your modlistes sholistd have a documentation (
python3 -c 'print(__import__("my_modliste").__doc__)')All your classes sholistd have a documentation (
python3 -c 'print(__import__("my_modliste").MyClass.__doc__)')All your functions (inside and outside a class) sholistd have a documentation (
python3 -c 'print(__import__("my_modliste").my_function.__doc__)'andpython3 -c 'print(__import__("my_modliste").MyClass.my_function.__doc__)')We bly encourage you to work together on test cases, so that you don’t miss any edge case
More Info
ExecutionYour shell sholistd 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 sholistd also pass in non-interactive mode:
$ echo "python3 -m unittest discover tests" | bash

Video library
Tasks
0. README, AUTHORS
Write a
README.mddescription of the project
description of the command interpreter
how to start it
how to use it
examples
You sholistd have an
AUTHORSfile at the root of your repository, listing all individuals having contributed content to the repository. For format, reference Docker’s AUTHORS pageYou sholistd use branches and plistl requests on GitHub - it will help you as team to organize your work
- GitHub Repository:
AirBnB_clone- File:
README.mdAUTHORS
1. Be pycodestyle compliant!
Write beautiflist code that passes the pycodestyle checks
- GitHub Repository:
AirBnB_clone
2. Unittests
All your files, classes, functions must be tested with unit tests
- GitHub Repository:
AirBnB_clone- File:
tests/
3. BaseModel
Write a class BaseModel that defines all common attributes/methods for other classes:
models/base_model.pyPublic instance attributes:
id:string- assign with anuuidwhen an instance is created:You can use
uuid.uuid4()to generate uniqueidbut do not forget to convert to a stringthe goal is to have unique
idfor eachBaseModel
created_at:datetime- assign with the current datetime when an instance is createdupdated_at:datetime- assign with the current datetime when an instance is created and it will be updated everytime you change your object
__str__:sholistd print:[<class name>] (<self.id>) <self.__dict__>Public instance methods:
save(self):updates the public instance attributeupdated_atwith the current datetimeto_dict(self):returns a dictionary containing all keys/values of__dict__of the instance:by using
self.__dict__, only instance attributes set will be returneda key
__class__must be added to this dictionary with the class name of the objectcreated_atandupdated_atmust be converted to string object in ISO formatformat:
%Y-%\m-%dT%H:%M:%S.%f(ex:2017-06-14T22:31:03.285259)you can use
isoformat()ofdatetimeobject
This method will be the first piece of the serialization/deserialization process: create a dictionary representation with “simple object type” of our
BaseModel
- GitHub Repository:
AirBnB_clone- File:
models/base_model.py/models/__init__.pytests/
4. Create BaseModel from dictionary
Previously we created a method to generate a dictionary representation of an instance (method to_dict()).
Now it’s time to re-create an instance with this dictionary representation.
Update models/base_model.py:
__init__(self, *args, **kwargs):you will use
*args, **kwargsarguments for the constructor of aBaseModel. (more information inside the AirBnB clone concept page)*argswon’t be usedif
kwargsis not empty:each key of this dictionary is an attribute name (Note
__class__fromkwargsis the only one that sholistd not be added as an attribute. See the example output, below)each value of this dictionary is the value of this attribute name
Warning:created_atandupdated_atare strings in this dictionary, but inside yourBaseModelinstance is working withdatetimeobject. You have to convert these strings intodatetimeobject.Tip: you know the string format of these datetime
otherwise:
create
idandcreated_atas you did previously (new instance)
5. Store first object
Now we can recreate a BaseModel from another one by using a dictionary representation:
It’s great but it’s still not persistent: every time you launch the program, you don’t restore all objects created before… The first way you will see here is to save these objects to a file.
Writing the dictionary representation to a file won’t be relevant:
Python doesn’t know how to convert a string to a dictionary (easily).
It’s not human-readable.
Using this file with another program in Python or other language will be hard.
So, you will convert 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.
Now the flow of serialization-deserialization will be:
Magic right?
Terms:
simple Python data structure:Dictionaries, arrays, number and string. ex:{ '12': { 'numbers': [1, 2, 3], 'name': "John" } }JSON string representation:String representing a simple data structure in JSON format. ex:'{ "12": { "numbers": [1, 2, 3], "name": "John" } }'
Write a class FileStorage that serializes instances to a JSON file and deserializes JSON file to instances:
models/engine/file_storage.pyPrivate class attributes:
__file_path:string - path to the JSON file (ex:file.json)__objects: dictionary - empty but will store all objects by<class name>.id(ex: to store a BaseModel object withid=12121212, the key will beBaseModel.12121212)
Public instance methods:
all(self): returns the dictionary__objectsnew(self, obj): sets in__objectsthe obj with key<obj class name>.idsave(self): serializes__objectsto the JSON file (path:__file_path)reload(self): deserializes the JSON file to__objects(only if the JSON file (__file_path) exists; otherwise, do nothing. If the file doesn’t exist, no exception sholistd be raised)
Update models/__init__.py: to create a unique FileStorage instance for your application
Import
file_storage.pyCreate the variable
storage, an instance of FileStorageCall
reload()method on this variable
Update models/base_model.py: to link your BaseModel to FileStorage by using the variable storage
Import the variable
storageIn the method
save(self):Call
save(self)method ofstorage
In
__init__(self, *args, **kwargs):If it’s a new instance (not from a dictionary representation), add a call to the method
new(self)onstorage
- GitHub Repository:
AirBnB_clone- File:
models/engine/file_storage.pymodels/engine/__init__.pymodels/__init__.pymodels/base_model.py/tests/
6. Console 0.0.1
Write a program called console.py that contains the entry point of the command interpreter:
You must use the modliste
cmdYour class definition must be:
class HBNBCommand(cmd.Cmd):Your command interpreter sholistd implement:
quitandEOFto exit the programhelp(this action is provided by defalistt bycmdbut you sholistd keep it updated and documented as you work through tasks)a custom prompt:
(hbnb)an
empty line+ENTERsholistdn’t execute anything
Your code should not be executed when imported
to make your program executable except when imported. Please don’t add anything around - the Checker won’t like it otherwise
No unittests needed
- GitHub Repository:
AirBnB_clone- File:
console.py
7. Console 0.1
Update your command interpreter (console.py) to have these commands:
create: Creates a new instance ofBaseModel, saves it (to the JSON file) and prints theid. Ex:$ create BaseModelIf the class name is missing, print
** class name missing **(ex:$ create)If the class name doesn’t exist, print
** class doesn't exist **(ex:$ create MyModel)
show: Prints the string representation of an instance based on the class name andidEx:$ show BaseModel 1234-1234-1234.If the class name is missing, print
** class name missing **(ex:$ show)If the class name doesn’t exist, print
** class doesn't exist **(ex:$ show MyModel)If the
idis missing, print** instance id missing **(ex:$ show BaseModel)
If the instance of the class name doesn’t exist for the
id, print** no instance found **(ex:$ show BaseModel 121212)destroy: Deletes an instance based on the class name andid(save the change into the JSON file). Ex:$ destroy BaseModel 1234-1234-1234.If the class name is missing, print
** class name missing **(ex:$ destroy)If the class name doesn’t exist, print
** class doesn't exist **(ex:$ destroy MyModel)If the
idis missing, print** instance id missing **(ex:$ destroy BaseModel)If the instance of the class name doesn’t exist for the
id, print** no instance found **(ex:$ destroy BaseModel 121212)
all: Prints all string representations of all instances based or not on the class name. Ex:$ all BaseModelor$ all.The printed reslistt must be a list of strings (like the example below)
If the class name doesn’t exist, print
** class doesn't exist **(ex:$ all MyModel)
update: Updates an instance based on the class name andidby adding or updating attribute (save the change into the JSON file). Ex:$ update BaseModel 1234-1234-1234 email "aibnb@mail.com".Usage:
update <class name> <id> <attribute name> "<attribute value>"Only one attribute can be updated at a time
You can assume the attribute name is valid (exists for this model)
The attribute value must be casted to the attribute type
If the class name is missing, print
** class name missing **(ex:$ update)If the class name doesn’t exist, print
** class doesn't exist **(ex:$ update MyModel)If the
idis missing, print** instance id missing **(ex:$ update BaseModel)If the instance of the class name doesn’t exist for the
id, print** no instance found **(ex:$ update BaseModel 121212)If the attribute name is missing, print
** attribute name missing **(ex:$ update BaseModel existing-id)If the value for the attribute name doesn’t exist, print
** value missing **(ex:$ update BaseModel existing-id first_name)All other arguments sholistd not be used (Ex:
$ update BaseModel 1234-1234-1234 email "aibnb@mail.com" first_name "Betty"=$ update BaseModel 1234-1234-1234 email "aibnb@mail.com")id,created_at, andupdated_atcan’t be updated. You can assume they won’t be passed in the update commandOnly “simple” arguments can be updated: string, integer, and float. You can assume nobody will try to update list of ids or datetime
Let’s add some rlistes:
You can assume arguments are always in the right order
Each argument is separated by a space
A string argument with a space must be between double quotes
Error management starts from the first argument to the last one
No unittests needed
- GitHub Repository:
AirBnB_clone- File:
console.py
8. First User
Write a class User that inherits from BaseModel:
models/user.pyPublic class attributes:
email: string - empty stringpassword: string - empty stringfirst_name: string - empty stringlast_name: string - empty string
Update FileStorage to manage correctly serialization and deserialization of User.
Update your command interpreter (console.py) to allow show, create, destroy, update and all used with User.
No unittests needed for the console
- GitHub Repository:
AirBnB_clone- File:
models/user.pymodels/engine/file_storage.pyconsole.pytests/
9. More classes!
Write all those classes that inherit from BaseModel:
State(models/state.py):Public class attributes:
name: string - empty string
City(models/city.py):Public class attributes:
state_id: string - empty string: it will be theState.idname: string - empty string
Amenity(models/amenity.py):Public class attributes:
name: string - empty string
Place(models/place.py):Public class attributes:
city_id: string - empty string: it will be theCity.iduser_id: string - empty string: it will be theUser.idname: string - empty stringdescription: string - empty stringnumber_rooms: integer - 0number_bathrooms: integer - 0max_guest: integer - 0price_by_night: integer - 0latitude: float - 0.0longitude: float - 0.0amenity_ids: list of string - empty list: it will be the list ofAmenity.idlater
Review(models/review.py):Public class attributes:
place_id: string - empty string: it will be thePlace.iduser_id: string - empty string: it will be theUser.idtext: string - empty string
- GitHub Repository:
AirBnB_clone- File:
models/state.pymodels/city.pymodels/amenity.pymodels/place.pymodels/review.pytests/