diff --git a/app/controllers/space.py b/app/controllers/space.py new file mode 100644 index 0000000000000000000000000000000000000000..a8e42cbaf319e7ba4ff2ace0b9d54c542ca11aab --- /dev/null +++ b/app/controllers/space.py @@ -0,0 +1,166 @@ +"""Space controllers""" +from flask import g +from werkzeug.exceptions import BadRequest +from app.lib.database import db +from .base import RestController +from ..forms.space import SpaceForm, ApartmentForm, CommonAreaForm, ServiceAreaForm, SpaceAreaForm +from ..models.space import Space, Apartment, CommonArea, ServiceArea, SpaceArea + + +class SpaceController(RestController): + """Space controller.""" + Model = Space + + def get_form(self, filter_data): + """ + Get the WTForms form for the model. + """ + return SpaceForm + + def post(self, data, filter_data): + """ + Create space in an area if area_id and area_type are specified + """ + dictionary = super(SpaceController, self).post(data, filter_data) + if 'area' in data: + space_area_data = { + 'space_id': dictionary['id'], + 'area_id': data['area']['area_id'], + 'area_type': data['area']['area_type'], + } + SpaceAreaController().post(space_area_data, []) + return dictionary + + def get(self, id_, filter_data): + dictionary = super(SpaceController, self).get(id_, filter_data) + if 'verbose' in filter_data: + # Load the space area information for space + space_area_data = db.session.query(SpaceArea)\ + .filter( + SpaceArea.space_id == id_, + )\ + .first() + dictionary['area'] = { + 'area_id': space_area_data['area_id'], + 'area_type': space_area_data['area_type'], + } + return dictionary + + +def add_verbose(area, area_type): + space_areas = db.session.query(SpaceArea)\ + .filter( + SpaceArea.area_id == area['id'], + SpaceArea.area_type == area_type, + )\ + .all() + space_ids = (space_area.space_id for space_area in space_areas) + # Load spaces for area + spaces = db.session.query(Space)\ + .filter(Space.id.in_(space_ids))\ + .all() + area['spaces'] = [space.get_dictionary() for space in spaces] + return area + + +def model_with_spaces(model, building_id): + q = db.session.query(model, SpaceArea, Space) \ + .join(SpaceArea, SpaceArea.area_id == model.id) \ + .join(Space, Space.id == SpaceArea.space_id) \ + .filter( + model.building_id == building_id, + SpaceArea.area_type == model.__table__.name + ) + query = q.all() + + results = {} + for m in query: + model_result = m[0] + space_result = m[2] + + if model_result.id not in results: + results[model_result.id] = model_result.get_dictionary() + results[model_result.id]['spaces'] = [] + + results[model_result.id]['spaces'].append(space_result.get_dictionary()) + + return list(results.values()) + + +class ApartmentController(RestController): + """Apartment Controller""" + Model = Apartment + filters = { + 'building_id': lambda d: Apartment.building_id == d['building_id'], + } + + def get_form(self, filter_data): + return ApartmentForm + + def get(self, id_, filter_data): + """Get area and if verbose on get spaces for area""" + dictionary = super(ApartmentController, self).get(id_, filter_data) + if 'verbose' in filter_data: + add_verbose(dictionary, 'apartment') + return dictionary + + def index(self, filter_data): + """Get area and if verbose on get spaces for area""" + if 'verbose' in filter_data: + return model_with_spaces(self.Model, filter_data['building_id']) + return super(ApartmentController, self).index(filter_data) + + +class CommonAreaController(RestController): + """Common Area Controller""" + Model = CommonArea + filters = { + 'building_id': lambda d: CommonArea.building_id == d['building_id'], + } + + def get_form(self, filter_data): + return CommonAreaForm + + def get(self, id_, filter_data): + """Get area and if verbose on get spaces for area""" + dictionary = super(CommonAreaController, self).get(id_, filter_data) + if 'verbose' in filter_data: + add_verbose(dictionary, 'common_area') + return dictionary + + def index(self, filter_data): + if 'verbose' in filter_data: + return model_with_spaces(self.Model, filter_data['building_id']) + return super(CommonAreaController, self).index(filter_data) + + +class ServiceAreaController(RestController): + """Service Area Controller""" + Model = ServiceArea + filters = { + 'building_id': lambda d: ServiceArea.building_id == d['building_id'], + } + + def get_form(self, filter_data): + return ServiceAreaForm + + def get(self, id_, filter_data): + """Get area and if verbose on get spaces for area""" + dictionary = super(ServiceAreaController, self).get(id_, filter_data) + if 'verbose' in filter_data: + add_verbose(dictionary, 'service_area') + return dictionary + + def index(self, filter_data): + """Get area and if verbose on get spaces for area""" + if 'verbose' in filter_data: + return model_with_spaces(self.Model, filter_data['building_id']) + return super(ServiceAreaController, self).index(filter_data) + + +class SpaceAreaController(RestController): + """Space Function Controller""" + Model = SpaceArea + + def get_form(self, filter_data): + return SpaceAreaForm diff --git a/app/forms/space.py b/app/forms/space.py new file mode 100644 index 0000000000000000000000000000000000000000..fb5cceb4725e0345e804c890f8c37607ce87f4c7 --- /dev/null +++ b/app/forms/space.py @@ -0,0 +1,81 @@ +"""Forms for space related models""" +import wtforms as wtf +from .base import Form, UserForm + +class NullableIntegerField(wtf.IntegerField): + """ + An IntegerField where the field can be null if the input data is an empty + string. + """ + + def process_formdata(self, valuelist): + if valuelist: + if valuelist[0] == '' or valuelist[0] is None: + self.data = None + else: + try: + self.data = int(valuelist[0]) + except ValueError: + self.data = None + raise ValueError(self.gettext('Not a valid integer value')) + + +class SpaceForm(UserForm, Form): + """Space Form""" + building_id = wtf.IntegerField( + validators=[wtf.validators.Optional()] + ) + conditioned = wtf.BooleanField( + validators=[wtf.validators.Optional()] + ) + orientation = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + description = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + floor = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + +class ApartmentForm(Form): + """Apartment Form""" + building_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + number = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + number_of_bedrooms = NullableIntegerField( + validators=[wtf.validators.Optional()] + ) + tenant_name = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + tenant_heating_issue = wtf.StringField( + validators=[wtf.validators.Optional()] + ) + +class ServiceAreaForm(Form): + """Service Area Form""" + building_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + +class CommonAreaForm(Form): + """Common Area Form""" + building_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + +class SpaceAreaForm(Form): + """Space Area Form""" + space_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + area_id = wtf.IntegerField( + validators=[wtf.validators.Required()] + ) + area_type = wtf.StringField( + validators=[wtf.validators.Required()] + ) diff --git a/app/models/space.py b/app/models/space.py new file mode 100644 index 0000000000000000000000000000000000000000..d91f0b1d89e2b86a3c44cc8b30d4d6a925856dd1 --- /dev/null +++ b/app/models/space.py @@ -0,0 +1,55 @@ +"""Models associated to spaces""" +from .base import Model, BaseModel, User +from ..lib.database import db + +class SpacesSchema(): + """Class to point to spaces schema""" + __table_args__ = {'schema': 'spaces'} + +class Space(SpacesSchema, User, Model, db.Model): + """Model for a Space""" + + id = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('space_id_seq'::regclass)")) + building_id = db.Column(db.Integer) # FIXME: Foregin key to building + conditioned = db.Column(db.Boolean) + orientation = db.Column(db.String(64)) + description = db.Column(db.String(64)) + floor = db.Column(db.String(64)) + time_created = db.Column(db.DateTime, server_default=db.text("timezone('utc'::text, now())")) + time_modified = db.Column(db.DateTime) + +class Apartment(SpacesSchema, User, Model, db.Model): + """Model for an Apartment""" + + id = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('apartment_id_seq'::regclass)")) + building_id = db.Column(db.Integer) # FIXME: Foregin key to building + number = db.Column(db.String(64)) + number_of_bedrooms = db.Column(db.Integer) + tenant_name = db.Column(db.String(64)) + tenant_heating_issue = db.Column(db.Text) + time_created = db.Column(db.DateTime, server_default=db.text("timezone('utc'::text, now())")) + time_modified = db.Column(db.DateTime) + + +class ServiceArea(SpacesSchema, User, Model, db.Model): + """Model for a Service Area""" + + id = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('service_area_id_seq'::regclass)")) + building_id = db.Column(db.Integer) # FIXME: Foregin key to building + time_created = db.Column(db.DateTime, server_default=db.text("timezone('utc'::text, now())")) + time_modified = db.Column(db.DateTime) + +class CommonArea(SpacesSchema, User, Model, db.Model): + """Model for a Common Area""" + + id = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('common_area_id_seq'::regclass)")) + building_id = db.Column(db.Integer) # FIXME: Foregin key to building + time_created = db.Column(db.DateTime, server_default=db.text("timezone('utc'::text, now())")) + time_modified = db.Column(db.DateTime) + +class SpaceArea(SpacesSchema, BaseModel, db.Model): + """Model for associating spaces to space metadata""" + + space_id = db.Column(db.Integer, primary_key=True) # FIXME: Foregin key to spaces + area_id = db.Column(db.Integer, primary_key=True) # FIXME: Foregin key to space child + area_type = db.Column(db.String(64), primary_key=True) diff --git a/app/views/__init__.py b/app/views/__init__.py index d4b406013f69d5a859160c8102febc020ef48005..f9b8bc1c6fd9369aa7f438ea8967369513e21bf1 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -5,7 +5,8 @@ from . import (bgroup, turk_hit, unapproved_point, unapproved_window_door, - unapproved_building_dimensions) + unapproved_building_dimensions, + space) def register(app): @@ -18,6 +19,10 @@ def register(app): bgroup.BGroupView.register(app) bgroup.BuildingBGroupView.register(app) bgroup_user_group.BGroupUserGroupView.register(app) + space.SpaceView.register(app) + space.ApartmentView.register(app) + space.CommonAreaView.register(app) + space.ServiceAreaView.register(app) turk_hit.TurkHitView.register(app) unapproved_point.UnapprovedPointView.register(app) unapproved_window_door.UnapprovedWindowDoorView.register(app) diff --git a/app/views/space.py b/app/views/space.py new file mode 100644 index 0000000000000000000000000000000000000000..c655926f798cfc74a1d3616d67f5789a406bca4d --- /dev/null +++ b/app/views/space.py @@ -0,0 +1,33 @@ +"""Space related views""" +from flask import request +from flask.ext.classy import route +from werkzeug.exceptions import MethodNotAllowed +from ..views.base import UnprotectedRestView +from ..controllers.space import SpaceController, ApartmentController, CommonAreaController, ServiceAreaController + + +class SpaceView(UnprotectedRestView): + """View for space""" + def get_controller(self): + return SpaceController() + +class ApartmentView(UnprotectedRestView): + """View for Apartment""" + def get_controller(self): + return ApartmentController() + +class CommonAreaView(UnprotectedRestView): + """View for Common Area""" + def get_controller(self): + return CommonAreaController() + + def put(self, id_): + return MethodNotAllowed() + +class ServiceAreaView(UnprotectedRestView): + """View for Service Area""" + def get_controller(self): + return ServiceAreaController() + + def put(self, id_): + return MethodNotAllowed()