Add Forecast.actual
column
This commit is contained in:
parent
6429165aaf
commit
a5b590b24c
3 changed files with 58 additions and 0 deletions
|
@ -0,0 +1,41 @@
|
|||
"""Store actuals with forecast.
|
||||
|
||||
Revision: #c2af85bada01 at 2021-01-29 11:13:15
|
||||
Revises: #e86290e7305e
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
from urban_meal_delivery import configuration
|
||||
|
||||
|
||||
revision = 'c2af85bada01'
|
||||
down_revision = 'e86290e7305e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
config = configuration.make_config('testing' if os.getenv('TESTING') else 'production')
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""Upgrade to revision c2af85bada01."""
|
||||
op.add_column(
|
||||
'forecasts',
|
||||
sa.Column('actual', sa.SmallInteger(), nullable=False),
|
||||
schema=config.CLEAN_SCHEMA,
|
||||
)
|
||||
op.create_check_constraint(
|
||||
op.f('ck_forecasts_on_actuals_must_be_non_negative'),
|
||||
'forecasts',
|
||||
'actual >= 0',
|
||||
schema=config.CLEAN_SCHEMA,
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
"""Downgrade to revision e86290e7305e."""
|
||||
op.drop_column('forecasts', 'actual', schema=config.CLEAN_SCHEMA)
|
|
@ -22,6 +22,10 @@ class Forecast(meta.Base):
|
|||
time_step = sa.Column(sa.SmallInteger, nullable=False)
|
||||
training_horizon = sa.Column(sa.SmallInteger, nullable=False)
|
||||
model = sa.Column(sa.Unicode(length=20), nullable=False)
|
||||
# We also store the actual order counts for convenient retrieval.
|
||||
# A `UniqueConstraint` below ensures that redundant values that
|
||||
# are to be expected are consistend across rows.
|
||||
actual = sa.Column(sa.SmallInteger, nullable=False)
|
||||
# Raw `.prediction`s are stored as `float`s (possibly negative).
|
||||
# The rounding is then done on the fly if required.
|
||||
prediction = sa.Column(postgresql.DOUBLE_PRECISION, nullable=False)
|
||||
|
@ -62,6 +66,7 @@ class Forecast(meta.Base):
|
|||
sa.CheckConstraint(
|
||||
'training_horizon > 0', name='training_horizon_must_be_positive',
|
||||
),
|
||||
sa.CheckConstraint('actual >= 0', name='actuals_must_be_non_negative'),
|
||||
sa.CheckConstraint(
|
||||
"""
|
||||
NOT (
|
||||
|
|
|
@ -18,6 +18,7 @@ def forecast(pixel):
|
|||
time_step=60,
|
||||
training_horizon=8,
|
||||
model='hets',
|
||||
actual=12,
|
||||
prediction=12.3,
|
||||
low80=1.23,
|
||||
high80=123.4,
|
||||
|
@ -131,6 +132,16 @@ class TestConstraints:
|
|||
):
|
||||
db_session.commit()
|
||||
|
||||
def test_non_negative_actuals(self, db_session, forecast):
|
||||
"""Insert an instance with invalid data."""
|
||||
forecast.actual = -1
|
||||
db_session.add(forecast)
|
||||
|
||||
with pytest.raises(
|
||||
sa_exc.IntegrityError, match='actuals_must_be_non_negative',
|
||||
):
|
||||
db_session.commit()
|
||||
|
||||
def test_set_prediction_without_ci(self, db_session, forecast):
|
||||
"""Sanity check to see that the check constraint ...
|
||||
|
||||
|
@ -388,6 +399,7 @@ class TestConstraints:
|
|||
time_step=forecast.time_step,
|
||||
training_horizon=forecast.training_horizon,
|
||||
model=forecast.model,
|
||||
actual=forecast.actual,
|
||||
prediction=2,
|
||||
low80=1,
|
||||
high80=3,
|
||||
|
|
Loading…
Reference in a new issue