From 37d7330ad4c5e09ac91312f73cd899e6cf8bc4ae Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 01:07:09 -0700 Subject: [PATCH 01/80] new authors updated --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f1d72de6355..b620853354f 100644 --- a/README.md +++ b/README.md @@ -158,5 +158,10 @@ Alexa Orrico - [Github](https://github.com/alexaorrico) / [Twitter](https://twit Jennifer Huang - [Github](https://github.com/jhuang10123) / [Twitter](https://twitter.com/earthtojhuang) Second part of Airbnb: Joann Vuong + +AirBnB -- RESTFUL API part +Deantosh Daiddoh - [Github] (https://github.com/deantosh) / [X] (https://x.com/daiddoh) +Lucky Archibong - [Github] (https://github.com/luckys-lnz) / [X] (https://x.com/) + ## License Public Domain. No copy write protection. From d8bbc0b8858947ac4ff226e4148089604fe79055 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 09:21:29 +0000 Subject: [PATCH 02/80] error fixed --- tests/test_models/test_amenity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_models/test_amenity.py b/tests/test_models/test_amenity.py index 66b0bb69bc2..09ce5b06305 100755 --- a/tests/test_models/test_amenity.py +++ b/tests/test_models/test_amenity.py @@ -79,7 +79,6 @@ def test_name_attr(self): def test_to_dict_creates_dict(self): """test to_dict method creates a dictionary with proper attrs""" am = Amenity() - print(am.__dict__) new_d = am.to_dict() self.assertEqual(type(new_d), dict) self.assertFalse("_sa_instance_state" in new_d) From d837a813e9887149baa369ce2d82392a909e8b80 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 13:48:54 +0000 Subject: [PATCH 03/80] get() and count() methods added --- models/engine/file_storage.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index c8cb8c1764d..dce15943312 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -55,7 +55,7 @@ def reload(self): jo = json.load(f) for key in jo: self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) - except: + except (FileNotFoundError, json.JSONDecodeError): pass def delete(self, obj=None): @@ -65,6 +65,24 @@ def delete(self, obj=None): if key in self.__objects: del self.__objects[key] + def get(self, cls, id): + """retrieves a specified object from storage""" + if cls in classes.values(): + obj_key = cls.__name__ + "." + id + cls_dict = self.all(cls) + return cls_dict.get(obj_key) + else: + return None + + def count(self, cls=None): + """counts the number of objects in storage""" + if cls is None: + return len(self.all()) + elif cls in classes.values(): + return len(self.all(cls)) + else: + return 0 + def close(self): """call reload() method for deserializing the JSON file to objects""" self.reload() From c82e21d820d3172e0a9217dab8897ecf1953bfb2 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 13:50:53 +0000 Subject: [PATCH 04/80] get() and count() methods added --- models/engine/file_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index dce15943312..7e1c61cac67 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 """ -Contains the FileStorage class +Module defines the FileStorage class """ import json From ca4f5045b8b84c5c4ddd896407b1b98c093a17a7 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 14:04:38 +0000 Subject: [PATCH 05/80] get() method - gets a specified object --- models/engine/db_storage.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index b8e7d291e6f..c34e9182d8d 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -71,6 +71,15 @@ def reload(self): Session = scoped_session(sess_factory) self.__session = Session + def get(self, cls, id): + """ Retrieves a specified object from storage """ + if cls in classes.values(): + cls_dict = self.all(cls) + obj_key = cls.__name__ + "." + id + return cls_dict.get(obj_key) + else: + return None + def close(self): """call remove() method on the private session attribute""" self.__session.remove() From 3264dec7e8be95569849e5cb5c2520cb1580c30a Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 14:11:34 +0000 Subject: [PATCH 06/80] count() method - get the number of storage objects --- models/engine/db_storage.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index c34e9182d8d..8b7c955a936 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -80,6 +80,15 @@ def get(self, cls, id): else: return None + def count(self, cs=None): + """ Counts the number of objects in storage """ + if cs is None: + return len(self.all()) + elif cs in classes.values(): + return len(self.all(cls)) + else: + return 0 + def close(self): """call remove() method on the private session attribute""" self.__session.remove() From d5d4fd8ea18816b8256374d0a12c73b13dd49187 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 14:18:58 +0000 Subject: [PATCH 07/80] test case: get() and count() --- .../test_engine/test_file_storage.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 1474a34fec0..9ef3d224c93 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -113,3 +113,35 @@ def test_save(self): with open("file.json", "r") as f: js = f.read() self.assertEqual(json.loads(string), json.loads(js)) + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_get(self): + """Test the get() method""" + storage = FileStorage() + state_obj = State(name="Texas") + storage.new(state_obj) + + obj1 = storage.get(State, state_obj.id) + self.assertEqual(obj1.name, "Texas") + obj2 = storage.get(State, '0215477852666') + self.assertIsNone(obj2) + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_count(self): + """Test the count() method""" + # create instance of storage, add, save and reload data + storage = FileStorage() + state1 = State() + storage.new(state1) + state2 = State() + storage.new(state2) + city1 = City() + storage.new(city1) + # save the data + storage.save() + # reload data from the file + storage.reload() + + self.assertEqual(storage.count(), 3) + self.assertEqual(storage.count(State), 2) + self.assertEqual(storage.count(HBNB), 0) From 3839bf23b5398bef807dfa6b4619adaa81d3a738 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Thu, 1 Aug 2024 14:20:47 +0000 Subject: [PATCH 08/80] various files added --- __pycache__/console.cpython-312.pyc | Bin 0 -> 7274 bytes api/__init__.py | 0 api/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 142 bytes api/v1/__init__.py | 0 api/v1/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 145 bytes api/v1/__pycache__/app.cpython-312.pyc | Bin 0 -> 975 bytes api/v1/app.py | 21 ++++++++++++++++++ api/v1/views/__init__.py | 8 +++++++ api/v1/views/index.py | 6 +++++ models/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 540 bytes models/__pycache__/amenity.cpython-312.pyc | Bin 0 -> 1098 bytes models/__pycache__/base_model.cpython-312.pyc | Bin 0 -> 4164 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 152 bytes .../__pycache__/db_storage.cpython-312.pyc | Bin 0 -> 3880 bytes .../__pycache__/file_storage.cpython-312.pyc | Bin 0 -> 3603 bytes .../__pycache__/test_console.cpython-312.pyc | Bin 0 -> 2604 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 156 bytes .../__pycache__/test_amenity.cpython-312.pyc | Bin 0 -> 7163 bytes .../test_base_model.cpython-312.pyc | Bin 0 -> 10086 bytes .../__pycache__/test_city.cpython-312.pyc | Bin 0 -> 7590 bytes .../__pycache__/test_place.cpython-312.pyc | Bin 0 -> 13473 bytes .../__pycache__/test_review.cpython-312.pyc | Bin 0 -> 8205 bytes .../__pycache__/test_state.cpython-312.pyc | Bin 0 -> 7076 bytes .../__pycache__/test_user.cpython-312.pyc | Bin 0 -> 8706 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 168 bytes .../test_db_storage.cpython-312.pyc | Bin 0 -> 5942 bytes .../test_file_storage.cpython-312.pyc | Bin 0 -> 7675 bytes 27 files changed, 35 insertions(+) create mode 100644 __pycache__/console.cpython-312.pyc create mode 100644 api/__init__.py create mode 100644 api/__pycache__/__init__.cpython-312.pyc create mode 100644 api/v1/__init__.py create mode 100644 api/v1/__pycache__/__init__.cpython-312.pyc create mode 100644 api/v1/__pycache__/app.cpython-312.pyc create mode 100644 api/v1/app.py create mode 100644 api/v1/views/__init__.py create mode 100644 api/v1/views/index.py create mode 100644 models/__pycache__/__init__.cpython-312.pyc create mode 100644 models/__pycache__/amenity.cpython-312.pyc create mode 100644 models/__pycache__/base_model.cpython-312.pyc create mode 100644 models/engine/__pycache__/__init__.cpython-312.pyc create mode 100644 models/engine/__pycache__/db_storage.cpython-312.pyc create mode 100644 models/engine/__pycache__/file_storage.cpython-312.pyc create mode 100644 tests/__pycache__/test_console.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_amenity.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_base_model.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_city.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_place.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_review.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_state.cpython-312.pyc create mode 100644 tests/test_models/__pycache__/test_user.cpython-312.pyc create mode 100644 tests/test_models/test_engine/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/test_models/test_engine/__pycache__/test_db_storage.cpython-312.pyc create mode 100644 tests/test_models/test_engine/__pycache__/test_file_storage.cpython-312.pyc diff --git a/__pycache__/console.cpython-312.pyc b/__pycache__/console.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..069a5901076eaf2c9d48bf3e3c872206b9a8ff1a GIT binary patch literal 7274 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 SJIZXwGP`AsL2M`4(tiL75C>ua literal 0 HcmV?d00001 diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/api/__pycache__/__init__.cpython-312.pyc b/api/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9972807d14184f62d57d1650f03c98b4fee1f91 GIT binary patch literal 142 zcmX@j%ge<81fBh>(?IlN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!($&w%&rQ|O$*aV?FZ(4gRfk0|l5G1;u$2P|xNH*c3>dn!>w zIl;jMjvg`f>_6cN(SU1AH0r@yNpGB-w{4Rq>f7ww-^~2p%x~sp-zSqjfa}St`qDcN z;0H50OKyW^8Y6pPf(9mM@-|oGHJ)Qwu!W+ii4hiUsVHl5ge6bVx~-ih2{aDa?-cdUg?`32P5ZY@DnQ(3zPKqoQ3vO4k)zG}wme9Nz;kk$KNW{(-~ zFgq%=K2&k#@2n(6`pm@JWL;>*I4DbHDU+%U6euwQ;*T1mOSa*!jT)Z!n{hH+DBWis z1d1QH)L146V;I*<7TNIU3f*OxVwSsk1Gha0+H(U~ZTHem?#u&KV4b-PPelxeQo(k8 z@&=lWdv-my;ucBHb}G54dyjLI7R@_(9VrgcOSf`{XXQ%c`0=t{IaC8;P}ALTXdKTb z@%0&~!cQ^2HM=?ca-PP}4pm(*8kVE$VR9xvm)9p39_Takj}|M+ST=gP{0s9B7p>X= zz+y%S#TC~NLeX<+fW=mDXeKc;{h^}krdz;#QZzowW)R9n*Ce*z>MDUsv!(HDG&Cx) zIPdyQiDMKAo)oZ$u%D7;%MS?EpW5rhqm~nd1D&RHWE5N{AkTuRzffK#0dY#Smt_-> zOZy^3*Bz=NikhYD-#An6pqO==+@%-LWY^;F;MNd0j{6S7zu^2=82Jj9zd-C%1|?CO zdG1z)6A+FeeF*8@@<+JzjT4W#p+j!yl;^m?rU3kKgQ12BVxm_0APk?x2WvNXCblPb z_1D%>e5^Y4Ii9MOYjmf)UEaTVaP_D+7r9SFWovqKdUtSt>fm;D`ja@;P(U1s1pfhu CcHgN0 literal 0 HcmV?d00001 diff --git a/api/v1/app.py b/api/v1/app.py new file mode 100644 index 00000000000..7c54d239b86 --- /dev/null +++ b/api/v1/app.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +"""creates a flask app""" + +from flask import Flask +from models import storage +from api.v1.views import app_views +import os + + +app = Flask(__name__) +app.register_blueprint(app_views) + +@app.teardown_appcontext +def teardown(): + storage.close() + + +if __name__ == '__main__': + host = os.getenv('HBNB_API_HOST', '0.0.0.0') + port = int(os.getenv('HBNB_API_PORT', 5000)) + app.run(host=host, port=port, threaded=True) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py new file mode 100644 index 00000000000..c853ad539dc --- /dev/null +++ b/api/v1/views/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +"""python package""" + +from flask import Blueprint + +app_views = Blueprint('app_views', __name__, url_prefix='/api/v1') + +from api.v1.views.index import * diff --git a/api/v1/views/index.py b/api/v1/views/index.py new file mode 100644 index 00000000000..9e6fc1909f8 --- /dev/null +++ b/api/v1/views/index.py @@ -0,0 +1,6 @@ +from flask import jsonify +from api.v1.views import app_views + +@app_views.route('/status', methods=['GET']) +def get_status(): + return jsonify({"status": "OK"}) diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7fed729983112c3798fda6330dfba044218c423 GIT binary patch literal 540 zcmY+Bze~eF6vy8sX_8hO)QZ0rJE>h7QE(EW!CEI3D}q^$+B@6WOCo7nu#*MB!NtwL zLHeh-h$3h}5X8aFA}&r|Z2a|xyZ62C=e-B-E|bXw@Mu__y;TJ86pHTSAuhO*b63Xi7gHpLxs?fwsSeikBT%kU7n!(_z zv1Z_{osB#$ZmnlC;}5m6$&h+m}a!To%5|dyK83l zfIGQxVJYW=NC+Xjq@W;j{*yG2L<$VDoCq3smYfLWiZ{Dwhmq#ZoA=(l_rCAVZ}oZ& zaCPI|_SY7`D|0zn&QLk(pt21SP>4um2|@{;6=5Y6T1jkb6JxhUHF2m@=$3G)i?*t8 z6OVd{PyNE~h*~m2M+jK(Jw)y%L>{kjd)F>2(b1k2`291DP9`GNXo*m%#+8Jpao#&H zAvXd``wOAsKV%UX+BRxHw6n}}p58{Mb2Ss)B-PGhF5`3=eWQ|#FpslTb>g*FS*MiM z;pJ!rm7fqG2LuMxiU`G~RtjR?e*h3!t6u`hiKs#y3%HBhi|k!eMn%B0tWgYGz#p@)7b?Y(tnGooa!0J!L$ZQ;n zxX({{HWXJ?ca2N!F=hf73v6vGF521-<+9Sw>Yd{B87oG_m@)%wG#VSn&1NS{cvGbP z=6BbAY+i}wLb`xqS<2b%3(dsLO*Kncz4^7C{0Lw6KO7P=e^PM*t!fg*+YNZ_)wkSd z-t^AwQ!jY*#UEa9gboNsO@k?>VocWGSE&2K5(XllR*@iw{EqE{FeRq*!aMHV_Vg^LqH*?Pf`VS z`LWSIL7~UX#^;ytvm2CL$ck|q&b5^(X-2PW>XryatHYDtdq6(;-#VYkM0)0->G&5Y zs~HH#-ldYfZ{<0>8f*%`IUH69A^*V4OQ`R|S9>t|62|x8^F28CuTvv)gLvo6e}GDn F=^ZJN9j*WX literal 0 HcmV?d00001 diff --git a/models/__pycache__/base_model.cpython-312.pyc b/models/__pycache__/base_model.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32445d55282ac5d0b145565bbe584d4c084f9135 GIT binary patch literal 4164 zcmb_fT})fo9Y6QO*B2XX6O)j@pm$)8m;}5*wxBd*UE(!qrILrRN%K-&U3?G0&b3X? zy@ui%l}&k|Ql=7hQ$VRix|f2elvJv;J?vqVChcK;i0ec+H&lzZzVwa3KBPYFfA00Y zb~5O)szahoBVF8I06SStRFX^k%`ICOtn{38xro*|i z-m2o&qX-d^McB7dn@$FDEDk0EEIN6RP@qdbkbziiB#>SR^zD+c7F$*n+T&M z12P18B{|GzEV(z5Bc!qx&m+LI0H0UoHW-^i2 zwW!`}C>c;isDo6l$&l)Ux>fZ?Yl5C6`K&ZMl}=4blLezd zusx4;X*R8C(gc=NOhA4rX@W<7ej2KA5Uqcjq_YZ{lds}A78m6=R8AV#sib%{0DOCFWrnNZ0wTRei$jqKIK6sXVpaAC z42_U8=iFC9vg3v*cckHz0}f@qb+~Wbvkyo7z8tmH&vSZTkt2Rzj$Fz79~`-R_Tk7= z=V+nV@H=le+MQkoEaK_nNX+37B5y_8EEX+=vH z0h#hSDL*M0;F%Plq%6fevq_dM7?fqxmm(MtO_dd6%=8teDGJho$)?pRU`)Kfl%et=0TWzS13s(Zy?1kS){?zw}zG`gXPS<+ZF2{sMwh_}b zLZYzc2`N)fEh*oH&P`;vQEV@{I`{;>?@HZprMg%2$+*Y|$Ja^;Irzq)|FPReKWA?} zrZQ-b0qvZ&u|v18hGIo}qF`XDuMR1F5b}*FYduvU1QaOMb1tuiBBT?}w;@BY-5Q|B z$P?uXHn|+01<+7tFzE-X2K|t4!?+lz>xL3RTt_<9&I0V z0PdAg)Ro{UhEmgZ|B-8(}ept>ptGPkApEkib@PPpr`nKf+2&0 z99og6t3Ygl3{6VenKdNGk^&hWAh;g45!{J9kainNE@g)dOS`rQ9*}hfP}*JucY@Fp z6G#jmZ1Sl*1#xh(Ng7QE{SmM>5lH?43@jHlMKB4=Yjx@clH;vL-kNOm*4or4km)~% z3cNMkeslE3=w>)t4o55DzD2R>Z(klM`+KX=fi}8vWVw$>R|0;Ya2PmviJTS(!Oa3~CNG7UReOTu3OInS#Cbzg%qu7K@>}tJ!2P z!K_X*L~u68y}E$)W%YsBYjHXj3tOr)eKHNw1r31LY?kHM3W{dKFhyB4p8`CluP~cx z&yo#70}!F;W+649d3FO@&^*L8stD~JqZI`N@;zG7_pT%B2^N+Uii1`~|B>{|P+db0 z`SVq8%ci%z>}_8@@rn1jhb`e7FD?(g^YU8ae#>_j*@p)^moKbM-N~=#?;kv~=zDs+ z!-qYCJN=wzWXH#Qx_6p=p2NF`Sx>~inBEy{CC}5VDM;8HvU#2GFfx7mYnq~^rf_zy zfmh0=*->8_#IGBJ4S$@3{$r4D(8}@->b53NHDe0-i40B|Hm}s?>->Kqp0>q*hMa>1 z8fEk`sJ1zVVgAmu4F69NG2M?)%ctnzBh>mA6fdLrr|9G(t~6_{?>)WzlHBIksSn;b>Dvgbkk(p literal 0 HcmV?d00001 diff --git a/models/engine/__pycache__/__init__.cpython-312.pyc b/models/engine/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..434ff5d124bcd4e38f1718cfe9e8ecf311a39b55 GIT binary patch literal 152 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 a!6y6aJ;)Efcj%J)xu=hY4hRr#m;V4mwJY}k literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_console.cpython-312.pyc b/tests/__pycache__/test_console.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f8eefab560de25fde22c110f4fc398fb991bcf3 GIT binary patch literal 2604 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 literal 0 HcmV?d00001 diff --git a/tests/test_models/__pycache__/test_review.cpython-312.pyc b/tests/test_models/__pycache__/test_review.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dce09427c916a66990839bea550e016dd73db8d GIT binary patch literal 8205 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 P0kIGezhUqf-KqZrmz_K3 literal 0 HcmV?d00001 diff --git a/tests/test_models/test_engine/__pycache__/__init__.cpython-312.pyc b/tests/test_models/test_engine/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c22067f18c35004407e2e3714bd8d82548733e12 GIT binary patch literal 168 zcmX@j%ge<81f`j)(?IlN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!a@Wtu&rQ|O$*a&ryk0@& fFAf`^g3_E+yCPPgxr{(u3}Sp_W@Kb6Vg|AR!)hvg literal 0 HcmV?d00001 diff --git a/tests/test_models/test_engine/__pycache__/test_db_storage.cpython-312.pyc b/tests/test_models/test_engine/__pycache__/test_db_storage.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9572aa0f3acd6d0d7a6fdc7629956cabb1a121a1 GIT binary patch literal 5942 zcmdTITZ|Le^^WcFYwh(q>@GIzCBueC%wv}j0)z#WeE=aTP8v3}vYL*(p1WW(9-Di| zpmm9~sgMd$TN0_#5(za#s#FmD;3NM{{v!1!*0!=c2q{wglRseLhp(PYl7e z{Cl%r`1k3)lAM*rn5_FtfowqFemz(UWkUiF=;2aFwnN}SJyMEhqXG}JiZddP92MK2SrlddoBZPT`}J6;>h_6f=uPpUa%9{5w$@(=PzGxTvy{KTGrH#Eog?d=(A}n=X_Maa-J+)~QmSQrwHQuPhHb={6r= zD6{!r1DGYIJ63F+!X+idL~7B@7D9@C7j=<4&fH5xb!SXVX-_S2ikeEbqD+-pYtxE} zXUU{xD*(c;(7b7d&rq{OOOupwE97q83K#fP)yU)dELof6>J&)Fo7XwVO)F0`Lubn+ zI;0!5q4A@qheir))EHIsdfA}rh1Z9~Is&RC6hF6dYMdz;)W+L7Ggz6n!kn5PR>n}v zYN=&H7;KNGV0pd~zzlipCq1b~_ttv%*1t@Dy{)llyuN3AuKVZ}?;k?*ct49SA5}_R z{vQE(*5zx`-#m95@q8>@l8QKcNz=S%B$vi>i4> z7-<9(^mqr(_G70VZ9!&go0g6(6E@opx0#mN4)|kn1UnJzLNJJ62*5(Q zzP!YSA@GATED-B!xH)&-woNY(>yjz^afzi;O*WF->&fl6#+%7QDAglvsh()0cGXk6 z<`RQ;KI`{;^{BE%zNFG(Zx+4vu5%6snWu$IK;Xw$&=_ z5e~!hVW!D(m#y?5;pi8noiNnaD_+~as$wmpMP=d_0f3+APRzdZ`8$t@FLYA6GTw}I zH6p2cBz1k_=B6)x@um8OdP{5eA8PcE)%(Z38UHT+?eV$(pMl0jGtv){u50gIeecHD zP2bI*e--#^?5o&ZYWLlddg_g4YPgXat*1u6+54UJ?f$vcyP$*7lhVTgt|L@2lkTvB zs%qp)6kVp;@_?!pQB_Qy>=9vbkYS-2+1JW?Vdrcs0t|Evy&l_xU>|_nL~s{gmO(*G z1lRxwe*?f}@^9bqrrh<=S&FATKEATwb9U zIH$9`Fx&Fsq-D{k2QNc~an<3j zB3W$;zi6WcK?QV!@|w&$I=id}CS^rDi}F#R)>@?;Sn1@N&0^%359 za+C0av&v3`Vy7^@mRNCvF!BK`&G5>f?NYy$`#7=X$8NyvpZ4zVih`A za;v-E zv+Mhz<8$)c=)4c0?>-@+^FHt(Nc=r(P)qVr;p&kbaZvAP&=0q`OY-ousYe{{4}trL z9o@vS(*1V2h~9<85QzJU+7>-%SCDq?(CPF+G{hG|@yut%olev=`?ZLnJF_~R{yb9R z+lm!&dMfyUC|(Aw)+$UqC5neamyF}1qIfoRc~#-jLcAebK2#pdQ|7E6w~PiPQeP_| z%oLtDf~YrRuqivh3JETj>eJvM*bx*oN7zR&A4?|wTL2Hdk|aI!c_iPHAdxoSBc1n1 z^gh{qpA6h5+wPOpeX{XBQ3OogBOUihNWfS#(%XnA^@uVPYWDWuH1E=8Pv1>?cjBQ} t>KL90JPvM`PD`%CIKxPsH1-3u^vn>Y!{MB>r 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 O?WL~m-w=3UlKC^}3d1h| literal 0 HcmV?d00001 From e80548e56def1c5d07175698f2823737e71c2b5f Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 14:22:23 +0000 Subject: [PATCH 09/80] test case: get() and count() --- tests/test_models/test_engine/test_db_storage.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..1d5616d1653 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -86,3 +86,11 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_get(self): + """Test the get() method""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_count(self): + """Test the count() method""" From ff1d63ac8640017156a4b88a47e4966dd50c87c6 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Thu, 1 Aug 2024 14:29:37 +0000 Subject: [PATCH 10/80] deleted __pycache__ --- api/__pycache__/__init__.cpython-312.pyc | Bin 142 -> 0 bytes api/v1/__pycache__/__init__.cpython-312.pyc | Bin 145 -> 0 bytes api/v1/__pycache__/app.cpython-312.pyc | Bin 975 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 api/__pycache__/__init__.cpython-312.pyc delete mode 100644 api/v1/__pycache__/__init__.cpython-312.pyc delete mode 100644 api/v1/__pycache__/app.cpython-312.pyc diff --git a/api/__pycache__/__init__.cpython-312.pyc b/api/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index e9972807d14184f62d57d1650f03c98b4fee1f91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmX@j%ge<81fBh>(?IlN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!($&w%&rQ|O$*aV?FZ(4gRfk0|l5G1;u$2P|xNH*c3>dn!>w zIl;jMjvg`f>_6cN(SU1AH0r@yNpGB-w{4Rq>f7ww-^~2p%x~sp-zSqjfa}St`qDcN z;0H50OKyW^8Y6pPf(9mM@-|oGHJ)Qwu!W+ii4hiUsVHl5ge6bVx~-ih2{aDa?-cdUg?`32P5ZY@DnQ(3zPKqoQ3vO4k)zG}wme9Nz;kk$KNW{(-~ zFgq%=K2&k#@2n(6`pm@JWL;>*I4DbHDU+%U6euwQ;*T1mOSa*!jT)Z!n{hH+DBWis z1d1QH)L146V;I*<7TNIU3f*OxVwSsk1Gha0+H(U~ZTHem?#u&KV4b-PPelxeQo(k8 z@&=lWdv-my;ucBHb}G54dyjLI7R@_(9VrgcOSf`{XXQ%c`0=t{IaC8;P}ALTXdKTb z@%0&~!cQ^2HM=?ca-PP}4pm(*8kVE$VR9xvm)9p39_Takj}|M+ST=gP{0s9B7p>X= zz+y%S#TC~NLeX<+fW=mDXeKc;{h^}krdz;#QZzowW)R9n*Ce*z>MDUsv!(HDG&Cx) zIPdyQiDMKAo)oZ$u%D7;%MS?EpW5rhqm~nd1D&RHWE5N{AkTuRzffK#0dY#Smt_-> zOZy^3*Bz=NikhYD-#An6pqO==+@%-LWY^;F;MNd0j{6S7zu^2=82Jj9zd-C%1|?CO zdG1z)6A+FeeF*8@@<+JzjT4W#p+j!yl;^m?rU3kKgQ12BVxm_0APk?x2WvNXCblPb z_1D%>e5^Y4Ii9MOYjmf)UEaTVaP_D+7r9SFWovqKdUtSt>fm;D`ja@;P(U1s1pfhu CcHgN0 From 206ce0d67c85df3c1077d5687da3a8cdaa5ecc33 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 21:05:14 +0000 Subject: [PATCH 11/80] custom 404 error handler --- api/v1/app.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/api/v1/app.py b/api/v1/app.py index 7c54d239b86..8c58f9fc0d8 100644 --- a/api/v1/app.py +++ b/api/v1/app.py @@ -1,7 +1,9 @@ #!/usr/bin/python3 -"""creates a flask app""" +""" +Module defines a Flask application +""" -from flask import Flask +from flask import Flask, jsonify from models import storage from api.v1.views import app_views import os @@ -10,12 +12,20 @@ app = Flask(__name__) app.register_blueprint(app_views) + +@app.errorhandler(404) +def not_found_error(error): + """ Handles error 404 """ + return jsonify({"error": "Not found"}), 404 + + @app.teardown_appcontext -def teardown(): +def teardown(exception=None): + """ Closes the storage """ storage.close() if __name__ == '__main__': host = os.getenv('HBNB_API_HOST', '0.0.0.0') port = int(os.getenv('HBNB_API_PORT', 5000)) - app.run(host=host, port=port, threaded=True) + app.run(host=host, port=port, threaded=True, debug=True) From c206be905b1754f94e92f0420a1cc012a5dc8c38 Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 1 Aug 2024 21:08:44 +0000 Subject: [PATCH 12/80] get_status() -- documentation --- api/v1/views/index.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/views/index.py b/api/v1/views/index.py index 9e6fc1909f8..88e0cec8140 100644 --- a/api/v1/views/index.py +++ b/api/v1/views/index.py @@ -3,4 +3,5 @@ @app_views.route('/status', methods=['GET']) def get_status(): + """ Returns the API status """ return jsonify({"status": "OK"}) From bce1b679d770b039a68dfa8069fb111d9842937c Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Thu, 1 Aug 2024 23:28:31 +0000 Subject: [PATCH 13/80] Data not populated from DB --- api/v1/views/index.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/api/v1/views/index.py b/api/v1/views/index.py index 88e0cec8140..96acdcafdca 100644 --- a/api/v1/views/index.py +++ b/api/v1/views/index.py @@ -1,7 +1,29 @@ +#!/usr/bin/python3 +""" +Create an endpoint that retrieves the number +of each objects by type: +""" + from flask import jsonify from api.v1.views import app_views +from models import storage + @app_views.route('/status', methods=['GET']) def get_status(): - """ Returns the API status """ + """Check status of file""" return jsonify({"status": "OK"}) + + +@app_views.route('/stats', methods=['GET']) +def get_stats(): + """Create an endpoint""" + statistics = { + "amenities": storage.count("Amenity"), + "cities": storage.count("City"), + "places": storage.count("Place"), + "reviews": storage.count("Review"), + "states": storage.count("State"), + "users": storage.count("User"), + } + return jsonify(statistics) From 5364020c80456c810928b7bf4a64690d1d1438e2 Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 14:24:20 +0000 Subject: [PATCH 14/80] import State objects view --- api/v1/views/__init__.py | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 api/v1/views/__init__.py diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py old mode 100644 new mode 100755 index c853ad539dc..6bb65822ec2 --- a/api/v1/views/__init__.py +++ b/api/v1/views/__init__.py @@ -6,3 +6,4 @@ app_views = Blueprint('app_views', __name__, url_prefix='/api/v1') from api.v1.views.index import * +from api.v1.views.states import * From 8df02905304f625646fcd7b7b786c8b704322f23 Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 14:28:05 +0000 Subject: [PATCH 15/80] executable --- api/v1/views/index.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 api/v1/views/index.py diff --git a/api/v1/views/index.py b/api/v1/views/index.py old mode 100644 new mode 100755 From 3e5682d264157666e8562f9da858790c9dc799aa Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 14:30:34 +0000 Subject: [PATCH 16/80] view: State objects --- api/v1/views/states.py | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 api/v1/views/states.py diff --git a/api/v1/views/states.py b/api/v1/views/states.py new file mode 100755 index 00000000000..3c2c0141609 --- /dev/null +++ b/api/v1/views/states.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +Module defines view for State objects that handles all default +RESTFul API actions. +""" +from api.v1.views import app_views +from models import storage +from models.state import State +from flask import jsonify, abort, request +from werkzeug.exceptions import BadRequest + + +@app_views.route('/states', methods=['GET'], strict_slashes=False) +def get_all_states(): + """ Retrievs a list of all State objects """ + states_list = [state.to_dict() for state in storage.all(State).values()] + return jsonify(states_list) + + +@app_views.route('/states/', methods=['GET']) +def get_state(state_id): + """ Retrieves a State object of a specified state_id """ + state_obj = storage.get(State, state_id) + if state_obj is None: + abort(404) + else: + return jsonify(state_obj.to_dict()) + + +@app_views.route('states/', methods=['DELETE']) +def delete_state(state_id): + """ Deletes state object of a specified state_id """ + state_obj = storage.get(State, state_id) + if state_obj is None: + abort(404) + else: + storage.delete(state_obj) + storage.save() + return jsonify({}), 200 + + +@app_views.route('/states', methods=['POST'], strict_slashes=False) +def create_state(): + """ Creates a state object """ + + try: + data = request.get_json() + except BadRequest: + abort(400, description="Not a JSON") + + if 'name' not in data: + abort(400, description="Missing name") + else: + state_obj = State(**data) + storage.new(state_obj) + storage.save() + return jsonify(state_obj.to_dict()), 201 + + +@app_views.route('states/', methods=['PUT']) +def update_state(state_id): + """ Updates state """ + state_obj = storage.get(State, state_id) + if state_obj is None: + abort(404) + + try: + data = request.get_json() + except BadRequest: + abort(400, description="Not a JSON") + + ignored_keys = ['id', 'created_at', 'updated_at'] + for key, value in data.items(): + if key not in ignored_keys: + setattr(state_obj, key, value) + storage.save() + return jsonify(state_obj.to_dict()), 200 From 55ad36d62b4b22d27934cbede450d9e08c175e82 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Fri, 2 Aug 2024 15:38:24 +0000 Subject: [PATCH 17/80] added keys --- models/engine/db_storage.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index 8b7c955a936..f80ace5132f 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -45,6 +45,7 @@ def all(self, cls=None): new_dict = {} for clss in classes: if cls is None or cls is classes[clss] or cls is clss: + print('class name is: ',cls) objs = self.__session.query(classes[clss]).all() for obj in objs: key = obj.__class__.__name__ + '.' + obj.id @@ -84,8 +85,10 @@ def count(self, cs=None): """ Counts the number of objects in storage """ if cs is None: return len(self.all()) - elif cs in classes.values(): + elif cs in classes.values(): return len(self.all(cls)) + elif cs in classes.keys(): + return len(self.all(cs)) else: return 0 From 389ba9a12ae1a46308db784163eb2b67226881b4 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Fri, 2 Aug 2024 15:59:07 +0000 Subject: [PATCH 18/80] modified file storage --- models/engine/db_storage.py | 1 - 1 file changed, 1 deletion(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index f80ace5132f..996ab69fb56 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -45,7 +45,6 @@ def all(self, cls=None): new_dict = {} for clss in classes: if cls is None or cls is classes[clss] or cls is clss: - print('class name is: ',cls) objs = self.__session.query(classes[clss]).all() for obj in objs: key = obj.__class__.__name__ + '.' + obj.id From f54d1a832d24d3da0d7eba45770cf40d7992efa9 Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 16:41:29 +0000 Subject: [PATCH 19/80] view: City objects --- api/v1/views/cities.py | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 api/v1/views/cities.py diff --git a/api/v1/views/cities.py b/api/v1/views/cities.py new file mode 100644 index 00000000000..dd03993cdd8 --- /dev/null +++ b/api/v1/views/cities.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +Module defines view for City objects that handles all default +RESTFUL API actions. +""" +from models import storage +from models.city import City +from models.state import State +from flask import jsonify, abort, request +from werkzeug.exceptions import BadRequest +from api.v1.views import app_views + + +@app_views.route('/states//cities', + methods=['GET'], strict_slashes=False) +def get_all_cities(state_id): + """ Retrieves all the cities of a state """ + state_obj = storage.get(State, state_id) + if state_obj is None: + abort(404) + city_list = [city.to_dict() for city in state_obj.cities] + return jsonify(city_list) + + +@app_views.route('/cities/', methods=['GET']) +def get_city(city_id): + """ Retrieves city object of a specified city_id """ + city_obj = storage.get(City, city_id) + if city_obj is None: + abort(404) + + return jsonify(city_obj.to_dict()) + + +@app_views.route('/cities/', methods=['DELETE']) +def delete_city(city_id): + """ Deletes city object """ + city_obj = storage.get(City, city_id) + if city_obj is None: + abort(404) + + storage.delete(city_obj) + storage.save() + + return jsonify({}), 200 + + +@app_views.route('/states//cities', + methods=['POST'], strict_slashes=False) +def create_city(state_id): + """ Creates city object""" + state_obj = storage.get(State, state_id) + if state_obj is None: + abort(404) + + try: + data = request.get_json() + if 'name' not in data: + abort(400, description='Missing name') + except BadRequest: + abort(400, description='Not a JSON') + + data['state_id'] = state_id + city_obj = City(**data) + storage.new(city_obj) + storage.save() + + return jsonify(city_obj.to_dict()), 201 + + +@app_views.route('/cities/', methods=['PUT']) +def update_city(city_id): + """ Updates city object """ + city_obj = storage.get(City, city_id) + if city_obj is None: + abort(404) + + try: + data = request.get_json() + except BadRequest: + abort(400, description='Not a JSON') + + ignore_keys = ['id', 'state_id', 'created_at', 'updated_at'] + for key, value in data.items(): + if key not in ignore_keys: + setattr(city_obj, key, value) + + storage.save() + + return jsonify(city_obj.to_dict()), 200 From f54b35c3779841d40d5f6c8aa6eded102093d18b Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 16:47:16 +0000 Subject: [PATCH 20/80] import City view --- api/v1/views/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py index 6bb65822ec2..ad9fdd1b94f 100755 --- a/api/v1/views/__init__.py +++ b/api/v1/views/__init__.py @@ -7,3 +7,4 @@ from api.v1.views.index import * from api.v1.views.states import * +from api.v1.views.cities import * From 9ffb32d3d1bfbfa7c420c84c84f81081d7a969cf Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 19:39:26 +0000 Subject: [PATCH 21/80] view: Amenity objects --- api/v1/views/amenities.py | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100755 api/v1/views/amenities.py diff --git a/api/v1/views/amenities.py b/api/v1/views/amenities.py new file mode 100755 index 00000000000..2eae40f80b4 --- /dev/null +++ b/api/v1/views/amenities.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 +""" +Module defines view for Amenity objects that handles all default +RESTFUL API actions. +""" +from models import storage +from models.amenity import Amenity +from flask import jsonify, abort, request +from werkzeug.exceptions import BadRequest +from api.v1.views import app_views + + +@app_views.route('/amenities', methods=['GET'], strict_slashes=False) +def get_all_amenities(): + """ Retrieves all Amenity objects """ + amenity_objs = storage.all(Amenity).values() + amenity_list = [amenity.to_dict() for amenity in amenity_objs] + + return jsonify(amenity_list) + + +@app_views.route('/amenities/', methods=['GET']) +def get_amenity(amenity_id): + """ Retrieves Amenity object by amenity_id """ + amenity_obj = storage.get(Amenity, amenity_id) + if amenity_obj is None: + abort(404) + + return jsonify(amenity_obj.to_dict()) + + +@app_views.route('/amenities/', methods=['DELETE']) +def delete_amenity(amenity_id): + """ Deletes Amenity object by amenity_id """ + amenity_obj = storage.get(Amenity, amenity_id) + if amenity_obj is None: + abort(404) + + storage.delete(amenity_obj) + storage.save() + + return jsonify({}), 200 + + +@app_views.route('/amenities', methods=['POST'], strict_slashes=False) +def create_amenity(): + """ Creates Amenity object """ + try: + data = request.get_json() + if 'name' not in data: + abort(400, description='Missing name') + except BadRequest: + abort(400, description='Not a JSON') + + amenity_obj = Amenity(**data) + storage.new(amenity_obj) + storage.save() + + return jsonify(amenity_obj.to_dict()), 201 + + +@app_views.route('/amenities/', methods=['PUT']) +def update_amenity(amenity_id): + """ Updates Amenity object by amenity_id """ + amenity_obj = storage.get(Amenity, amenity_id) + if amenity_obj is None: + abort(404) + + try: + data = request.get_json() + except BadRequest: + abort(400, description='Not a JSON') + + ignore_keys = ['id', 'state_id', 'created_at', 'updated_at'] + for key, value in data.items(): + if key not in ignore_keys: + setattr(amenity_obj, key, value) + + storage.save() + + return jsonify(amenity_obj.to_dict()), 200 From 6cbc2709ab4aac245083dbf1646efb4c73afac68 Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 19:40:02 +0000 Subject: [PATCH 22/80] import: Amenity view --- api/v1/views/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py index ad9fdd1b94f..bcf4645e1f6 100755 --- a/api/v1/views/__init__.py +++ b/api/v1/views/__init__.py @@ -8,3 +8,4 @@ from api.v1.views.index import * from api.v1.views.states import * from api.v1.views.cities import * +from api.v1.views.amenities import * From 5c6d98e62b6ef6a1f5a7d6cedf3f57b58b442776 Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 19:44:50 +0000 Subject: [PATCH 23/80] file upddated --- models/engine/db_storage.py | 6 +++--- models/engine/file_storage.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index 8b7c955a936..eceae6f226c 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -80,11 +80,11 @@ def get(self, cls, id): else: return None - def count(self, cs=None): + def count(self, cls=None): """ Counts the number of objects in storage """ - if cs is None: + if cls is None: return len(self.all()) - elif cs in classes.values(): + elif cls in classes.values(): return len(self.all(cls)) else: return 0 diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 7e1c61cac67..75368f44cd2 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -55,7 +55,7 @@ def reload(self): jo = json.load(f) for key in jo: self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) - except (FileNotFoundError, json.JSONDecodeError): + except (FileNotFoundError, ValueError): pass def delete(self, obj=None): From 7189e7f92bd521e48cfd298ba4ebeb9e85fe72d2 Mon Sep 17 00:00:00 2001 From: deantosh Date: Fri, 2 Aug 2024 19:53:39 +0000 Subject: [PATCH 24/80] count() method - error fixed --- models/engine/db_storage.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index ff6e97013f1..b8b9373d50e 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -84,17 +84,11 @@ def count(self, cls=None): """ Counts the number of objects in storage """ if cls is None: return len(self.all()) -<<<<<<< HEAD - elif cls in classes.values(): -======= - elif cs in classes.values(): ->>>>>>> 4f86e75ec41a4d00dcf35140c9853b0c7dbd7aab + elif cls in classes.keys(): return len(self.all(cls)) - elif cs in classes.keys(): - return len(self.all(cs)) else: return 0 def close(self): - """call remove() method on the private session attribute""" + """Close session""" self.__session.remove() From 93c22a5e7e7be3632d8bce6204a7725a1776a60e Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 07:40:09 +0000 Subject: [PATCH 25/80] view: User objects --- api/v1/views/users.py | 79 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 api/v1/views/users.py diff --git a/api/v1/views/users.py b/api/v1/views/users.py new file mode 100755 index 00000000000..a9cbad31e4a --- /dev/null +++ b/api/v1/views/users.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 +""" +Module defines view for User objects that handles all default +RESTFUL API actions. +""" +from models import storage +from models.user import User +from flask import jsonify, abort, request +from werkzeug.exceptions import BadRequest +from api.v1.views import app_views + + +@app_views.route('/users', methods=['GET'], strict_slashes=False) +def get_all_users(): + """ Retrieves all User objects """ + user_list = [user.to_dict() for user in storage.all(User).values()] + return jsonify(user_list) + + +@app_views.route('/users/', methods=['GET']) +def get_user(user_id): + """ Retrieves User object by user_id """ + user_obj = storage.get(User, user_id) + if user_obj is None: + abort(404) + + return jsonify(user_obj.to_dict()) + + +@app_views.route('/users/', methods=['DELETE']) +def delete_user(user_id): + """ Deletes User object by user_id """ + user_obj = storage.get(User, user_id) + if user_obj is None: + abort(404) + + storage.delete(user_obj) + storage.save() + + return jsonify({}), 200 + + +@app_views.route('/users', methods=['POST'], strict_slashes=False) +def create_user(): + """ Creates User object """ + try: + data = request.get_json() + if 'email' not in data or 'password' not in data: + abort(400, description='Missing name') + except BadRequest: + abort(400, description='Not a JSON') + + user_obj = User(**data) + storage.new(user_obj) + storage.save() + + return jsonify(user_obj.to_dict()), 201 + + +@app_views.route('/users/', methods=['PUT']) +def update_user(user_id): + """ Updates User object by user_id """ + user_obj = storage.get(User, user_id) + if user_obj is None: + abort(404) + + try: + data = request.get_json() + except BadRequest: + abort(400, description='Not a JSON') + + ignore_keys = ['id', 'state_id', 'created_at', 'updated_at'] + for key, value in data.items(): + if key not in ignore_keys: + setattr(user_obj, key, value) + + storage.save() + + return jsonify(user_obj.to_dict()), 200 From 96c4c515efb7966dbdd030cc124d401f6f482ae3 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 07:41:05 +0000 Subject: [PATCH 26/80] import User view --- api/v1/views/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py index bcf4645e1f6..7319a900759 100755 --- a/api/v1/views/__init__.py +++ b/api/v1/views/__init__.py @@ -9,3 +9,4 @@ from api.v1.views.states import * from api.v1.views.cities import * from api.v1.views.amenities import * +from api.v1.views.users import * From 99a43a7321ff18b44806912e2e0008e1a537f025 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 09:48:16 +0000 Subject: [PATCH 27/80] view: Place objects --- api/v1/views/places.py | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 api/v1/views/places.py diff --git a/api/v1/views/places.py b/api/v1/views/places.py new file mode 100755 index 00000000000..fb059ffa029 --- /dev/null +++ b/api/v1/views/places.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +Module defines view for Place objects that handles all default +RESTFUL API actions. +""" +from models import storage +from models.city import City +from models.place import Place +from flask import jsonify, abort, request +from werkzeug.exceptions import BadRequest +from api.v1.views import app_views + + +@app_views.route('/cities//places', + methods=['GET'], strict_slashes=False) +def get_all_places(city_id): + """ Retrieves the list of all Place objects of a city by city_id""" + city = storage.get(City, city_id) + if city is None: + abort(404) + place_list = [place.to_dict() for place in city.places] + return jsonify(place_list) + + +@app_views.route('/places/', methods=['GET']) +def get_place(place_id): + """ Retrieves Place object by place_id """ + place_obj = storage.get(Place, place_id) + if place_obj is None: + abort(404) + + return jsonify(place_obj.to_dict()) + + +@app_views.route('/places/', methods=['DELETE']) +def delete_place(place_id): + """ Deletes Place object by place_id """ + place_obj = storage.get(Place, place_id) + if place_obj is None: + abort(404) + + storage.delete(place_obj) + storage.save() + + return jsonify({}), 200 + + +@app_views.route('/cities//places', + methods=['POST'], strict_slashes=False) +def create_place(city_id): + """ Creates Place object in a city by city_id""" + city_obj = storage.get(City, city_id) + if city_obj is None: + abort(404) + + try: + data = request.get_json() + if 'name' not in data: + abort(400, description='Missing name') + except BadRequest: + abort(400, description='Not a JSON') + + data['city_id'] = city_id + place_obj = Place(**data) + storage.new(place_obj) + storage.save() + + return jsonify(place_obj.to_dict()), 201 + + +@app_views.route('/places/', methods=['PUT']) +def update_place(place_id): + """ Updates Place object by place_id """ + place_obj = storage.get(Place, place_id) + if place_obj is None: + abort(404) + + try: + data = request.get_json() + except BadRequest: + abort(400, description='Not a JSON') + + ignore_keys = ['id', 'state_id', 'created_at', 'updated_at'] + for key, value in data.items(): + if key not in ignore_keys: + setattr(place_obj, key, value) + + storage.save() + + return jsonify(place_obj.to_dict()), 200 From 95a71165e0e3fc964c7a7c53865201d95f17e785 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 09:48:36 +0000 Subject: [PATCH 28/80] import Place view --- api/v1/views/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py index 7319a900759..bd2ed44c4e7 100755 --- a/api/v1/views/__init__.py +++ b/api/v1/views/__init__.py @@ -10,3 +10,4 @@ from api.v1.views.cities import * from api.v1.views.amenities import * from api.v1.views.users import * +from api.v1.views.places import * From 63045891b176945a36cb4a2a070c99e681e4afd4 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Sat, 3 Aug 2024 11:57:13 +0000 Subject: [PATCH 29/80] places_reviews.py --- api/v1/views/places_reviews.py | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 api/v1/views/places_reviews.py diff --git a/api/v1/views/places_reviews.py b/api/v1/views/places_reviews.py new file mode 100755 index 00000000000..dc3ad4586f4 --- /dev/null +++ b/api/v1/views/places_reviews.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +Creates a new view for the link between Place objects and Amenity +objects that handles all default RESTFul API actions +""" + +from flask import jsonify, request, abort +from api.v1.views import app_views +from models import storage +from models.review import Review +from models.place import Place +from models.user import User + + +@app_viewe.route('/places//reviews', methods=['GET'], + strict_slashes=False) +def get_reviews(place_id): + """Retrieves the list of all review objects of place""" + place = storage.get(Place, place_id) + if not place: + abort(404) + reviews = [review.to_dict() for review in place.reviews] + return jsonify(reviews) + + +@app_views.route('/reviews/', methods=['GET'], + strict_slashes=False) +def get_review(review_id): + """Retrieve a specific Review object""" + review = storage.get(Review, review_id) + if not review: + abort(404) + return jsonify(review.to_dict()) + + +@app_views.route('/reviews/', methods=['DELETE'], + strict_slashes=False) +def delete_review(review_id): + """Delete a Review object""" + review = storage.get(Review, review_id) + if not review: + abort(404) + review.delete() + storage.save() + return jsonify({}), 200 + + +@app_views.route('/places//reviews', methods=['POST'], + strict_slashes=False) +def create_review(place_id): + """Create a new Review object""" + place = storage.get(Place, place_id) + if not place: + abort(404) + if not request.json: + abort(400, description="Not a JSON") + if 'user_id' not in request.json: + abort(400, description="Missing user_id") + if 'text' not in request.json: + abort(400, description="Missing text") + + user_id = request.json.get('user_id') + user = storage.get(User, user_id) + if not user: + abort(404) + + data = request.json + data['place_id'] = place_id + new_review = Review(**data) + new_review.save() + return jsonify(new_review.to_dict()), 201 + + +@app_views.route('/reviews/', methods=['PUT'], + strict_slashes=False) +def update_review(review_id): + """Update a Review object""" + review = storage.get(Review, review_id) + if not review: + abort(404) + if not request.json: + abort(400, description="Not a JSON") + + ignore_fields = ['id', 'user_id', 'place_id', 'created_at', 'updated_at'] + data = request.json + for key, value in data.items(): + if key not in ignore_fields: + setattr(review, key, value) + review.save() + return jsonify(review.to_dict()) From 5c3183c6cb22ca18c5990fc8d4d7b5403a071dc6 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 17:20:11 +0000 Subject: [PATCH 30/80] get() method test: added --- tests/test_models/test_engine/test_db_storage.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 1d5616d1653..cbb12cca4db 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,8 +68,12 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" +class TestDBStorage(unittest.TestCase): + """Test the DBStorage class""" + def tearDown(self): + """Tear down the tests""" + self.storage.close() + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): """Test that all returns a dictionaty""" From c7e35271eab6719668964d54018b8ba6379c0e5a Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 17:20:50 +0000 Subject: [PATCH 31/80] get() method test: added --- tests/test_models/test_engine/test_file_storage.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 3576f38e6f0..d25afc386dc 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -70,6 +70,13 @@ def test_fs_func_docstrings(self): class TestFileStorage(unittest.TestCase): """Test the FileStorage class""" + def tearDown(self): + """Cleans up test environment""" + try: + os.remove("file.json") + except FileNotFoundError: + pass + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_all_returns_dict(self): """Test that all returns the FileStorage.__objects attr""" From e7573e2752b46d86383ec0ee8fb374929e43341c Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 17:33:11 +0000 Subject: [PATCH 32/80] to be deleted -- testing checker --- tests/test_models/test_engine/test_db_storage.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index cbb12cca4db..766e625b5af 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,12 +68,8 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestDBStorage(unittest.TestCase): - """Test the DBStorage class""" - def tearDown(self): - """Tear down the tests""" - self.storage.close() - +class TestFileStorage(unittest.TestCase): + """Test the FileStorage class""" @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): """Test that all returns a dictionaty""" @@ -90,11 +86,3 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_get(self): - """Test the get() method""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_count(self): - """Test the count() method""" From 68af43ff6d5b7808be26df9c49dc77ff02e6756f Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 17:35:55 +0000 Subject: [PATCH 33/80] to be deleted -- testing checker --- .../test_engine/test_file_storage.py | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index d25afc386dc..1474a34fec0 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -70,13 +70,6 @@ def test_fs_func_docstrings(self): class TestFileStorage(unittest.TestCase): """Test the FileStorage class""" - def tearDown(self): - """Cleans up test environment""" - try: - os.remove("file.json") - except FileNotFoundError: - pass - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_all_returns_dict(self): """Test that all returns the FileStorage.__objects attr""" @@ -120,35 +113,3 @@ def test_save(self): with open("file.json", "r") as f: js = f.read() self.assertEqual(json.loads(string), json.loads(js)) - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_get(self): - """Test the get() method""" - storage = FileStorage() - state_obj = State(name="Texas") - storage.new(state_obj) - - obj1 = storage.get(State, state_obj.id) - self.assertEqual(obj1.name, "Texas") - obj2 = storage.get(State, '0215477852666') - self.assertIsNone(obj2) - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_count(self): - """Test the count() method""" - # create instance of storage, add, save and reload data - storage = FileStorage() - state1 = State() - storage.new(state1) - state2 = State() - storage.new(state2) - city1 = City() - storage.new(city1) - # save the data - storage.save() - # reload data from the file - storage.reload() - - self.assertEqual(storage.count(), 3) - self.assertEqual(storage.count(State), 2) - self.assertEqual(storage.count(Place), 0) From 34fdf9d8e99bcb28960bfe27ef0374f1f31ca830 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 18:07:43 +0000 Subject: [PATCH 34/80] tests: DStorage class --- tests/test_models/test_engine/test_db_storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..3e852c551f8 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,8 +68,8 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" +class TestDBStorage(unittest.TestCase): + """Test the DBStorage class""" @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): """Test that all returns a dictionaty""" From fc46f8e9e51fc27256529ddf68cb27cefe339617 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 18:12:51 +0000 Subject: [PATCH 35/80] tests: DStorage class --- tests/test_models/test_engine/test_db_storage.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 3e852c551f8..17ce8f537e6 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -86,3 +86,11 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_get(self): + """Test the get() method""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_count(self): + """Test the count() method""" From 04e91c920909b3ea018d1168b3272c565ff9072e Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 18:18:43 +0000 Subject: [PATCH 36/80] test: storage get() --- tests/test_models/test_engine/test_file_storage.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 1474a34fec0..57c9c99cf61 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -113,3 +113,15 @@ def test_save(self): with open("file.json", "r") as f: js = f.read() self.assertEqual(json.loads(string), json.loads(js)) + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_get(self): + """Test the get() method""" + storage = FileStorage() + state_obj = State(name="Texas") + storage.new(state_obj) + + obj1 = storage.get(State, state_obj.id) + self.assertEqual(obj1.name, "Texas") + obj2 = storage.get(State, '0215477852666') + self.assertIsNone(obj2) From c17307d61f64a4d0c2d38db783be4e894746306d Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 18:25:00 +0000 Subject: [PATCH 37/80] test: storage count() --- .../test_engine/test_file_storage.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 57c9c99cf61..3576f38e6f0 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -125,3 +125,23 @@ def test_get(self): self.assertEqual(obj1.name, "Texas") obj2 = storage.get(State, '0215477852666') self.assertIsNone(obj2) + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_count(self): + """Test the count() method""" + # create instance of storage, add, save and reload data + storage = FileStorage() + state1 = State() + storage.new(state1) + state2 = State() + storage.new(state2) + city1 = City() + storage.new(city1) + # save the data + storage.save() + # reload data from the file + storage.reload() + + self.assertEqual(storage.count(), 3) + self.assertEqual(storage.count(State), 2) + self.assertEqual(storage.count(Place), 0) From 0cc7d4181762640b46fe958dcd53154abe25f336 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 18:38:31 +0000 Subject: [PATCH 38/80] test: storage count() updated --- .../test_models/test_engine/test_file_storage.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 3576f38e6f0..859a6283812 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -129,19 +129,3 @@ def test_get(self): @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_count(self): """Test the count() method""" - # create instance of storage, add, save and reload data - storage = FileStorage() - state1 = State() - storage.new(state1) - state2 = State() - storage.new(state2) - city1 = City() - storage.new(city1) - # save the data - storage.save() - # reload data from the file - storage.reload() - - self.assertEqual(storage.count(), 3) - self.assertEqual(storage.count(State), 2) - self.assertEqual(storage.count(Place), 0) From ac61035ca21730d23c31a1ecb522817585e03daa Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 21:16:07 +0000 Subject: [PATCH 39/80] get() and count() method added --- models/engine/db_storage.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index b8b9373d50e..36cdec6ae69 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -73,21 +73,19 @@ def reload(self): def get(self, cls, id): """ Retrieves a specified object from storage """ - if cls in classes.values(): - cls_dict = self.all(cls) - obj_key = cls.__name__ + "." + id - return cls_dict.get(obj_key) - else: + if cls is None or id is None: return None + return self.__session.query(cls).get(id) def count(self, cls=None): """ Counts the number of objects in storage """ + num_objs = 0 if cls is None: - return len(self.all()) - elif cls in classes.keys(): - return len(self.all(cls)) + for cls in classes.values(): + num_objs += self.__session.query(cls).count() else: - return 0 + num_objs += self.__session.query(cls).count() + return num_objs def close(self): """Close session""" From 27185e74cb74f7601fff6ca92a2a817d68334600 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 21:49:58 +0000 Subject: [PATCH 40/80] route: /stats added --- api/v1/views/index.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/api/v1/views/index.py b/api/v1/views/index.py index 96acdcafdca..73eced73144 100755 --- a/api/v1/views/index.py +++ b/api/v1/views/index.py @@ -7,6 +7,12 @@ from flask import jsonify from api.v1.views import app_views from models import storage +from models.amenity import Amenity +from models.city import City +from models.state import State +from models.place import Place +from models.user import User +from models.review import Review @app_views.route('/status', methods=['GET']) @@ -19,11 +25,11 @@ def get_status(): def get_stats(): """Create an endpoint""" statistics = { - "amenities": storage.count("Amenity"), - "cities": storage.count("City"), - "places": storage.count("Place"), - "reviews": storage.count("Review"), - "states": storage.count("State"), - "users": storage.count("User"), + "amenities": storage.count(Amenity), + "cities": storage.count(City), + "places": storage.count(Place), + "reviews": storage.count(Review), + "states": storage.count(State), + "users": storage.count(User), } return jsonify(statistics) From 1a3809760d3bee19ae301f7a2d6b61110ae2394f Mon Sep 17 00:00:00 2001 From: deantosh Date: Sat, 3 Aug 2024 22:15:30 +0000 Subject: [PATCH 41/80] get() and count() method added --- models/engine/file_storage.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 75368f44cd2..a6dd398ca1c 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -67,6 +67,8 @@ def delete(self, obj=None): def get(self, cls, id): """retrieves a specified object from storage""" + if cls is None or id is None: + return None if cls in classes.values(): obj_key = cls.__name__ + "." + id cls_dict = self.all(cls) @@ -76,12 +78,7 @@ def get(self, cls, id): def count(self, cls=None): """counts the number of objects in storage""" - if cls is None: - return len(self.all()) - elif cls in classes.values(): - return len(self.all(cls)) - else: - return 0 + return len(self.all(cls)) def close(self): """call reload() method for deserializing the JSON file to objects""" From b13ef56ad85109deee48456dd072b0b85e360e02 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 09:22:24 +0000 Subject: [PATCH 42/80] route: /stats -- fix strict slashes url error --- api/v1/views/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/views/index.py b/api/v1/views/index.py index 73eced73144..07e352c4e73 100755 --- a/api/v1/views/index.py +++ b/api/v1/views/index.py @@ -21,7 +21,7 @@ def get_status(): return jsonify({"status": "OK"}) -@app_views.route('/stats', methods=['GET']) +@app_views.route('/stats', methods=['GET'], strict_slashes=False) def get_stats(): """Create an endpoint""" statistics = { From f6ec490f74bafb8f694dc872b224b55aa7390416 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Sun, 4 Aug 2024 09:45:26 +0000 Subject: [PATCH 43/80] places reviews --- api/v1/views/places_reviews.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v1/views/places_reviews.py b/api/v1/views/places_reviews.py index dc3ad4586f4..d4583df023e 100755 --- a/api/v1/views/places_reviews.py +++ b/api/v1/views/places_reviews.py @@ -12,8 +12,8 @@ from models.user import User -@app_viewe.route('/places//reviews', methods=['GET'], - strict_slashes=False) +@app_views.route('/places//reviews', methods=['GET'], + strict_slashes=False) def get_reviews(place_id): """Retrieves the list of all review objects of place""" place = storage.get(Place, place_id) From 006ad67fb7d2a5959fe0d9392274d253a95cbb13 Mon Sep 17 00:00:00 2001 From: luckys-lnz Date: Sun, 4 Aug 2024 09:45:56 +0000 Subject: [PATCH 44/80] updated package --- api/v1/views/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py index bd2ed44c4e7..7b10816d9c0 100755 --- a/api/v1/views/__init__.py +++ b/api/v1/views/__init__.py @@ -11,3 +11,4 @@ from api.v1.views.amenities import * from api.v1.views.users import * from api.v1.views.places import * +from api.v1.views.places_reviews import * From 8e39c701d4d959af4882c2d771b2625c9072bdfa Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 10:07:27 +0000 Subject: [PATCH 45/80] view: State objects --- api/v1/views/states.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v1/views/states.py b/api/v1/views/states.py index 3c2c0141609..67e444b9c83 100755 --- a/api/v1/views/states.py +++ b/api/v1/views/states.py @@ -10,7 +10,7 @@ from werkzeug.exceptions import BadRequest -@app_views.route('/states', methods=['GET'], strict_slashes=False) +@app_views.route('/states/', methods=['GET']) def get_all_states(): """ Retrievs a list of all State objects """ states_list = [state.to_dict() for state in storage.all(State).values()] @@ -39,7 +39,7 @@ def delete_state(state_id): return jsonify({}), 200 -@app_views.route('/states', methods=['POST'], strict_slashes=False) +@app_views.route('/states/', methods=['POST']) def create_state(): """ Creates a state object """ From b98059210c8a60771bebfc0d2542ce44464f35d2 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 11:14:27 +0000 Subject: [PATCH 46/80] tests: get() and count() --- tests/test_models/test_engine/test_db_storage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 1d5616d1653..5fd82fc9bdd 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,8 +68,8 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" +class TestDBStorage(unittest.TestCase): + """Test the DBStorage class""" @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): """Test that all returns a dictionaty""" @@ -89,8 +89,8 @@ def test_save(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_get(self): - """Test the get() method""" + """Test the db storage get() method""" @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_count(self): - """Test the count() method""" + """Test the db storage count() method""" From 861556faafaa55ca9f205b708451d4a8e884b2b6 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 11:18:35 +0000 Subject: [PATCH 47/80] tests: get() and count() --- .../test_models/test_engine/test_file_storage.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 9ef3d224c93..859a6283812 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -129,19 +129,3 @@ def test_get(self): @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_count(self): """Test the count() method""" - # create instance of storage, add, save and reload data - storage = FileStorage() - state1 = State() - storage.new(state1) - state2 = State() - storage.new(state2) - city1 = City() - storage.new(city1) - # save the data - storage.save() - # reload data from the file - storage.reload() - - self.assertEqual(storage.count(), 3) - self.assertEqual(storage.count(State), 2) - self.assertEqual(storage.count(HBNB), 0) From 164db3898c5e37f556e5151440ac8b706303e0d2 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 11:28:07 +0000 Subject: [PATCH 48/80] method: get(0 and count() added --- models/engine/db_storage.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index 8b7c955a936..533c14cfae4 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -80,14 +80,12 @@ def get(self, cls, id): else: return None - def count(self, cs=None): + def count(self, cls=None): """ Counts the number of objects in storage """ - if cs is None: + if cls is None: return len(self.all()) - elif cs in classes.values(): - return len(self.all(cls)) else: - return 0 + return len(self.all(cls)) def close(self): """call remove() method on the private session attribute""" From 7120d740b92f7afdfbc1a3f7707d2a51796ccab5 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 11:36:52 +0000 Subject: [PATCH 49/80] method: get() and count() added --- models/engine/file_storage.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 7e1c61cac67..8e2178b5dab 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -67,21 +67,20 @@ def delete(self, obj=None): def get(self, cls, id): """retrieves a specified object from storage""" - if cls in classes.values(): - obj_key = cls.__name__ + "." + id - cls_dict = self.all(cls) - return cls_dict.get(obj_key) - else: + if cls is None or id is None: return None + obj_key = cls.__name__ + "." + id + cls_dict = self.all(cls) + + return cls_dict.get(obj_key) + def count(self, cls=None): """counts the number of objects in storage""" if cls is None: return len(self.all()) - elif cls in classes.values(): - return len(self.all(cls)) else: - return 0 + return len(self.all(cls)) def close(self): """call reload() method for deserializing the JSON file to objects""" From 7791867344b8b2d44887f4569d4bae36b7ed6a06 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 18:40:30 +0000 Subject: [PATCH 50/80] test: get() and count() --- tests/test_models/test_engine/test_file_storage.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 859a6283812..0e0ab0527c4 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -117,14 +117,6 @@ def test_save(self): @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_get(self): """Test the get() method""" - storage = FileStorage() - state_obj = State(name="Texas") - storage.new(state_obj) - - obj1 = storage.get(State, state_obj.id) - self.assertEqual(obj1.name, "Texas") - obj2 = storage.get(State, '0215477852666') - self.assertIsNone(obj2) @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_count(self): From bc95edcddeaa548919f8b52e9b45838730011fe7 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 18:55:28 +0000 Subject: [PATCH 51/80] file updated --- tests/test_models/test_engine/test_file_storage.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 0e0ab0527c4..1474a34fec0 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -113,11 +113,3 @@ def test_save(self): with open("file.json", "r") as f: js = f.read() self.assertEqual(json.loads(string), json.loads(js)) - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_get(self): - """Test the get() method""" - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_count(self): - """Test the count() method""" From 599670dfac3a41aca27fcfb2353af7179abe0973 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 19:02:20 +0000 Subject: [PATCH 52/80] file updated --- tests/test_models/test_engine/test_db_storage.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 5fd82fc9bdd..3e852c551f8 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -86,11 +86,3 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_get(self): - """Test the db storage get() method""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_count(self): - """Test the db storage count() method""" From 53c2551cb2b5d941eec1750b4292408d06d84373 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 19:20:19 +0000 Subject: [PATCH 53/80] files deleted --- .../test_engine/test_db_storage.py | 88 -------------- .../test_engine/test_file_storage.py | 115 ------------------ 2 files changed, 203 deletions(-) delete mode 100755 tests/test_models/test_engine/test_db_storage.py delete mode 100755 tests/test_models/test_engine/test_file_storage.py diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py deleted file mode 100755 index 3e852c551f8..00000000000 --- a/tests/test_models/test_engine/test_db_storage.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/python3 -""" -Contains the TestDBStorageDocs and TestDBStorage classes -""" - -from datetime import datetime -import inspect -import models -from models.engine import db_storage -from models.amenity import Amenity -from models.base_model import BaseModel -from models.city import City -from models.place import Place -from models.review import Review -from models.state import State -from models.user import User -import json -import os -import pep8 -import unittest -DBStorage = db_storage.DBStorage -classes = {"Amenity": Amenity, "City": City, "Place": Place, - "Review": Review, "State": State, "User": User} - - -class TestDBStorageDocs(unittest.TestCase): - """Tests to check the documentation and style of DBStorage class""" - @classmethod - def setUpClass(cls): - """Set up for the doc tests""" - cls.dbs_f = inspect.getmembers(DBStorage, inspect.isfunction) - - def test_pep8_conformance_db_storage(self): - """Test that models/engine/db_storage.py conforms to PEP8.""" - pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['models/engine/db_storage.py']) - self.assertEqual(result.total_errors, 0, - "Found code style errors (and warnings).") - - def test_pep8_conformance_test_db_storage(self): - """Test tests/test_models/test_db_storage.py conforms to PEP8.""" - pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['tests/test_models/test_engine/\ -test_db_storage.py']) - self.assertEqual(result.total_errors, 0, - "Found code style errors (and warnings).") - - def test_db_storage_module_docstring(self): - """Test for the db_storage.py module docstring""" - self.assertIsNot(db_storage.__doc__, None, - "db_storage.py needs a docstring") - self.assertTrue(len(db_storage.__doc__) >= 1, - "db_storage.py needs a docstring") - - def test_db_storage_class_docstring(self): - """Test for the DBStorage class docstring""" - self.assertIsNot(DBStorage.__doc__, None, - "DBStorage class needs a docstring") - self.assertTrue(len(DBStorage.__doc__) >= 1, - "DBStorage class needs a docstring") - - def test_dbs_func_docstrings(self): - """Test for the presence of docstrings in DBStorage methods""" - for func in self.dbs_f: - self.assertIsNot(func[1].__doc__, None, - "{:s} method needs a docstring".format(func[0])) - self.assertTrue(len(func[1].__doc__) >= 1, - "{:s} method needs a docstring".format(func[0])) - - -class TestDBStorage(unittest.TestCase): - """Test the DBStorage class""" - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_all_returns_dict(self): - """Test that all returns a dictionaty""" - self.assertIs(type(models.storage.all()), dict) - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_all_no_class(self): - """Test that all returns all rows when no class is passed""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_new(self): - """test that new adds an object to the database""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_save(self): - """Test that save properly saves objects to file.json""" diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py deleted file mode 100755 index 1474a34fec0..00000000000 --- a/tests/test_models/test_engine/test_file_storage.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python3 -""" -Contains the TestFileStorageDocs classes -""" - -from datetime import datetime -import inspect -import models -from models.engine import file_storage -from models.amenity import Amenity -from models.base_model import BaseModel -from models.city import City -from models.place import Place -from models.review import Review -from models.state import State -from models.user import User -import json -import os -import pep8 -import unittest -FileStorage = file_storage.FileStorage -classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, - "Place": Place, "Review": Review, "State": State, "User": User} - - -class TestFileStorageDocs(unittest.TestCase): - """Tests to check the documentation and style of FileStorage class""" - @classmethod - def setUpClass(cls): - """Set up for the doc tests""" - cls.fs_f = inspect.getmembers(FileStorage, inspect.isfunction) - - def test_pep8_conformance_file_storage(self): - """Test that models/engine/file_storage.py conforms to PEP8.""" - pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['models/engine/file_storage.py']) - self.assertEqual(result.total_errors, 0, - "Found code style errors (and warnings).") - - def test_pep8_conformance_test_file_storage(self): - """Test tests/test_models/test_file_storage.py conforms to PEP8.""" - pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['tests/test_models/test_engine/\ -test_file_storage.py']) - self.assertEqual(result.total_errors, 0, - "Found code style errors (and warnings).") - - def test_file_storage_module_docstring(self): - """Test for the file_storage.py module docstring""" - self.assertIsNot(file_storage.__doc__, None, - "file_storage.py needs a docstring") - self.assertTrue(len(file_storage.__doc__) >= 1, - "file_storage.py needs a docstring") - - def test_file_storage_class_docstring(self): - """Test for the FileStorage class docstring""" - self.assertIsNot(FileStorage.__doc__, None, - "FileStorage class needs a docstring") - self.assertTrue(len(FileStorage.__doc__) >= 1, - "FileStorage class needs a docstring") - - def test_fs_func_docstrings(self): - """Test for the presence of docstrings in FileStorage methods""" - for func in self.fs_f: - self.assertIsNot(func[1].__doc__, None, - "{:s} method needs a docstring".format(func[0])) - self.assertTrue(len(func[1].__doc__) >= 1, - "{:s} method needs a docstring".format(func[0])) - - -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_all_returns_dict(self): - """Test that all returns the FileStorage.__objects attr""" - storage = FileStorage() - new_dict = storage.all() - self.assertEqual(type(new_dict), dict) - self.assertIs(new_dict, storage._FileStorage__objects) - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_new(self): - """test that new adds an object to the FileStorage.__objects attr""" - storage = FileStorage() - save = FileStorage._FileStorage__objects - FileStorage._FileStorage__objects = {} - test_dict = {} - for key, value in classes.items(): - with self.subTest(key=key, value=value): - instance = value() - instance_key = instance.__class__.__name__ + "." + instance.id - storage.new(instance) - test_dict[instance_key] = instance - self.assertEqual(test_dict, storage._FileStorage__objects) - FileStorage._FileStorage__objects = save - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_save(self): - """Test that save properly saves objects to file.json""" - storage = FileStorage() - new_dict = {} - for key, value in classes.items(): - instance = value() - instance_key = instance.__class__.__name__ + "." + instance.id - new_dict[instance_key] = instance - save = FileStorage._FileStorage__objects - FileStorage._FileStorage__objects = new_dict - storage.save() - FileStorage._FileStorage__objects = save - for key, value in new_dict.items(): - new_dict[key] = value.to_dict() - string = json.dumps(new_dict) - with open("file.json", "r") as f: - js = f.read() - self.assertEqual(json.loads(string), json.loads(js)) From faa72496b7dfa6a3b0b4b6e74dacd7e40fd9d666 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 19:45:17 +0000 Subject: [PATCH 54/80] tests: FileStorage class --- .../test_engine/test_file_storage.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 tests/test_models/test_engine/test_file_storage.py diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py new file mode 100755 index 00000000000..766e625b5af --- /dev/null +++ b/tests/test_models/test_engine/test_file_storage.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 +""" +Contains the TestDBStorageDocs and TestDBStorage classes +""" + +from datetime import datetime +import inspect +import models +from models.engine import db_storage +from models.amenity import Amenity +from models.base_model import BaseModel +from models.city import City +from models.place import Place +from models.review import Review +from models.state import State +from models.user import User +import json +import os +import pep8 +import unittest +DBStorage = db_storage.DBStorage +classes = {"Amenity": Amenity, "City": City, "Place": Place, + "Review": Review, "State": State, "User": User} + + +class TestDBStorageDocs(unittest.TestCase): + """Tests to check the documentation and style of DBStorage class""" + @classmethod + def setUpClass(cls): + """Set up for the doc tests""" + cls.dbs_f = inspect.getmembers(DBStorage, inspect.isfunction) + + def test_pep8_conformance_db_storage(self): + """Test that models/engine/db_storage.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['models/engine/db_storage.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_pep8_conformance_test_db_storage(self): + """Test tests/test_models/test_db_storage.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['tests/test_models/test_engine/\ +test_db_storage.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_db_storage_module_docstring(self): + """Test for the db_storage.py module docstring""" + self.assertIsNot(db_storage.__doc__, None, + "db_storage.py needs a docstring") + self.assertTrue(len(db_storage.__doc__) >= 1, + "db_storage.py needs a docstring") + + def test_db_storage_class_docstring(self): + """Test for the DBStorage class docstring""" + self.assertIsNot(DBStorage.__doc__, None, + "DBStorage class needs a docstring") + self.assertTrue(len(DBStorage.__doc__) >= 1, + "DBStorage class needs a docstring") + + def test_dbs_func_docstrings(self): + """Test for the presence of docstrings in DBStorage methods""" + for func in self.dbs_f: + self.assertIsNot(func[1].__doc__, None, + "{:s} method needs a docstring".format(func[0])) + self.assertTrue(len(func[1].__doc__) >= 1, + "{:s} method needs a docstring".format(func[0])) + + +class TestFileStorage(unittest.TestCase): + """Test the FileStorage class""" + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_all_returns_dict(self): + """Test that all returns a dictionaty""" + self.assertIs(type(models.storage.all()), dict) + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_all_no_class(self): + """Test that all returns all rows when no class is passed""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_new(self): + """test that new adds an object to the database""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_save(self): + """Test that save properly saves objects to file.json""" From 9bef908fd5ba1d39f24d33a5dfbf62d7789ba8ed Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 20:05:00 +0000 Subject: [PATCH 55/80] tests: DBStorage class --- .../test_engine/test_db_storage.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) 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 00000000000..766e625b5af --- /dev/null +++ b/tests/test_models/test_engine/test_db_storage.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 +""" +Contains the TestDBStorageDocs and TestDBStorage classes +""" + +from datetime import datetime +import inspect +import models +from models.engine import db_storage +from models.amenity import Amenity +from models.base_model import BaseModel +from models.city import City +from models.place import Place +from models.review import Review +from models.state import State +from models.user import User +import json +import os +import pep8 +import unittest +DBStorage = db_storage.DBStorage +classes = {"Amenity": Amenity, "City": City, "Place": Place, + "Review": Review, "State": State, "User": User} + + +class TestDBStorageDocs(unittest.TestCase): + """Tests to check the documentation and style of DBStorage class""" + @classmethod + def setUpClass(cls): + """Set up for the doc tests""" + cls.dbs_f = inspect.getmembers(DBStorage, inspect.isfunction) + + def test_pep8_conformance_db_storage(self): + """Test that models/engine/db_storage.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['models/engine/db_storage.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_pep8_conformance_test_db_storage(self): + """Test tests/test_models/test_db_storage.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['tests/test_models/test_engine/\ +test_db_storage.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_db_storage_module_docstring(self): + """Test for the db_storage.py module docstring""" + self.assertIsNot(db_storage.__doc__, None, + "db_storage.py needs a docstring") + self.assertTrue(len(db_storage.__doc__) >= 1, + "db_storage.py needs a docstring") + + def test_db_storage_class_docstring(self): + """Test for the DBStorage class docstring""" + self.assertIsNot(DBStorage.__doc__, None, + "DBStorage class needs a docstring") + self.assertTrue(len(DBStorage.__doc__) >= 1, + "DBStorage class needs a docstring") + + def test_dbs_func_docstrings(self): + """Test for the presence of docstrings in DBStorage methods""" + for func in self.dbs_f: + self.assertIsNot(func[1].__doc__, None, + "{:s} method needs a docstring".format(func[0])) + self.assertTrue(len(func[1].__doc__) >= 1, + "{:s} method needs a docstring".format(func[0])) + + +class TestFileStorage(unittest.TestCase): + """Test the FileStorage class""" + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_all_returns_dict(self): + """Test that all returns a dictionaty""" + self.assertIs(type(models.storage.all()), dict) + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_all_no_class(self): + """Test that all returns all rows when no class is passed""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_new(self): + """test that new adds an object to the database""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_save(self): + """Test that save properly saves objects to file.json""" From 0a3428d98c5c3232aa66ef2a859821f4272121b4 Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 20:18:33 +0000 Subject: [PATCH 56/80] tests: DBStorage class --- tests/test_models/test_engine/test_db_storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..3e852c551f8 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,8 +68,8 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" +class TestDBStorage(unittest.TestCase): + """Test the DBStorage class""" @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): """Test that all returns a dictionaty""" From 1dcd48e615312a35c98edd0a053a52f7d4bac9bf Mon Sep 17 00:00:00 2001 From: deantosh Date: Sun, 4 Aug 2024 20:38:16 +0000 Subject: [PATCH 57/80] tests: DBStorage class --- tests/test_models/test_engine/test_db_storage.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 3e852c551f8..0cfb7e0dd83 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -86,3 +86,11 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_get(self): + """Test that gets an object from the database""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_count(self): + """Test that counts objects in the database""" From 519141a4aa39b962379a2de75dcc83cebeced998 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 08:08:20 +0000 Subject: [PATCH 58/80] tests --- .../__pycache__/test_console.cpython-312.pyc | Bin 2604 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 156 -> 0 bytes .../__pycache__/test_amenity.cpython-312.pyc | Bin 7163 -> 0 bytes .../test_base_model.cpython-312.pyc | Bin 10086 -> 0 bytes .../__pycache__/test_city.cpython-312.pyc | Bin 7590 -> 0 bytes .../__pycache__/test_place.cpython-312.pyc | Bin 13473 -> 0 bytes .../__pycache__/test_review.cpython-312.pyc | Bin 8205 -> 0 bytes .../__pycache__/test_state.cpython-312.pyc | Bin 7076 -> 0 bytes .../__pycache__/test_user.cpython-312.pyc | Bin 8706 -> 0 bytes tests/test_models/test_amenity.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 168 -> 0 bytes .../test_db_storage.cpython-312.pyc | Bin 5942 -> 0 bytes .../test_file_storage.cpython-312.pyc | Bin 7675 -> 0 bytes .../test_engine/test_db_storage.py | 12 +- .../test_engine/test_file_storage.py | 105 +++++++++++------- 15 files changed, 69 insertions(+), 49 deletions(-) delete mode 100644 tests/__pycache__/test_console.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/__init__.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_amenity.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_base_model.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_city.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_place.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_review.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_state.cpython-312.pyc delete mode 100644 tests/test_models/__pycache__/test_user.cpython-312.pyc delete mode 100644 tests/test_models/test_engine/__pycache__/__init__.cpython-312.pyc delete mode 100644 tests/test_models/test_engine/__pycache__/test_db_storage.cpython-312.pyc delete mode 100644 tests/test_models/test_engine/__pycache__/test_file_storage.cpython-312.pyc diff --git a/tests/__pycache__/test_console.cpython-312.pyc b/tests/__pycache__/test_console.cpython-312.pyc deleted file mode 100644 index 2f8eefab560de25fde22c110f4fc398fb991bcf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2604 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 diff --git a/tests/test_models/__pycache__/test_review.cpython-312.pyc b/tests/test_models/__pycache__/test_review.cpython-312.pyc deleted file mode 100644 index 1dce09427c916a66990839bea550e016dd73db8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8205 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 P0kIGezhUqf-KqZrmz_K3 diff --git a/tests/test_models/test_amenity.py b/tests/test_models/test_amenity.py index 09ce5b06305..66b0bb69bc2 100755 --- a/tests/test_models/test_amenity.py +++ b/tests/test_models/test_amenity.py @@ -79,6 +79,7 @@ def test_name_attr(self): def test_to_dict_creates_dict(self): """test to_dict method creates a dictionary with proper attrs""" am = Amenity() + print(am.__dict__) new_d = am.to_dict() self.assertEqual(type(new_d), dict) self.assertFalse("_sa_instance_state" in new_d) diff --git a/tests/test_models/test_engine/__pycache__/__init__.cpython-312.pyc b/tests/test_models/test_engine/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index c22067f18c35004407e2e3714bd8d82548733e12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 zcmX@j%ge<81f`j)(?IlN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!a@Wtu&rQ|O$*a&ryk0@& fFAf`^g3_E+yCPPgxr{(u3}Sp_W@Kb6Vg|AR!)hvg diff --git a/tests/test_models/test_engine/__pycache__/test_db_storage.cpython-312.pyc b/tests/test_models/test_engine/__pycache__/test_db_storage.cpython-312.pyc deleted file mode 100644 index 9572aa0f3acd6d0d7a6fdc7629956cabb1a121a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5942 zcmdTITZ|Le^^WcFYwh(q>@GIzCBueC%wv}j0)z#WeE=aTP8v3}vYL*(p1WW(9-Di| zpmm9~sgMd$TN0_#5(za#s#FmD;3NM{{v!1!*0!=c2q{wglRseLhp(PYl7e z{Cl%r`1k3)lAM*rn5_FtfowqFemz(UWkUiF=;2aFwnN}SJyMEhqXG}JiZddP92MK2SrlddoBZPT`}J6;>h_6f=uPpUa%9{5w$@(=PzGxTvy{KTGrH#Eog?d=(A}n=X_Maa-J+)~QmSQrwHQuPhHb={6r= zD6{!r1DGYIJ63F+!X+idL~7B@7D9@C7j=<4&fH5xb!SXVX-_S2ikeEbqD+-pYtxE} zXUU{xD*(c;(7b7d&rq{OOOupwE97q83K#fP)yU)dELof6>J&)Fo7XwVO)F0`Lubn+ zI;0!5q4A@qheir))EHIsdfA}rh1Z9~Is&RC6hF6dYMdz;)W+L7Ggz6n!kn5PR>n}v zYN=&H7;KNGV0pd~zzlipCq1b~_ttv%*1t@Dy{)llyuN3AuKVZ}?;k?*ct49SA5}_R z{vQE(*5zx`-#m95@q8>@l8QKcNz=S%B$vi>i4> z7-<9(^mqr(_G70VZ9!&go0g6(6E@opx0#mN4)|kn1UnJzLNJJ62*5(Q zzP!YSA@GATED-B!xH)&-woNY(>yjz^afzi;O*WF->&fl6#+%7QDAglvsh()0cGXk6 z<`RQ;KI`{;^{BE%zNFG(Zx+4vu5%6snWu$IK;Xw$&=_ z5e~!hVW!D(m#y?5;pi8noiNnaD_+~as$wmpMP=d_0f3+APRzdZ`8$t@FLYA6GTw}I zH6p2cBz1k_=B6)x@um8OdP{5eA8PcE)%(Z38UHT+?eV$(pMl0jGtv){u50gIeecHD zP2bI*e--#^?5o&ZYWLlddg_g4YPgXat*1u6+54UJ?f$vcyP$*7lhVTgt|L@2lkTvB zs%qp)6kVp;@_?!pQB_Qy>=9vbkYS-2+1JW?Vdrcs0t|Evy&l_xU>|_nL~s{gmO(*G z1lRxwe*?f}@^9bqrrh<=S&FATKEATwb9U zIH$9`Fx&Fsq-D{k2QNc~an<3j zB3W$;zi6WcK?QV!@|w&$I=id}CS^rDi}F#R)>@?;Sn1@N&0^%359 za+C0av&v3`Vy7^@mRNCvF!BK`&G5>f?NYy$`#7=X$8NyvpZ4zVih`A za;v-E zv+Mhz<8$)c=)4c0?>-@+^FHt(Nc=r(P)qVr;p&kbaZvAP&=0q`OY-ousYe{{4}trL z9o@vS(*1V2h~9<85QzJU+7>-%SCDq?(CPF+G{hG|@yut%olev=`?ZLnJF_~R{yb9R z+lm!&dMfyUC|(Aw)+$UqC5neamyF}1qIfoRc~#-jLcAebK2#pdQ|7E6w~PiPQeP_| z%oLtDf~YrRuqivh3JETj>eJvM*bx*oN7zR&A4?|wTL2Hdk|aI!c_iPHAdxoSBc1n1 z^gh{qpA6h5+wPOpeX{XBQ3OogBOUihNWfS#(%XnA^@uVPYWDWuH1E=8Pv1>?cjBQ} t>KL90JPvM`PD`%CIKxPsH1-3u^vn>Y!{MB>r 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 O?WL~m-w=3UlKC^}3d1h| diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 0cfb7e0dd83..766e625b5af 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,8 +68,8 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestDBStorage(unittest.TestCase): - """Test the DBStorage class""" +class TestFileStorage(unittest.TestCase): + """Test the FileStorage class""" @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): """Test that all returns a dictionaty""" @@ -86,11 +86,3 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_get(self): - """Test that gets an object from the database""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_count(self): - """Test that counts objects in the database""" diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 766e625b5af..1474a34fec0 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -1,12 +1,12 @@ #!/usr/bin/python3 """ -Contains the TestDBStorageDocs and TestDBStorage classes +Contains the TestFileStorageDocs classes """ from datetime import datetime import inspect import models -from models.engine import db_storage +from models.engine import file_storage from models.amenity import Amenity from models.base_model import BaseModel from models.city import City @@ -18,50 +18,50 @@ import os import pep8 import unittest -DBStorage = db_storage.DBStorage -classes = {"Amenity": Amenity, "City": City, "Place": Place, - "Review": Review, "State": State, "User": User} +FileStorage = file_storage.FileStorage +classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, + "Place": Place, "Review": Review, "State": State, "User": User} -class TestDBStorageDocs(unittest.TestCase): - """Tests to check the documentation and style of DBStorage class""" +class TestFileStorageDocs(unittest.TestCase): + """Tests to check the documentation and style of FileStorage class""" @classmethod def setUpClass(cls): """Set up for the doc tests""" - cls.dbs_f = inspect.getmembers(DBStorage, inspect.isfunction) + cls.fs_f = inspect.getmembers(FileStorage, inspect.isfunction) - def test_pep8_conformance_db_storage(self): - """Test that models/engine/db_storage.py conforms to PEP8.""" + def test_pep8_conformance_file_storage(self): + """Test that models/engine/file_storage.py conforms to PEP8.""" pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['models/engine/db_storage.py']) + result = pep8s.check_files(['models/engine/file_storage.py']) self.assertEqual(result.total_errors, 0, "Found code style errors (and warnings).") - def test_pep8_conformance_test_db_storage(self): - """Test tests/test_models/test_db_storage.py conforms to PEP8.""" + def test_pep8_conformance_test_file_storage(self): + """Test tests/test_models/test_file_storage.py conforms to PEP8.""" pep8s = pep8.StyleGuide(quiet=True) result = pep8s.check_files(['tests/test_models/test_engine/\ -test_db_storage.py']) +test_file_storage.py']) self.assertEqual(result.total_errors, 0, "Found code style errors (and warnings).") - def test_db_storage_module_docstring(self): - """Test for the db_storage.py module docstring""" - self.assertIsNot(db_storage.__doc__, None, - "db_storage.py needs a docstring") - self.assertTrue(len(db_storage.__doc__) >= 1, - "db_storage.py needs a docstring") + def test_file_storage_module_docstring(self): + """Test for the file_storage.py module docstring""" + self.assertIsNot(file_storage.__doc__, None, + "file_storage.py needs a docstring") + self.assertTrue(len(file_storage.__doc__) >= 1, + "file_storage.py needs a docstring") - def test_db_storage_class_docstring(self): - """Test for the DBStorage class docstring""" - self.assertIsNot(DBStorage.__doc__, None, - "DBStorage class needs a docstring") - self.assertTrue(len(DBStorage.__doc__) >= 1, - "DBStorage class needs a docstring") + def test_file_storage_class_docstring(self): + """Test for the FileStorage class docstring""" + self.assertIsNot(FileStorage.__doc__, None, + "FileStorage class needs a docstring") + self.assertTrue(len(FileStorage.__doc__) >= 1, + "FileStorage class needs a docstring") - def test_dbs_func_docstrings(self): - """Test for the presence of docstrings in DBStorage methods""" - for func in self.dbs_f: + def test_fs_func_docstrings(self): + """Test for the presence of docstrings in FileStorage methods""" + for func in self.fs_f: self.assertIsNot(func[1].__doc__, None, "{:s} method needs a docstring".format(func[0])) self.assertTrue(len(func[1].__doc__) >= 1, @@ -70,19 +70,46 @@ def test_dbs_func_docstrings(self): class TestFileStorage(unittest.TestCase): """Test the FileStorage class""" - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_all_returns_dict(self): - """Test that all returns a dictionaty""" - self.assertIs(type(models.storage.all()), dict) + """Test that all returns the FileStorage.__objects attr""" + storage = FileStorage() + new_dict = storage.all() + self.assertEqual(type(new_dict), dict) + self.assertIs(new_dict, storage._FileStorage__objects) - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") - def test_all_no_class(self): - """Test that all returns all rows when no class is passed""" - - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_new(self): - """test that new adds an object to the database""" + """test that new adds an object to the FileStorage.__objects attr""" + storage = FileStorage() + save = FileStorage._FileStorage__objects + FileStorage._FileStorage__objects = {} + test_dict = {} + for key, value in classes.items(): + with self.subTest(key=key, value=value): + instance = value() + instance_key = instance.__class__.__name__ + "." + instance.id + storage.new(instance) + test_dict[instance_key] = instance + self.assertEqual(test_dict, storage._FileStorage__objects) + FileStorage._FileStorage__objects = save - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_save(self): """Test that save properly saves objects to file.json""" + storage = FileStorage() + new_dict = {} + for key, value in classes.items(): + instance = value() + instance_key = instance.__class__.__name__ + "." + instance.id + new_dict[instance_key] = instance + save = FileStorage._FileStorage__objects + FileStorage._FileStorage__objects = new_dict + storage.save() + FileStorage._FileStorage__objects = save + for key, value in new_dict.items(): + new_dict[key] = value.to_dict() + string = json.dumps(new_dict) + with open("file.json", "r") as f: + js = f.read() + self.assertEqual(json.loads(string), json.loads(js)) From add9c20dc0c6d675a3ee5a116219d7180156a6b5 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 08:28:49 +0000 Subject: [PATCH 59/80] models --- models/__init__.py | 25 ++-- models/__pycache__/__init__.cpython-312.pyc | Bin 540 -> 0 bytes models/__pycache__/amenity.cpython-312.pyc | Bin 1098 -> 0 bytes models/__pycache__/base_model.cpython-312.pyc | Bin 4164 -> 0 bytes models/amenity.py | 37 ++++-- models/base_model.py | 117 +++++++++++------- models/city.py | 26 ++-- models/engine/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 152 -> 0 bytes .../__pycache__/db_storage.cpython-312.pyc | Bin 3880 -> 0 bytes .../__pycache__/file_storage.cpython-312.pyc | Bin 3603 -> 0 bytes models/engine/db_storage.py | 109 ++++++++-------- models/engine/file_storage.py | 113 ++++++++--------- models/place.py | 98 ++++++++------- models/review.py | 26 ++-- models/state.py | 52 ++++---- models/user.py | 27 ++-- 17 files changed, 332 insertions(+), 298 deletions(-) mode change 100755 => 100644 models/__init__.py delete mode 100644 models/__pycache__/__init__.cpython-312.pyc delete mode 100644 models/__pycache__/amenity.cpython-312.pyc delete mode 100644 models/__pycache__/base_model.cpython-312.pyc mode change 100755 => 100644 models/amenity.py mode change 100755 => 100644 models/base_model.py mode change 100755 => 100644 models/city.py mode change 100755 => 100644 models/engine/__init__.py delete mode 100644 models/engine/__pycache__/__init__.cpython-312.pyc delete mode 100644 models/engine/__pycache__/db_storage.cpython-312.pyc delete mode 100644 models/engine/__pycache__/file_storage.cpython-312.pyc mode change 100755 => 100644 models/place.py mode change 100755 => 100644 models/review.py mode change 100755 => 100644 models/state.py mode change 100755 => 100644 models/user.py diff --git a/models/__init__.py b/models/__init__.py old mode 100755 new mode 100644 index defef6378c1..3919d9b90f1 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,17 +1,22 @@ #!/usr/bin/python3 -""" -initialize the models package -""" +"""This module instantiates an object of class FileStorage""" +import os +from models.engine.db_storage import DBStorage +from models.engine.file_storage import FileStorage +from models.base_model import Base +from models.user import User +from models.review import Review +from models.place import Place +from models.city import City +from models.state import State +from models.amenity import Amenity -from os import getenv +storage_type = os.getenv("HBNB_TYPE_STORAGE") -storage_t = getenv("HBNB_TYPE_STORAGE") - -if storage_t == "db": - from models.engine.db_storage import DBStorage +if storage_type == "db": storage = DBStorage() + storage.reload() else: - from models.engine.file_storage import FileStorage storage = FileStorage() -storage.reload() + storage.reload() diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index e7fed729983112c3798fda6330dfba044218c423..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 540 zcmY+Bze~eF6vy8sX_8hO)QZ0rJE>h7QE(EW!CEI3D}q^$+B@6WOCo7nu#*MB!NtwL zLHeh-h$3h}5X8aFA}&r|Z2a|xyZ62C=e-B-E|bXw@Mu__y;TJ86pHTSAuhO*b63Xi7gHpLxs?fwsSeikBT%kU7n!(_z zv1Z_{osB#$ZmnlC;}5m6$&h+m}a!To%5|dyK83l zfIGQxVJYW=NC+Xjq@W;j{*yG2L<$VDoCq3smYfLWiZ{Dwhmq#ZoA=(l_rCAVZ}oZ& zaCPI|_SY7`D|0zn&QLk(pt21SP>4um2|@{;6=5Y6T1jkb6JxhUHF2m@=$3G)i?*t8 z6OVd{PyNE~h*~m2M+jK(Jw)y%L>{kjd)F>2(b1k2`291DP9`GNXo*m%#+8Jpao#&H zAvXd``wOAsKV%UX+BRxHw6n}}p58{Mb2Ss)B-PGhF5`3=eWQ|#FpslTb>g*FS*MiM z;pJ!rm7fqG2LuMxiU`G~RtjR?e*h3!t6u`hiKs#y3%HBhi|k!eMn%B0tWgYGz#p@)7b?Y(tnGooa!0J!L$ZQ;n zxX({{HWXJ?ca2N!F=hf73v6vGF521-<+9Sw>Yd{B87oG_m@)%wG#VSn&1NS{cvGbP z=6BbAY+i}wLb`xqS<2b%3(dsLO*Kncz4^7C{0Lw6KO7P=e^PM*t!fg*+YNZ_)wkSd z-t^AwQ!jY*#UEa9gboNsO@k?>VocWGSE&2K5(XllR*@iw{EqE{FeRq*!aMHV_Vg^LqH*?Pf`VS z`LWSIL7~UX#^;ytvm2CL$ck|q&b5^(X-2PW>XryatHYDtdq6(;-#VYkM0)0->G&5Y zs~HH#-ldYfZ{<0>8f*%`IUH69A^*V4OQ`R|S9>t|62|x8^F28CuTvv)gLvo6e}GDn F=^ZJN9j*WX diff --git a/models/__pycache__/base_model.cpython-312.pyc b/models/__pycache__/base_model.cpython-312.pyc deleted file mode 100644 index 32445d55282ac5d0b145565bbe584d4c084f9135..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4164 zcmb_fT})fo9Y6QO*B2XX6O)j@pm$)8m;}5*wxBd*UE(!qrILrRN%K-&U3?G0&b3X? zy@ui%l}&k|Ql=7hQ$VRix|f2elvJv;J?vqVChcK;i0ec+H&lzZzVwa3KBPYFfA00Y zb~5O)szahoBVF8I06SStRFX^k%`ICOtn{38xro*|i z-m2o&qX-d^McB7dn@$FDEDk0EEIN6RP@qdbkbziiB#>SR^zD+c7F$*n+T&M z12P18B{|GzEV(z5Bc!qx&m+LI0H0UoHW-^i2 zwW!`}C>c;isDo6l$&l)Ux>fZ?Yl5C6`K&ZMl}=4blLezd zusx4;X*R8C(gc=NOhA4rX@W<7ej2KA5Uqcjq_YZ{lds}A78m6=R8AV#sib%{0DOCFWrnNZ0wTRei$jqKIK6sXVpaAC z42_U8=iFC9vg3v*cckHz0}f@qb+~Wbvkyo7z8tmH&vSZTkt2Rzj$Fz79~`-R_Tk7= z=V+nV@H=le+MQkoEaK_nNX+37B5y_8EEX+=vH z0h#hSDL*M0;F%Plq%6fevq_dM7?fqxmm(MtO_dd6%=8teDGJho$)?pRU`)Kfl%et=0TWzS13s(Zy?1kS){?zw}zG`gXPS<+ZF2{sMwh_}b zLZYzc2`N)fEh*oH&P`;vQEV@{I`{;>?@HZprMg%2$+*Y|$Ja^;Irzq)|FPReKWA?} zrZQ-b0qvZ&u|v18hGIo}qF`XDuMR1F5b}*FYduvU1QaOMb1tuiBBT?}w;@BY-5Q|B z$P?uXHn|+01<+7tFzE-X2K|t4!?+lz>xL3RTt_<9&I0V z0PdAg)Ro{UhEmgZ|B-8(}ept>ptGPkApEkib@PPpr`nKf+2&0 z99og6t3Ygl3{6VenKdNGk^&hWAh;g45!{J9kainNE@g)dOS`rQ9*}hfP}*JucY@Fp z6G#jmZ1Sl*1#xh(Ng7QE{SmM>5lH?43@jHlMKB4=Yjx@clH;vL-kNOm*4or4km)~% z3cNMkeslE3=w>)t4o55DzD2R>Z(klM`+KX=fi}8vWVw$>R|0;Ya2PmviJTS(!Oa3~CNG7UReOTu3OInS#Cbzg%qu7K@>}tJ!2P z!K_X*L~u68y}E$)W%YsBYjHXj3tOr)eKHNw1r31LY?kHM3W{dKFhyB4p8`CluP~cx z&yo#70}!F;W+649d3FO@&^*L8stD~JqZI`N@;zG7_pT%B2^N+Uii1`~|B>{|P+db0 z`SVq8%ci%z>}_8@@rn1jhb`e7FD?(g^YU8ae#>_j*@p)^moKbM-N~=#?;kv~=zDs+ z!-qYCJN=wzWXH#Qx_6p=p2NF`Sx>~inBEy{CC}5VDM;8HvU#2GFfx7mYnq~^rf_zy zfmh0=*->8_#IGBJ4S$@3{$r4D(8}@->b53NHDe0-i40B|Hm}s?>->Kqp0>q*hMa>1 z8fEk`sJ1zVVgAmu4F69NG2M?)%ctnzBh>mA6fdLrr|9G(t~6_{?>)WzlHBIksSn;b>Dvgbkk(p diff --git a/models/amenity.py b/models/amenity.py old mode 100755 new mode 100644 index 557728bafdc..fe4e35d5a26 --- a/models/amenity.py +++ b/models/amenity.py @@ -1,21 +1,32 @@ -#!/usr/bin/python -""" holds class Amenity""" -import models -from models.base_model import BaseModel, Base -from os import getenv -import sqlalchemy -from sqlalchemy import Column, String +#!/usr/bin/python3 +""" State Module for HBNB project """ +import os +from sqlalchemy import Column, String, ForeignKey from sqlalchemy.orm import relationship +from models.base_model import BaseModel, Base + + +# Get the storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") + +# lazy import association table +if storage_type == "db": + from models.place import place_amenity class Amenity(BaseModel, Base): - """Representation of Amenity """ - if models.storage_t == 'db': + """ Defines Amenity class """ + + if storage_type == "db": __tablename__ = 'amenities' + # Handle database storage name = Column(String(128), nullable=False) + + # Many-to-Many relationship + place_amenities = relationship('Place', + secondary='place_amenity', + back_populates='amenities', + viewonly=False) else: + # Handle file storage name = "" - - def __init__(self, *args, **kwargs): - """initializes Amenity""" - super().__init__(*args, **kwargs) diff --git a/models/base_model.py b/models/base_model.py old mode 100755 new mode 100644 index 9a86addb366..92bea00bb4f --- a/models/base_model.py +++ b/models/base_model.py @@ -1,75 +1,102 @@ #!/usr/bin/python3 """ -Contains class BaseModel +This module defines a base class for all models in our hbnb clone """ - +import os +import uuid from datetime import datetime -import models -from os import getenv -import sqlalchemy from sqlalchemy import Column, String, DateTime from sqlalchemy.ext.declarative import declarative_base -import uuid -time = "%Y-%m-%dT%H:%M:%S.%f" -if models.storage_t == "db": +# Get storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") +if storage_type == "db": Base = declarative_base() else: Base = object +def generate_uuid(): + return str(uuid.uuid4()) + + class BaseModel: - """The BaseModel class from which future classes will be derived""" - if models.storage_t == "db": - id = Column(String(60), primary_key=True) - created_at = Column(DateTime, default=datetime.utcnow) - updated_at = Column(DateTime, default=datetime.utcnow) + """A base class for all hbnb models""" + if storage_type == "db": + id = Column( + String(60), primary_key=True, nullable=False, + default=generate_uuid) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + updated_at = Column(DateTime, nullable=False, default=datetime.utcnow) + # Handle File storage def __init__(self, *args, **kwargs): - """Initialization of the base model""" + """Instantiates a new model""" if kwargs: - for key, value in kwargs.items(): - if key != "__class__": - setattr(self, key, value) - if kwargs.get("created_at", None) and type(self.created_at) is str: - self.created_at = datetime.strptime(kwargs["created_at"], time) + if 'updated_at' in kwargs: + # recreate obj -- from to_dict() + kwargs['updated_at'] = datetime.strptime( + kwargs['updated_at'], '%Y-%m-%dT%H:%M:%S.%f') else: - self.created_at = datetime.utcnow() - if kwargs.get("updated_at", None) and type(self.updated_at) is str: - self.updated_at = datetime.strptime(kwargs["updated_at"], time) + # new obj + self.updated_at = datetime.now() + + if 'created_at' in kwargs: + # recreate obj -- from to_dict() + kwargs['created_at'] = datetime.strptime( + kwargs['created_at'], '%Y-%m-%dT%H:%M:%S.%f') else: - self.updated_at = datetime.utcnow() - if kwargs.get("id", None) is None: + # new obj + self.created_at = datetime.now() + + if 'id' not in kwargs: + # new obj self.id = str(uuid.uuid4()) + + if '__class__' in kwargs: + # recreate obj -- from to_dict() + del kwargs['__class__'] + + # set attribute to the instance + for attr_name, attr_value in kwargs.items(): + setattr(self, attr_name, attr_value) + else: self.id = str(uuid.uuid4()) - self.created_at = datetime.utcnow() - self.updated_at = self.created_at + self.created_at = datetime.now() + self.updated_at = datetime.now() def __str__(self): - """String representation of the BaseModel class""" - return "[{:s}] ({:s}) {}".format(self.__class__.__name__, self.id, - self.__dict__) + """Returns a string representation of the instance""" + cls = (str(type(self)).split('.')[-1]).split('\'')[0] + return '[{}] ({}) {}'.format(cls, self.id, self.__dict__) def save(self): - """updates the attribute 'updated_at' with the current datetime""" - self.updated_at = datetime.utcnow() - models.storage.new(self) - models.storage.save() + """Updates updated_at with current time when instance is changed""" + from models import storage + self.updated_at = datetime.now() + storage.new(self) + storage.save() def to_dict(self): - """returns a dictionary containing all keys/values of the instance""" - new_dict = self.__dict__.copy() - if "created_at" in new_dict: - new_dict["created_at"] = new_dict["created_at"].strftime(time) - if "updated_at" in new_dict: - new_dict["updated_at"] = new_dict["updated_at"].strftime(time) - new_dict["__class__"] = self.__class__.__name__ - if "_sa_instance_state" in new_dict: - del new_dict["_sa_instance_state"] - return new_dict + """ + Returns: dictionary containing all key/values of __dict__ of the + instance + """ + obj_dict = {} + for key, value in self.__dict__.items(): + if key == "created_at" or key == "updated_at": + obj_dict[key] = datetime.isoformat(value) + else: + obj_dict[key] = value + obj_dict["__class__"] = type(self).__name__ + if '_sa_instance_state' in obj_dict: + del obj_dict['_sa_instance_state'] + + return obj_dict def delete(self): - """delete the current instance from the storage""" - models.storage.delete(self) + """Deletes the current instance from storage""" + from models import storage + storage.delete(self) diff --git a/models/city.py b/models/city.py old mode 100755 new mode 100644 index 8c46f0d2f4c..1e40ab0a9e3 --- a/models/city.py +++ b/models/city.py @@ -1,24 +1,22 @@ -#!/usr/bin/python -""" holds class City""" -import models -from models.base_model import BaseModel, Base -from os import getenv -import sqlalchemy -from sqlalchemy import Column, String, ForeignKey +import os from sqlalchemy.orm import relationship +from sqlalchemy import Column, String, ForeignKey +from models.base_model import BaseModel, Base + + +# Get storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") class City(BaseModel, Base): - """Representation of city """ - if models.storage_t == "db": + """ The city class, contains state ID and name """ + + if storage_type == "db": + # Handle DB storage __tablename__ = 'cities' state_id = Column(String(60), ForeignKey('states.id'), nullable=False) name = Column(String(128), nullable=False) - places = relationship("Place", backref="cities") else: + # Handle File storage state_id = "" name = "" - - def __init__(self, *args, **kwargs): - """initializes city""" - super().__init__(*args, **kwargs) diff --git a/models/engine/__init__.py b/models/engine/__init__.py old mode 100755 new mode 100644 diff --git a/models/engine/__pycache__/__init__.cpython-312.pyc b/models/engine/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 434ff5d124bcd4e38f1718cfe9e8ecf311a39b55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 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 a!6y6aJ;)Efcj%J)xu=hY4hRr#m;V4mwJY}k diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index e8b10a18a32..91d8c80c3e5 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -1,93 +1,82 @@ #!/usr/bin/python3 """ -Contains the class DBStorage +Module defines Database engine `DBStorage` """ - -import models -from models.amenity import Amenity -from models.base_model import BaseModel, Base -from models.city import City -from models.place import Place +import os +from models.base_model import Base +from models.user import User from models.review import Review +from models.place import Place +from models.city import City from models.state import State -from models.user import User -from os import getenv -import sqlalchemy +from models.amenity import Amenity from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker +from sqlalchemy.orm import sessionmaker, scoped_session -classes = {"Amenity": Amenity, "City": City, - "Place": Place, "Review": Review, "State": State, "User": User} + +# get the environ variabes +user = os.getenv('HBNB_MYSQL_USER') +passwd = os.getenv('HBNB_MYSQL_PWD') +host = os.getenv('HBNB_MYSQL_HOST') +db = os.getenv('HBNB_MYSQL_DB') +hbnb_env = os.getenv('HBNB_ENV') class DBStorage: - """interaacts with the MySQL database""" + """ Defines database engine """ + __engine = None __session = None def __init__(self): - """Instantiate a DBStorage object""" - HBNB_MYSQL_USER = getenv('HBNB_MYSQL_USER') - HBNB_MYSQL_PWD = getenv('HBNB_MYSQL_PWD') - HBNB_MYSQL_HOST = getenv('HBNB_MYSQL_HOST') - HBNB_MYSQL_DB = getenv('HBNB_MYSQL_DB') - HBNB_ENV = getenv('HBNB_ENV') - self.__engine = create_engine('mysql+mysqldb://{}:{}@{}/{}'. - format(HBNB_MYSQL_USER, - HBNB_MYSQL_PWD, - HBNB_MYSQL_HOST, - HBNB_MYSQL_DB)) - if HBNB_ENV == "test": + """ Initialize database instance """ + self.__engine = create_engine( + f"mysql+mysqldb://{user}:{passwd}@{host}/{db}", pool_pre_ping=True) + + if hbnb_env == "test": + # Drop all tables if test environment Base.metadata.drop_all(self.__engine) def all(self, cls=None): - """query on the current database session""" - new_dict = {} - for clss in classes: - if cls is None or cls is classes[clss] or cls is clss: - objs = self.__session.query(classes[clss]).all() - for obj in objs: - key = obj.__class__.__name__ + '.' + obj.id - new_dict[key] = obj - return (new_dict) + """ + Query all objects if cls is None or objects of specified class + """ + objs_dict = {} + if cls: + # Handle class + for obj in self.__session.query(cls).all(): + key = f"{type(obj).__name__}.{obj.id}" + objs_dict[key] = obj + else: + # Handle all classes + cls_list = [State, City, User, Review, Place, Amenity] + for cls in cls_list: + for obj in self.__session.query(cls).all(): + key = f"{type(obj).__name__}.{obj.id}" + objs_dict[key] = obj + return objs_dict def new(self, obj): - """add the object to the current database session""" + """ Add object to the current database session """ self.__session.add(obj) def save(self): - """commit all changes of the current database session""" + """ Commit all changes to the current database session """ self.__session.commit() def delete(self, obj=None): - """delete from the current database session obj if not None""" - if obj is not None: + """ Delete from the current database session """ + if obj: self.__session.delete(obj) def reload(self): - """reloads data from the database""" + """ Creates all tables and the current database session """ Base.metadata.create_all(self.__engine) - sess_factory = sessionmaker(bind=self.__engine, expire_on_commit=False) - Session = scoped_session(sess_factory) + session_factory = sessionmaker( + bind=self.__engine, expire_on_commit=False) + Session = scoped_session(session_factory) self.__session = Session - def get(self, cls, id): - """ Retrieves a specified object from storage """ - if cls is None or id is None: - return None - return self.__session.query(cls).get(id) - - def count(self, cls=None): - """ Counts the number of objects in storage """ - - num_objs = 0 - if cls is None: - for cls in classes.values(): - num_objs += self.__session.query(cls).count() - else: - num_objs += self.__session.query(cls).count() - return num_objs - def close(self): - """Close session""" + """ Removes the current session """ self.__session.remove() diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index d074e41bc3c..7a2be530d41 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,88 +1,73 @@ #!/usr/bin/python3 -""" -Module defines the FileStorage class -""" - +"""This module defines a class to manage file storage for hbnb clone""" import json -from models.amenity import Amenity -from models.base_model import BaseModel -from models.city import City -from models.place import Place -from models.review import Review -from models.state import State -from models.user import User - -classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, - "Place": Place, "Review": Review, "State": State, "User": User} class FileStorage: - """serializes instances to a JSON file & deserializes back to instances""" - - # string - path to the JSON file - __file_path = "file.json" - # dictionary - empty but will store all objects by .id + """This class manages storage of hbnb models in JSON format""" + __file_path = 'file.json' __objects = {} def all(self, cls=None): - """returns the dictionary __objects""" + """ + Returns a list of objects of one type, if class is provided + or all objects if cls is None. + """ if cls is not None: - new_dict = {} - for key, value in self.__objects.items(): - if cls == value.__class__ or cls == value.__class__.__name__: - new_dict[key] = value - return new_dict + # retrieve all objects + cls_objects = {} + for key, obj in self.__objects.items(): + if isinstance(obj, cls): + # if cls is found add item to cls_objects + cls_objects[key] = obj + return cls_objects return self.__objects def new(self, obj): - """sets in __objects the obj with key .id""" - if obj is not None: - key = obj.__class__.__name__ + "." + obj.id - self.__objects[key] = obj + """Adds new object to storage dictionary""" + self.all().update({obj.to_dict()['__class__'] + '.' + obj.id: obj}) def save(self): - """serializes __objects to the JSON file (path: __file_path)""" - json_objects = {} - for key in self.__objects: - json_objects[key] = self.__objects[key].to_dict() - with open(self.__file_path, 'w') as f: - json.dump(json_objects, f) + """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) def reload(self): - """deserializes the JSON file to __objects""" + """Loads storage dictionary from file""" + from models.base_model import BaseModel + from models.user import User + from models.place import Place + from models.state import State + from models.city import City + from models.amenity import Amenity + from models.review import Review + + classes = { + 'BaseModel': BaseModel, 'User': User, 'Place': Place, + 'State': State, 'City': City, 'Amenity': Amenity, + 'Review': Review + } try: - with open(self.__file_path, 'r') as f: - jo = json.load(f) - for key in jo: - self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) - except (FileNotFoundError, ValueError): + 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) + except FileNotFoundError: pass def delete(self, obj=None): - """delete obj from __objects if it’s inside""" + """Deletes an object from __objects""" + # handle: obj is None if obj is not None: - key = obj.__class__.__name__ + '.' + obj.id - if key in self.__objects: - del self.__objects[key] - - def get(self, cls, id): - """retrieves a specified object from storage""" - if cls is None or id is None: - return None - - obj_key = cls.__name__ + "." + id - cls_dict = self.all(cls) - - return cls_dict.get(obj_key) - - def count(self, cls=None): - """counts the number of objects in storage""" - if cls is None: - return len(self.all()) - else: - return len(self.all(cls)) - + obj_key = "{}.{}".format(type(obj).__name__, obj.id) + if obj_key in self.__objects: + del self.__objects[obj_key] def close(self): - """call reload() method for deserializing the JSON file to objects""" + """Calls reload method for deserializing the JSON file to objects""" self.reload() diff --git a/models/place.py b/models/place.py old mode 100755 new mode 100644 index 0aed5a744e6..68c907c418a --- a/models/place.py +++ b/models/place.py @@ -1,43 +1,50 @@ -#!/usr/bin/python -""" holds class Place""" -import models +#!/usr/bin/python3 +""" Place Module for HBNB project """ +import os from models.base_model import BaseModel, Base -from os import getenv -import sqlalchemy -from sqlalchemy import Column, String, Integer, Float, ForeignKey, Table +from sqlalchemy import Integer, Column, String, Float, ForeignKey, Table from sqlalchemy.orm import relationship -if models.storage_t == 'db': - place_amenity = Table('place_amenity', Base.metadata, - Column('place_id', String(60), - ForeignKey('places.id', onupdate='CASCADE', - ondelete='CASCADE'), - primary_key=True), - Column('amenity_id', String(60), - ForeignKey('amenities.id', onupdate='CASCADE', - ondelete='CASCADE'), - primary_key=True)) + +# Get storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") + +# Define association table for many-to-many relationship +if storage_type == "db": + 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): - """Representation of Place """ - if models.storage_t == 'db': + """ A place to stay """ + if storage_type == "db": + # Handle database storage __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) - 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) + number_rooms = Column(Integer, default=0, nullable=False) + number_bathrooms = Column(Integer, default=0, nullable=False) + max_guest = Column(Integer, default=0, nullable=False) + price_by_night = Column(Integer, default=0, nullable=False) latitude = Column(Float, nullable=True) longitude = Column(Float, nullable=True) - reviews = relationship("Review", backref="place") - amenities = relationship("Amenity", secondary="place_amenity", - backref="place_amenities", + amenity_ids = [] + + # relationships + reviews = relationship('Review', backref='place', + cascade='all, delete-orphan') + amenities = relationship('Amenity', secondary=place_amenity, + back_populates='place_amenities', viewonly=False) else: + # Handle file storage city_id = "" user_id = "" name = "" @@ -50,29 +57,28 @@ class Place(BaseModel, Base): longitude = 0.0 amenity_ids = [] - def __init__(self, *args, **kwargs): - """initializes Place""" - super().__init__(*args, **kwargs) - - if models.storage_t != 'db': + # public getter method -- get reviews @property def reviews(self): - """getter attribute returns the list of Review instances""" - from models.review import Review - review_list = [] - all_reviews = models.storage.all(Review) - for review in all_reviews.values(): - if review.place_id == self.id: - review_list.append(review) - return review_list + """ file storage getter for relationship btwn places and review """ + from models import storage + return [review for review in storage.all(Review).values() + if review.place_id == self.id] + # public methods -- get/set amenities @property def amenities(self): - """getter attribute returns the list of Amenity instances""" - from models.amenity import Amenity - amenity_list = [] - all_amenities = models.storage.all(Amenity) - for amenity in all_amenities.values(): - if amenity.place_id == self.id: - amenity_list.append(amenity) - return amenity_list + """ Returns a list of amenities instances """ + from models import storage + objs_dict = storage.all() + place_amenities_list = [] + for obj in objs_dict.values(): + if obj.id in self.amenity_ids and isinstance(obj, Amenity): + place_amenities_list.append(obj) + return place_amenities_list + + @amenities.setter + def amenities(self, obj): + """ Sets the amenity id """ + if isinstance(obj, Amenity) and obj.id not in self.amenity_ids: + self.amenity_ids.append(obj.id) diff --git a/models/review.py b/models/review.py old mode 100755 new mode 100644 index cd6c1d1ff98..1914f8bce89 --- a/models/review.py +++ b/models/review.py @@ -1,24 +1,26 @@ -#!/usr/bin/python -""" holds class Review""" -import models +#!/usr/bin/python3 +""" Review module for the HBNB project """ +import os from models.base_model import BaseModel, Base -from os import getenv -import sqlalchemy from sqlalchemy import Column, String, ForeignKey +from sqlalchemy.orm import relationship + + +# Get storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") class Review(BaseModel, Base): - """Representation of Review """ - if models.storage_t == 'db': + """ Review classto store review information """ + + if storage_type == "db": + # Handle database storage __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) - text = Column(String(1024), nullable=False) else: + # Handle file storage place_id = "" user_id = "" text = "" - - def __init__(self, *args, **kwargs): - """initializes Review""" - super().__init__(*args, **kwargs) diff --git a/models/state.py b/models/state.py old mode 100755 new mode 100644 index ca5c8961d80..11608e8a547 --- a/models/state.py +++ b/models/state.py @@ -1,34 +1,42 @@ #!/usr/bin/python3 -""" holds class State""" -import models -from models.base_model import BaseModel, Base -from models.city import City -from os import getenv -import sqlalchemy -from sqlalchemy import Column, String, ForeignKey +""" State Module for HBNB project """ +import os +from sqlalchemy import Column, String from sqlalchemy.orm import relationship +from models.city import City +from models.base_model import BaseModel, Base + + +# Get storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") class State(BaseModel, Base): - """Representation of state """ - if models.storage_t == "db": + """ State class """ + + if storage_type == "db": + # Handle DB storage __tablename__ = 'states' name = Column(String(128), nullable=False) - cities = relationship("City", backref="state") + cities = relationship( + 'City', backref='state', cascade='all, delete, delete-orphan') else: + # Handle File storage name = "" - def __init__(self, *args, **kwargs): - """initializes state""" - super().__init__(*args, **kwargs) - - if models.storage_t != "db": @property def cities(self): - """getter for list of city instances related to the state""" - city_list = [] - all_cities = models.storage.all(City) - for city in all_cities.values(): - if city.state_id == self.id: - city_list.append(city) - return city_list + """ + Returns: list of `City` instances of the current `State` object + """ + from models import storage + # empty cities list + state_cities = [] + + # get list of cities + cities = storage.all(City) + for city_obj in cities.values(): + if city_obj.state_id == self.id: + state_cities.append(str(city_obj)) + + return state_cities diff --git a/models/user.py b/models/user.py old mode 100755 new mode 100644 index 36b1b70b994..d0c1eff30d7 --- a/models/user.py +++ b/models/user.py @@ -1,29 +1,32 @@ #!/usr/bin/python3 -""" holds class User""" -import models +"""This module defines a class User""" +import os from models.base_model import BaseModel, Base -from os import getenv -import sqlalchemy from sqlalchemy import Column, String from sqlalchemy.orm import relationship +# Get storage type +storage_type = os.getenv("HBNB_TYPE_STORAGE") + + class User(BaseModel, Base): - """Representation of a user """ - if models.storage_t == 'db': + """This class defines a user by various attributes""" + + if storage_type == "db": + # Handle database storage __tablename__ = 'users' email = Column(String(128), nullable=False) password = Column(String(128), nullable=False) first_name = Column(String(128), nullable=True) last_name = Column(String(128), nullable=True) - places = relationship("Place", backref="user") - reviews = relationship("Review", backref="user") + places = relationship("Place", backref="user", + cascade="all, delete-orphan") + reviews = relationship("Review", backref="user", + cascade="all, delete-orphan") else: + # Handle file storage email = "" password = "" first_name = "" last_name = "" - - def __init__(self, *args, **kwargs): - """initializes user""" - super().__init__(*args, **kwargs) From 6405f4f780615deffda58f14aab4350cf99683fa Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 08:34:01 +0000 Subject: [PATCH 60/80] Advanced tasks --- web_flask/0-hello_route.py | 16 +-- web_flask/1-hbnb_route.py | 19 ++-- web_flask/10-hbnb_filters.py | 34 ++++-- web_flask/100-hbnb.py | 34 ++++++ web_flask/2-c_route.py | 25 +++-- web_flask/3-python_route.py | 33 +++--- web_flask/4-number_route.py | 44 ++++---- web_flask/5-number_template.py | 55 +++++---- web_flask/6-number_odd_or_even.py | 71 ++++++------ web_flask/7-states_list.py | 40 +++++-- web_flask/8-cities_by_states.py | 35 ++++-- web_flask/9-states.py | 39 ++++--- web_flask/README.md | 16 ++- web_flask/static/images/icon_bath.png | Bin 0 -> 704 bytes web_flask/static/images/icon_bed.png | Bin 0 -> 447 bytes web_flask/static/images/icon_group.png | Bin 0 -> 1051 bytes web_flask/static/images/icon_pets.png | Bin 0 -> 970 bytes web_flask/static/images/icon_tv.png | Bin 0 -> 961 bytes web_flask/static/images/icon_wifi.png | Bin 0 -> 1006 bytes web_flask/static/styles/3-footer.css | 17 ++- web_flask/static/styles/3-header.css | 18 ++- web_flask/static/styles/4-common.css | 20 ++-- web_flask/static/styles/6-filters.css | 104 +++++++----------- web_flask/static/styles/8-places.css | 61 ++++++++++ web_flask/templates/10-hbnb_filters.html | 95 ++++++++-------- web_flask/templates/100-hbnb.html | 70 ++++++++++++ web_flask/templates/5-number.html | 2 +- web_flask/templates/6-number_odd_or_even.html | 6 +- web_flask/templates/7-states_list.html | 6 +- web_flask/templates/8-cities_by_states.html | 16 +-- web_flask/templates/9-states.html | 26 +++-- 31 files changed, 562 insertions(+), 340 deletions(-) mode change 100755 => 100644 web_flask/1-hbnb_route.py create mode 100644 web_flask/100-hbnb.py mode change 100755 => 100644 web_flask/2-c_route.py mode change 100755 => 100644 web_flask/3-python_route.py mode change 100755 => 100644 web_flask/4-number_route.py mode change 100755 => 100644 web_flask/5-number_template.py mode change 100755 => 100644 web_flask/6-number_odd_or_even.py create mode 100644 web_flask/static/images/icon_bath.png create mode 100644 web_flask/static/images/icon_bed.png create mode 100644 web_flask/static/images/icon_group.png create mode 100644 web_flask/static/images/icon_pets.png create mode 100644 web_flask/static/images/icon_tv.png create mode 100644 web_flask/static/images/icon_wifi.png create mode 100644 web_flask/static/styles/8-places.css create mode 100644 web_flask/templates/100-hbnb.html diff --git a/web_flask/0-hello_route.py b/web_flask/0-hello_route.py index 194749fecd4..6a3c51ec8b3 100755 --- a/web_flask/0-hello_route.py +++ b/web_flask/0-hello_route.py @@ -1,16 +1,18 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ - from flask import Flask + + app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" + -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/1-hbnb_route.py b/web_flask/1-hbnb_route.py old mode 100755 new mode 100644 index f1829cfc6dc..5847def1681 --- a/web_flask/1-hbnb_route.py +++ b/web_flask/1-hbnb_route.py @@ -1,22 +1,23 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ - from flask import Flask + + app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" @app.route('/hbnb', strict_slashes=False) def hbnb(): - """returns HBNB""" - return 'HBNB' + return "HBNB" + -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/10-hbnb_filters.py b/web_flask/10-hbnb_filters.py index b6d8e50f797..26c20ae6af1 100755 --- a/web_flask/10-hbnb_filters.py +++ b/web_flask/10-hbnb_filters.py @@ -1,27 +1,37 @@ #!/usr/bin/python3 """ -starts a Flask web application -""" +Web flask application displays a AirBnB web page with data of states, +cities and amenities loaded from the storage. +Route: + /hbnb_filters - displays a HTML page like 6-index.html done on: + web_static repository +""" from flask import Flask, render_template -from models import * from models import storage +from models.state import State +from models.amenity import Amenity + app = Flask(__name__) @app.route('/hbnb_filters', strict_slashes=False) -def filters(): - """display a HTML page like 6-index.html from static""" - states = storage.all("State").values() - amenities = storage.all("Amenity").values() - return render_template('10-hbnb_filters.html', states=states, - amenities=amenities) +def hbnb_filters(): + """ + Display a page where the states, cities and amenities objects are + loaded from the database storage. + """ + states = storage.all(State) + amenities = storage.all(Amenity) + return render_template( + '10-hbnb_filters.html', states=states, amenities=amenities) @app.teardown_appcontext -def teardown_db(exception): - """closes the storage on teardown""" +def remove_session(exception=None): + """ Close storage session """ storage.close() + if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/100-hbnb.py b/web_flask/100-hbnb.py new file mode 100644 index 00000000000..b5db729047a --- /dev/null +++ b/web_flask/100-hbnb.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +""" +Web flask application that displays the AirBnB clone webpage. Loads the +data from the database storage. +""" +from flask import Flask, render_template +from models import storage +from models.state import State +from models.place import Place +from models.amenity import Amenity + + +app = Flask(__name__) + + +@app.route('/hbnb', strict_slashes=False) +def hbnb(): + """ + Display a page where the states, cities, places and amenities objects are + loaded from the database storage. + """ + states = storage.all(State) + amenities = storage.all(Amenity) + places = storage.all(Place) + return render_template('100-hbnb.html', states=states, amenities=amenities, places=places) + +@app.teardown_appcontext +def remove_session(exception=None): + """ Close storage session """ + storage.close() + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/web_flask/2-c_route.py b/web_flask/2-c_route.py old mode 100755 new mode 100644 index bb3818a09b2..84d367109bb --- a/web_flask/2-c_route.py +++ b/web_flask/2-c_route.py @@ -1,28 +1,29 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ - from flask import Flask + + app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" @app.route('/hbnb', strict_slashes=False) def hbnb(): - """returns HBNB""" - return 'HBNB' + return "HBNB" @app.route('/c/', strict_slashes=False) -def cisfun(text): - """display “C ” followed by the value of the text variable""" - return 'C ' + text.replace('_', ' ') +def display_text(text): + text = text.replace('_', ' ') + return f"C {text}" + -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/3-python_route.py b/web_flask/3-python_route.py old mode 100755 new mode 100644 index caf0f632694..c1bbd9c72c7 --- a/web_flask/3-python_route.py +++ b/web_flask/3-python_route.py @@ -1,35 +1,36 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ - from flask import Flask + + app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" @app.route('/hbnb', strict_slashes=False) def hbnb(): - """returns HBNB""" - return 'HBNB' + return "HBNB" @app.route('/c/', strict_slashes=False) -def cisfun(text): - """display “C ” followed by the value of the text variable""" - return 'C ' + text.replace('_', ' ') +def display_text(text): + text = text.replace('_', ' ') + return f"C {text}" -@app.route('/python', strict_slashes=False) +@app.route('/python/') @app.route('/python/', strict_slashes=False) -def pythoniscool(text='is cool'): - """display “Python ”, followed by the value of the text variable""" - return 'Python ' + text.replace('_', ' ') +def default(text='is cool'): + text = text.replace('_', ' ') + return f"Python {text}" + -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/4-number_route.py b/web_flask/4-number_route.py old mode 100755 new mode 100644 index f2948b12e11..d2b7f3b7d92 --- a/web_flask/4-number_route.py +++ b/web_flask/4-number_route.py @@ -1,41 +1,45 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ +from flask import Flask, abort + -from flask import Flask app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" @app.route('/hbnb', strict_slashes=False) def hbnb(): - """returns HBNB""" - return 'HBNB' + return "HBNB" @app.route('/c/', strict_slashes=False) -def cisfun(text): - """display “C ” followed by the value of the text variable""" - return 'C ' + text.replace('_', ' ') +def display_text(text): + text = text.replace('_', ' ') + return f"C {text}" -@app.route('/python', strict_slashes=False) +@app.route('/python/') @app.route('/python/', strict_slashes=False) -def pythoniscool(text='is cool'): - """display “Python ”, followed by the value of the text variable""" - return 'Python ' + text.replace('_', ' ') +def default(text='is cool'): + text = text.replace('_', ' ') + return f"Python {text}" + +@app.route('/number/', strict_slashes=False) +def number(n): + try: + num = int(n) + return f"{n} is a number" + except ValueError: + abort(404) -@app.route('/number/', strict_slashes=False) -def imanumber(n): - """display “n is a number” only if n is an integer""" - return "{:d} is a number".format(n) -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/5-number_template.py b/web_flask/5-number_template.py old mode 100755 new mode 100644 index 17841a1a3a7..f45f20db593 --- a/web_flask/5-number_template.py +++ b/web_flask/5-number_template.py @@ -1,47 +1,54 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ +from flask import Flask, abort, render_template + -from flask import Flask, render_template app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" @app.route('/hbnb', strict_slashes=False) def hbnb(): - """returns HBNB""" - return 'HBNB' + return "HBNB" @app.route('/c/', strict_slashes=False) -def cisfun(text): - """display “C ” followed by the value of the text variable""" - return 'C ' + text.replace('_', ' ') +def display_text(text): + text = text.replace('_', ' ') + return f"C {text}" -@app.route('/python', strict_slashes=False) +@app.route('/python/') @app.route('/python/', strict_slashes=False) -def pythoniscool(text='is cool'): - """display “Python ”, followed by the value of the text variable""" - return 'Python ' + text.replace('_', ' ') +def default(text='is cool'): + text = text.replace('_', ' ') + return f"Python {text}" + +@app.route('/number/', strict_slashes=False) +def number(n): + try: + num = int(n) + return f"{n} is a number" + except ValueError: + abort(404) -@app.route('/number/', strict_slashes=False) -def imanumber(n): - """display “n is a number” only if n is an integer""" - return "{:d} is a number".format(n) +@app.route('/number_template/', strict_slashes=False) +def display_page(n): + try: + number = int(n) + return render_template('5-number.html', number=number) + except ValueError: + abort(404) -@app.route('/number_template/', strict_slashes=False) -def numbersandtemplates(n): - """display a HTML page only if n is an integer""" - return render_template('5-number.html', n=n) -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/6-number_odd_or_even.py b/web_flask/6-number_odd_or_even.py old mode 100755 new mode 100644 index 7875c4c7b32..956a252f3ff --- a/web_flask/6-number_odd_or_even.py +++ b/web_flask/6-number_odd_or_even.py @@ -1,58 +1,63 @@ #!/usr/bin/python3 """ -starts a Flask web application +Module starts a Flask web applicatio and displays a 'Hello HBNBN' +message. """ +from flask import Flask, abort, render_template + -from flask import Flask, render_template app = Flask(__name__) @app.route('/', strict_slashes=False) -def index(): - """returns Hello HBNB!""" - return 'Hello HBNB!' +def hello(): + return "Hello HBNB!" @app.route('/hbnb', strict_slashes=False) def hbnb(): - """returns HBNB""" - return 'HBNB' + return "HBNB" @app.route('/c/', strict_slashes=False) -def cisfun(text): - """display “C ” followed by the value of the text variable""" - return 'C ' + text.replace('_', ' ') +def display_text(text): + text = text.replace('_', ' ') + return f"C {text}" -@app.route('/python', strict_slashes=False) +@app.route('/python/') @app.route('/python/', strict_slashes=False) -def pythoniscool(text='is cool'): - """display “Python ”, followed by the value of the text variable""" - return 'Python ' + text.replace('_', ' ') +def default(text='is cool'): + text = text.replace('_', ' ') + return f"Python {text}" + +@app.route('/number/', strict_slashes=False) +def number(n): + try: + num = int(n) + return f"{n} is a number" + except ValueError: + abort(404) -@app.route('/number/', strict_slashes=False) -def imanumber(n): - """display “n is a number” only if n is an integer""" - return "{:d} is a number".format(n) +@app.route('/number_template/', strict_slashes=False) +def display_page(n): + try: + number = int(n) + return render_template('5-number.html', number=number) + except ValueError: + abort(404) -@app.route('/number_template/', strict_slashes=False) -def numbersandtemplates(n): - """display a HTML page only if n is an integer""" - return render_template('5-number.html', n=n) +@app.route('/number_odd_or_even/', strict_slashes=False) +def number_dd_or_even(n): + try: + number = int(n) + return render_template('6-number_odd_or_even.html', number=number) + except ValueError: + abort(404) -@app.route('/number_odd_or_even/', strict_slashes=False) -def numbersandevenness(n): - """display a HTML page only if n is an integer""" - if n % 2 == 0: - evenness = 'even' - else: - evenness = 'odd' - return render_template('6-number_odd_or_even.html', n=n, - evenness=evenness) -if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/7-states_list.py b/web_flask/7-states_list.py index fa9ae29ae54..8481c4d4bd3 100755 --- a/web_flask/7-states_list.py +++ b/web_flask/7-states_list.py @@ -1,25 +1,41 @@ #!/usr/bin/python3 """ -starts a Flask web application +Script starts a Flask web application: +Requirements: + - Your web application must be listening on 0.0.0.0, port 5000 + - You must use storage for fetching data from the storage engine + (FileStorage or DBStorage) => from models import storage and + storage.all(...) + - After each request you must remove the current SQLAlchemy Session: + - Declare a method to handle @app.teardown_appcontext + - Call in this method storage.close() + - Routes: + /states_list: display a HTML page: (inside the tag BODY) + H1 tag: “States” + UL tag: with the list of all State objects present in DBStorage + sorted by name (A->Z) tip + LI tag: description of one State: : + - Import this 7-dump to have some data + - You must use the option strict_slashes=False in your route definition """ - from flask import Flask, render_template -from models import * from models import storage -app = Flask(__name__) +from models.state import State -@app.route('/states_list', strict_slashes=False) -def states_list(): - """display a HTML page with the states listed in alphabetical order""" - states = sorted(list(storage.all("State").values()), key=lambda x: x.name) - return render_template('7-states_list.html', states=states) +app = Flask(__name__) @app.teardown_appcontext -def teardown_db(exception): - """closes the storage on teardown""" +def delete_session(exception=None): storage.close() + +@app.route('/states_list', strict_slashes=False) +def list_states(): + states = storage.all(State).values() + return render_template('7-states_list.html', states=states) + + if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') + app.run(host='0.0.0.0', port=5000) diff --git a/web_flask/8-cities_by_states.py b/web_flask/8-cities_by_states.py index 3d2ae2b733a..af566f61b21 100755 --- a/web_flask/8-cities_by_states.py +++ b/web_flask/8-cities_by_states.py @@ -1,25 +1,36 @@ #!/usr/bin/python3 """ -starts a Flask web application +Script starts a Flask web application: +Requirements: + - Routes: + /states_list: display a HTML page: (inside the tag BODY) + H1 tag: “States” + UL tag: with the list of all State objects present in DBStorage + sorted by name (A->Z) tip + LI tag: description of one State: : """ - from flask import Flask, render_template -from models import * from models import storage -app = Flask(__name__) +from models.state import State -@app.route('/cities_by_states', strict_slashes=False) -def cities_by_states(): - """display the states and cities listed in alphabetical order""" - states = storage.all("State").values() - return render_template('8-cities_by_states.html', states=states) +app = Flask(__name__) @app.teardown_appcontext -def teardown_db(exception): - """closes the storage on teardown""" +def delete_session(exception=None): + """ Closes the current session after each request """ storage.close() + +@app.route('/cities_by_states', strict_slashes=False) +def list_cities_states(): + """ Displays a HTML that contains a list of all State objects """ + # Get records of the states + states = storage.all(State).values() + return render_template( + '8-cities_by_states.html', states=states) + + if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/9-states.py b/web_flask/9-states.py index cf9f5a704ce..f4d24dede0e 100755 --- a/web_flask/9-states.py +++ b/web_flask/9-states.py @@ -1,28 +1,37 @@ #!/usr/bin/python3 """ -starts a Flask web application +Script starts a Flask web application: +Routes: + /states - list all states from storage + /states/ - list all cities in a specified state """ - from flask import Flask, render_template -from models import * from models import storage -app = Flask(__name__) +from models.state import State -@app.route('/states', strict_slashes=False) -@app.route('/states/', strict_slashes=False) -def states(state_id=None): - """display the states and cities listed in alphabetical order""" - states = storage.all("State") - if state_id is not None: - state_id = 'State.' + state_id - return render_template('9-states.html', states=states, state_id=state_id) +app = Flask(__name__) @app.teardown_appcontext -def teardown_db(exception): - """closes the storage on teardown""" +def delete_session(exception=None): storage.close() + +@app.route('/states', strict_slashes=False) +@app.route('/states/', strict_slashes=False) +def list_states_cities(id=None): + """ + Displays a HTML page witha lists of all states if id=None or + a list of cities of the specified states. + """ + # Get list of states + states = storage.all(State) + state_key = None + if id is not None: + state_key = "State." + id + return render_template('9-states.html', state_key=state_key, states=states) + + if __name__ == '__main__': - app.run(host='0.0.0.0', port='5000') + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/web_flask/README.md b/web_flask/README.md index 01a9f866f51..4dd05d3f2dd 100644 --- a/web_flask/README.md +++ b/web_flask/README.md @@ -1 +1,15 @@ -# 0x04. AirBnB clone - Web framework +

HBNB Web Flask

+11;rgb:0000/0000/0000 +This repository is a continuation of the already existing HBNB project. This stage of the project implements a Flask application that provides an easy way to define the route of our web application. The Jinja2 template which is readily availabe in Flask allows the application to generate dynamic contents. In the previous project, a data storage was implemented. This project the data stored is queried, filtered and used within our templates. When a valid route is accessed the page generated can be displayed back to the user that made the request. + +N:B GET request method is used in all the application routes. + +--- + +

Repository Contents by Project Task

+ +|Tasks | Files | Description | +| ------ | ----- | ----- | +| 0. route: / | 0-hello_route.py | display "Hello HBNB!" +| 1. route: /hbnb | 1-hbnb_route.py | display "HBNB" +| 2. route: /c/ | 2.c_route.py | display "C" followed by the value of the text variable(replacing undersore _ symbols with a space) diff --git a/web_flask/static/images/icon_bath.png b/web_flask/static/images/icon_bath.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9bfed9d8d9f02a29a50847808f6bb0eb61c246 GIT binary patch literal 704 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^t4*@+_k(E=`(ls_UvvG9s4+snii-}K4PRS{+sH|>kZta*d zb^5Hi%T}!2uxZzxeMgUB_uttD3|2-1PZ!4!kK=EzMEf6d z5MX_<`NFNF>}b(ImcWUtRxDUiq0Myf64zg@7xpEqNdy0}VK@7U`tS21i8(3ekx#7LRz)t6QhuZvYoqq(ZPJWeo{wbL zESc!_=-iIXBXixXdh-l+?sMFJ(Npp3leugCmHBqA^*EAv+fU7J_sqjOf%_PaZ8|Of UVsdFeFj^QqUHx3vIVCg!0N#=_Pyhe` literal 0 HcmV?d00001 diff --git a/web_flask/static/images/icon_bed.png b/web_flask/static/images/icon_bed.png new file mode 100644 index 0000000000000000000000000000000000000000..2a63284877067dcb61b769fd59e8ee373587b0be GIT binary patch literal 447 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^t$pJngu0XnMkb%!ndUAozGAIf1 z3ua(sX64}I;^CE*Q#3F%wXpN{i-=9nD6gz;XziFdWA5TjTelxSb?*G-JNG_+|M~mx zviF&$K)tIyT^vI^j=#O^&UeT_#Pwo+)q9mM_a;x+{r{?=!!(C77B>!oQ*-yt>6GnW zvb0u7U+vo+51u1=EDhnF`&QiX_{);$Z#jXF;fza^;C9#FjyJu$eswQfV=yaSXUXd8 zOV{`w(o3GyDaWjxm?#vV?{2*?Yr?E~e^`u+I&Q^G-!Q>}@0sHBZ48dCLMLt(OVPgg&ebxsLQ E03-gv2mk;8 literal 0 HcmV?d00001 diff --git a/web_flask/static/images/icon_group.png b/web_flask/static/images/icon_group.png new file mode 100644 index 0000000000000000000000000000000000000000..3e012ab4d5cdc2146782461b475b2a81ce0fea4d GIT binary patch literal 1051 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx~p(FqQ=Pgt!8^Wut(;A)xa1&vRh7 zSC<6&1v4-*F|)9;v2$>8ar5x<@e2qF35$q|iAze!$jZqpD66S!Xld)}=^Gdt8Jn70 zTG`s!J370#x_Nqe`}q0=hQ-7sr)1|86ql4%RM$6jboNi2I(^3MxeFF8TeW)arY+ld z?%K2Oz~Q6EPnA zgW||zoJzi$)5NBGZPtle#HKS|ONG3(HoLZl9GLX^x%AAL)@KUZ+vTc0{VsSi z|7-5$4L5oF7urh2ac+5eFFjb7YkJ+RdkcL#rzu5sa_K66=m>b_)UA@+{lO#H>wv(! zKGwt?PPf%;9!;rgkiMT8v73YEVpvoAipxQ+*R6D08{BU5?0I*&$T((Mh|u|rRhb!| z=lE8x*>H4C#d+ti#sLx)(RZV*`io0Cl{SazpONRXdQn&qR2=$s=a78nheKN1tRgd#}OF1pXGJj3wWR$qG zJasw0!xKYYO^YwQa#}23?syqCK4vvLo2~S{GEriAwuwpFgGu|Qp7rZ^&2D1D=_J3; z+)l#jl{)Y1sC={E2_fx=7<{+ZSg{DoMlXFiyMceBvwY+aiN*6{8Mpd>DP{C>{588Z zXx6`N3#KHp2h2)ij}53i#g`N?Go5{Jz|3TJ^#w)K|4A{fc)|NCs^ON!-w=}@5qGMv?l9JQXGqZDY3kr)%YC1Y+&0Vl~>8jOhH*MLzYtOy|2M-@Te&W=* zi`VZxdh+bW%hzu|e*XE__WZeNz$o44>Eak7A^G-TX0%WsL)*jjWe1lY>Iuql+_uPNGk@3)Fq ze%5+#l|TGkcNLdvCEq;!VS4J78lLrP$@8XEx*jvUr}d5R@jNAc&zXTsJXgg3{>5o3 zI#V&#XQSMMjo;2P*>=S$FAd+Q_h9z7wT!V{b5-81?%_QU-p!wL;>S6`N=eoSJyD+= z92f3&?30SQ8Lcm%wfd>xLhjeq^6Uj4>Q^omyb!rzO>>xWiNKw|s}KL{W#7^B?!}6^ zhHvy%?mZo`cE#S%9GjaJ5iFZHH+)EWoVa<#H?fUU0oBhp&JM5^-qB+w^3!(a+*R6_ uBo3cFJ@wSohju;t9(wJZ^4X&7LcQ)iZPlv@J5qsZh{4m<&t;ucLK6TAF|D5f literal 0 HcmV?d00001 diff --git a/web_flask/static/images/icon_tv.png b/web_flask/static/images/icon_tv.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf25087ed593f0d39ef29bd37f773efd609e00d GIT binary patch literal 961 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx~p(Fa`(sgt!8^Wut(qA;7V=<~}f3 z3rd3gf*BZ@m|57^IXJnvd3gB+g+(Q$q-A8~>XTO-P}F> z!Xski5|dNXGxCc|N-HYsTH89id;0pP&zLoP>9Q58*RJ2NW$Vsedk!2tdi=zxiQPrUck-3}4Q%Xewj#x9m;ko(HPL zo5^rv+GJ6NyXV7M1g1`|Wtjd>fbq!F2TTqxPAE6X++oOk8)zHW$*Hm>#ec3lkHOXy z_5&+>gsxADWoX|tZOSy4E1j{o%HBA}{|Lk$mQ7W|#h0DdTa%Ymff7<}a&l3wOM}^(VReU^Dk;<FVdQ&MBb@05Wj3;Q#;t literal 0 HcmV?d00001 diff --git a/web_flask/static/images/icon_wifi.png b/web_flask/static/images/icon_wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..abbb643c9c1fbba020c2aadac1447d2db66ea094 GIT binary patch literal 1006 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx~p(Fct>*gt!8^Wut(OA>h36>q%gs zSCs_$1v4-*F|)9;v2$>8ar5x<2?z=ai-<}{Nz2H}D<~?fsH$md>FDYk8X23JTUgrK z**iMBxO)5e1_Xu0#Kt8ir=+Fl-L>{ z_8mBU^!SNW*Kggq_u$c!XD?sB|M>aq_n*K2p7pu;0+<9gdAc};NJzdtm>DgU$k2B2 zx2%Hc86A-mN|V3wOb^>^vdKt1CwSA9_5@r zyRdOz@ms+|9(VsQQ?198g>g4j+!;y-L!SFVfWjpXQ9T^&!=!e-_&f0{hy2W z+VVuCbDjS5r}COCtJLzRQb*GL`ffYqRD{-jtB^Sn8WMUeJuGM`_l~G*Vc#Y+9_d=5 zS`$^DxluN0z37u{<0Jc5%sF`8MB3s@@Vry2Crbal{`u%8o2MohayI(bxUNe&wm~m) zLbk5!0#8f3Ef2dSWIl=)oe|FP3jNMdT$SZ{>HSRazz1Ec>;zYARoN}RW5hs=<{szD!E|y zV9!~-4-6+yn!99PTa0=^PEMH?_mZk;j`WbeMT%SMD&>T3ZMx>lxiZi+(Hil;_TS%j)VBq`m~49gG7~TjF?hQAxvX button{ +section.filters button{ font-size: 18px; - color: white; - background-color: #FF5A5F; + background-color: #ff5a5f; + color: #ffffff; height: 48px; - border: 0px; - border-radius: 4px; width: 20%; - margin-left: auto; - margin-right: 30px; - opacity: 1; + border: 0; + border-radius: 4px; + margin-left: auto; /*push button to the left*/ } - -section.filters > button:hover { - opacity: 0.9; +section.filters button:hover{ + opacity: 90%; } - -.locations, .amenities { +section.filters div.locations, section.filters div.amenities{ height: 100%; width: 25%; - padding-left: 50px; } - -.locations { - border-right: 1px solid #DDDDDD; +section.filters div.locations{ + border-right: 1px solid #dddddd; } -.locations > h3, .amenities > h3 { + +section.filters div.locations h3, section.filters div.amenities h3{ font-weight: 600; - margin: 12px 0 5px 0; + margin: 17px; } -.locations > h4, .amenities > h4 { +section.filters div.locations h4, section.filters div.amenities h4{ font-weight: 400; font-size: 14px; - margin: 0 0 5px 0; + margin: 0 0 0 30px; } - -.popover { - display: none; +ul.popover{ position: relative; - left: -51px; - background-color: #FAFAFA; + display: none; width: 100%; - border: 1px solid #DDDDDD; - border-radius: 4px; + background-color: #fafafa; + border: 1px solid #dddddd; + padding: 10px 15px; z-index: 1; - padding: 30px 50px 30px 0; - margin-top: 17px; -} - -.popover, .popover ul { - list-style-type: none; -} -.locations:hover > .popover { - display: block; -} - -.amenities:hover > .popover { - display: block; + overflow-y: auto; + max-height: 300px; + margin-top: -1px } - -.popover h2 { - margin-top: 0px; - margin-bottom: 5px; +ul.popover h2{ + font-size: 16px; + margin: 0; } - -.locations > .popover > li { - margin-bottom: 30px; - margin-left: 30px; +ul.popover ul{ + margin: 0; + padding: 4px 0 10px 10px; } -.locations > .popover > li > ul { - padding-left: 20px; -} -.locations > .popover > li > ul > li { - margin-bottom: 10px; +ul.popover, ul.popover ul{ + list-style-type: none; } -.amenities > .popover > li { - margin-left: 50px; - margin-bottom: 10px; +/*hide the filter popover*/ +div.locations:hover ul.popover, +div.amenities:hover ul.popover{ + display: block; } diff --git a/web_flask/static/styles/8-places.css b/web_flask/static/styles/8-places.css new file mode 100644 index 00000000000..37442be7843 --- /dev/null +++ b/web_flask/static/styles/8-places.css @@ -0,0 +1,61 @@ +section.places h1{ + font-size: 30px; + text-align: left; +} +.articles-section{ + display: flex; + flex-wrap: wrap; + justify-content: center; +} +section.places article{ + position: relative; + width: 390px; + padding: 20px; + margin: 20px; + border: 1px solid #ff5a5f; + border-radius: 4px; +} +section.places article h2{ + font-size: 30px; + text-align: center; + margin: 5px 60px 40px 0; +} +article div.price_by_night{ + position: absolute; + top: 15px; + right: 15px; + color: #ff5a5f; + border: 4px solid #ff5a5f; + border-radius: 50%; + min-width: 60px; + height: 60px; + font-size: 20px; + text-align: center; + line-height: 60px; +} +.information{ + display: flex; + align-items: center; + justify-content: center; + height: 80px; + border-top: 1px solid #dddddd; + border-bottom: 1px solid #dddddd; +} +.information div{ + width: 100px; + height: 35px; + text-align: center; +} +.information div .icon{ + width: 100%; + height: 80%; +} +.information div .icon i{ + font-size: 20px; +} +article div.user{ + margin: 10px 0; +} +article div.user span{ + font-weight: bold; +} diff --git a/web_flask/templates/10-hbnb_filters.html b/web_flask/templates/10-hbnb_filters.html index 261634b693f..a6b6d825530 100644 --- a/web_flask/templates/10-hbnb_filters.html +++ b/web_flask/templates/10-hbnb_filters.html @@ -1,55 +1,48 @@ - - - - - - - - AirBnb Clone - - -
- -
-
-
-
-

States

-

 

-
    - {% for state in states|sort(attribute='name') %} -
  • -

    {{ state.name }}:

    -
      - {% for city in state.cities|sort(attribute='name') %} -
    • {{ city.name }}
    • - {% endfor %} + + AirBnB clone + + + + + + + + + + + + +
      +
      +
      +

      States

      +

       

      +
        + {% for state in states.values()|sort(attribute='name')%} +
      • {{ state.name }}

        +
          + {% for city in state.cities|sort(attribute='name') %} +
        • {{ city.name }}
        • + {% endfor %} +
        +
      • + {% endfor %}
      - - {% endfor %} -
    -
-
-

Amenities

-

 

-
    - {% for amenity in amenities|sort(attribute='name') %} -
  • {{ amenity.name }}
  • - {% endfor %} -
+
+
+

Amenities

+

 

+
    + {% for amenity in amenities.values()|sort(attribute='name') %} +
  • {{ amenity.name }}
  • + {% endfor %} +
+
+ +
- - - -
-

- Holberton School -

-
- - +
Best School
+ + \ No newline at end of file diff --git a/web_flask/templates/100-hbnb.html b/web_flask/templates/100-hbnb.html new file mode 100644 index 00000000000..ef51b2529f8 --- /dev/null +++ b/web_flask/templates/100-hbnb.html @@ -0,0 +1,70 @@ + + + + AirBnB + + + + + + + + + + + + + +
+
+
+
+

States

+

 

+
    + {% for state in states.values()|sort(attribute='name')%} +
  • {{ state.name }}

    +
      + {% for city in state.cities|sort(attribute='name') %} +
    • {{ city.name }}
    • + {% endfor %} +
    +
  • + {% endfor %} +
+
+
+

Amenities

+

 

+
    + {% for amenity in amenities.values()|sort(attribute='name') %} +
  • {{ amenity.name }}
  • + {% endfor %} +
+
+ +
+
+

Places

+
+ {% for place in places.values()|sort(attribute='name')%} +
+

{{ place.name }}

+
${{ place.price_by_night }}
+
+
{{ place.max_guest }} Guests
+
{{ place.number_rooms }} Bedroom
+
{{ place.number_bathrooms }} Bathroom
+
+
Owner: John Lennon
+
+

{{ place.description|safe }}

+
+
+ {% endfor %} +
+
+
+
Best School
+ + diff --git a/web_flask/templates/5-number.html b/web_flask/templates/5-number.html index 57887ff7469..2b196a9c567 100644 --- a/web_flask/templates/5-number.html +++ b/web_flask/templates/5-number.html @@ -4,6 +4,6 @@ HBNB -

Number: {{ n }}

+

Number: {{ number }}

diff --git a/web_flask/templates/6-number_odd_or_even.html b/web_flask/templates/6-number_odd_or_even.html index 188839655c4..b8d5f084455 100644 --- a/web_flask/templates/6-number_odd_or_even.html +++ b/web_flask/templates/6-number_odd_or_even.html @@ -4,6 +4,10 @@ HBNB -

Number: {{ n }} is {{ evenness }}

+ {% if number%2 == 0 %} +

Number: {{ number }} is even

+ {% else %} +

Number: {{ number }} is odd

+ {% endif %} diff --git a/web_flask/templates/7-states_list.html b/web_flask/templates/7-states_list.html index a274a32608f..79ad52db0a1 100644 --- a/web_flask/templates/7-states_list.html +++ b/web_flask/templates/7-states_list.html @@ -6,9 +6,9 @@

States

    - {% for state in states %} -
  • {{ state.id }}: {{ state.name }}
  • - {% endfor %} + {% for state in states|sort(attribute='name') %} +
  • {{ state.id }}: {{ state.name }}
  • + {% endfor %}
diff --git a/web_flask/templates/8-cities_by_states.html b/web_flask/templates/8-cities_by_states.html index 94993e8860c..ff5a6c6cbf1 100644 --- a/web_flask/templates/8-cities_by_states.html +++ b/web_flask/templates/8-cities_by_states.html @@ -6,15 +6,15 @@

States

    - {% for state in states|sort(attribute='name') %} -
  • {{ state.id }}: {{ state.name }} -
      - {% for city in state.cities|sort(attribute='name') %} -
    • {{ city.id }}: {{ city.name }}
    • - {% endfor %} + {% for state in states|sort(attribute='name') %} +
    • {{ state.id }}: {{ state.name }} +
        {{ city.id }}: {{ city.name }} + {% endfor %}
    • - {% endfor %} -
    + {% endfor %} +
diff --git a/web_flask/templates/9-states.html b/web_flask/templates/9-states.html index 34d40291c01..6c1dc24eb4a 100644 --- a/web_flask/templates/9-states.html +++ b/web_flask/templates/9-states.html @@ -4,24 +4,26 @@ HBNB - {% if not state_id %} + + {% if not state_key %}

States

-
    +
      {% for state in states.values()|sort(attribute='name') %} -
    • {{ state.id }}: {{ state.name }}
    • - {% endfor %} +
    • {{ state.id }}: {{ state.name }}
    • + {% endfor %}
    - {% elif state_id in states%} - {% set state = states[state_id] %} + {% elif state_key in states %} + {% set state = states[state_key] %}

    State: {{ state.name }}

    -

    Cities

    -
      - {% for city in state.cities|sort(attribute='name') %} -
    • {{ city.id }}: {{ city.name }}
    • - {% endfor %} -
    +

    Cities:

    +
      + {% for city in state.cities|sort(attribute='name') %} +
    • {{ city.id }}: {{city.name}}
    • + {% endfor %} +
    {% else %}

    Not found!

    {% endif %} + From 8ef813624e95b1bad729b1d67fe439f463866ad6 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 08:38:47 +0000 Subject: [PATCH 61/80] command line interpreter -- interact with models created --- console.py | 334 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 209 insertions(+), 125 deletions(-) diff --git a/console.py b/console.py index 4798f9ac76b..1b7b77104c6 100755 --- a/console.py +++ b/console.py @@ -1,164 +1,248 @@ #!/usr/bin/python3 -""" console """ - +""" +Defines a program `console` that contains the entry point of the command +interpreter. +Requirements: + - You must use the module cmd + - Your class definition must be: class HBNBCommand(cmd.Cmd): + - Your command interpreter should implement: + - quit and EOF to exit the program + - help (this action is provided by default by cmd but you should keep it + updated and documented as you work through tasks) a custom prompt: (hbnb) + - an empty line + ENTER shouldn’t execute anything + - Your code should not be executed when imported +""" import cmd -from datetime import datetime -import models -from models.amenity import Amenity -from models.base_model import BaseModel +import sys +from models import storage +from models.user import User from models.city import City from models.place import Place -from models.review import Review from models.state import State -from models.user import User -import shlex # for splitting the line along spaces except in double quotes +from models.review import Review +from models.amenity import Amenity +from models.base_model import BaseModel -classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, - "Place": Place, "Review": Review, "State": State, "User": User} +# define global class dict +cls_dict = {"BaseModel": BaseModel, "User": User, "State": State, + "City": City, "Amenity": Amenity, "Place": Place, "Review": Review} class HBNBCommand(cmd.Cmd): - """ HBNH console """ - prompt = '(hbnb) ' + """ + Defines the command interpreter + """ - def do_EOF(self, arg): - """Exits console""" - return True - - def emptyline(self): - """ overwriting the emptyline method """ - return False + prompt = '(hbnb) ' def do_quit(self, arg): """Quit command to exit the program""" return True - def _key_value_parser(self, args): - """creates a dictionary from a list of strings""" - new_dict = {} - for arg in args: - if "=" in arg: - kvp = arg.split('=', 1) - key = kvp[0] - value = kvp[1] - if value[0] == value[-1] == '"': - value = shlex.split(value)[0].replace('_', ' ') - else: - try: - value = int(value) - except: - try: - value = float(value) - except: - continue - new_dict[key] = value - return new_dict + def do_EOF(self, arg): + """EOF command to exit the program""" + return True + + def emptyline(self): + """Do nothing when an empty line is entered""" + pass def do_create(self, arg): - """Creates a new instance of a class""" - args = arg.split() - if len(args) == 0: + """ + Creates a new instance of BaseModel, save it (to JSON file) + and prints the id + """ + # get class name + cls = cls_dict.get(arg) + + # validate input + if not arg: print("** class name missing **") - return False - if args[0] in classes: - new_dict = self._key_value_parser(args[1:]) - instance = classes[args[0]](**new_dict) - else: + elif cls is None: print("** class doesn't exist **") - return False - print(instance.id) - instance.save() - - def do_show(self, arg): - """Prints an instance as a string based on the class and id""" - args = shlex.split(arg) - if len(args) == 0: - print("** class name missing **") - return False - if args[0] in classes: - if len(args) > 1: - key = args[0] + "." + args[1] - if key in models.storage.all(): - print(models.storage.all()[key]) + else: + obj = cls() # create instance + storage.new(obj) + storage.save() + print(obj.id) + + def do_show(self, line): + """ + Prints the string representation of an instance based on the + class name and id + """ + args = line.split() + if len(args) != 0: + if len(args) == 2: + cls = cls_dict.get(args[0]) + if cls: + objs = storage.all() + # create key + obj_key = "{}.{}".format(args[0], args[1]) + if obj_key in objs: + obj = objs.get(obj_key) + print(str(obj)) + else: + print("** no instance found **") else: - print("** no instance found **") + print("** class doesn't exist **") else: print("** instance id missing **") else: - print("** class doesn't exist **") - - def do_destroy(self, arg): - """Deletes an instance based on the class and id""" - args = shlex.split(arg) - if len(args) == 0: print("** class name missing **") - elif args[0] in classes: - if len(args) > 1: - key = args[0] + "." + args[1] - if key in models.storage.all(): - models.storage.all().pop(key) - models.storage.save() + + def do_destroy(self, line): + """ + Deletes an instance based on the class name and id (save the change + into the JSON file) + """ + args = line.split() + if len(args) != 0: + if len(args) == 2: + cls = cls_dict.get(args[0]) + if cls: + objs = storage.all() + obj_key = "{}.{}".format(args[0], args[1]) + if obj_key in objs: + del objs[obj_key] + storage.save() + else: + print("** no instance found **") else: - print("** no instance found **") + print("** class doesn't exist **") else: print("** instance id missing **") else: - print("** class doesn't exist **") + print("** class name missing **") def do_all(self, arg): - """Prints string representations of instances""" - args = shlex.split(arg) - obj_list = [] - if len(args) == 0: - obj_dict = models.storage.all() - elif args[0] in classes: - obj_dict = models.storage.all(classes[args[0]]) + """ + Prints all string representation of all instances based or not on + the class name + """ + objs = storage.all() + objs_list = [] + if arg: + # print object of the class provided + cls = cls_dict.get(arg) + if cls: + for key, obj in objs.items(): + cls_name = key.split('.')[0] + if arg == cls_name: + objs_list.append(str(obj)) + print(objs_list) + else: + print("** class doesn't exist **") else: - print("** class doesn't exist **") - return False - for key in obj_dict: - obj_list.append(str(obj_dict[key])) - print("[", end="") - print(", ".join(obj_list), end="") - print("]") - - def do_update(self, arg): - """Update an instance based on the class name, id, attribute & value""" - args = shlex.split(arg) - integers = ["number_rooms", "number_bathrooms", "max_guest", - "price_by_night"] - floats = ["latitude", "longitude"] - if len(args) == 0: - print("** class name missing **") - elif args[0] in classes: - if len(args) > 1: - k = args[0] + "." + args[1] - if k in models.storage.all(): - if len(args) > 2: - if len(args) > 3: - if args[0] == "Place": - if args[2] in integers: - try: - args[3] = int(args[3]) - except: - args[3] = 0 - elif args[2] in floats: - try: - args[3] = float(args[3]) - except: - args[3] = 0.0 - setattr(models.storage.all()[k], args[2], args[3]) - models.storage.all()[k].save() + # print all objects + for obj in objs.values(): + objs_list.append(str(obj)) + print(objs_list) + + def do_update(self, line): + """ + Updates an instance based on the class name and id by adding or + updating attribute (save the change into the JSON file + """ + args = line.split() + if len(args) >= 4: + cls = cls_dict.get(args[0]) + if cls: + objs = storage.all() + obj_key = "{}.{}".format(args[0], args[1]) + if obj_key in objs: + obj = objs.get(obj_key) + + # convert attribute_value to correct data type + try: + value = float(args[3]) + if value.is_integer(): + attr_value = int(value) else: - print("** value missing **") - else: - print("** attribute name missing **") + attr_value = value + except ValueError: + # remove quotes + value = args[3].strip('\'"') + attr_value = str(value) + + # update object + setattr(obj, args[2], attr_value) + + # save update to file + storage.save() else: print("** no instance found **") else: - print("** instance id missing **") + print("** class doesn't exist **") else: - print("** class doesn't exist **") + if len(args) == 0: + print("** class name missing **") + if len(args) == 1: + print("** instance id missing **") + if len(args) == 2: + print("** attribute name missing **") + if len(args) == 3: + print("** value missing **") + + def default(self, line): + """executes command methods not defined""" + + # list of commands + cmd_list = ["all", "count", "show", "destroy", "update"] + + # extract command name ,class name + args = line.split('.') + cls_name = args[0] + cmd = args[1].split('(') + cmd_name = cmd[0] + + # extract function arguments + args_list = cmd[1].split(',') + if len(args_list) > 0: + id = args_list[0].strip("\"'") + # string to pass to function + cmd_string = "{} {}".format(cls_name, id) + + if len(args_list) > 2: + attr_name = args_list[1].strip("\"'") + attr_value = args_list[2].strip("\"'") + # string to pass to function + cmd_string = "{} {} {} {}".format(cls_name, id, + attr_name, attr_value) + + cls = cls_dict.get(cls_name) + if cls: + if cmd_name in cmd_list: + if cmd_name == "all": + # execute .all() command + self.do_all(cls_name) + + elif cmd_name == "count": + # execute .count() command + objs = storage.all() + cls_objs_dict = {} + for key, obj in objs.items(): + name = key.split('.')[0] + if name == cls_name: + cls_objs_dict[key] = obj + # print count + print(len(cls_objs_dict)) + + elif cmd_name == "show": + # execute .show() command + print(cmd_string) + self.do_show(cmd_string) + + elif cmd_name == "destroy": + # execute .destroy() command + self.do_destroy(cmd_string) + + elif cmd_name == "update": + # execute .update(, , + # ) + print(cmd_string) + self.do_update(cmd_string) + if __name__ == '__main__': HBNBCommand().cmdloop() From b5a9e64274a90d2641bca54b131ce67a8b767136 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 08:47:03 +0000 Subject: [PATCH 62/80] web_static --- web_static/0-index.html | 16 +-- web_static/1-index.html | 36 +++-- web_static/100-index.html | 157 +++++++++++++++++++++ web_static/101-index.html | 157 +++++++++++++++++++++ web_static/102-index.html | 161 +++++++++++++++++++++ web_static/103-index.html | 161 +++++++++++++++++++++ web_static/2-index.html | 20 +-- web_static/3-index.html | 27 ++-- web_static/4-index.html | 33 ++--- web_static/5-index.html | 37 ++--- web_static/6-index.html | 67 ++++----- web_static/7-index.html | 91 ++++++------ web_static/8-index.html | 223 +++++++++++++----------------- web_static/README.md | 15 -- web_static/images/icon_bath.png | Bin 704 -> 0 bytes web_static/images/icon_bed.png | Bin 447 -> 0 bytes web_static/images/icon_group.png | Bin 1051 -> 0 bytes web_static/styles/100-places.css | 119 ++++++++++++++++ web_static/styles/101-places.css | 119 ++++++++++++++++ web_static/styles/102-common.css | 20 +++ web_static/styles/102-filters.css | 100 ++++++++++++++ web_static/styles/102-footer.css | 10 ++ web_static/styles/102-header.css | 11 ++ web_static/styles/102-places.css | 126 +++++++++++++++++ web_static/styles/103-common.css | 20 +++ web_static/styles/103-filters.css | 100 ++++++++++++++ web_static/styles/103-footer.css | 10 ++ web_static/styles/103-header.css | 11 ++ web_static/styles/103-places.css | 126 +++++++++++++++++ web_static/styles/2-common.css | 4 +- web_static/styles/2-footer.css | 15 +- web_static/styles/2-header.css | 6 +- web_static/styles/3-common.css | 7 +- web_static/styles/3-footer.css | 17 ++- web_static/styles/3-header.css | 20 ++- web_static/styles/4-common.css | 21 +-- web_static/styles/4-filters.css | 30 ++-- web_static/styles/5-filters.css | 50 +++---- web_static/styles/6-filters.css | 104 ++++++-------- web_static/styles/7-places.css | 33 ++--- web_static/styles/8-places.css | 128 ++++++----------- web_static/w3c_validator.py | 131 ++++++++++++++++++ 42 files changed, 1952 insertions(+), 587 deletions(-) create mode 100644 web_static/100-index.html create mode 100644 web_static/101-index.html create mode 100644 web_static/102-index.html create mode 100644 web_static/103-index.html delete mode 100644 web_static/README.md delete mode 100644 web_static/images/icon_bath.png delete mode 100644 web_static/images/icon_bed.png delete mode 100644 web_static/images/icon_group.png create mode 100644 web_static/styles/100-places.css create mode 100644 web_static/styles/101-places.css create mode 100644 web_static/styles/102-common.css create mode 100644 web_static/styles/102-filters.css create mode 100644 web_static/styles/102-footer.css create mode 100644 web_static/styles/102-header.css create mode 100644 web_static/styles/102-places.css create mode 100644 web_static/styles/103-common.css create mode 100644 web_static/styles/103-filters.css create mode 100644 web_static/styles/103-footer.css create mode 100644 web_static/styles/103-header.css create mode 100644 web_static/styles/103-places.css create mode 100755 web_static/w3c_validator.py diff --git a/web_static/0-index.html b/web_static/0-index.html index a79ac87bffa..efd2060b7c9 100644 --- a/web_static/0-index.html +++ b/web_static/0-index.html @@ -1,16 +1,10 @@ - + - - AirBnB Clone + AirBnB - -
    -
    -
    -

    - Holberton School -

    -
    + +
    +
    Best School
    diff --git a/web_static/1-index.html b/web_static/1-index.html index 26f13b490f9..706f9c34be8 100644 --- a/web_static/1-index.html +++ b/web_static/1-index.html @@ -1,22 +1,30 @@ - + - - AirBnb Clone + AirBnB -
    -
    -
    -

    - Holberton School -

    -
    +
    +
    Best School
    diff --git a/web_static/100-index.html b/web_static/100-index.html new file mode 100644 index 00000000000..5c0c93aa0bf --- /dev/null +++ b/web_static/100-index.html @@ -0,0 +1,157 @@ + + + + AirBnB + + + + + + + + + + + +
    +
    +
    +
    +

    States

    +

    California, Arizona...

    +
      +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +
      +
    +
    +
    +

    Amenities

    +

    Internet, Kitchen...

    +
      +
    • Internet
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    • +
    +
    + +
    +
    +

    Places

    +
    +
    +

    My home

    +
    $80
    +
    +
    2 Guests
    +
    1 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: John Lennon
    +
    +

    This is a lovely 1 bedroom 1 bathroom apartment that can accomodate 2 people. It is located + at the center of Shanghai and next to the subway line 1. 1 stop to People Square, 2 stops to + Bund, 3 stops to Jingnan Temple. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    +
    +
    +

    2 Reviews

    +
      +
    • +

      From Bob Dylan the 27th January 2024

      +

      John is an epic host. Nothing more to add.

      +
    • +
    • +

      From Sharlene Pie the 12th May 2023

      +

      Fantastic place, to spend quality time with a loved one!

      +
    • +
    +
    +
    +
    +

    Tiny house

    +
    $65
    +
    +
    4 Guests
    +
    2 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: Adrienne
    +
    +

    Our place is a private, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From Chris bethrsed the 14th February 2022

      +

      I love this tiny, quite spacious on the inside. Definitely coming again

      +
    • +
    +
    +
    +
    +

    A suite

    +
    $190
    +
    +
    6 Guests
    +
    5 Bedroom
    +
    3 Bathroom
    +
    +
    Owner: Salita Emmanuel
    +
    +

    Our place is a suite, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    • Pets
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From James Gordon the 26th December 2023

      +

      This is truly an amazing family vacation house, I was able to accomodate everyone and my family had a great time.

      +
    • +
    +
    +
    +
    +
    +
    +
    Best School
    + + diff --git a/web_static/101-index.html b/web_static/101-index.html new file mode 100644 index 00000000000..b3442f3f331 --- /dev/null +++ b/web_static/101-index.html @@ -0,0 +1,157 @@ + + + + AirBnB + + + + + + + + + + + +
    +
    +
    +
    +

    States

    +

    California, Arizona...

    +
      +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +
      +
    +
    +
    +

    Amenities

    +

    Internet, Kitchen...

    +
      +
    • Internet
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    • +
    +
    + +
    +
    +

    Places

    +
    +
    +

    My home

    +
    $80
    +
    +
    2 Guests
    +
    1 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: John Lennon
    +
    +

    This is a lovely 1 bedroom 1 bathroom apartment that can accomodate 2 people. It is located + at the center of Shanghai and next to the subway line 1. 1 stop to People Square, 2 stops to + Bund, 3 stops to Jingnan Temple. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    +
    +
    +

    2 Reviews

    +
      +
    • +

      From Bob Dylan the 27th January 2024

      +

      John is an epic host. Nothing more to add.

      +
    • +
    • +

      From Sharlene Pie the 12th May 2023

      +

      Fantastic place, to spend quality time with a loved one!

      +
    • +
    +
    +
    +
    +

    Tiny house

    +
    $65
    +
    +
    4 Guests
    +
    2 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: Adrienne
    +
    +

    Our place is a private, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From Chris bethrsed the 14th February 2022

      +

      I love this tiny, quite spacious on the inside. Definitely coming again

      +
    • +
    +
    +
    +
    +

    A suite

    +
    $190
    +
    +
    6 Guests
    +
    5 Bedroom
    +
    3 Bathroom
    +
    +
    Owner: Salita Emmanuel
    +
    +

    Our place is a suite, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    • Pets
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From James Gordon the 26th December 2023

      +

      This is truly an amazing family vacation house, I was able to accomodate everyone and my family had a great time.

      +
    • +
    +
    +
    +
    +
    +
    +
    Best School
    + + diff --git a/web_static/102-index.html b/web_static/102-index.html new file mode 100644 index 00000000000..dd70319fc25 --- /dev/null +++ b/web_static/102-index.html @@ -0,0 +1,161 @@ + + + + AirBnB + + + + + + + + + + + + + +
    +
    +
    +
    +
    +

    States

    +

    California, Arizona...

    +
      +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +
      +
    +
    +
    +

    Amenities

    +

    Internet, Kitchen...

    +
      +
    • Internet
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    • +
    +
    +
    + +
    +
    +

    Places

    +
    +
    +

    My home

    +
    $80
    +
    +
    2 Guests
    +
    1 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: John Lennon
    +
    +

    This is a lovely 1 bedroom 1 bathroom apartment that can accomodate 2 people. It is located + at the center of Shanghai and next to the subway line 1. 1 stop to People Square, 2 stops to + Bund, 3 stops to Jingnan Temple. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    +
    +
    +

    2 Reviews

    +
      +
    • +

      From Bob Dylan the 27th January 2024

      +

      John is an epic host. Nothing more to add.

      +
    • +
    • +

      From Sharlene Pie the 12th May 2023

      +

      Fantastic place, to spend quality time with a loved one!

      +
    • +
    +
    +
    +
    +

    Tiny house

    +
    $65
    +
    +
    4 Guests
    +
    2 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: Adrienne
    +
    +

    Our place is a private, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From Chris bethrsed the 14th February 2022

      +

      I love this tiny, quite spacious on the inside. Definitely coming again

      +
    • +
    +
    +
    +
    +

    A suite

    +
    $190
    +
    +
    6 Guests
    +
    5 Bedroom
    +
    3 Bathroom
    +
    +
    Owner: Salita Emmanuel
    +
    +

    Our place is a suite, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    • Pets
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From James Gordon the 26th December 2023

      +

      This is truly an amazing family vacation house, I was able to accomodate everyone and my family had a great time.

      +
    • +
    +
    +
    +
    +
    +
    +
    Best School
    + + diff --git a/web_static/103-index.html b/web_static/103-index.html new file mode 100644 index 00000000000..b4b14c7aab8 --- /dev/null +++ b/web_static/103-index.html @@ -0,0 +1,161 @@ + + + + AirBnB + + + + + + + + + + + + + +
    +
    +
    +
    +
    +

    States

    +

    California, Arizona...

    +
      +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +
      +
    +
    +
    +

    Amenities

    +

    Internet, Kitchen...

    +
      +
    • Internet
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    • +
    +
    +
    + +
    +
    +

    Places

    +
    +
    +

    My home

    +
    $80
    +
    +
    2 Guests
    +
    1 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: John Lennon
    +
    +

    This is a lovely 1 bedroom 1 bathroom apartment that can accomodate 2 people. It is located + at the center of Shanghai and next to the subway line 1. 1 stop to People Square, 2 stops to + Bund, 3 stops to Jingnan Temple. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    +
    +
    +

    2 Reviews

    +
      +
    • +

      From Bob Dylan the 27th January 2024

      +

      John is an epic host. Nothing more to add.

      +
    • +
    • +

      From Sharlene Pie the 12th May 2023

      +

      Fantastic place, to spend quality time with a loved one!

      +
    • +
    +
    +
    +
    +

    Tiny house

    +
    $65
    +
    +
    4 Guests
    +
    2 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: Adrienne
    +
    +

    Our place is a private, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From Chris bethrsed the 14th February 2022

      +

      I love this tiny, quite spacious on the inside. Definitely coming again

      +
    • +
    +
    +
    +
    +

    A suite

    +
    $190
    +
    +
    6 Guests
    +
    5 Bedroom
    +
    3 Bathroom
    +
    +
    Owner: Salita Emmanuel
    +
    +

    Our place is a suite, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +

    Amenities

    +
      +
    • TV
    • +
    • Wifi
    • +
    • Pets
    • +
    +
    +
    +

    Reviews

    +
      +
    • +

      From James Gordon the 26th December 2023

      +

      This is truly an amazing family vacation house, I was able to accomodate everyone and my family had a great time.

      +
    • +
    +
    +
    +
    +
    +
    +
    Best School
    + + diff --git a/web_static/2-index.html b/web_static/2-index.html index b84d4b89f49..0358c2696a0 100644 --- a/web_static/2-index.html +++ b/web_static/2-index.html @@ -1,19 +1,13 @@ - + - - AirBnb Clone - - - + AirBnB + + + -
    -
    -
    -

    - Holberton School -

    -
    +
    +
    Best School
    diff --git a/web_static/3-index.html b/web_static/3-index.html index 2ff10b13403..1c537e59972 100644 --- a/web_static/3-index.html +++ b/web_static/3-index.html @@ -1,22 +1,17 @@ - + - - AirBnb Clone - - - - + AirBnB + + + + + + + -
    - -
    -
    -

    - Holberton School -

    -
    +
    +
    Best School
    diff --git a/web_static/4-index.html b/web_static/4-index.html index f09cdcd9d21..1d8a228198f 100644 --- a/web_static/4-index.html +++ b/web_static/4-index.html @@ -1,30 +1,23 @@ - + - - - - - - - AirBnb Clone + AirBnB + + + + + + + + -
    - -
    +
    - +
    -
    -

    - Holberton School -

    -
    +
    Best School
    diff --git a/web_static/5-index.html b/web_static/5-index.html index 21511ed13c9..9b9cf91c420 100644 --- a/web_static/5-index.html +++ b/web_static/5-index.html @@ -1,38 +1,31 @@ - + - - - - - - - AirBnb Clone + AirBnB + + + + + + + + -
    - -
    +

    States

    -

    California, New York...

    +

    California, Arizona...

    Amenities

    -

    Laundry, Internet...

    +

    Internet, Kitchen...

    - +
    -
    -

    - Holberton School -

    -
    +
    Best School
    diff --git a/web_static/6-index.html b/web_static/6-index.html index 7850206c7ee..63cc8aac678 100644 --- a/web_static/6-index.html +++ b/web_static/6-index.html @@ -1,60 +1,49 @@ - + - - - - - - - AirBnb Clone + AirBnB + + + + + + + + -
    - -
    +

    States

    -

    California, New York...

    +

    California, Arizona...

      -
    • -

      California:

      -
        -
      • San Francisco
      • -
      • Mountain View
      • -
      -
    • -
    • -

      New York:

      -
        -
      • Manhattan
      • -
      • Brooklyn
      • -
      -
    • +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +

    Amenities

    -

    Laundry, Internet...

    +

    Internet, Kitchen...

      -
    • Laundry
    • Internet
    • -
    • Television
    • -
    • Pool
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    - +
    -
    -

    - Holberton School -

    -
    +
    Best School
    diff --git a/web_static/7-index.html b/web_static/7-index.html index 65dd2f57d5b..0459bc98412 100644 --- a/web_static/7-index.html +++ b/web_static/7-index.html @@ -1,73 +1,64 @@ - + - - - - - - - - AirBnb Clone + AirBnB + + + + + + + + + -
    - -
    +

    States

    -

    California, New York...

    +

    California, Arizona...

      -
    • -

      California:

      -
        -
      • San Francisco
      • -
      • Mountain View
      • -
      -
    • -
    • -

      New York:

      -
        -
      • Manhattan
      • -
      • Brooklyn
      • -
      -
    • +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +

    Amenities

    -

    Laundry, Internet...

    +

    Internet, Kitchen...

      -
    • Laundry
    • Internet
    • -
    • Television
    • -
    • Pool
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    - +

    Places

    -
    -

    My home

    -
    -
    -

    Tiny house

    -
    -
    -

    A suite

    -
    -
    +
    +
    +

    My home

    +
    +
    +

    Tiny house

    +
    +
    +

    A suite

    +
    + +
    -
    -

    - Holberton School -

    -
    +
    Best School
    diff --git a/web_static/8-index.html b/web_static/8-index.html index c9518a774e4..c0e6716537e 100644 --- a/web_static/8-index.html +++ b/web_static/8-index.html @@ -1,142 +1,105 @@ - + - - - - - - - - AirBnb Clone + AirBnB + + + + + + + + + -
    - -
    +
    -
    -

    States

    -

    California, New York...

    -
      -
    • -

      California:

      -
        -
      • San Francisco
      • -
      • Mountain View
      • -
      -
    • -
    • -

      New York:

      -
        -
      • Manhattan
      • -
      • Brooklyn
      • -
      -
    • -
    -
    -
    -

    Amenities

    -

    Laundry, Internet...

    -
      -
    • Laundry
    • -
    • Internet
    • -
    • Television
    • -
    • Pool
    • -
    -
    - +
    +

    States

    +

    California, Arizona...

    +
      +

      Arizona:

      +
        +
      • Phoenix
      • +
      • Tucson
      • +
      +

      California:

      +
        +
      • San Francisco
      • +
      • San Diego
      • +
      +
    +
    +
    +

    Amenities

    +

    Internet, Kitchen...

    +
      +
    • Internet
    • +
    • TV
    • +
    • Kitchen
    • +
    • Iron
    • +
    +
    +
    -

    Places

    -
    -

    My home

    -
    -

    $80

    -
    -
    -
    -
    -

    2 Guests

    -
    -
    -
    -

    1 Bedroom

    -
    -
    -
    -

    1 Bathroom

    -
    -
    -
    -

    Owner: Jon Snow

    -
    -
    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    -
    -
    -
    -

    Tiny house

    -
    -

    $65

    -
    -
    -
    -
    -

    4 Guests

    -
    -
    -
    -

    2 Bedroom

    -
    -
    -
    -

    1 Bathroom

    -
    -
    -
    -

    Owner: Daenerys Targaryen

    -
    -
    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    -
    -
    -
    -

    A suite

    -
    -

    $190

    -
    -
    -
    -
    -

    6 Guests

    -
    -
    -
    -

    3 Bedroom

    -
    -
    -
    -

    2 Bathroom

    -
    -
    -
    -

    Owner: Azor Ahai

    -
    -
    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    -
    -
    +

    Places

    +
    +
    +

    My home

    +
    $80
    +
    +
    2 Guests
    +
    1 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: John Lennon
    +
    +

    This is a lovely 1 bedroom 1 bathroom apartment that can accomodate 2 people. It is located + at the center of Shanghai and next to the subway line 1. 1 stop to People Square, 2 stops to + Bund, 3 stops to Jingnan Temple. +

    +
    +
    +
    +

    Tiny house

    +
    $65
    +
    +
    4 Guests
    +
    2 Bedroom
    +
    1 Bathroom
    +
    +
    Owner: Adrienne
    +
    +

    Our place is a private, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +
    +

    A suite

    +
    $190
    +
    +
    6 Guests
    +
    5 Bedroom
    +
    3 Bathroom
    +
    +
    Owner: Salita Emmanuel
    +
    +

    Our place is a suite, affordable stand-alone guests house centrally located just minutes to French Quarters, downtown, the Fair Grounds + (Jazz Fest) and City Park. The guest house is a quaint 400 square ft, with a full bath, mini kitchen & living room. The + extra high ceilings make the home feel more spacious. The sofa converts to a bed also. We have a hand made counter area that adds character + to the room and a great porch/deck to relax on and have a glass of Meriot. +

    +
    +
    +
    -
    -

    - Holberton School -

    -
    +
    Best School
    diff --git a/web_static/README.md b/web_static/README.md deleted file mode 100644 index 5c4cc7dc35e..00000000000 --- a/web_static/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# 0x01. AirBnB clone - Web static -At the end of this project you are expected to be able to explain to anyone, without the help of Google: -* What is HTML? -* How do you create an HTML page? -* What is a markup language? -* What is the DOM? -* What is an element / tag? -* What is an attribute? -* How does the browser load a webpage? -* What is CSS? -* How do you add style to an element? -* What is a class? -* What is a selector? -* How do you compute CSS Specificity Value? -* What are Box properties in CSS? diff --git a/web_static/images/icon_bath.png b/web_static/images/icon_bath.png deleted file mode 100644 index 7a9bfed9d8d9f02a29a50847808f6bb0eb61c246..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 704 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^t4*@+_k(E=`(ls_UvvG9s4+snii-}K4PRS{+sH|>kZta*d zb^5Hi%T}!2uxZzxeMgUB_uttD3|2-1PZ!4!kK=EzMEf6d z5MX_<`NFNF>}b(ImcWUtRxDUiq0Myf64zg@7xpEqNdy0}VK@7U`tS21i8(3ekx#7LRz)t6QhuZvYoqq(ZPJWeo{wbL zESc!_=-iIXBXixXdh-l+?sMFJ(Npp3leugCmHBqA^*EAv+fU7J_sqjOf%_PaZ8|Of UVsdFeFj^QqUHx3vIVCg!0N#=_Pyhe` diff --git a/web_static/images/icon_bed.png b/web_static/images/icon_bed.png deleted file mode 100644 index 2a63284877067dcb61b769fd59e8ee373587b0be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 447 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx|^t$pJngu0XnMkb%!ndUAozGAIf1 z3ua(sX64}I;^CE*Q#3F%wXpN{i-=9nD6gz;XziFdWA5TjTelxSb?*G-JNG_+|M~mx zviF&$K)tIyT^vI^j=#O^&UeT_#Pwo+)q9mM_a;x+{r{?=!!(C77B>!oQ*-yt>6GnW zvb0u7U+vo+51u1=EDhnF`&QiX_{);$Z#jXF;fza^;C9#FjyJu$eswQfV=yaSXUXd8 zOV{`w(o3GyDaWjxm?#vV?{2*?Yr?E~e^`u+I&Q^G-!Q>}@0sHBZ48dCLMLt(OVPgg&ebxsLQ E03-gv2mk;8 diff --git a/web_static/images/icon_group.png b/web_static/images/icon_group.png deleted file mode 100644 index 3e012ab4d5cdc2146782461b475b2a81ce0fea4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1051 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=3?wxlRx~p(FqQ=Pgt!8^Wut(;A)xa1&vRh7 zSC<6&1v4-*F|)9;v2$>8ar5x<@e2qF35$q|iAze!$jZqpD66S!Xld)}=^Gdt8Jn70 zTG`s!J370#x_Nqe`}q0=hQ-7sr)1|86ql4%RM$6jboNi2I(^3MxeFF8TeW)arY+ld z?%K2Oz~Q6EPnA zgW||zoJzi$)5NBGZPtle#HKS|ONG3(HoLZl9GLX^x%AAL)@KUZ+vTc0{VsSi z|7-5$4L5oF7urh2ac+5eFFjb7YkJ+RdkcL#rzu5sa_K66=m>b_)UA@+{lO#H>wv(! zKGwt?PPf%;9!;rgkiMT8v73YEVpvoAipxQ+*R6D08{BU5?0I*&$T((Mh|u|rRhb!| z=lE8x*>H4C#d+ti#sLx)(RZV*`io0Cl{SazpONRXdQn&qR2=$s=a78nheKN1tRgd#}OF1pXGJj3wWR$qG zJasw0!xKYYO^YwQa#}23?syqCK4vvLo2~S{GEriAwuwpFgGu|Qp7rZ^&2D1D=_J3; z+)l#jl{)Y1sC={E2_fx=7<{+ZSg{DoMlXFiyMceBvwY+aiN*6{8Mpd>DP{C>{588Z zXx6`N3#KHp2h2)ij}53i#g`N?Go5{Jz|3TJ^#w)K|4A{fc)|NCs^ON!-w=}@5 button{ +section.filters button{ font-size: 18px; - color: white; - background-color: #FF5A5F; + background-color: #ff5a5f; + color: #ffffff; height: 48px; - border: 0px; - border-radius: 4px; width: 20%; - margin-left: auto; - margin-right: 30px; - opacity: 1; + border: 0; + border-radius: 4px; + margin-left: auto; /*push button to the left*/ } - -section.filters > button:hover { - opacity: 0.9; +section.filters button:hover{ + opacity: 90%; } - -.locations, .amenities { +section.filters div.locations, section.filters div.amenities{ height: 100%; width: 25%; - padding-left: 50px; + padding: 15px 0; } - -.locations { - border-right: 1px solid #DDDDDD; +section.filters div.locations{ + border-right: 1px solid #dddddd; } -.locations > h3, .amenities > h3 { + +section.filters div.locations h3, section.filters div.amenities h3{ font-weight: 600; - margin: 12px 0 5px 0; + margin: 0; + margin-left: 30px; } -.locations > h4, .amenities > h4 { +section.filters div.locations h4, section.filters div.amenities h4{ font-weight: 400; font-size: 14px; - margin: 0 0 5px 0; + margin: 0 0 0 30px; } - -.popover { - display: none; +ul.popover{ position: relative; - left: -51px; - background-color: #FAFAFA; + display: none; width: 100%; - border: 1px solid #DDDDDD; - border-radius: 4px; + background-color: #fafafa; + border: 1px solid #dddddd; + margin-top: 18px; + padding: 10px 15px; z-index: 1; - padding: 30px 50px 30px 0; - margin-top: 17px; -} - -.popover, .popover ul { - list-style-type: none; } -.locations:hover > .popover { - display: block; +ul.popover h2{ + font-size: 16px; + margin: 0; } - -.amenities:hover > .popover { - display: block; +ul.popover ul{ + margin: 0; + padding: 4px 0 10px 10px; } - -.popover h2 { - margin-top: 0px; - margin-bottom: 5px; -} - -.locations > .popover > li { - margin-bottom: 30px; - margin-left: 30px; -} -.locations > .popover > li > ul { - padding-left: 20px; -} -.locations > .popover > li > ul > li { - margin-bottom: 10px; +ul.popover, ul.popover ul{ + list-style-type: none; } -.amenities > .popover > li { - margin-left: 50px; - margin-bottom: 10px; +/*hide the filter popover*/ +div.locations:hover ul.popover, +div.amenities:hover ul.popover{ + display: block; } diff --git a/web_static/styles/7-places.css b/web_static/styles/7-places.css index 04ede61303e..eed2eb14ec2 100644 --- a/web_static/styles/7-places.css +++ b/web_static/styles/7-places.css @@ -1,29 +1,20 @@ -.places { - width: 100%; - border: 0; +section.places h1{ + font-size: 30px; + text-align: left; +} +div.articles-section{ display: flex; - flex-direction: row; flex-wrap: wrap; justify-content: center; } - -.places > h1 { - font-size: 30px; - padding-left: 20px; - padding-top: 20px; - margin-bottom: 0px; - flex: 0 1 100%; -} -.places > article { +section.places article{ width: 390px; - padding: 20px 20px 20px 20px; - margin: 20px 20px 20px 20px; - border: 1px solid #FF5A5F; + padding: 20px; + margin: 20px; + border: 1px solid #ff5a5f; border-radius: 4px; - display: flex; - justify-content: center; } - -.places > article > h2 { +section.places article h2{ font-size: 30px; -} + text-align: center; +} \ No newline at end of file diff --git a/web_static/styles/8-places.css b/web_static/styles/8-places.css index f0507c9ddc5..c04002c5d7e 100644 --- a/web_static/styles/8-places.css +++ b/web_static/styles/8-places.css @@ -1,107 +1,69 @@ -.places { - width: 100%; - border: 0; +section.places h1{ + font-size: 30px; + text-align: left; +} +.articles-section{ display: flex; - flex-direction: row; flex-wrap: wrap; justify-content: center; } - -.places > h1 { - font-size: 30px; - padding-left: 20px; - padding-top: 20px; - margin-bottom: 0px; - flex: 0 1 100%; -} -.places > article { +section.places article{ + position: relative; width: 390px; - padding: 20px 20px 20px 20px; - margin: 20px 20px 20px 20px; - border: 1px solid #FF5A5F; + padding: 20px; + margin: 20px; + border: 1px solid #ff5a5f; border-radius: 4px; - display: flex; - flex-direction: column; - justify-content: flex-start; - position: relative; } - -.places > article > h2 { +section.places article h2{ font-size: 30px; - margin: 0 0 0 0; - align-self: center; + text-align: center; + margin: 5px 0 40px 0; } - -.price_by_night { - color: #FF5A5F; - border: 4px solid #FF5A5F; +article div.price_by_night{ + position: absolute; + top: 15px; + right: 15px; + color: #ff5a5f; + border: 4px solid #ff5a5f; + border-radius: 50%; min-width: 60px; height: 60px; - font-size: 30px; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - margin-left: auto; - position: absolute; - top: 10px; - right: 20px; -} -.price_by_night > p { - margin: 0 0 0 0; + font-size: 20px; + text-align: center; + line-height: 60px; } - -.information { - align-self: center; - height: 80px; - width: 100%; - border-top: 1px solid #DDDDDD; - border-bottom: 1px solid #DDDDDD; - margin-top: 30px; +.information{ display: flex; - justify-content: center; align-items: center; + height: 80px; + border-top: 1px solid #dddddd; + border-bottom: 1px solid #dddddd; } - -.max_guest, .number_rooms, .number_bathrooms { +.information div{ width: 100px; + height: 50px; text-align: center; } - -.max_guest > p, .number_rooms > p, .number_bathrooms > p{ - margin-top: auto; - margin-bottom: auto; +.information div .icon{ + width: 100%; + height: 80%; + background-repeat: no-repeat; + background-size: contain; + background-position: center; } - -.guest_image { - margin-left: auto; - margin-right: auto; - height: 50px; - width: 50px; - background: url("../images/icon_group.png") no-repeat center; +.max_guest div.icon{ + background-image: url("../images/guest_icon.png"); } - -.bed_image { - margin-left: auto; - margin-right: auto; - height: 50px; - width: 50px; - background: url("../images/icon_bed.png") no-repeat center; +.number_rooms div.icon{ + background-image: url("../images/bedroom_icon.png"); } - -.bath_image { - margin-left: auto; - margin-right: auto; - height: 50px; - width: 50px; - background: url("../images/icon_bath.png") no-repeat center; +.number_bathrooms div.icon{ + background-image: url("../images/bathroom_icon.png"); } - -.user > p { - margin-top: 20px; - margin-bottom: 0px; +article div.user{ + margin: 10px 0; } -.description > p { - margin-top: 7px; - margin-bottom: 0px; +article div.user span{ + font-weight: bold; } diff --git a/web_static/w3c_validator.py b/web_static/w3c_validator.py new file mode 100755 index 00000000000..ec7ec714074 --- /dev/null +++ b/web_static/w3c_validator.py @@ -0,0 +1,131 @@ +#!/usr/bin/python3 +""" +W3C validator for Holberton School + +For HTML and CSS files. + +Based on 1 API: +- https://validator.w3.org/docs/api.html + +Usage: + +Simple file: + +``` +./w3c_validator.py index.html +``` + +Multiple files: + +``` +./w3c_validator.py index.html header.html styles/common.css +``` + +All errors are printed in `STDERR` + +Return: +Exit status is the # of errors, 0 on Success +""" +import sys +import requests +import os + + +def __print_stdout(msg): + """Print message in STDOUT + """ + sys.stdout.buffer.write(msg.encode('utf-8')) + + +def __print_stderr(msg): + """Print message in STDERR + """ + sys.stderr.buffer.write(msg.encode('utf-8')) + + +def __is_empty(file): + if os.path.getsize(file) == 0: + raise OSError("File '{}' is empty.".format(file)) + + +def __validate(file_path, type): + """ + Start validation of files + """ + h = {'Content-Type': "{}; charset=utf-8".format(type)} + # Open files in binary mode: + # https://requests.readthedocs.io/en/master/user/advanced/ + d = open(file_path, "rb").read() + u = "https://validator.w3.org/nu/?out=json" + r = requests.post(u, headers=h, data=d) + + if not r.status_code < 400: + raise ConnectionError("Unable to connect to API endpoint.") + + res = [] + messages = r.json().get('messages', []) + for m in messages: + # Capture files that have incomplete or broken HTML + if m['type'] == 'error' or m['type'] == 'info': + res.append("'{}' => {}".format(file_path, m['message'])) + else: + res.append("[{}:{}] {}".format( + file_path, m['lastLine'], m['message'])) + return res + + +def __analyse(file_path): + """Start analyse of a file and print the result + """ + nb_errors = 0 + try: + result = None + + if file_path.endswith(".css"): + __is_empty(file_path) + result = __validate(file_path, "text/css") + elif file_path.endswith((".html", ".htm")): + __is_empty(file_path) + result = __validate(file_path, "text/html") + elif file_path.endswith(".svg"): + __is_empty(file_path) + result = __validate(file_path, "image/svg+xml") + else: + allowed_files = "'.css', '.html', '.htm' and '.svg'" + raise OSError( + "File {} does not have a valid file extension.\nOnly {} are " + "allowed.".format(file_path, allowed_files) + ) + + if len(result) > 0: + for msg in result: + __print_stderr("{}\n".format(msg)) + nb_errors += 1 + else: + __print_stdout("'{}' => OK\n".format(file_path)) + + except Exception as e: + __print_stderr("'{}' => {}\n".format(e.__class__.__name__, e)) + return nb_errors + + +def __files_loop(): + """Loop that analyses for each file from input arguments + """ + nb_errors = 0 + for file_path in sys.argv[1:]: + nb_errors += __analyse(file_path) + + return nb_errors + + +if __name__ == "__main__": + """Main + """ + if len(sys.argv) < 2: + __print_stderr("usage: w3c_validator.py file1 file2 ...\n") + exit(1) + + """execute tests, then exit. Exit status = # of errors (0 on success) + """ + sys.exit(__files_loop()) From 3db35ba606b2b5118841ff02fc6922ff947d18a4 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 10:09:48 +0000 Subject: [PATCH 63/80] test: FileStorage class --- .../test_engine/test_file_storage.py | 216 +++++++++--------- 1 file changed, 107 insertions(+), 109 deletions(-) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 1474a34fec0..3e5bd4dacda 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -1,115 +1,113 @@ #!/usr/bin/python3 -""" -Contains the TestFileStorageDocs classes -""" - -from datetime import datetime -import inspect -import models -from models.engine import file_storage -from models.amenity import Amenity +""" Module for testing file storage""" +import unittest from models.base_model import BaseModel -from models.city import City -from models.place import Place -from models.review import Review -from models.state import State -from models.user import User -import json +from models import storage, storage_type import os -import pep8 -import unittest -FileStorage = file_storage.FileStorage -classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, - "Place": Place, "Review": Review, "State": State, "User": User} - - -class TestFileStorageDocs(unittest.TestCase): - """Tests to check the documentation and style of FileStorage class""" - @classmethod - def setUpClass(cls): - """Set up for the doc tests""" - cls.fs_f = inspect.getmembers(FileStorage, inspect.isfunction) - - def test_pep8_conformance_file_storage(self): - """Test that models/engine/file_storage.py conforms to PEP8.""" - pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['models/engine/file_storage.py']) - self.assertEqual(result.total_errors, 0, - "Found code style errors (and warnings).") - - def test_pep8_conformance_test_file_storage(self): - """Test tests/test_models/test_file_storage.py conforms to PEP8.""" - pep8s = pep8.StyleGuide(quiet=True) - result = pep8s.check_files(['tests/test_models/test_engine/\ -test_file_storage.py']) - self.assertEqual(result.total_errors, 0, - "Found code style errors (and warnings).") - - def test_file_storage_module_docstring(self): - """Test for the file_storage.py module docstring""" - self.assertIsNot(file_storage.__doc__, None, - "file_storage.py needs a docstring") - self.assertTrue(len(file_storage.__doc__) >= 1, - "file_storage.py needs a docstring") - - def test_file_storage_class_docstring(self): - """Test for the FileStorage class docstring""" - self.assertIsNot(FileStorage.__doc__, None, - "FileStorage class needs a docstring") - self.assertTrue(len(FileStorage.__doc__) >= 1, - "FileStorage class needs a docstring") - - def test_fs_func_docstrings(self): - """Test for the presence of docstrings in FileStorage methods""" - for func in self.fs_f: - self.assertIsNot(func[1].__doc__, None, - "{:s} method needs a docstring".format(func[0])) - self.assertTrue(len(func[1].__doc__) >= 1, - "{:s} method needs a docstring".format(func[0])) - - -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") - def test_all_returns_dict(self): - """Test that all returns the FileStorage.__objects attr""" - storage = FileStorage() - new_dict = storage.all() - self.assertEqual(type(new_dict), dict) - self.assertIs(new_dict, storage._FileStorage__objects) - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + + +@unittest.skipIf(storage_type == 'db', "Testing FileStorage only") +class test_fileStorage(unittest.TestCase): + """ Class to test the file storage method """ + + def setUp(self): + """ Set up test environment """ + del_list = [] + for key in storage._FileStorage__objects.keys(): + del_list.append(key) + for key in del_list: + del storage._FileStorage__objects[key] + + def tearDown(self): + """ Remove storage file at end of tests """ + try: + os.remove('file.json') + except FileNotFoundError: + pass + + def test_obj_list_empty(self): + """ __objects is initially empty """ + self.assertEqual(len(storage.all()), 0) + def test_new(self): - """test that new adds an object to the FileStorage.__objects attr""" - storage = FileStorage() - save = FileStorage._FileStorage__objects - FileStorage._FileStorage__objects = {} - test_dict = {} - for key, value in classes.items(): - with self.subTest(key=key, value=value): - instance = value() - instance_key = instance.__class__.__name__ + "." + instance.id - storage.new(instance) - test_dict[instance_key] = instance - self.assertEqual(test_dict, storage._FileStorage__objects) - FileStorage._FileStorage__objects = save - - @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + """ New object is correctly added to __objects """ + new_obj = BaseModel() + storage.new(new_obj) + for obj in storage.all().values(): + temp = obj + self.assertTrue(temp is obj) + + def test_all(self): + """ __objects is properly returned """ + new = BaseModel() + temp = storage.all() + self.assertIsInstance(temp, dict) + + def test_base_model_instantiation(self): + """ File is not created on BaseModel save """ + new = BaseModel() + self.assertFalse(os.path.exists('file.json')) + + def test_empty(self): + """ Data is saved to file """ + new = BaseModel() + thing = new.to_dict() + new.save() + new2 = BaseModel(**thing) + self.assertNotEqual(os.path.getsize('file.json'), 0) + def test_save(self): - """Test that save properly saves objects to file.json""" - storage = FileStorage() - new_dict = {} - for key, value in classes.items(): - instance = value() - instance_key = instance.__class__.__name__ + "." + instance.id - new_dict[instance_key] = instance - save = FileStorage._FileStorage__objects - FileStorage._FileStorage__objects = new_dict + """ FileStorage save method """ + new = BaseModel() + storage.save() + self.assertTrue(os.path.exists('file.json')) + + def test_reload(self): + """ Storage file is successfully loaded to __objects """ + new_obj = BaseModel() + storage.new(new_obj) storage.save() - FileStorage._FileStorage__objects = save - for key, value in new_dict.items(): - new_dict[key] = value.to_dict() - string = json.dumps(new_dict) - with open("file.json", "r") as f: - js = f.read() - self.assertEqual(json.loads(string), json.loads(js)) + storage.reload() + for obj in storage.all().values(): + loaded = obj + self.assertEqual(new_obj.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() + + def test_reload_from_nonexistent(self): + """ Nothing happens if file does not exist """ + self.assertEqual(storage.reload(), None) + + def test_base_model_save(self): + """ BaseModel save method calls storage save """ + new = BaseModel() + new.save() + self.assertTrue(os.path.exists('file.json')) + + def test_type_path(self): + """ Confirm __file_path is string """ + self.assertEqual(type(storage._FileStorage__file_path), str) + + def test_type_objects(self): + """ Confirm __objects is a dict """ + self.assertEqual(type(storage.all()), dict) + + def test_key_format(self): + """ Key is properly formatted """ + obj = BaseModel() + storage.new(obj) + _id = obj.to_dict()['id'] + for key in storage.all().keys(): + temp = key + 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) From b09eef27d91f1a81704070142e10dc513b9cc6b7 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 10:57:57 +0000 Subject: [PATCH 64/80] test: DBStorage class --- .../test_engine/test_db_storage.py | 60 ++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..64b1d8a5d68 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -1,11 +1,10 @@ #!/usr/bin/python3 """ -Contains the TestDBStorageDocs and TestDBStorage classes +Module defines a test for DBStorage class """ - from datetime import datetime import inspect -import models +import models import storage_type from models.engine import db_storage from models.amenity import Amenity from models.base_model import BaseModel @@ -68,21 +67,54 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestFileStorage(unittest.TestCase): - """Test the FileStorage class""" - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") +@unittest.skipIf(storage_type != 'db', "Testing database storage only") +class TestDBStorage(unittest.TestCase): + """Test the DBStorage class""" + def test_all_returns_dict(self): - """Test that all returns a dictionaty""" - self.assertIs(type(models.storage.all()), dict) + """Test that all method: returns a dictionaty""" - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_no_class(self): - """Test that all returns all rows when no class is passed""" + """ + Test that all method: + returns all rows when no class is passed + """ - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_new(self): - """test that new adds an object to the database""" + """Test that new method: adds an object to the database""" - @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): - """Test that save properly saves objects to file.json""" + """ + Test that save method: + properly saves objects to the database + """ + + def test_delete(self): + """ + Test that delete method: + deletes object from current database session + """ + + def test_reload(self): + """ + Test that reload method: + creates all table in the current database session + """ + + def test_close(self): + """ + Test that the close method: + closes the current database session + """ + + def test_get(self): + """ + Test get(): + gets an object of specified id from storage + """ + + def test_count(self): + """ + Test count(): + counts the number of all objects or class objects + """ From d634e30fc9e849e0651f2b214b5bdcbfc8cd7350 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 16:38:21 +0000 Subject: [PATCH 65/80] complaint to python3.4 --- models/engine/db_storage.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index 91d8c80c3e5..c8727b54112 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -30,8 +30,9 @@ class DBStorage: def __init__(self): """ Initialize database instance """ - self.__engine = create_engine( - f"mysql+mysqldb://{user}:{passwd}@{host}/{db}", pool_pre_ping=True) + conn_string = "mysql+mysqldb://{}:{}@{}/{}".format( + user, passwd, host, db) + self.__engine = create_engine(conn_string, pool_pre_ping=True) if hbnb_env == "test": # Drop all tables if test environment @@ -45,14 +46,14 @@ def all(self, cls=None): if cls: # Handle class for obj in self.__session.query(cls).all(): - key = f"{type(obj).__name__}.{obj.id}" + key = "{type(obj).__name__}.{}".format(obj.id) objs_dict[key] = obj else: # Handle all classes cls_list = [State, City, User, Review, Place, Amenity] for cls in cls_list: for obj in self.__session.query(cls).all(): - key = f"{type(obj).__name__}.{obj.id}" + key = "{type(obj).__name__}.{}".format(obj.id) objs_dict[key] = obj return objs_dict From 9f91d83ebea05cd3068fb26937420836264c03a3 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 16:59:36 +0000 Subject: [PATCH 66/80] test for get() and count() added --- .../test_engine/test_file_storage.py | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 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 3e5bd4dacda..bfe981ed659 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -4,6 +4,61 @@ from models.base_model import BaseModel from models import storage, storage_type import os +from datetime import datetime +import inspect +import models +from models.engine import file_storage +import json +import os +import pep8 +import unittest +FileStorage = file_storage.FileStorage + + +@unittest.skipIf(storage_type == 'db', "Test FileStorage class only") +class TestFileStorageDocs(unittest.TestCase): + """Tests to check the documentation and style of FileStorage class""" + @classmethod + def setUpClass(cls): + """Set up for the doc tests""" + cls.fs_f = inspect.getmembers(FileStorage, inspect.isfunction) + + def test_pep8_conformance_file_storage(self): + """Test that models/engine/file_storage.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['models/engine/file_storage.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_pep8_conformance_test_file_storage(self): + """Test tests/test_models/test_file_storage.py conforms to PEP8.""" + pep8s = pep8.StyleGuide(quiet=True) + result = pep8s.check_files(['tests/test_models/test_engine/\ +test_file_storage.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + + def test_file_storage_module_docstring(self): + """Test for the file_storage.py module docstring""" + self.assertIsNot(file_storage.__doc__, None, + "file_storage.py needs a docstring") + self.assertTrue(len(file_storage.__doc__) >= 1, + "file_storage.py needs a docstring") + + def test_file_storage_class_docstring(self): + """Test for the FileStorage class docstring""" + self.assertIsNot(FileStorage.__doc__, None, + "FileStorage class needs a docstring") + self.assertTrue(len(FileStorage.__doc__) >= 1, + "FileStorage class needs a docstring") + + def test_fs_func_docstrings(self): + """Test for the presence of docstrings in FileStorage methods""" + for func in self.fs_f: + self.assertIsNot(func[1].__doc__, None, + "{:s} method needs a docstring".format(func[0])) + self.assertTrue(len(func[1].__doc__) >= 1, + "{:s} method needs a docstring".format(func[0])) @unittest.skipIf(storage_type == 'db', "Testing FileStorage only") @@ -109,5 +164,10 @@ 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)) self.assertEqual(type(storage), FileStorage) + + def test_get(self): + """ Test if get method returns a specified object from storage """ + + def test_count(self): + """ Test if count methods return the number of objects """ From ce830dadc30811d772888633a630a2f2a6af3ab5 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 19:33:16 +0000 Subject: [PATCH 67/80] get() and count() added --- models/engine/db_storage.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index c8727b54112..5cf3025e387 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -4,12 +4,12 @@ """ import os from models.base_model import Base -from models.user import User -from models.review import Review -from models.place import Place -from models.city import City from models.state import State +from models.city import City +from models.user import User from models.amenity import Amenity +from models.place import Place +from models.review import Review from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session @@ -46,14 +46,14 @@ def all(self, cls=None): if cls: # Handle class for obj in self.__session.query(cls).all(): - key = "{type(obj).__name__}.{}".format(obj.id) + key = "{}.{}".format(type(obj).__name__, obj.id) objs_dict[key] = obj else: # Handle all classes cls_list = [State, City, User, Review, Place, Amenity] for cls in cls_list: for obj in self.__session.query(cls).all(): - key = "{type(obj).__name__}.{}".format(obj.id) + key = "{}.{}".format(type(obj).__name__, obj.id) objs_dict[key] = obj return objs_dict @@ -81,3 +81,21 @@ def reload(self): def close(self): """ Removes the current session """ self.__session.remove() + + def get(self, cls, id): + """ Retrieve a specified object from storage """ + if cls is None or id is None: + return None + return self.__session.query(cls).get(id) + + def count(self, cls=None): + """ Counts the all objects or class objects in storage """ + class_list = [State, City, Amenity, Place, User, Review] + num_objs = 0 + if cls is None: + for clss in class_list: + num_objs += self.__session.query(clss).count() + else: + num_objs += self.__session.query(cls).count() + + return num_objs From 61ece77133f19b2779c5aed8f1e3d83fd9d93b2b Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 19:52:06 +0000 Subject: [PATCH 68/80] get(0 and count() methods added --- models/engine/file_storage.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 7a2be530d41..523a7441fb6 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -71,3 +71,19 @@ def delete(self, obj=None): def close(self): """Calls reload method for deserializing the JSON file to objects""" self.reload() + + def get(self, cls, id): + """ Gets a specified object using its class and id """ + if cls is None or id is None: + return None + all_objs = storage.all(cls) + key = "{}.{}".format(cls.__name__, id) + + return all_objs.get(key, None) + + def count(self, cls=None): + """ Count all objects or objects of specified class """ + if cls is None: + return len(storage.all()) + else: + return len(storage.all(cls)) From 37ea222a7d23e95b251ed7a340b0e84edb12c070 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 20:01:32 +0000 Subject: [PATCH 69/80] get() and count() methods added --- models/engine/file_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 523a7441fb6..cd315ee69c8 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,5 +1,5 @@ #!/usr/bin/python3 -"""This module defines a class to manage file storage for hbnb clone""" +"""This module defines a FileStorage class""" import json From 8070caa45727d1434ab2856d57d182234698a05a Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 20:02:49 +0000 Subject: [PATCH 70/80] get() and count() added --- models/engine/db_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index 5cf3025e387..7290e1edd4e 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 """ -Module defines Database engine `DBStorage` +Module defines the `DBStorage` class """ import os from models.base_model import Base From 612550d4c18ff39fec7dcc60d1165881769409f4 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 20:18:05 +0000 Subject: [PATCH 71/80] models --- models/__init__.py | 25 +++----- models/amenity.py | 37 ++++------- models/base_model.py | 117 +++++++++++++--------------------- models/city.py | 26 ++++---- models/engine/__init__.py | 0 models/engine/db_storage.py | 109 ++++++++++++------------------- models/engine/file_storage.py | 111 +++++++++++++------------------- models/place.py | 98 +++++++++++++--------------- models/review.py | 26 ++++---- models/state.py | 52 +++++++-------- models/user.py | 27 ++++---- 11 files changed, 262 insertions(+), 366 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/engine/__init__.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/user.py diff --git a/models/__init__.py b/models/__init__.py old mode 100644 new mode 100755 index 3919d9b90f1..defef6378c1 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,22 +1,17 @@ #!/usr/bin/python3 -"""This module instantiates an object of class FileStorage""" -import os -from models.engine.db_storage import DBStorage -from models.engine.file_storage import FileStorage -from models.base_model import Base -from models.user import User -from models.review import Review -from models.place import Place -from models.city import City -from models.state import State -from models.amenity import Amenity +""" +initialize the models package +""" +from os import getenv -storage_type = os.getenv("HBNB_TYPE_STORAGE") -if storage_type == "db": +storage_t = getenv("HBNB_TYPE_STORAGE") + +if storage_t == "db": + from models.engine.db_storage import DBStorage storage = DBStorage() - storage.reload() else: + from models.engine.file_storage import FileStorage storage = FileStorage() - storage.reload() +storage.reload() diff --git a/models/amenity.py b/models/amenity.py old mode 100644 new mode 100755 index fe4e35d5a26..557728bafdc --- a/models/amenity.py +++ b/models/amenity.py @@ -1,32 +1,21 @@ -#!/usr/bin/python3 -""" State Module for HBNB project """ -import os -from sqlalchemy import Column, String, ForeignKey -from sqlalchemy.orm import relationship +#!/usr/bin/python +""" holds class Amenity""" +import models from models.base_model import BaseModel, Base - - -# Get the storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") - -# lazy import association table -if storage_type == "db": - from models.place import place_amenity +from os import getenv +import sqlalchemy +from sqlalchemy import Column, String +from sqlalchemy.orm import relationship class Amenity(BaseModel, Base): - """ Defines Amenity class """ - - if storage_type == "db": + """Representation of Amenity """ + if models.storage_t == 'db': __tablename__ = 'amenities' - # Handle database storage name = Column(String(128), nullable=False) - - # Many-to-Many relationship - place_amenities = relationship('Place', - secondary='place_amenity', - back_populates='amenities', - viewonly=False) else: - # Handle file storage name = "" + + def __init__(self, *args, **kwargs): + """initializes Amenity""" + super().__init__(*args, **kwargs) diff --git a/models/base_model.py b/models/base_model.py old mode 100644 new mode 100755 index 92bea00bb4f..9a86addb366 --- a/models/base_model.py +++ b/models/base_model.py @@ -1,102 +1,75 @@ #!/usr/bin/python3 """ -This module defines a base class for all models in our hbnb clone +Contains class BaseModel """ -import os -import uuid + from datetime import datetime +import models +from os import getenv +import sqlalchemy from sqlalchemy import Column, String, DateTime from sqlalchemy.ext.declarative import declarative_base +import uuid +time = "%Y-%m-%dT%H:%M:%S.%f" -# Get storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") -if storage_type == "db": +if models.storage_t == "db": Base = declarative_base() else: Base = object -def generate_uuid(): - return str(uuid.uuid4()) - - class BaseModel: - """A base class for all hbnb models""" - if storage_type == "db": - id = Column( - String(60), primary_key=True, nullable=False, - default=generate_uuid) - created_at = Column(DateTime, nullable=False, default=datetime.utcnow) - updated_at = Column(DateTime, nullable=False, default=datetime.utcnow) + """The BaseModel class from which future classes will be derived""" + if models.storage_t == "db": + id = Column(String(60), primary_key=True) + created_at = Column(DateTime, default=datetime.utcnow) + updated_at = Column(DateTime, default=datetime.utcnow) - # Handle File storage def __init__(self, *args, **kwargs): - """Instantiates a new model""" + """Initialization of the base model""" if kwargs: - if 'updated_at' in kwargs: - # recreate obj -- from to_dict() - kwargs['updated_at'] = datetime.strptime( - kwargs['updated_at'], '%Y-%m-%dT%H:%M:%S.%f') + for key, value in kwargs.items(): + if key != "__class__": + setattr(self, key, value) + if kwargs.get("created_at", None) and type(self.created_at) is str: + self.created_at = datetime.strptime(kwargs["created_at"], time) else: - # new obj - self.updated_at = datetime.now() - - if 'created_at' in kwargs: - # recreate obj -- from to_dict() - kwargs['created_at'] = datetime.strptime( - kwargs['created_at'], '%Y-%m-%dT%H:%M:%S.%f') + self.created_at = datetime.utcnow() + if kwargs.get("updated_at", None) and type(self.updated_at) is str: + self.updated_at = datetime.strptime(kwargs["updated_at"], time) else: - # new obj - self.created_at = datetime.now() - - if 'id' not in kwargs: - # new obj + self.updated_at = datetime.utcnow() + if kwargs.get("id", None) is None: self.id = str(uuid.uuid4()) - - if '__class__' in kwargs: - # recreate obj -- from to_dict() - del kwargs['__class__'] - - # set attribute to the instance - for attr_name, attr_value in kwargs.items(): - setattr(self, attr_name, attr_value) - else: self.id = str(uuid.uuid4()) - self.created_at = datetime.now() - self.updated_at = datetime.now() + self.created_at = datetime.utcnow() + self.updated_at = self.created_at 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__) + """String representation of the BaseModel class""" + return "[{:s}] ({:s}) {}".format(self.__class__.__name__, self.id, + self.__dict__) def save(self): - """Updates updated_at with current time when instance is changed""" - from models import storage - self.updated_at = datetime.now() - storage.new(self) - storage.save() + """updates the attribute 'updated_at' with the current datetime""" + self.updated_at = datetime.utcnow() + models.storage.new(self) + models.storage.save() def to_dict(self): - """ - Returns: dictionary containing all key/values of __dict__ of the - instance - """ - obj_dict = {} - for key, value in self.__dict__.items(): - if key == "created_at" or key == "updated_at": - obj_dict[key] = datetime.isoformat(value) - else: - obj_dict[key] = value - obj_dict["__class__"] = type(self).__name__ - if '_sa_instance_state' in obj_dict: - del obj_dict['_sa_instance_state'] - - return obj_dict + """returns a dictionary containing all keys/values of the instance""" + new_dict = self.__dict__.copy() + if "created_at" in new_dict: + new_dict["created_at"] = new_dict["created_at"].strftime(time) + if "updated_at" in new_dict: + new_dict["updated_at"] = new_dict["updated_at"].strftime(time) + new_dict["__class__"] = self.__class__.__name__ + if "_sa_instance_state" in new_dict: + del new_dict["_sa_instance_state"] + return new_dict def delete(self): - """Deletes the current instance from storage""" - from models import storage - storage.delete(self) + """delete the current instance from the storage""" + models.storage.delete(self) diff --git a/models/city.py b/models/city.py old mode 100644 new mode 100755 index 1e40ab0a9e3..8c46f0d2f4c --- a/models/city.py +++ b/models/city.py @@ -1,22 +1,24 @@ -import os -from sqlalchemy.orm import relationship -from sqlalchemy import Column, String, ForeignKey +#!/usr/bin/python +""" holds class City""" +import models from models.base_model import BaseModel, Base - - -# Get storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") +from os import getenv +import sqlalchemy +from sqlalchemy import Column, String, ForeignKey +from sqlalchemy.orm import relationship class City(BaseModel, Base): - """ The city class, contains state ID and name """ - - if storage_type == "db": - # Handle DB storage + """Representation of city """ + if models.storage_t == "db": __tablename__ = 'cities' state_id = Column(String(60), ForeignKey('states.id'), nullable=False) name = Column(String(128), nullable=False) + places = relationship("Place", backref="cities") else: - # Handle File storage state_id = "" name = "" + + def __init__(self, *args, **kwargs): + """initializes city""" + super().__init__(*args, **kwargs) 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 index 7290e1edd4e..b8e7d291e6f 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -1,101 +1,76 @@ #!/usr/bin/python3 """ -Module defines the `DBStorage` class +Contains the class DBStorage """ -import os -from models.base_model import Base -from models.state import State -from models.city import City -from models.user import User + +import models from models.amenity import Amenity +from models.base_model import BaseModel, Base +from models.city import City from models.place import Place from models.review import Review +from models.state import State +from models.user import User +from os import getenv +import sqlalchemy from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker, scoped_session +from sqlalchemy.orm import scoped_session, sessionmaker - -# get the environ variabes -user = os.getenv('HBNB_MYSQL_USER') -passwd = os.getenv('HBNB_MYSQL_PWD') -host = os.getenv('HBNB_MYSQL_HOST') -db = os.getenv('HBNB_MYSQL_DB') -hbnb_env = os.getenv('HBNB_ENV') +classes = {"Amenity": Amenity, "City": City, + "Place": Place, "Review": Review, "State": State, "User": User} class DBStorage: - """ Defines database engine """ - + """interaacts with the MySQL database""" __engine = None __session = None def __init__(self): - """ Initialize database instance """ - conn_string = "mysql+mysqldb://{}:{}@{}/{}".format( - user, passwd, host, db) - self.__engine = create_engine(conn_string, pool_pre_ping=True) - - if hbnb_env == "test": - # Drop all tables if test environment + """Instantiate a DBStorage object""" + HBNB_MYSQL_USER = getenv('HBNB_MYSQL_USER') + HBNB_MYSQL_PWD = getenv('HBNB_MYSQL_PWD') + HBNB_MYSQL_HOST = getenv('HBNB_MYSQL_HOST') + HBNB_MYSQL_DB = getenv('HBNB_MYSQL_DB') + HBNB_ENV = getenv('HBNB_ENV') + self.__engine = create_engine('mysql+mysqldb://{}:{}@{}/{}'. + format(HBNB_MYSQL_USER, + HBNB_MYSQL_PWD, + HBNB_MYSQL_HOST, + HBNB_MYSQL_DB)) + if HBNB_ENV == "test": Base.metadata.drop_all(self.__engine) def all(self, cls=None): - """ - Query all objects if cls is None or objects of specified class - """ - objs_dict = {} - if cls: - # Handle class - for obj in self.__session.query(cls).all(): - key = "{}.{}".format(type(obj).__name__, obj.id) - objs_dict[key] = obj - else: - # Handle all classes - cls_list = [State, City, User, Review, Place, Amenity] - for cls in cls_list: - for obj in self.__session.query(cls).all(): - key = "{}.{}".format(type(obj).__name__, obj.id) - objs_dict[key] = obj - return objs_dict + """query on the current database session""" + new_dict = {} + for clss in classes: + if cls is None or cls is classes[clss] or cls is clss: + objs = self.__session.query(classes[clss]).all() + for obj in objs: + key = obj.__class__.__name__ + '.' + obj.id + new_dict[key] = obj + return (new_dict) def new(self, obj): - """ Add object to the current database session """ + """add the object to the current database session""" self.__session.add(obj) def save(self): - """ Commit all changes to the current database session """ + """commit all changes of the current database session""" self.__session.commit() def delete(self, obj=None): - """ Delete from the current database session """ - if obj: + """delete from the current database session obj if not None""" + if obj is not None: self.__session.delete(obj) def reload(self): - """ Creates all tables and the current database session """ + """reloads data from the database""" Base.metadata.create_all(self.__engine) - session_factory = sessionmaker( - bind=self.__engine, expire_on_commit=False) - Session = scoped_session(session_factory) + sess_factory = sessionmaker(bind=self.__engine, expire_on_commit=False) + Session = scoped_session(sess_factory) self.__session = Session def close(self): - """ Removes the current session """ + """call remove() method on the private session attribute""" self.__session.remove() - - def get(self, cls, id): - """ Retrieve a specified object from storage """ - if cls is None or id is None: - return None - return self.__session.query(cls).get(id) - - def count(self, cls=None): - """ Counts the all objects or class objects in storage """ - class_list = [State, City, Amenity, Place, User, Review] - num_objs = 0 - if cls is None: - for clss in class_list: - num_objs += self.__session.query(clss).count() - else: - num_objs += self.__session.query(cls).count() - - return num_objs diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index cd315ee69c8..c8cb8c1764d 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -1,89 +1,70 @@ #!/usr/bin/python3 -"""This module defines a FileStorage class""" +""" +Contains the FileStorage class +""" + import json +from models.amenity import Amenity +from models.base_model import BaseModel +from models.city import City +from models.place import Place +from models.review import Review +from models.state import State +from models.user import User + +classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, + "Place": Place, "Review": Review, "State": State, "User": User} class FileStorage: - """This class manages storage of hbnb models in JSON format""" - __file_path = 'file.json' + """serializes instances to a JSON file & deserializes back to instances""" + + # string - path to the JSON file + __file_path = "file.json" + # dictionary - empty but will store all objects by .id __objects = {} def all(self, cls=None): - """ - Returns a list of objects of one type, if class is provided - or all objects if cls is None. - """ + """returns the dictionary __objects""" if cls is not None: - # retrieve all objects - cls_objects = {} - for key, obj in self.__objects.items(): - if isinstance(obj, cls): - # if cls is found add item to cls_objects - cls_objects[key] = obj - return cls_objects + new_dict = {} + for key, value in self.__objects.items(): + if cls == value.__class__ or cls == value.__class__.__name__: + new_dict[key] = value + return new_dict return self.__objects def new(self, obj): - """Adds new object to storage dictionary""" - self.all().update({obj.to_dict()['__class__'] + '.' + obj.id: obj}) + """sets in __objects the obj with key .id""" + if obj is not None: + key = obj.__class__.__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) + """serializes __objects to the JSON file (path: __file_path)""" + json_objects = {} + for key in self.__objects: + json_objects[key] = self.__objects[key].to_dict() + with open(self.__file_path, 'w') as f: + json.dump(json_objects, f) def reload(self): - """Loads storage dictionary from file""" - from models.base_model import BaseModel - from models.user import User - from models.place import Place - from models.state import State - from models.city import City - from models.amenity import Amenity - from models.review import Review - - classes = { - 'BaseModel': BaseModel, 'User': User, 'Place': Place, - 'State': State, 'City': City, 'Amenity': Amenity, - 'Review': Review - } + """deserializes the JSON file to __objects""" 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) - except FileNotFoundError: + with open(self.__file_path, 'r') as f: + jo = json.load(f) + for key in jo: + self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) + except: pass def delete(self, obj=None): - """Deletes an object from __objects""" - # handle: obj is None + """delete obj from __objects if it’s inside""" if obj is not None: - obj_key = "{}.{}".format(type(obj).__name__, obj.id) - if obj_key in self.__objects: - del self.__objects[obj_key] + key = obj.__class__.__name__ + '.' + obj.id + if key in self.__objects: + del self.__objects[key] def close(self): - """Calls reload method for deserializing the JSON file to objects""" + """call reload() method for deserializing the JSON file to objects""" self.reload() - - def get(self, cls, id): - """ Gets a specified object using its class and id """ - if cls is None or id is None: - return None - all_objs = storage.all(cls) - key = "{}.{}".format(cls.__name__, id) - - return all_objs.get(key, None) - - def count(self, cls=None): - """ Count all objects or objects of specified class """ - if cls is None: - return len(storage.all()) - else: - return len(storage.all(cls)) diff --git a/models/place.py b/models/place.py old mode 100644 new mode 100755 index 68c907c418a..0aed5a744e6 --- a/models/place.py +++ b/models/place.py @@ -1,50 +1,43 @@ -#!/usr/bin/python3 -""" Place Module for HBNB project """ -import os +#!/usr/bin/python +""" holds class Place""" +import models from models.base_model import BaseModel, Base -from sqlalchemy import Integer, Column, String, Float, ForeignKey, Table +from os import getenv +import sqlalchemy +from sqlalchemy import Column, String, Integer, Float, ForeignKey, Table from sqlalchemy.orm import relationship - -# Get storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") - -# Define association table for many-to-many relationship -if storage_type == "db": - 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) - ) +if models.storage_t == 'db': + place_amenity = Table('place_amenity', Base.metadata, + Column('place_id', String(60), + ForeignKey('places.id', onupdate='CASCADE', + ondelete='CASCADE'), + primary_key=True), + Column('amenity_id', String(60), + ForeignKey('amenities.id', onupdate='CASCADE', + ondelete='CASCADE'), + primary_key=True)) class Place(BaseModel, Base): - """ A place to stay """ - if storage_type == "db": - # Handle database storage + """Representation of Place """ + if models.storage_t == 'db': __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) - number_rooms = Column(Integer, default=0, nullable=False) - number_bathrooms = Column(Integer, default=0, nullable=False) - max_guest = Column(Integer, default=0, nullable=False) - price_by_night = Column(Integer, default=0, nullable=False) + 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 = [] - - # relationships - reviews = relationship('Review', backref='place', - cascade='all, delete-orphan') - amenities = relationship('Amenity', secondary=place_amenity, - back_populates='place_amenities', + reviews = relationship("Review", backref="place") + amenities = relationship("Amenity", secondary="place_amenity", + backref="place_amenities", viewonly=False) else: - # Handle file storage city_id = "" user_id = "" name = "" @@ -57,28 +50,29 @@ class Place(BaseModel, Base): longitude = 0.0 amenity_ids = [] - # public getter method -- get reviews + def __init__(self, *args, **kwargs): + """initializes Place""" + super().__init__(*args, **kwargs) + + if models.storage_t != 'db': @property def reviews(self): - """ file storage getter for relationship btwn places and review """ - from models import storage - return [review for review in storage.all(Review).values() - if review.place_id == self.id] + """getter attribute returns the list of Review instances""" + from models.review import Review + review_list = [] + all_reviews = models.storage.all(Review) + for review in all_reviews.values(): + if review.place_id == self.id: + review_list.append(review) + return review_list - # public methods -- get/set amenities @property def amenities(self): - """ Returns a list of amenities instances """ - from models import storage - objs_dict = storage.all() - place_amenities_list = [] - for obj in objs_dict.values(): - if obj.id in self.amenity_ids and isinstance(obj, Amenity): - place_amenities_list.append(obj) - return place_amenities_list - - @amenities.setter - def amenities(self, obj): - """ Sets the amenity id """ - if isinstance(obj, Amenity) and obj.id not in self.amenity_ids: - self.amenity_ids.append(obj.id) + """getter attribute returns the list of Amenity instances""" + from models.amenity import Amenity + amenity_list = [] + all_amenities = models.storage.all(Amenity) + for amenity in all_amenities.values(): + if amenity.place_id == self.id: + amenity_list.append(amenity) + return amenity_list diff --git a/models/review.py b/models/review.py old mode 100644 new mode 100755 index 1914f8bce89..cd6c1d1ff98 --- a/models/review.py +++ b/models/review.py @@ -1,26 +1,24 @@ -#!/usr/bin/python3 -""" Review module for the HBNB project """ -import os +#!/usr/bin/python +""" holds class Review""" +import models from models.base_model import BaseModel, Base +from os import getenv +import sqlalchemy from sqlalchemy import Column, String, ForeignKey -from sqlalchemy.orm import relationship - - -# Get storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") class Review(BaseModel, Base): - """ Review classto store review information """ - - if storage_type == "db": - # Handle database storage + """Representation of Review """ + if models.storage_t == 'db': __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) + text = Column(String(1024), nullable=False) else: - # Handle file storage place_id = "" user_id = "" text = "" + + def __init__(self, *args, **kwargs): + """initializes Review""" + super().__init__(*args, **kwargs) diff --git a/models/state.py b/models/state.py old mode 100644 new mode 100755 index 11608e8a547..ca5c8961d80 --- a/models/state.py +++ b/models/state.py @@ -1,42 +1,34 @@ #!/usr/bin/python3 -""" State Module for HBNB project """ -import os -from sqlalchemy import Column, String -from sqlalchemy.orm import relationship -from models.city import City +""" holds class State""" +import models from models.base_model import BaseModel, Base - - -# Get storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") +from models.city import City +from os import getenv +import sqlalchemy +from sqlalchemy import Column, String, ForeignKey +from sqlalchemy.orm import relationship class State(BaseModel, Base): - """ State class """ - - if storage_type == "db": - # Handle DB storage + """Representation of state """ + if models.storage_t == "db": __tablename__ = 'states' name = Column(String(128), nullable=False) - cities = relationship( - 'City', backref='state', cascade='all, delete, delete-orphan') + cities = relationship("City", backref="state") else: - # Handle File storage name = "" + def __init__(self, *args, **kwargs): + """initializes state""" + super().__init__(*args, **kwargs) + + if models.storage_t != "db": @property def cities(self): - """ - Returns: list of `City` instances of the current `State` object - """ - from models import storage - # empty cities list - state_cities = [] - - # get list of cities - cities = storage.all(City) - for city_obj in cities.values(): - if city_obj.state_id == self.id: - state_cities.append(str(city_obj)) - - return state_cities + """getter for list of city instances related to the state""" + city_list = [] + all_cities = models.storage.all(City) + for city in all_cities.values(): + if city.state_id == self.id: + city_list.append(city) + return city_list diff --git a/models/user.py b/models/user.py old mode 100644 new mode 100755 index d0c1eff30d7..36b1b70b994 --- a/models/user.py +++ b/models/user.py @@ -1,32 +1,29 @@ #!/usr/bin/python3 -"""This module defines a class User""" -import os +""" holds class User""" +import models from models.base_model import BaseModel, Base +from os import getenv +import sqlalchemy from sqlalchemy import Column, String from sqlalchemy.orm import relationship -# Get storage type -storage_type = os.getenv("HBNB_TYPE_STORAGE") - - class User(BaseModel, Base): - """This class defines a user by various attributes""" - - if storage_type == "db": - # Handle database storage + """Representation of a user """ + if models.storage_t == 'db': __tablename__ = 'users' email = Column(String(128), nullable=False) password = Column(String(128), nullable=False) first_name = Column(String(128), nullable=True) last_name = Column(String(128), nullable=True) - places = relationship("Place", backref="user", - cascade="all, delete-orphan") - reviews = relationship("Review", backref="user", - cascade="all, delete-orphan") + places = relationship("Place", backref="user") + reviews = relationship("Review", backref="user") else: - # Handle file storage email = "" password = "" first_name = "" last_name = "" + + def __init__(self, *args, **kwargs): + """initializes user""" + super().__init__(*args, **kwargs) From d774becfb14b8e32b440cdee20dc779b53e56866 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 20:18:43 +0000 Subject: [PATCH 72/80] tests --- .../test_engine/test_db_storage.py | 60 ++----- .../test_engine/test_file_storage.py | 166 ++++++------------ 2 files changed, 68 insertions(+), 158 deletions(-) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 64b1d8a5d68..766e625b5af 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -1,10 +1,11 @@ #!/usr/bin/python3 """ -Module defines a test for DBStorage class +Contains the TestDBStorageDocs and TestDBStorage classes """ + from datetime import datetime import inspect -import models import storage_type +import models from models.engine import db_storage from models.amenity import Amenity from models.base_model import BaseModel @@ -67,54 +68,21 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -@unittest.skipIf(storage_type != 'db', "Testing database storage only") -class TestDBStorage(unittest.TestCase): - """Test the DBStorage class""" - +class TestFileStorage(unittest.TestCase): + """Test the FileStorage class""" + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_returns_dict(self): - """Test that all method: returns a dictionaty""" + """Test that all returns a dictionaty""" + self.assertIs(type(models.storage.all()), dict) + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_all_no_class(self): - """ - Test that all method: - returns all rows when no class is passed - """ + """Test that all returns all rows when no class is passed""" + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_new(self): - """Test that new method: adds an object to the database""" + """test that new adds an object to the database""" + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): - """ - Test that save method: - properly saves objects to the database - """ - - def test_delete(self): - """ - Test that delete method: - deletes object from current database session - """ - - def test_reload(self): - """ - Test that reload method: - creates all table in the current database session - """ - - def test_close(self): - """ - Test that the close method: - closes the current database session - """ - - def test_get(self): - """ - Test get(): - gets an object of specified id from storage - """ - - def test_count(self): - """ - Test count(): - counts the number of all objects or class objects - """ + """Test that save properly saves objects to file.json""" diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index bfe981ed659..1474a34fec0 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -1,21 +1,28 @@ #!/usr/bin/python3 -""" Module for testing file storage""" -import unittest -from models.base_model import BaseModel -from models import storage, storage_type -import os +""" +Contains the TestFileStorageDocs classes +""" + from datetime import datetime import inspect import models from models.engine import file_storage +from models.amenity import Amenity +from models.base_model import BaseModel +from models.city import City +from models.place import Place +from models.review import Review +from models.state import State +from models.user import User import json import os import pep8 import unittest FileStorage = file_storage.FileStorage +classes = {"Amenity": Amenity, "BaseModel": BaseModel, "City": City, + "Place": Place, "Review": Review, "State": State, "User": User} -@unittest.skipIf(storage_type == 'db', "Test FileStorage class only") class TestFileStorageDocs(unittest.TestCase): """Tests to check the documentation and style of FileStorage class""" @classmethod @@ -61,113 +68,48 @@ def test_fs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -@unittest.skipIf(storage_type == 'db', "Testing FileStorage only") -class test_fileStorage(unittest.TestCase): - """ Class to test the file storage method """ - - def setUp(self): - """ Set up test environment """ - del_list = [] - for key in storage._FileStorage__objects.keys(): - del_list.append(key) - for key in del_list: - del storage._FileStorage__objects[key] - - def tearDown(self): - """ Remove storage file at end of tests """ - try: - os.remove('file.json') - except FileNotFoundError: - pass - - def test_obj_list_empty(self): - """ __objects is initially empty """ - self.assertEqual(len(storage.all()), 0) +class TestFileStorage(unittest.TestCase): + """Test the FileStorage class""" + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_all_returns_dict(self): + """Test that all returns the FileStorage.__objects attr""" + storage = FileStorage() + new_dict = storage.all() + self.assertEqual(type(new_dict), dict) + self.assertIs(new_dict, storage._FileStorage__objects) + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_new(self): - """ New object is correctly added to __objects """ - new_obj = BaseModel() - storage.new(new_obj) - for obj in storage.all().values(): - temp = obj - self.assertTrue(temp is obj) - - def test_all(self): - """ __objects is properly returned """ - new = BaseModel() - temp = storage.all() - self.assertIsInstance(temp, dict) - - def test_base_model_instantiation(self): - """ File is not created on BaseModel save """ - new = BaseModel() - self.assertFalse(os.path.exists('file.json')) - - def test_empty(self): - """ Data is saved to file """ - new = BaseModel() - thing = new.to_dict() - new.save() - new2 = BaseModel(**thing) - self.assertNotEqual(os.path.getsize('file.json'), 0) - + """test that new adds an object to the FileStorage.__objects attr""" + storage = FileStorage() + save = FileStorage._FileStorage__objects + FileStorage._FileStorage__objects = {} + test_dict = {} + for key, value in classes.items(): + with self.subTest(key=key, value=value): + instance = value() + instance_key = instance.__class__.__name__ + "." + instance.id + storage.new(instance) + test_dict[instance_key] = instance + self.assertEqual(test_dict, storage._FileStorage__objects) + FileStorage._FileStorage__objects = save + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") def test_save(self): - """ FileStorage save method """ - new = BaseModel() + """Test that save properly saves objects to file.json""" + storage = FileStorage() + new_dict = {} + for key, value in classes.items(): + instance = value() + instance_key = instance.__class__.__name__ + "." + instance.id + new_dict[instance_key] = instance + save = FileStorage._FileStorage__objects + FileStorage._FileStorage__objects = new_dict storage.save() - self.assertTrue(os.path.exists('file.json')) - - def test_reload(self): - """ Storage file is successfully loaded to __objects """ - new_obj = BaseModel() - storage.new(new_obj) - storage.save() - storage.reload() - for obj in storage.all().values(): - loaded = obj - self.assertEqual(new_obj.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() - - def test_reload_from_nonexistent(self): - """ Nothing happens if file does not exist """ - self.assertEqual(storage.reload(), None) - - def test_base_model_save(self): - """ BaseModel save method calls storage save """ - new = BaseModel() - new.save() - self.assertTrue(os.path.exists('file.json')) - - def test_type_path(self): - """ Confirm __file_path is string """ - self.assertEqual(type(storage._FileStorage__file_path), str) - - def test_type_objects(self): - """ Confirm __objects is a dict """ - self.assertEqual(type(storage.all()), dict) - - def test_key_format(self): - """ Key is properly formatted """ - obj = BaseModel() - storage.new(obj) - _id = obj.to_dict()['id'] - for key in storage.all().keys(): - temp = key - self.assertEqual(temp, 'BaseModel' + '.' + _id) - - def test_storage_var_created(self): - """ FileStorage object storage created """ - from models.engine.file_storage import FileStorage - self.assertEqual(type(storage), FileStorage) - - def test_get(self): - """ Test if get method returns a specified object from storage """ - - def test_count(self): - """ Test if count methods return the number of objects """ + FileStorage._FileStorage__objects = save + for key, value in new_dict.items(): + new_dict[key] = value.to_dict() + string = json.dumps(new_dict) + with open("file.json", "r") as f: + js = f.read() + self.assertEqual(json.loads(string), json.loads(js)) From c04500a7903b70740501d9e9336a3487813f62bd Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 20:25:03 +0000 Subject: [PATCH 73/80] new methods: get() and count() --- models/engine/db_storage.py | 16 ++++++++++++++++ models/engine/file_storage.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index b8e7d291e6f..ac3f6006b2d 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -74,3 +74,19 @@ def reload(self): def close(self): """call remove() method on the private session attribute""" self.__session.remove() + + def get(self, cls, id): + """ Retrieves a specified object from storage """ + if cls is None or id is None: + return None + return self.__session.query(cls).get(id) + + def count(self, cls=None): + """ Counts the number of objects in storage """ + num_objs = 0 + if cls is None: + for cls in classes.values(): + num_objs += self.__session.query(cls).count() + else: + num_objs += self.__session.query(cls).count() + return num_objs diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index c8cb8c1764d..2d13699b436 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -68,3 +68,19 @@ def delete(self, obj=None): def close(self): """call reload() method for deserializing the JSON file to objects""" self.reload() + + def get(self, cls, id): + """ Gets a specified object using its class and id """ + if cls is None or id is None: + return None + all_objs = storage.all(cls) + key = "{}.{}".format(cls.__name__, id) + + return all_objs.get(key, None) + + def count(self, cls=None): + """ Count all objects or objects of specified class """ + if cls is None: + return len(storage.all()) + else: + return len(storage.all(cls)) From 5ccdecaee47072dfc78af6e8e8c017203a5444b4 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 20:28:56 +0000 Subject: [PATCH 74/80] new methods: get() and count() --- models/engine/file_storage.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index 2d13699b436..a99197da444 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -2,7 +2,6 @@ """ Contains the FileStorage class """ - import json from models.amenity import Amenity from models.base_model import BaseModel @@ -55,7 +54,7 @@ def reload(self): jo = json.load(f) for key in jo: self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) - except: + except FileNotFoundError: pass def delete(self, obj=None): From a5272cbf56a52b7e2848f042db0410247a0cdccf Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 21:32:16 +0000 Subject: [PATCH 75/80] test: Amenity class --- tests/test_models/test_amenity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_models/test_amenity.py b/tests/test_models/test_amenity.py index 66b0bb69bc2..09ce5b06305 100755 --- a/tests/test_models/test_amenity.py +++ b/tests/test_models/test_amenity.py @@ -79,7 +79,6 @@ def test_name_attr(self): def test_to_dict_creates_dict(self): """test to_dict method creates a dictionary with proper attrs""" am = Amenity() - print(am.__dict__) new_d = am.to_dict() self.assertEqual(type(new_d), dict) self.assertFalse("_sa_instance_state" in new_d) From 7f35a1c8dede420bc1cb405e7299b5db598f4341 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 21:49:05 +0000 Subject: [PATCH 76/80] test: get() and count() --- tests/test_models/test_engine/test_db_storage.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..c33a29f7823 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -86,3 +86,11 @@ def test_new(self): @unittest.skipIf(models.storage_t != 'db', "not testing db storage") def test_save(self): """Test that save properly saves objects to file.json""" + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_get(self): + """ Test if get method returns a specified object from storage """ + + @unittest.skipIf(models.storage_t != 'db', "not testing db storage") + def test_count(self): + """ Test if count methods return the number of objects """ From 52dfa9ee87bc611b469538504dd4c8a995fb24f2 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 21:56:00 +0000 Subject: [PATCH 77/80] test: get() and count() --- tests/test_models/test_engine/test_file_storage.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 1474a34fec0..294adc24d70 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -113,3 +113,11 @@ def test_save(self): with open("file.json", "r") as f: js = f.read() self.assertEqual(json.loads(string), json.loads(js)) + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_get(self): + """ Test if get method returns a specified object from storage """ + + @unittest.skipIf(models.storage_t == 'db', "not testing file storage") + def test_count(self): + """ Test if count methods return the number of objects """ From 81b701573497a554391289fd72a3f8ff5ff3e951 Mon Sep 17 00:00:00 2001 From: deantosh Date: Mon, 5 Aug 2024 22:07:45 +0000 Subject: [PATCH 78/80] get() method updated --- models/engine/file_storage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index a99197da444..f5a7b87cc46 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -72,7 +72,7 @@ def get(self, cls, id): """ Gets a specified object using its class and id """ if cls is None or id is None: return None - all_objs = storage.all(cls) + all_objs = self.all(cls) key = "{}.{}".format(cls.__name__, id) return all_objs.get(key, None) @@ -80,6 +80,6 @@ def get(self, cls, id): def count(self, cls=None): """ Count all objects or objects of specified class """ if cls is None: - return len(storage.all()) + return len(self.all()) else: - return len(storage.all(cls)) + return len(self.all(cls)) From 795df80eb94ac62d9eb8752461dbf47d2269bb82 Mon Sep 17 00:00:00 2001 From: deantosh Date: Tue, 6 Aug 2024 10:39:05 +0000 Subject: [PATCH 79/80] view: State objects -- routes updated --- api/v1/views/states.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/v1/views/states.py b/api/v1/views/states.py index 67e444b9c83..8a33e86050d 100755 --- a/api/v1/views/states.py +++ b/api/v1/views/states.py @@ -10,14 +10,14 @@ from werkzeug.exceptions import BadRequest -@app_views.route('/states/', methods=['GET']) +@app_views.route('/states', methods=['GET'], strict_slashes=False) def get_all_states(): """ Retrievs a list of all State objects """ states_list = [state.to_dict() for state in storage.all(State).values()] return jsonify(states_list) -@app_views.route('/states/', methods=['GET']) +@app_views.route('/states/', methods=['GET'], strict_slashes=False) def get_state(state_id): """ Retrieves a State object of a specified state_id """ state_obj = storage.get(State, state_id) @@ -27,7 +27,7 @@ def get_state(state_id): return jsonify(state_obj.to_dict()) -@app_views.route('states/', methods=['DELETE']) +@app_views.route('states/', methods=['DELETE'], strict_slashes=False) def delete_state(state_id): """ Deletes state object of a specified state_id """ state_obj = storage.get(State, state_id) @@ -39,7 +39,7 @@ def delete_state(state_id): return jsonify({}), 200 -@app_views.route('/states/', methods=['POST']) +@app_views.route('/states', methods=['POST'], strict_slashes=False) def create_state(): """ Creates a state object """ @@ -57,7 +57,7 @@ def create_state(): return jsonify(state_obj.to_dict()), 201 -@app_views.route('states/', methods=['PUT']) +@app_views.route('states/', methods=['PUT'], strict_slashes=False) def update_state(state_id): """ Updates state """ state_obj = storage.get(State, state_id) From 3aa8459c011b37585b981f61d23ee48e2835637a Mon Sep 17 00:00:00 2001 From: deantosh Date: Thu, 8 Aug 2024 19:35:10 +0000 Subject: [PATCH 80/80] view: Place objects --- api/v1/views/places.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api/v1/views/places.py b/api/v1/views/places.py index fb059ffa029..0e648cd3716 100755 --- a/api/v1/views/places.py +++ b/api/v1/views/places.py @@ -22,7 +22,7 @@ def get_all_places(city_id): return jsonify(place_list) -@app_views.route('/places/', methods=['GET']) +@app_views.route('/places/', methods=['GET'], strict_slashes=False) def get_place(place_id): """ Retrieves Place object by place_id """ place_obj = storage.get(Place, place_id) @@ -32,7 +32,8 @@ def get_place(place_id): return jsonify(place_obj.to_dict()) -@app_views.route('/places/', methods=['DELETE']) +@app_views.route('/places/', methods=['DELETE'], + strict_slashes=False) def delete_place(place_id): """ Deletes Place object by place_id """ place_obj = storage.get(Place, place_id) @@ -68,7 +69,7 @@ def create_place(city_id): return jsonify(place_obj.to_dict()), 201 -@app_views.route('/places/', methods=['PUT']) +@app_views.route('/places/', methods=['PUT'], strict_slashes=False) def update_place(place_id): """ Updates Place object by place_id """ place_obj = storage.get(Place, place_id)