2020-12-29 14:37:37 +01:00
|
|
|
"""Provide the ORM's `City` model."""
|
2020-08-09 03:45:19 +02:00
|
|
|
|
|
|
|
|
import sqlalchemy as sa
|
|
|
|
|
from sqlalchemy import orm
|
|
|
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
|
|
|
|
|
|
from urban_meal_delivery.db import meta
|
2021-01-02 16:29:50 +01:00
|
|
|
from urban_meal_delivery.db import utils
|
2020-08-09 03:45:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class City(meta.Base):
|
2020-12-29 14:37:37 +01:00
|
|
|
"""A city where the UDP operates in."""
|
2020-08-09 03:45:19 +02:00
|
|
|
|
|
|
|
|
__tablename__ = 'cities'
|
|
|
|
|
|
|
|
|
|
# Generic columns
|
|
|
|
|
id = sa.Column( # noqa:WPS125
|
|
|
|
|
sa.SmallInteger, primary_key=True, autoincrement=False,
|
|
|
|
|
)
|
|
|
|
|
name = sa.Column(sa.Unicode(length=10), nullable=False)
|
|
|
|
|
kml = sa.Column(sa.UnicodeText, nullable=False)
|
|
|
|
|
|
|
|
|
|
# Google Maps related columns
|
2021-01-05 22:37:12 +01:00
|
|
|
center_latitude = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
|
|
|
|
center_longitude = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
|
|
|
|
northeast_latitude = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
|
|
|
|
northeast_longitude = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
|
|
|
|
southwest_latitude = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
|
|
|
|
southwest_longitude = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
2020-08-09 03:45:19 +02:00
|
|
|
initial_zoom = sa.Column(sa.SmallInteger, nullable=False)
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
addresses = orm.relationship('Address', back_populates='city')
|
2021-01-03 19:33:36 +01:00
|
|
|
grids = orm.relationship('Grid', back_populates='city')
|
2020-08-09 03:45:19 +02:00
|
|
|
|
2021-01-04 20:33:10 +01:00
|
|
|
# We do not implement a `.__init__()` method and leave that to SQLAlchemy.
|
2021-01-09 17:47:45 +01:00
|
|
|
# Instead, we use `hasattr()` to check for uninitialized attributes. grep:d334120e
|
2021-01-02 16:29:50 +01:00
|
|
|
|
2020-08-09 03:45:19 +02:00
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
"""Non-literal text representation."""
|
|
|
|
|
return '<{cls}({name})>'.format(cls=self.__class__.__name__, name=self.name)
|
|
|
|
|
|
|
|
|
|
@property
|
2021-01-04 20:33:10 +01:00
|
|
|
def center(self) -> utils.Location:
|
|
|
|
|
"""Location of the city's center.
|
2020-08-09 03:45:19 +02:00
|
|
|
|
2021-01-04 20:33:10 +01:00
|
|
|
Implementation detail: This property is cached as none of the
|
|
|
|
|
underlying attributes to calculate the value are to be changed.
|
2020-08-09 03:45:19 +02:00
|
|
|
"""
|
2021-01-04 20:33:10 +01:00
|
|
|
if not hasattr(self, '_center'): # noqa:WPS421 note:d334120e
|
2021-01-05 22:37:12 +01:00
|
|
|
self._center = utils.Location(self.center_latitude, self.center_longitude)
|
2021-01-04 20:33:10 +01:00
|
|
|
return self._center
|
2020-08-09 03:45:19 +02:00
|
|
|
|
|
|
|
|
@property
|
2021-01-05 18:58:48 +01:00
|
|
|
def northeast(self) -> utils.Location:
|
|
|
|
|
"""The city's northeast corner of the Google Maps viewport.
|
2020-08-09 03:45:19 +02:00
|
|
|
|
2021-01-04 20:33:10 +01:00
|
|
|
Implementation detail: This property is cached as none of the
|
|
|
|
|
underlying attributes to calculate the value are to be changed.
|
|
|
|
|
"""
|
2021-01-05 18:58:48 +01:00
|
|
|
if not hasattr(self, '_northeast'): # noqa:WPS421 note:d334120e
|
|
|
|
|
self._northeast = utils.Location(
|
2021-01-05 22:37:12 +01:00
|
|
|
self.northeast_latitude, self.northeast_longitude,
|
2021-01-05 18:58:48 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return self._northeast
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def southwest(self) -> utils.Location:
|
|
|
|
|
"""The city's southwest corner of the Google Maps viewport.
|
|
|
|
|
|
|
|
|
|
Implementation detail: This property is cached as none of the
|
|
|
|
|
underlying attributes to calculate the value are to be changed.
|
|
|
|
|
"""
|
|
|
|
|
if not hasattr(self, '_southwest'): # noqa:WPS421 note:d334120e
|
|
|
|
|
self._southwest = utils.Location(
|
2021-01-05 22:37:12 +01:00
|
|
|
self.southwest_latitude, self.southwest_longitude,
|
2021-01-05 18:58:48 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return self._southwest
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def total_x(self) -> int:
|
|
|
|
|
"""The horizontal distance from the city's west to east end in meters.
|
|
|
|
|
|
|
|
|
|
The city borders refer to the Google Maps viewport.
|
|
|
|
|
"""
|
|
|
|
|
return self.northeast.easting - self.southwest.easting
|
2021-01-02 16:29:50 +01:00
|
|
|
|
|
|
|
|
@property
|
2021-01-05 18:58:48 +01:00
|
|
|
def total_y(self) -> int:
|
|
|
|
|
"""The vertical distance from the city's south to north end in meters.
|
2021-01-02 16:29:50 +01:00
|
|
|
|
2021-01-05 18:58:48 +01:00
|
|
|
The city borders refer to the Google Maps viewport.
|
2021-01-02 16:29:50 +01:00
|
|
|
"""
|
2021-01-05 18:58:48 +01:00
|
|
|
return self.northeast.northing - self.southwest.northing
|