"""Création des six tables Data et de l'hypertable reading. Revision ID: e6d2026091501 Revises: 821f71be74c0 """ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql revision = "e6d2026091501" down_revision = "821f71be74c0" branch_labels = None depends_on = None def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.create_table( "dataset", sa.Column("dataset_id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("dataset_name", sa.Text(), nullable=False), sa.Column("archive_sha256", sa.String(length=64), nullable=False), sa.Column("storage_uri", sa.Text(), nullable=False), sa.Column("source_timezone", sa.Text(), nullable=True), sa.Column( "metadata", postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), nullable=False ), sa.CheckConstraint("dataset_id > 0", name="ck_dataset_positive_id"), sa.PrimaryKeyConstraint("dataset_id"), sa.UniqueConstraint("archive_sha256", name="uq_dataset_archive_sha256"), ) op.create_table( "site", sa.Column("site_id", sa.Text(), nullable=False), sa.Column("site_name", sa.Text(), nullable=False), sa.Column("site_type", sa.Text(), nullable=False), sa.Column("location", sa.Text(), nullable=True), sa.Column("capacity_kw", sa.Double(), nullable=True), sa.Column("status", sa.Text(), nullable=True), sa.PrimaryKeyConstraint("site_id"), ) op.create_table( "prediction", sa.Column("prediction_id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("site_id", sa.Text(), nullable=False), sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), sa.Column("target_at", sa.DateTime(timezone=True), nullable=False), sa.Column("target_metric", sa.Text(), nullable=False), sa.Column("period_minutes", sa.Integer(), nullable=True), sa.Column("predicted_value", sa.Double(), nullable=True), sa.Column("model_reference", sa.Text(), nullable=False), sa.Column("status", sa.Text(), nullable=False), sa.Column("failure_reason", sa.Text(), nullable=True), sa.CheckConstraint( "(status = 'available' AND predicted_value IS NOT NULL AND failure_reason IS NULL) OR (status IN ('insufficient_data', 'error') AND predicted_value IS NULL AND failure_reason IS NOT NULL)", name="ck_prediction_status", ), sa.CheckConstraint( "target_metric <> 'consumption_kwh' OR period_minutes IS NOT NULL", name="ck_prediction_energy_period", ), sa.CheckConstraint( "target_metric IN ('consumption_kwh', 'consumption_kw')", name="ck_prediction_metric" ), sa.CheckConstraint( "period_minutes IS NULL OR period_minutes > 0", name="ck_prediction_period" ), sa.ForeignKeyConstraint( ["site_id"], ["site.site_id"], name="fk_prediction_site", ondelete="RESTRICT" ), sa.PrimaryKeyConstraint("prediction_id"), sa.UniqueConstraint("prediction_id", "site_id", name="uq_prediction_id_site"), ) op.create_index( "ix_prediction_site_target", "prediction", ["site_id", "target_at"], unique=False ) op.create_table( "reading", sa.Column("reading_id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("site_id", sa.Text(), nullable=False), sa.Column("timestamp", sa.DateTime(timezone=True), nullable=False), sa.Column("source", sa.Text(), nullable=False), sa.Column("dataset_id", sa.BigInteger(), nullable=True), sa.Column("consumption_kw", sa.Double(), nullable=True), sa.Column("consumption_kwh", sa.Double(), nullable=True), sa.Column("consumption_euros", sa.Numeric(precision=14, scale=2), nullable=True), sa.Column("voltage_v", sa.Double(), nullable=True), sa.Column("current_a", sa.Double(), nullable=True), sa.Column("power_factor", sa.Double(), nullable=True), sa.Column("temperature_celsius", sa.Double(), nullable=True), sa.Column("humidity_percent", sa.Double(), nullable=True), sa.Column("solar_irradiance_wm2", sa.Double(), nullable=True), sa.Column("is_working_hours", sa.Boolean(), nullable=True), sa.Column("data_quality", sa.Text(), nullable=True), sa.Column("null_reasons", postgresql.ARRAY(sa.Text()), nullable=True), sa.Column( "imputed_values", postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), nullable=True ), sa.Column("imputation_method", sa.Text(), nullable=True), sa.Column( "ingested_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), sa.Column( "raw_data", postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), nullable=False ), sa.CheckConstraint( "(source = 'csv' AND dataset_id IS NOT NULL) OR (source IN ('api_current', 'api_history') AND dataset_id IS NULL)", name="ck_reading_dataset_source", ), sa.CheckConstraint( "data_quality IS NULL OR data_quality IN ('good', 'partial', 'degraded', 'critical')", name="ck_reading_quality", ), sa.CheckConstraint( "source IN ('csv', 'api_current', 'api_history')", name="ck_reading_source" ), sa.CheckConstraint( "(imputed_values IS NULL AND imputation_method IS NULL) OR (imputed_values IS NOT NULL AND imputation_method IS NOT NULL)", name="ck_reading_imputation", ), sa.ForeignKeyConstraint( ["dataset_id"], ["dataset.dataset_id"], name="fk_reading_dataset", ondelete="RESTRICT" ), sa.ForeignKeyConstraint( ["site_id"], ["site.site_id"], name="fk_reading_site", ondelete="RESTRICT" ), sa.PrimaryKeyConstraint("reading_id", "timestamp"), ) op.create_index("ix_reading_dataset_id", "reading", ["dataset_id"], unique=False) op.create_index( "ix_reading_site_timestamp", "reading", ["site_id", "timestamp"], unique=False ) op.create_index( "uq_reading_source", "reading", ["site_id", "timestamp", "source", sa.literal_column("coalesce(dataset_id, 0)")], unique=True, ) op.execute( "SELECT create_hypertable('reading', by_range('timestamp'), create_default_indexes => FALSE)" ) op.create_table( "alert", sa.Column("alert_id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("source_alert_id", sa.Text(), nullable=False), sa.Column("site_id", sa.Text(), nullable=False), sa.Column("source", sa.Text(), nullable=False), sa.Column("timestamp", sa.DateTime(timezone=True), nullable=False), sa.Column("type", sa.Text(), nullable=False), sa.Column("severity", sa.Text(), nullable=False), sa.Column("message", sa.Text(), nullable=False), sa.Column("value", sa.Double(), nullable=True), sa.Column("threshold", sa.Double(), nullable=True), sa.Column("metric", sa.Text(), nullable=True), sa.Column("prediction_id", sa.BigInteger(), nullable=True), sa.Column( "raw_data", postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), nullable=False ), sa.CheckConstraint( "severity IN ('low', 'medium', 'high', 'critical')", name="ck_alert_severity" ), sa.CheckConstraint("source IN ('api_mock', 'enervision')", name="ck_alert_source"), sa.CheckConstraint( "type IN ('spike', 'threshold', 'anomaly', 'outage', 'sensor')", name="ck_alert_type" ), sa.ForeignKeyConstraint( ["prediction_id", "site_id"], ["prediction.prediction_id", "prediction.site_id"], name="fk_alert_prediction_site", ondelete="RESTRICT", ), sa.ForeignKeyConstraint( ["site_id"], ["site.site_id"], name="fk_alert_site", ondelete="RESTRICT" ), sa.PrimaryKeyConstraint("alert_id"), sa.UniqueConstraint( "source", "site_id", "source_alert_id", name="uq_alert_source_reference" ), ) op.create_index("ix_alert_site_timestamp", "alert", ["site_id", "timestamp"], unique=False) op.create_table( "recommendation", sa.Column("recommendation_id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("alert_id", sa.BigInteger(), nullable=False), sa.Column("action", sa.Text(), nullable=False), sa.Column("explanation", sa.Text(), nullable=False), sa.Column("rule_reference", sa.Text(), nullable=False), sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), sa.ForeignKeyConstraint( ["alert_id"], ["alert.alert_id"], name="fk_recommendation_alert", ondelete="RESTRICT" ), sa.PrimaryKeyConstraint("recommendation_id"), sa.UniqueConstraint("alert_id", "rule_reference", name="uq_recommendation_alert_rule"), ) # ### end Alembic commands ### def downgrade() -> None: op.drop_table("recommendation") op.drop_table("alert") op.drop_table("reading") op.drop_table("prediction") op.drop_table("site") op.drop_table("dataset")