Add functionality for drawing folium.Maps

- this code is not unit-tested due to the complexity involving
  interactive `folium.Map`s => visual checks give high confidence
This commit is contained in:
Alexander Hess 2021-01-26 17:07:50 +01:00
commit 4b6d92958d
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
12 changed files with 714 additions and 13 deletions

View file

@ -1,8 +1,13 @@
"""Provide the ORM's `Restaurant` model."""
from __future__ import annotations
import folium
import sqlalchemy as sa
from sqlalchemy import orm
from urban_meal_delivery import config
from urban_meal_delivery import db
from urban_meal_delivery.db import meta
@ -45,3 +50,92 @@ class Restaurant(meta.Base):
def __repr__(self) -> str:
"""Non-literal text representation."""
return '<{cls}({name})>'.format(cls=self.__class__.__name__, name=self.name)
def clear_map(self) -> Restaurant: # pragma: no cover
"""Shortcut to the `.address.city.clear_map()` method.
Returns:
self: enabling method chaining
""" # noqa:D402,DAR203
self.address.city.clear_map()
return self
@property # pragma: no cover
def map(self) -> folium.Map: # noqa:WPS125
"""Shortcut to the `.address.city.map` object."""
return self.address.city.map
def draw( # noqa:WPS231
self, customers: bool = True, order_counts: bool = False, # pragma: no cover
) -> folium.Map:
"""Draw the restaurant on the `.address.city.map`.
By default, the restaurant's delivery locations are also shown.
Args:
customers: show the restaurant's delivery locations
order_counts: show the number of orders at the delivery locations;
only useful if `customers=True`
Returns:
`.address.city.map` for convenience in interactive usage
"""
if customers:
# Obtain all primary `Address`es in the city that
# received at least one delivery from `self`.
delivery_addresses = ( # noqa:ECE001
db.session.query(db.Address)
.filter(
db.Address.id.in_(
db.session.query(db.Address.primary_id) # noqa:WPS221
.join(db.Order, db.Address.id == db.Order.delivery_address_id)
.filter(db.Order.restaurant_id == self.id)
.distinct()
.all(),
),
)
.all()
)
for address in delivery_addresses:
if order_counts:
n_orders = ( # noqa:ECE001
db.session.query(db.Order)
.join(db.Address, db.Order.delivery_address_id == db.Address.id)
.filter(db.Order.restaurant_id == self.id)
.filter(db.Address.primary_id == address.id)
.count()
)
if n_orders >= 25:
radius = 20 # noqa:WPS220
elif n_orders >= 10:
radius = 15 # noqa:WPS220
elif n_orders >= 5:
radius = 10 # noqa:WPS220
elif n_orders > 1:
radius = 5 # noqa:WPS220
else:
radius = 1 # noqa:WPS220
address.draw(
radius=radius,
color=config.CUSTOMER_COLOR,
fill_color=config.CUSTOMER_COLOR,
fill_opacity=0.3,
tooltip=f'n_orders={n_orders}',
)
else:
address.draw(
radius=1, color=config.CUSTOMER_COLOR,
)
self.address.draw(
radius=20,
color=config.RESTAURANT_COLOR,
fill_color=config.RESTAURANT_COLOR,
fill_opacity=0.3,
tooltip=f'{self.name} (#{self.id}) | n_orders={len(self.orders)}',
)
return self.map