from flask import Flask, redirect, jsonify, abort, request, url_for, make_response from where.model import with_session, Point, Category, Field from where.model.field_types import FieldType from where.validation import PointSchema, CategorySchema, FieldSchema app = Flask(__name__) # Endpoints: @app.route('/') def index(): return """

W H E R E

This is the WHERE project.

Click here to nuke the database and make it all be test data. """ @app.route('/test_data') @with_session def test_data(session): session.query(Point).delete() session.query(Field).delete() session.query(Category).delete() # Water Fountain, the class. wf = Category() wf.name = "Water Fountain" wf.icon = "https://karel.pw/water.png" session.add(wf) session.commit() # Building bd = Category() bd.name = "Building" bd.icon = "https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/basket-building-news-photo-1572015168.jpg?resize=980:*" session.add(bd) session.commit() # Radius (Really the simplest metric we can have for building size) rd = Field() rd.name = "Radius" rd.slug = "radius" rd.type = FieldType.FLOAT rd.category_id = bd.id session.add(rd) session.commit() # coldness cd = Field() cd.name = "Coldness" cd.slug = "coldness" cd.type = FieldType.RATING cd.category_id = wf.id # filler fl = Field() fl.slug = "bottle_filler" fl.name = "Has Bottle Filler" fl.type = FieldType.BOOLEAN fl.category_id = wf.id session.add(cd) session.add(fl) session.commit() # The johnson center jc = Point() jc.category = bd jc.name = "Johnson Center" jc.lat = 38 jc.lon = -77 jc.parent = None jc.attributes = { "radius": { "value": 2.0 } } session.add(jc) # A water fountain inside the JC fn = Point() fn.name = None fn.lat = 38.829791 fn.lon = -77.307043 # fn.category_id = wf.id fn.category = wf fn.parent = jc fn.attributes = { "coldness": { "num_reviews": 32, "average_rating": 0.5 }, "bottle_filler": { "value": True } } session.add(fn) session.commit() return redirect('/') @app.route('/category/') @with_session def get_category(session, id): return get_resource(session, Category, id) @app.route('/category//children') @with_session def get_category_children(session, id): data = dict(request.args) data['parent_id'] = id return search_resource(session, Point, data) @app.route('/point', methods=['GET']) @with_session def search_point(session): return search_resource(session, Point, dict(request.args)) @app.route('/point', methods=['POST']) def create_point(session): data = request.get_json() data['category'] = session.query(Category).get(data.pop('category_id')) return create_resource(session, Point, data, 'get_point') @app.route('/point/', methods=['GET']) @with_session def get_point(session, id): return get_resource(session, Point, id) @app.route('/point/', methods=['DELETE']) @with_session def del_point(session, id): return delete_resource(session, Point, id) @app.route('/point/', methods=['PUT']) @with_session def edit_point(session, id): return edit_resource(session, Point, id, request.get_json()) @app.route('/point//children', methods=['GET']) @with_session def get_point_children(session, id): data = dict(request.args) data['parent_id'] = id return search_resource(session, Point, data) # Helper functions: # TODO: Add helper functions for data validation def create_resource(session, model_cls, data, get_function): ''' Create the resource specified by the given model class and initialized with the data dict, returning an appropriate JSON response. :param session: The sqlalchemy session :param model_cls: The class of the model for this resource :param data: The initial data for this resource stored as a dictionary :param get_function: The name of the view function (as a string) that gets a single instance of this resource. This is used for the response Location header. :return: a Flask Response object ''' resource = model_cls(**data) session.add(resource) session.commit() response = make_response(jsonify(resource.as_json()), 201) response.headers['Location'] = url_for(get_function, id=resource.id) return response def get_resource(session, model_cls, id): ''' Get a single resource of the specified model class by its ID. :param session: The sqlalchemy session :param model_cls: The class of the model for this resource :param id: The id of this resource :return: a Flask Response object ''' resource = session.query(model_cls).get(id) return make_response(jsonify(resource.as_json()), 200) def edit_resource(session, model_cls, id, data): ''' Modify the resource of the specified model class and id with the data from data. Does not perform data validation. :param session: The sqlalchemy session :param model_cls: The class of the model for this resource :param id: The id of this resource :param data: The new data for this resource stored as a dictionary :return: a Flask Response object ''' resource = session.query(model_cls).get(id) for attr in data: setattr(resource, attr, data[attr]) session.commit() return make_response(jsonify(resource.as_json()), 200) def delete_resource(session, model_cls, id): ''' Delete the resource of the specified model class and id and return the appropriate response. :param session: The sqlalchemy session :param model_cls: The class of the model for this resource :param id: The id of this resource :return: a Flask Response object ''' resource = session.query(model_cls).get(id) session.delete(resource) session.commit() return make_response('', 204) def search_resource(session, model_cls, data): ''' Search the database for a list of instances of the specified model class that have the attributes given in data and return the appropriate JSON response. Does not perform validation on search parameters. :param session: The sqlalchemy session :param model_cls: The class of the model for this resource :param data: A dictionary containing search parameters :return: a Flask Response object ''' query = session.query(model_cls).filter_by(**data) results = list(map(lambda m: m.as_json(), query.limit(100).all())) response = make_response(jsonify(results), 200) return response if __name__ == '__main__': app.run()