Add ORM models for the simulation data
- add new ORM models `ReplaySimulation` and `ReplayedOrder`
to store the data generated by the routing simulations
- add migrations script to create the corresponding database tables
+ create "replay_simulations" and "replayed_orders" tables
+ add missing check constraints to "orders" table
+ add unique constraint to "orders" table to enable compound key
+ drop unnecessary check constraints from the "orders" table
- add tests for the new ORM models
+ add `simulation`, `replayed_order`, `make_replay_order()`, and
`pre_order` fixtures
+ add `ReplayedOrderFactor` faker class
- fix some typos
This commit is contained in:
parent
41f75f507d
commit
e4e543bd40
19 changed files with 1677 additions and 10 deletions
120
tests/db/fake_data/factories/orders/replayed.py
Normal file
120
tests/db/fake_data/factories/orders/replayed.py
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
"""Factory to create `Order` and `ReplayedOrder` instances."""
|
||||
|
||||
import datetime as dt
|
||||
|
||||
import factory
|
||||
from factory import alchemy
|
||||
|
||||
from tests.db.fake_data.factories import utils
|
||||
from urban_meal_delivery import db
|
||||
|
||||
|
||||
class ReplayedOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||
"""Create instances of the `db.ReplayedOrder` model.
|
||||
|
||||
For simplicity, we assume the underlying `.order` to be an ad-hoc `Order`.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = db.ReplayedOrder
|
||||
sqlalchemy_get_or_create = ('simulation_id', 'order_id')
|
||||
|
||||
# Generic columns
|
||||
# simulation -> set by the `make_replay_order` fixture for better control
|
||||
# actual (`Order`) -> set by the `make_replay_order` fixture for better control
|
||||
|
||||
# `Order`-type related columns
|
||||
ad_hoc = factory.LazyAttribute(lambda obj: obj.actual.ad_hoc)
|
||||
placed_at = factory.LazyAttribute(lambda obj: obj.actual.placed_at)
|
||||
scheduled_delivery_at = factory.LazyAttribute(
|
||||
lambda obj: obj.actual.scheduled_delivery_at,
|
||||
)
|
||||
cancelled_at = None
|
||||
|
||||
# Restaurant-related columns
|
||||
estimated_prep_duration = 1200
|
||||
restaurant_notified_at = factory.LazyAttribute(
|
||||
lambda obj: obj.placed_at
|
||||
+ utils.random_timespan(min_seconds=1, max_seconds=30),
|
||||
)
|
||||
restaurant_confirmed_at = factory.LazyAttribute(
|
||||
lambda obj: obj.restaurant_notified_at
|
||||
+ utils.random_timespan(min_seconds=1, max_seconds=60),
|
||||
)
|
||||
restaurant_ready_at = factory.LazyAttribute(
|
||||
lambda obj: obj.restaurant_confirmed_at
|
||||
+ dt.timedelta(seconds=obj.estimated_prep_duration)
|
||||
+ utils.random_timespan(min_seconds=300, max_seconds=300),
|
||||
)
|
||||
|
||||
# Dispatch-related columns
|
||||
dispatch_at = factory.LazyAttribute(
|
||||
lambda obj: obj.actual.placed_at
|
||||
+ utils.random_timespan(min_seconds=30, max_seconds=60),
|
||||
)
|
||||
first_estimated_delivery_at = factory.LazyAttribute(
|
||||
lambda obj: obj.restaurant_notified_at
|
||||
+ dt.timedelta(seconds=obj.estimated_prep_duration)
|
||||
+ dt.timedelta(minutes=10),
|
||||
)
|
||||
# courier -> set by the `make_replay_order` fixture for better control
|
||||
courier_notified_at = factory.LazyAttribute(
|
||||
lambda obj: obj.dispatch_at
|
||||
+ utils.random_timespan(min_seconds=1, max_seconds=30),
|
||||
)
|
||||
courier_accepted_at = factory.LazyAttribute(
|
||||
lambda obj: obj.courier_notified_at
|
||||
+ utils.random_timespan(min_seconds=1, max_seconds=60),
|
||||
)
|
||||
utilization = None
|
||||
|
||||
# Pickup-related columns
|
||||
reached_pickup_at = factory.LazyAttribute(
|
||||
lambda obj: obj.restaurant_ready_at
|
||||
+ utils.random_timespan(min_seconds=1, max_seconds=60),
|
||||
)
|
||||
pickup_at = factory.LazyAttribute(
|
||||
lambda obj: obj.reached_pickup_at
|
||||
+ utils.random_timespan(min_seconds=30, max_seconds=60),
|
||||
)
|
||||
left_pickup_at = factory.LazyAttribute(
|
||||
lambda obj: obj.pickup_at
|
||||
+ utils.random_timespan(min_seconds=30, max_seconds=60),
|
||||
)
|
||||
|
||||
# Delivery-related columns
|
||||
reached_delivery_at = factory.LazyAttribute(
|
||||
lambda obj: obj.left_pickup_at
|
||||
+ utils.random_timespan(min_minutes=5, max_minutes=10),
|
||||
)
|
||||
delivery_at = factory.LazyAttribute(
|
||||
lambda obj: obj.reached_delivery_at
|
||||
+ utils.random_timespan(min_seconds=30, max_seconds=60),
|
||||
)
|
||||
|
||||
@factory.post_generation
|
||||
def post(obj, _create, _extracted, **_kwargs): # noqa:B902,C901,N805
|
||||
"""Discard timestamps that occur after cancellation."""
|
||||
if obj.cancelled_at:
|
||||
if obj.cancelled_at <= obj.restaurant_notified_at:
|
||||
obj.restaurant_notified_at = None
|
||||
if obj.cancelled_at <= obj.restaurant_confirmed_at:
|
||||
obj.restaurant_confirmed_at = None
|
||||
if obj.cancelled_at <= obj.restaurant_ready_at:
|
||||
obj.restaurant_ready_at = None
|
||||
if obj.cancelled_at <= obj.dispatch_at:
|
||||
obj.dispatch_at = None
|
||||
if obj.cancelled_at <= obj.courier_notified_at:
|
||||
obj.courier_notified_at = None
|
||||
if obj.cancelled_at <= obj.courier_accepted_at:
|
||||
obj.courier_accepted_at = None
|
||||
if obj.cancelled_at <= obj.reached_pickup_at:
|
||||
obj.reached_pickup_at = None
|
||||
if obj.cancelled_at <= obj.pickup_at:
|
||||
obj.pickup_at = None
|
||||
if obj.cancelled_at <= obj.left_pickup_at:
|
||||
obj.left_pickup_at = None
|
||||
if obj.cancelled_at <= obj.reached_delivery_at:
|
||||
obj.reached_delivery_at = None
|
||||
if obj.cancelled_at <= obj.delivery_at:
|
||||
obj.delivery_at = None
|
||||
Loading…
Add table
Add a link
Reference in a new issue