diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/api/__pycache__/__init__.cpython-38.pyc b/api/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000000..c4cb3ac134c Binary files /dev/null and b/api/__pycache__/__init__.cpython-38.pyc differ diff --git a/api/v1/__init__.py b/api/v1/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/api/v1/__pycache__/__init__.cpython-38.pyc b/api/v1/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000000..64e4a10d3e2 Binary files /dev/null and b/api/v1/__pycache__/__init__.cpython-38.pyc differ diff --git a/api/v1/__pycache__/app.cpython-38.pyc b/api/v1/__pycache__/app.cpython-38.pyc new file mode 100644 index 00000000000..0e578fd06d4 Binary files /dev/null and b/api/v1/__pycache__/app.cpython-38.pyc differ diff --git a/api/v1/app.py b/api/v1/app.py new file mode 100644 index 00000000000..357bfa3c099 --- /dev/null +++ b/api/v1/app.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +'''Contains a Flask web application API. +''' +import os +from flask import Flask, jsonify +from flask_cors import CORS + +from models import storage +from api.v1.views import app_views + + +app = Flask(__name__) +'''The Flask web application instance.''' +app_host = os.getenv('HBNB_API_HOST', '0.0.0.0') +app_port = int(os.getenv('HBNB_API_PORT', '5000')) +app.url_map.strict_slashes = False +app.register_blueprint(app_views) +CORS(app, resources={'/*': {'origins': app_host}}) + + +@app.teardown_appcontext +def teardown_flask(exception): + '''The Flask app/request context end event listener.''' + # print(exception) + storage.close() + + +@app.errorhandler(404) +def error_404(error): + '''Handles the 404 HTTP error code.''' + return jsonify(error='Not found'), 404 + + +@app.errorhandler(400) +def error_400(error): + '''Handles the 400 HTTP error code.''' + msg = 'Bad request' + if isinstance(error, Exception) and hasattr(error, 'description'): + msg = error.description + return jsonify(error=msg), 400 + + +if __name__ == '__main__': + app_host = os.getenv('HBNB_API_HOST', '0.0.0.0') + app_port = int(os.getenv('HBNB_API_PORT', '5000')) + app.run( + host=app_host, + port=app_port, + threaded=True + ) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py new file mode 100644 index 00000000000..aaa1fd261f0 --- /dev/null +++ b/api/v1/views/__init__.py @@ -0,0 +1,17 @@ +#!/usr/bin/python3 +'''Contains the blueprint for the API.''' +from flask import Blueprint + + +app_views = Blueprint('app_views', __name__, url_prefix='/api/v1') +'''The blueprint for the AirBnB clone API.''' + + +from api.v1.views.amenities import * +from api.v1.views.cities import * +from api.v1.views.index import * +from api.v1.views.places_amenities import * +from api.v1.views.places import * +from api.v1.views.places_reviews import * +from api.v1.views.states import * +from api.v1.views.users import * diff --git a/api/v1/views/__pycache__/__init__.cpython-38.pyc b/api/v1/views/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000000..60e84f6229e Binary files /dev/null and b/api/v1/views/__pycache__/__init__.cpython-38.pyc differ diff --git a/api/v1/views/__pycache__/index.cpython-38.pyc b/api/v1/views/__pycache__/index.cpython-38.pyc new file mode 100644 index 00000000000..27f54f61e2c Binary files /dev/null and b/api/v1/views/__pycache__/index.cpython-38.pyc differ diff --git a/api/v1/views/amenities.py b/api/v1/views/amenities.py new file mode 100644 index 00000000000..6714adeae7a --- /dev/null +++ b/api/v1/views/amenities.py @@ -0,0 +1,86 @@ +#!/usr/bin/python3 +'''Contains the amenities view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, MethodNotAllowed, BadRequest + +from api.v1.views import app_views +from models import storage +from models.amenity import Amenity + + +ALLOWED_METHODS = ['GET', 'DELETE', 'POST', 'PUT'] +'''Methods allowed for the amenities endpoint.''' + + +@app_views.route('/amenities', methods=ALLOWED_METHODS) +@app_views.route('/amenities/', methods=ALLOWED_METHODS) +def handle_amenities(amenity_id=None): + '''The method handler for the amenities endpoint. + ''' + handlers = { + 'GET': get_amenities, + 'DELETE': remove_amenity, + 'POST': add_amenity, + 'PUT': update_amenity, + } + if request.method in handlers: + return handlers[request.method](amenity_id) + else: + raise MethodNotAllowed(list(handlers.keys())) + + +def get_amenities(amenity_id=None): + '''Gets the amenity with the given id or all amenities. + ''' + all_amenities = storage.all(Amenity).values() + if amenity_id: + res = list(filter(lambda x: x.id == amenity_id, all_amenities)) + if res: + return jsonify(res[0].to_dict()) + raise NotFound() + all_amenities = list(map(lambda x: x.to_dict(), all_amenities)) + return jsonify(all_amenities) + + +def remove_amenity(amenity_id=None): + '''Removes a amenity with the given id. + ''' + all_amenities = storage.all(Amenity).values() + res = list(filter(lambda x: x.id == amenity_id, all_amenities)) + if res: + storage.delete(res[0]) + storage.save() + return jsonify({}), 200 + raise NotFound() + + +def add_amenity(amenity_id=None): + '''Adds a new amenity. + ''' + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + if 'name' not in data: + raise BadRequest(description='Missing name') + new_amenity = Amenity(**data) + new_amenity.save() + return jsonify(new_amenity.to_dict()), 201 + + +def update_amenity(amenity_id=None): + '''Updates the amenity with the given id. + ''' + xkeys = ('id', 'created_at', 'updated_at') + all_amenities = storage.all(Amenity).values() + res = list(filter(lambda x: x.id == amenity_id, all_amenities)) + if res: + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + old_amenity = res[0] + for key, value in data.items(): + if key not in xkeys: + setattr(old_amenity, key, value) + old_amenity.save() + return jsonify(old_amenity.to_dict()), 200 + raise NotFound() diff --git a/api/v1/views/cities.py b/api/v1/views/cities.py new file mode 100644 index 00000000000..ff75c32e7e5 --- /dev/null +++ b/api/v1/views/cities.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +'''Contains the cities view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, MethodNotAllowed, BadRequest + +from api.v1.views import app_views +from models import storage, storage_t +from models.city import City +from models.place import Place +from models.review import Review +from models.state import State + + +@app_views.route('/states//cities', methods=['GET', 'POST']) +@app_views.route('/cities/', methods=['GET', 'DELETE', 'PUT']) +def handle_cities(state_id=None, city_id=None): + '''The method handler for the cities endpoint. + ''' + handlers = { + 'GET': get_cities, + 'DELETE': remove_city, + 'POST': add_city, + 'PUT': update_city, + } + if request.method in handlers: + return handlers[request.method](state_id, city_id) + else: + raise MethodNotAllowed(list(handlers.keys())) + + +def get_cities(state_id=None, city_id=None): + '''Gets the city with the given id or all cities in + the state with the given id. + ''' + if state_id: + state = storage.get(State, state_id) + if state: + cities = list(map(lambda x: x.to_dict(), state.cities)) + return jsonify(cities) + elif city_id: + city = storage.get(City, city_id) + if city: + return jsonify(city.to_dict()) + raise NotFound() + + +def remove_city(state_id=None, city_id=None): + '''Removes a city with the given id. + ''' + if city_id: + city = storage.get(City, city_id) + if city: + storage.delete(city) + if storage_t != "db": + for place in storage.all(Place).values(): + if place.city_id == city_id: + for review in storage.all(Review).values(): + if review.place_id == place.id: + storage.delete(review) + storage.delete(place) + storage.save() + return jsonify({}), 200 + raise NotFound() + + +def add_city(state_id=None, city_id=None): + '''Adds a new city. + ''' + state = storage.get(State, state_id) + if not state: + raise NotFound() + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + if 'name' not in data: + raise BadRequest(description='Missing name') + data['state_id'] = state_id + city = City(**data) + city.save() + return jsonify(city.to_dict()), 201 + + +def update_city(state_id=None, city_id=None): + '''Updates the city with the given id. + ''' + xkeys = ('id', 'state_id', 'created_at', 'updated_at') + if city_id: + city = storage.get(City, city_id) + if city: + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + for key, value in data.items(): + if key not in xkeys: + setattr(city, key, value) + city.save() + return jsonify(city.to_dict()), 200 + raise NotFound() diff --git a/api/v1/views/index.py b/api/v1/views/index.py new file mode 100644 index 00000000000..ff219605366 --- /dev/null +++ b/api/v1/views/index.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +'''Contains the index view for the API.''' +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.place import Place +from models.review import Review +from models.state import State +from models.user import User + + +@app_views.route('/status') +def get_status(): + '''Gets the status of the API. + ''' + return jsonify(status='OK') + + +@app_views.route('/stats') +def get_stats(): + '''Gets the number of objects for each type. + ''' + objects = { + 'amenities': Amenity, + 'cities': City, + 'places': Place, + 'reviews': Review, + 'states': State, + 'users': User + } + for key, value in objects.items(): + objects[key] = storage.count(value) + return jsonify(objects) diff --git a/api/v1/views/places.py b/api/v1/views/places.py new file mode 100644 index 00000000000..71126290566 --- /dev/null +++ b/api/v1/views/places.py @@ -0,0 +1,199 @@ +#!/usr/bin/python3 +'''Contains the places view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, MethodNotAllowed, BadRequest + +from api.v1.views import app_views +from models import storage, storage_t +from models.amenity import Amenity +from models.city import City +from models.place import Place +from models.state import State +from models.user import User + + +@app_views.route('/cities//places', methods=['GET', 'POST']) +@app_views.route('/places/', methods=['GET', 'DELETE', 'PUT']) +def handle_places(city_id=None, place_id=None): + '''The method handler for the places endpoint. + ''' + handlers = { + 'GET': get_places, + 'DELETE': remove_place, + 'POST': add_place, + 'PUT': update_place + } + if request.method in handlers: + return handlers[request.method](city_id, place_id) + else: + raise MethodNotAllowed(list(handlers.keys())) + + +def get_places(city_id=None, place_id=None): + '''Gets the place with the given id or all places in + the city with the given id. + ''' + if city_id: + city = storage.get(City, city_id) + if city: + all_places = [] + if storage_t == 'db': + all_places = list(city.places) + else: + all_places = list(filter( + lambda x: x.city_id == city_id, + storage.all(Place).values() + )) + places = list(map(lambda x: x.to_dict(), all_places)) + return jsonify(places) + elif place_id: + place = storage.get(Place, place_id) + if place: + return jsonify(place.to_dict()) + raise NotFound() + + +def remove_place(city_id=None, place_id=None): + '''Removes a place with the given id. + ''' + if place_id: + place = storage.get(Place, place_id) + if place: + storage.delete(place) + storage.save() + return jsonify({}), 200 + raise NotFound() + + +def add_place(city_id=None, place_id=None): + '''Adds a new place. + ''' + city = storage.get(City, city_id) + if not city: + raise NotFound() + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + if 'user_id' not in data: + raise BadRequest(description='Missing user_id') + user = storage.get(User, data['user_id']) + if not user: + raise NotFound() + if 'name' not in data: + raise BadRequest(description='Missing name') + data['city_id'] = city_id + new_place = Place(**data) + new_place.save() + return jsonify(new_place.to_dict()), 201 + + +def update_place(city_id=None, place_id=None): + '''Updates the place with the given id. + ''' + xkeys = ('id', 'user_id', 'city_id', 'created_at', 'updated_at') + place = storage.get(Place, place_id) + if place: + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + for key, value in data.items(): + if key not in xkeys: + setattr(place, key, value) + place.save() + return jsonify(place.to_dict()), 200 + raise NotFound() + + +@app_views.route('/places_search', methods=['POST']) +def find_places(): + '''Finds places based on a list of State, City, or Amenity ids. + ''' + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + all_places = storage.all(Place).values() + places = [] + places_id = [] + keys_status = ( + all([ + 'states' in data and type(data['states']) is list, + 'states' in data and len(data['states']) + ]), + all([ + 'cities' in data and type(data['cities']) is list, + 'cities' in data and len(data['cities']) + ]), + all([ + 'amenities' in data and type(data['amenities']) is list, + 'amenities' in data and len(data['amenities']) + ]) + ) + if keys_status[0]: + for state_id in data['states']: + if not state_id: + continue + state = storage.get(State, state_id) + if not state: + continue + for city in state.cities: + new_places = [] + if storage_t == 'db': + new_places = list( + filter(lambda x: x.id not in places_id, city.places) + ) + else: + new_places = [] + for place in all_places: + if place.id in places_id: + continue + if place.city_id == city.id: + new_places.append(place) + places.extend(new_places) + places_id.extend(list(map(lambda x: x.id, new_places))) + if keys_status[1]: + for city_id in data['cities']: + if not city_id: + continue + city = storage.get(City, city_id) + if city: + new_places = [] + if storage_t == 'db': + new_places = list( + filter(lambda x: x.id not in places_id, city.places) + ) + else: + new_places = [] + for place in all_places: + if place.id in places_id: + continue + if place.city_id == city.id: + new_places.append(place) + places.extend(new_places) + del places_id + if all([not keys_status[0], not keys_status[1]]) or not data: + places = all_places + if keys_status[2]: + amenity_ids = [] + for amenity_id in data['amenities']: + if not amenity_id: + continue + amenity = storage.get(Amenity, amenity_id) + if amenity and amenity.id not in amenity_ids: + amenity_ids.append(amenity.id) + del_indices = [] + for place in places: + place_amenities_ids = list(map(lambda x: x.id, place.amenities)) + if not amenity_ids: + continue + for amenity_id in amenity_ids: + if amenity_id not in place_amenities_ids: + del_indices.append(place.id) + break + places = list(filter(lambda x: x.id not in del_indices, places)) + result = [] + for place in places: + obj = place.to_dict() + if 'amenities' in obj: + del obj['amenities'] + result.append(obj) + return jsonify(result) diff --git a/api/v1/views/places_amenities.py b/api/v1/views/places_amenities.py new file mode 100644 index 00000000000..dad1389ad11 --- /dev/null +++ b/api/v1/views/places_amenities.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +'''Contains the places_amenities view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, MethodNotAllowed + +from api.v1.views import app_views +from models import storage, storage_t +from models.amenity import Amenity +from models.place import Place + + +@app_views.route('/places//amenities', methods=['GET']) +@app_views.route( + '/places//amenities/', + methods=['DELETE', 'POST'] +) +def handle_places_amenities(place_id=None, amenity_id=None): + '''The method handler for the places endpoint. + ''' + handlers = { + 'GET': get_place_amenities, + 'DELETE': remove_place_amenity, + 'POST': add_place_amenity + } + if request.method in handlers: + return handlers[request.method](place_id, amenity_id) + else: + raise MethodNotAllowed(list(handlers.keys())) + + +def get_place_amenities(place_id=None, amenity_id=None): + '''Gets the amenities of a place with the given id. + ''' + if place_id: + place = storage.get(Place, place_id) + if place: + all_amenities = list(map(lambda x: x.to_dict(), place.amenities)) + return jsonify(all_amenities) + raise NotFound() + + +def remove_place_amenity(place_id=None, amenity_id=None): + '''Removes an amenity with a given id from a place with a given id. + ''' + if place_id and amenity_id: + place = storage.get(Place, place_id) + if not place: + raise NotFound() + amenity = storage.get(Amenity, amenity_id) + if not amenity: + raise NotFound() + place_amenity_link = list( + filter(lambda x: x.id == amenity_id, place.amenities) + ) + if not place_amenity_link: + raise NotFound() + if storage_t == 'db': + amenity_place_link = list( + filter(lambda x: x.id == place_id, amenity.place_amenities) + ) + if not amenity_place_link: + raise NotFound() + place.amenities.remove(amenity) + place.save() + return jsonify({}), 200 + else: + amenity_idx = place.amenity_ids.index(amenity_id) + place.amenity_ids.pop(amenity_idx) + place.save() + return jsonify({}), 200 + raise NotFound() + + +def add_place_amenity(place_id=None, amenity_id=None): + '''Adds an amenity with a given id to a place with a given id. + ''' + if place_id and amenity_id: + place = storage.get(Place, place_id) + if not place: + raise NotFound() + amenity = storage.get(Amenity, amenity_id) + if not amenity: + raise NotFound() + if storage_t == 'db': + place_amenity_link = list( + filter(lambda x: x.id == amenity_id, place.amenities) + ) + amenity_place_link = list( + filter(lambda x: x.id == place_id, amenity.place_amenities) + ) + if amenity_place_link and place_amenity_link: + res = amenity.to_dict() + del res['place_amenities'] + return jsonify(res), 200 + place.amenities.append(amenity) + place.save() + res = amenity.to_dict() + del res['place_amenities'] + return jsonify(res), 201 + else: + if amenity_id in place.amenity_ids: + return jsonify(amenity.to_dict()), 200 + place.amenity_ids.push(amenity_id) + place.save() + return jsonify(amenity.to_dict()), 201 + raise NotFound() diff --git a/api/v1/views/places_reviews.py b/api/v1/views/places_reviews.py new file mode 100644 index 00000000000..1962ad75b18 --- /dev/null +++ b/api/v1/views/places_reviews.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +'''Contains the places_reviews view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, MethodNotAllowed, BadRequest + +from api.v1.views import app_views +from models import storage +from models.place import Place +from models.review import Review +from models.user import User + + +@app_views.route('/places//reviews', methods=['GET', 'POST']) +@app_views.route('/reviews/', methods=['GET', 'DELETE', 'PUT']) +def handle_reviews(place_id=None, review_id=None): + '''The method handler for the reviews endpoint. + ''' + handlers = { + 'GET': get_reviews, + 'DELETE': remove_review, + 'POST': add_review, + 'PUT': update_review + } + if request.method in handlers: + return handlers[request.method](place_id, review_id) + else: + raise MethodNotAllowed(list(handlers.keys())) + + +def get_reviews(place_id=None, review_id=None): + '''Gets the review with the given id or all reviews in + the place with the given id. + ''' + if place_id: + place = storage.get(Place, place_id) + if place: + reviews = [] + for review in place.reviews: + reviews.append(review.to_dict()) + return jsonify(reviews) + elif review_id: + review = storage.get(Review, review_id) + if review: + return jsonify(review.to_dict()) + raise NotFound() + + +def remove_review(place_id=None, review_id=None): + '''Removes a review with the given id. + ''' + review = storage.get(Review, review_id) + if review: + storage.delete(review) + storage.save() + return jsonify({}), 200 + raise NotFound() + + +def add_review(place_id=None, review_id=None): + '''Adds a new review. + ''' + place = storage.get(Place, place_id) + if not place: + raise NotFound() + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + if 'user_id' not in data: + raise BadRequest(description='Missing user_id') + user = storage.get(User, data['user_id']) + if not user: + raise NotFound() + if 'text' not in data: + raise BadRequest(description='Missing text') + data['place_id'] = place_id + new_review = Review(**data) + new_review.save() + return jsonify(new_review.to_dict()), 201 + + +def update_review(place_id=None, review_id=None): + '''Updates the review with the given id. + ''' + xkeys = ('id', 'user_id', 'place_id', 'created_at', 'updated_at') + if review_id: + review = storage.get(Review, review_id) + if review: + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + for key, value in data.items(): + if key not in xkeys: + setattr(review, key, value) + review.save() + return jsonify(review.to_dict()), 200 + raise NotFound() diff --git a/api/v1/views/states.py b/api/v1/views/states.py new file mode 100644 index 00000000000..2307ff158d5 --- /dev/null +++ b/api/v1/views/states.py @@ -0,0 +1,86 @@ +#!/usr/bin/python3 +'''Contains the states view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, MethodNotAllowed, BadRequest + +from api.v1.views import app_views +from models import storage +from models.state import State + + +ALLOWED_METHODS = ['GET', 'DELETE', 'POST', 'PUT'] +'''Methods allowed for the states endpoint.''' + + +@app_views.route('/states', methods=ALLOWED_METHODS) +@app_views.route('/states/', methods=ALLOWED_METHODS) +def handle_states(state_id=None): + '''The method handler for the states endpoint. + ''' + handlers = { + 'GET': get_states, + 'DELETE': remove_state, + 'POST': add_state, + 'PUT': update_state, + } + if request.method in handlers: + return handlers[request.method](state_id) + else: + raise MethodNotAllowed(list(handlers.keys())) + + +def get_states(state_id=None): + '''Gets the state with the given id or all states. + ''' + all_states = storage.all(State).values() + if state_id: + res = list(filter(lambda x: x.id == state_id, all_states)) + if res: + return jsonify(res[0].to_dict()) + raise NotFound() + all_states = list(map(lambda x: x.to_dict(), all_states)) + return jsonify(all_states) + + +def remove_state(state_id=None): + '''Removes a state with the given id. + ''' + all_states = storage.all(State).values() + res = list(filter(lambda x: x.id == state_id, all_states)) + if res: + storage.delete(res[0]) + storage.save() + return jsonify({}), 200 + raise NotFound() + + +def add_state(state_id=None): + '''Adds a new state. + ''' + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + if 'name' not in data: + raise BadRequest(description='Missing name') + new_state = State(**data) + new_state.save() + return jsonify(new_state.to_dict()), 201 + + +def update_state(state_id=None): + '''Updates the state with the given id. + ''' + xkeys = ('id', 'created_at', 'updated_at') + all_states = storage.all(State).values() + res = list(filter(lambda x: x.id == state_id, all_states)) + if res: + data = request.get_json() + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + old_state = res[0] + for key, value in data.items(): + if key not in xkeys: + setattr(old_state, key, value) + old_state.save() + return jsonify(old_state.to_dict()), 200 + raise NotFound() diff --git a/api/v1/views/users.py b/api/v1/views/users.py new file mode 100644 index 00000000000..ec68ce94636 --- /dev/null +++ b/api/v1/views/users.py @@ -0,0 +1,99 @@ +#!/usr/bin/python3 +'''Contains the users view for the API.''' +from flask import jsonify, request +from werkzeug.exceptions import NotFound, BadRequest + +from api.v1.views import app_views +from models import storage +from models.user import User + + +@app_views.route('/users', methods=['GET']) +@app_views.route('/users/', methods=['GET']) +def get_users(user_id=None): + '''Gets the user with the given id or all users. + ''' + if user_id: + user = storage.get(User, user_id) + if user: + obj = user.to_dict() + if 'places' in obj: + del obj['places'] + if 'reviews' in obj: + del obj['reviews'] + return jsonify(obj) + raise NotFound() + all_users = storage.all(User).values() + users = [] + for user in all_users: + obj = user.to_dict() + if 'places' in obj: + del obj['places'] + if 'reviews' in obj: + del obj['reviews'] + users.append(obj) + return jsonify(users) + + +@app_views.route('/users/', methods=['DELETE']) +def remove_user(user_id): + '''Removes a user with the given id. + ''' + user = storage.get(User, user_id) + if user: + storage.delete(user) + storage.save() + return jsonify({}), 200 + raise NotFound() + + +@app_views.route('/users', methods=['POST']) +def add_user(): + '''Adds a new user. + ''' + data = {} + try: + data = request.get_json() + except Exception: + data = None + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + if 'email' not in data: + raise BadRequest(description='Missing email') + if 'password' not in data: + raise BadRequest(description='Missing password') + user = User(**data) + user.save() + obj = user.to_dict() + if 'places' in obj: + del obj['places'] + if 'reviews' in obj: + del obj['reviews'] + return jsonify(obj), 201 + + +@app_views.route('/users/', methods=['PUT']) +def update_user(user_id): + '''Updates the user with the given id. + ''' + xkeys = ('id', 'email', 'created_at', 'updated_at') + user = storage.get(User, user_id) + if user: + data = {} + try: + data = request.get_json() + except Exception: + data = None + if type(data) is not dict: + raise BadRequest(description='Not a JSON') + for key, value in data.items(): + if key not in xkeys: + setattr(user, key, value) + user.save() + obj = user.to_dict() + if 'places' in obj: + del obj['places'] + if 'reviews' in obj: + del obj['reviews'] + return jsonify(obj), 200 + raise NotFound() diff --git a/console.py b/console.py index 4798f9ac76b..e133363e235 100755 --- a/console.py +++ b/console.py @@ -46,10 +46,10 @@ def _key_value_parser(self, args): else: try: value = int(value) - except: + except Exception: try: value = float(value) - except: + except Exception: continue new_dict[key] = value return new_dict @@ -140,12 +140,12 @@ def do_update(self, arg): if args[2] in integers: try: args[3] = int(args[3]) - except: + except Exception: args[3] = 0 elif args[2] in floats: try: args[3] = float(args[3]) - except: + except Exception: args[3] = 0.0 setattr(models.storage.all()[k], args[2], args[3]) models.storage.all()[k].save() @@ -160,5 +160,6 @@ def do_update(self, arg): else: print("** class doesn't exist **") + if __name__ == '__main__': HBNBCommand().cmdloop() diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index b8e7d291e6f..121de953931 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -51,6 +51,17 @@ def all(self, cls=None): new_dict[key] = obj return (new_dict) + def get(self, cls, id): + """retrieves an object of a class with id""" + obj = None + if cls is not None and issubclass(cls, BaseModel): + obj = self.__session.query(cls).filter(cls.id == id).first() + return obj + + def count(self, cls=None): + """retrieves the number of objects of a class or all (if cls==None)""" + return len(self.all(cls)) + def new(self, obj): """add the object to the current database session""" self.__session.add(obj) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index c8cb8c1764d..e8152c827e8 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -34,6 +34,23 @@ def all(self, cls=None): return new_dict return self.__objects + def get(self, cls, id): + """retrieves an object of a class with id""" + if cls is not None: + res = list( + filter( + lambda x: type(x) is cls and x.id == id, + self.__objects.values() + ) + ) + if res: + return res[0] + return None + + def count(self, cls=None): + """retrieves the number of objects of a class or all (if cls==None)""" + return len(self.all(cls)) + def new(self, obj): """sets in __objects the obj with key .id""" if obj is not None: @@ -55,7 +72,7 @@ def reload(self): jo = json.load(f) for key in jo: self.__objects[key] = classes[jo[key]["__class__"]](**jo[key]) - except: + except Exception: pass def delete(self, obj=None): diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..152aaeb90e3 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -68,21 +68,56 @@ def test_dbs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) -class TestFileStorage(unittest.TestCase): +@unittest.skipIf(models.storage_t != 'db', "not testing db storage") +class TestDBStorage(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""" + + def test_get(self): + """test that get returns an object of a given class by id.""" + storage = models.storage + obj = State(name='Michigan') + obj.save() + self.assertEqual(obj.id, storage.get(State, obj.id).id) + self.assertEqual(obj.name, storage.get(State, obj.id).name) + self.assertIsNot(obj, storage.get(State, obj.id + 'op')) + self.assertIsNone(storage.get(State, obj.id + 'op')) + self.assertIsNone(storage.get(State, 45)) + self.assertIsNone(storage.get(None, obj.id)) + self.assertIsNone(storage.get(int, obj.id)) + with self.assertRaises(TypeError): + storage.get(State, obj.id, 'op') + with self.assertRaises(TypeError): + storage.get(State) + with self.assertRaises(TypeError): + storage.get() + + def test_count(self): + """test that count returns the number of objects of a given class.""" + storage = models.storage + self.assertIs(type(storage.count()), int) + self.assertIs(type(storage.count(None)), int) + self.assertIs(type(storage.count(int)), int) + self.assertIs(type(storage.count(State)), int) + self.assertEqual(storage.count(), storage.count(None)) + State(name='Lagos').save() + self.assertGreater(storage.count(State), 0) + self.assertEqual(storage.count(), storage.count(None)) + a = storage.count(State) + State(name='Enugu').save() + self.assertGreater(storage.count(State), a) + Amenity(name='Free WiFi').save() + self.assertGreater(storage.count(), storage.count(State)) + with self.assertRaises(TypeError): + storage.count(State, 'op') diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 1474a34fec0..e2df4ff8fc9 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -68,9 +68,9 @@ def test_fs_func_docstrings(self): "{:s} method needs a docstring".format(func[0])) +@unittest.skipIf(models.storage_t == 'db', "not testing file storage") 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() @@ -78,7 +78,6 @@ def test_all_returns_dict(self): 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() @@ -94,7 +93,6 @@ def test_new(self): 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() @@ -113,3 +111,41 @@ def test_save(self): with open("file.json", "r") as f: js = f.read() self.assertEqual(json.loads(string), json.loads(js)) + + def test_get(self): + """test that get returns an object of a given class by id.""" + storage = models.storage + obj = State(name='Michigan') + obj.save() + self.assertEqual(obj.id, storage.get(State, obj.id).id) + self.assertEqual(obj.name, storage.get(State, obj.id).name) + self.assertIsNot(obj, storage.get(State, obj.id + 'op')) + self.assertIsNone(storage.get(State, obj.id + 'op')) + self.assertIsNone(storage.get(State, 45)) + self.assertIsNone(storage.get(None, obj.id)) + self.assertIsNone(storage.get(int, obj.id)) + with self.assertRaises(TypeError): + storage.get(State, obj.id, 'op') + with self.assertRaises(TypeError): + storage.get(State) + with self.assertRaises(TypeError): + storage.get() + + def test_count(self): + """test that count returns the number of objects of a given class.""" + storage = models.storage + self.assertIs(type(storage.count()), int) + self.assertIs(type(storage.count(None)), int) + self.assertIs(type(storage.count(int)), int) + self.assertIs(type(storage.count(State)), int) + self.assertEqual(storage.count(), storage.count(None)) + State(name='Lagos').save() + self.assertGreater(storage.count(State), 0) + self.assertEqual(storage.count(), storage.count(None)) + a = storage.count(State) + State(name='Enugu').save() + self.assertGreater(storage.count(State), a) + Amenity(name='Free WiFi').save() + self.assertGreater(storage.count(), storage.count(State)) + with self.assertRaises(TypeError): + storage.count(State, 'op')