diff --git a/flatisfy/database/__init__.py b/flatisfy/database/__init__.py index d6780fd..82e6c33 100644 --- a/flatisfy/database/__init__.py +++ b/flatisfy/database/__init__.py @@ -13,7 +13,12 @@ from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker from sqlalchemy.exc import OperationalError, SQLAlchemyError +# Import models +import flatisfy.models.postal_code # noqa: F401 +import flatisfy.models.public_transport # noqa: F401 import flatisfy.models.flat # noqa: F401 +import flatisfy.models.constraint # noqa: F401 + from flatisfy.database.base import BASE from flatisfy.database.whooshalchemy import IndexService diff --git a/flatisfy/models/constraint.py b/flatisfy/models/constraint.py index 75e1df6..d421f3d 100644 --- a/flatisfy/models/constraint.py +++ b/flatisfy/models/constraint.py @@ -7,8 +7,9 @@ from __future__ import absolute_import, print_function, unicode_literals import logging from sqlalchemy import ( - Column, Float, Integer, String + Column, Float, ForeignKey, Integer, String, Table ) +from sqlalchemy.orm import relationship from sqlalchemy_utils.types.json import JSONType from sqlalchemy_utils.types.scalar_list import ScalarListType @@ -42,6 +43,13 @@ class PostTypes(enum.Enum): SHARING = 2 +association_table = Table( + 'constraint_postal_codes_association', BASE.metadata, + Column('constraint_id', Integer, ForeignKey('constraints.id')), + Column('postal_code_id', Integer, ForeignKey('postal_codes.id')) +) + + class Constraint(BASE): """ SQLAlchemy ORM model to store a search constraint. @@ -52,18 +60,28 @@ class Constraint(BASE): name = Column(String) type = Column(EnumListType(PostTypes, int)) house_types = Column(EnumListType(HouseTypes, int)) - postal_codes = Column(ScalarListType()) # TODO + # TODO: What happens when one delete a postal code? + postal_codes = relationship("PostalCode", secondary=association_table) + area_min = Column(Float, default=None) # in m^2 area_max = Column(Float, default=None) # in m^2 + cost_min = Column(Float, default=None) # in currency unit cost_max = Column(Float, default=None) # in currency unit + rooms_min = Column(Integer, default=None) rooms_max = Column(Integer, default=None) + bedrooms_min = Column(Integer, default=None) bedrooms_max = Column(Integer, default=None) + minimum_nb_photos = Column(Integer, default=None) description_should_contain = Column(ScalarListType()) # list of terms - time_to = Column(JSONType) # TODO + + # Dict mapping names to {"gps": [lat, lng], "time": (min, max) } + # ``min`` and ``max`` are in seconds and can be ``null``. + # TODO: Use an additional time_to_places table? + time_to = Column(JSONType) def __repr__(self): return "" % (self.id, self.name) diff --git a/flatisfy/models/flat.py b/flatisfy/models/flat.py index 6fece03..1627444 100644 --- a/flatisfy/models/flat.py +++ b/flatisfy/models/flat.py @@ -8,12 +8,11 @@ from __future__ import absolute_import, print_function, unicode_literals import logging import enum -import arrow - from sqlalchemy import ( - Column, Enum, Float, SmallInteger, String, Text, inspect + Column, Enum, Float, ForeignKey, Integer, SmallInteger, String, Table, + Text, inspect ) -from sqlalchemy.orm import validates +from sqlalchemy.orm import relationship, validates from sqlalchemy_utils.types.arrow import ArrowType from sqlalchemy_utils.types.json import JSONType from sqlalchemy_utils.types.scalar_list import ScalarListType @@ -55,6 +54,14 @@ AUTOMATED_STATUSES = [ FlatStatus.ignored ] +stations_association_table = Table( + 'stations_flats_association', BASE.metadata, + Column( + 'public_transport_id', Integer, ForeignKey('public_transports.id') + ), + Column('flat_id', Integer, ForeignKey('flats.id')) +) + class Flat(BASE): """ @@ -72,7 +79,7 @@ class Flat(BASE): cost = Column(Float) currency = Column(String) utilities = Column(Enum(FlatUtilities), default=FlatUtilities.unknown) - date = Column(ArrowDate) + date = Column(ArrowType) details = Column(JSONType) location = Column(String) phone = Column(String) @@ -86,18 +93,27 @@ class Flat(BASE): notes = Column(Text) notation = Column(SmallInteger, default=0) - # Flatisfy data - # TODO: Should be in another table with relationships - flatisfy_stations = Column(JSONType) - flatisfy_postal_code = Column(String) + # Flatisfy found stations + # TODO: What happens when one deletes a station? + flatisfy_stations = relationship("PublicTransport", + secondary=stations_association_table) + # Flatisfy found postal code + # TODO: What happens when one deletes a postal code? + flatisfy_postal_code_id = Column(Integer, ForeignKey('postal_codes.id')) + flatisfy_postal_code = relationship("PostalCode") + # Computed time to flatisfy_time_to = Column(JSONType) - flatisfy_constraint = Column(String) + # Constraint relationship + # TODO: What happens when one deletes a constraint? + # TODO: A flat could match multiple constraints + flatisfy_constraint_id = Column(Integer, ForeignKey('constraints.id')) + flatisfy_constraint = relationship("Constraint") # Status status = Column(Enum(FlatStatus), default=FlatStatus.new) # Date for visit - visit_date = Column(ArrowDate) + visit_date = Column(ArrowType) @validates('utilities') def validate_utilities(self, _, utilities):