Skip to content
Open

SMURFI #5042

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
8cb7aff
route
brettedw Jan 20, 2026
8de2d2f
tabs + page skeleton
brettedw Jan 20, 2026
b38b3a6
added chefs_config and script to download CHEFS form data via API
andrea-williams Jan 21, 2026
55a45a0
Management UI wip (#5038)
dgboss Jan 21, 2026
2e7be16
Forecaster form (#5039)
brettedw Jan 21, 2026
0f5c599
migration to create smurfi related tables (#5041)
acatchpole Jan 21, 2026
074c2ce
Form updates (#5043)
brettedw Jan 21, 2026
372d7c5
Add 1 mock marker to SmurfiMap (#5044)
andrea-williams Jan 21, 2026
420fb4f
fixed broken frontend because of merge oopsie
andrea-williams Jan 22, 2026
3870a9c
smurfi data models and migration (#5046)
acatchpole Jan 22, 2026
9cd0cce
uv lock
brettedw Jan 22, 2026
8b99f08
Management wip (#5049)
dgboss Jan 22, 2026
ab25b34
SMURFI create spot and model updates (#5048)
acatchpole Jan 22, 2026
a2b3179
model changes that were supposed to accompany migration
acatchpole Jan 22, 2026
42cff81
SMURFI map popup & request link (#5052)
brettedw Jan 22, 2026
744752b
added fire_size; smurfi data schema (#5055)
acatchpole Jan 22, 2026
6adae4a
lat long added to spot
acatchpole Jan 22, 2026
5237e0d
functions to get save and update spots and versions (#5056)
acatchpole Jan 22, 2026
4cb146c
Wire up management button & fancy-up map (#5057)
andrea-williams Jan 22, 2026
a27f878
Load chefs data to db
brettedw Jan 22, 2026
8d5667d
endpoints for getting and saving spot forecast
acatchpole Jan 22, 2026
ab32ebf
skip
brettedw Jan 22, 2026
d76a745
0
brettedw Jan 22, 2026
c145057
check existing
brettedw Jan 22, 2026
fb48a5d
entirely vibe-coded my way through creating a history view of forecas…
andrea-williams Jan 22, 2026
300a1ea
removed unneeded Spot Forecast tab from SmurfiPage (#5059)
andrea-williams Jan 22, 2026
f82703c
smurfi cards (#5062)
dgboss Jan 22, 2026
f2f2c0c
frontend post forecast
brettedw Jan 22, 2026
c1d8740
dashboard tab title
brettedw Jan 22, 2026
f0727e3
pdf view
brettedw Jan 22, 2026
4ba168d
Smurfi rebase (#5407)
dgboss May 14, 2026
5b7cba7
smurfi models (#5408)
dgboss May 19, 2026
38bdee4
SMURFI Request Form (#5418)
brettedw May 19, 2026
affa6ca
SMURFI endpoints, crud and schema (#5419)
dgboss May 19, 2026
ceaa094
SMURFI forecast form refactor (#5421)
brettedw May 19, 2026
18e8613
SMURFI - Spot Request requests (#5424)
brettedw May 20, 2026
3f76f58
SMURFI: Spot request subscribe (#5423)
conbrad May 21, 2026
d9b1351
SpotRequests landing page (#5431)
dgboss May 21, 2026
686d1f4
Spot Request detaill (#5434)
dgboss May 22, 2026
1c2a07d
Merge branch 'main' into smurfi
brettedw May 22, 2026
512b1fb
yarn
brettedw May 22, 2026
44701da
hook form
brettedw May 22, 2026
c4821de
fix test
brettedw May 22, 2026
e1d6fbd
vite config
brettedw May 22, 2026
9a74eda
SMURFI wire forecasts (#5435)
brettedw May 25, 2026
309ba7e
SMURFI Forecast - Part 2 (#5436)
brettedw May 25, 2026
2410fe0
Wire VANITY_DOMAIN into nats deployment for WEB_BASE_URL (#5443)
conbrad May 25, 2026
68e2b96
SMURFI: Remove Admin tab (#5445)
brettedw May 25, 2026
f1f0c6c
SMURFI: Fire polygons & layer switcher (#5442)
brettedw May 25, 2026
8581bce
SMURFI: Fix smurfi nats subject (#5446)
conbrad May 25, 2026
ecf4534
SMURFI: icons (#5447)
brettedw May 25, 2026
08cee46
SMURFI: Dashboard forecast frequency (#5448)
brettedw May 25, 2026
e51b2a6
fix
brettedw May 25, 2026
fca715d
SMURFI: Configure email merge url (#5449)
conbrad May 25, 2026
69c4d37
SMURFI: Form validation error on submit button (#5451)
brettedw May 25, 2026
c5a53b3
centre frequency
brettedw May 26, 2026
2cb7101
Full spot forecast detail page (#5450)
dgboss May 26, 2026
3d40477
SMURFI Map Updates (#5452)
brettedw May 26, 2026
6b56648
sort column
brettedw May 26, 2026
01e3247
SMURFI: Adjust email template (#5455)
conbrad May 26, 2026
07ace4c
Mini forecast detail pages (#5453)
dgboss May 26, 2026
8268200
SMURFI: Add Fire number filter to map view legend (#5457)
conbrad May 26, 2026
ab1eeeb
forecast list (#5459)
dgboss May 26, 2026
47c87d4
SMURFI: Data Model rework (#5458)
brettedw May 26, 2026
a53abfd
SMURFI: Forecast form improvements & editing (#5461)
brettedw May 27, 2026
2531925
email link
brettedw May 27, 2026
ec3e715
SMURFI: Email Dist list mgmt (#5460)
conbrad May 28, 2026
6a2f1f5
SMURFI: Add fire points to map (#5464)
brettedw May 28, 2026
b621a81
SMURFI: Subscribe requestor by default (#5468)
brettedw May 28, 2026
e62d3b9
Allow new request on map click (#5465)
dgboss May 28, 2026
df5cb99
Spot Request Edits and Forecast Views (#5470)
dgboss Jun 4, 2026
ec8c34b
SMURFI: Status Change (#5484)
brettedw Jun 4, 2026
23835c6
Merge branch 'main' into smurfi
brettedw Jun 8, 2026
0f9c8b3
SMURFI Forecaster Info & Stats (#5492)
brettedw Jun 8, 2026
5443e23
Merge branch 'main' into smurfi
brettedw Jun 8, 2026
9b8aefd
repoint
brettedw Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,10 @@ GRIB_RETENTION_THRESHOLD=2
WX_OBJECT_STORE_SERVER=wx_object_store_server
WX_OBJECT_STORE_USER_ID=wx_object_store_server
WX_OBJECT_STORE_SECRET=wx_object_store_server
WX_OBJECT_STORE_BUCKET=wx_object_store_server
WX_OBJECT_STORE_BUCKET=wx_object_store_server
CHES_TOKEN_URL=ches-token-url.com"
CHES_CLIENT_ID=client
CHES_CLIENT_SECRET=secret
CHES_SENDER_EMAIL=ches@example.com
CHES_EMAIL_MERGE_URL=ches-merge.com
WEB_BASE_URL=base@url.com
2 changes: 1 addition & 1 deletion .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ jobs:
- name: NATS Message Queue
shell: bash
run: |
MEMORY_REQUEST=250Mi MEMORY_LIMIT=500Mi CPU_REQUEST="250m" bash openshift/scripts/oc_provision_nats.sh ${SUFFIX} apply
MEMORY_REQUEST=250Mi MEMORY_LIMIT=500Mi CPU_REQUEST="250m" VANITY_DOMAIN="${SUFFIX}-dev-psu.apps.silver.devops.gov.bc.ca" bash openshift/scripts/oc_provision_nats.sh ${SUFFIX} apply

deploy-dev:
name: Deploy to Dev
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""spot forecast columns - issued/expired

Revision ID: 3b9310ff54f5
Revises: 8ad2e0d77c9f
Create Date: 2026-05-22 10:52:22.798252

"""

import sqlalchemy as sa
from alembic import op
from wps_shared.db.models.common import TZTimeStamp

# revision identifiers, used by Alembic.
revision = "3b9310ff54f5"
down_revision = "8ad2e0d77c9f"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic ###
op.add_column("spot_forecast", sa.Column("issued_at", TZTimeStamp(), nullable=False))
op.add_column("spot_forecast", sa.Column("expires_at", TZTimeStamp(), nullable=True))
op.drop_column("spot_forecast", "for_date")
op.drop_column("spot_forecast", "updated_at")
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic ###

op.add_column(
"spot_forecast",
sa.Column("updated_at", TZTimeStamp(), autoincrement=False, nullable=True),
)
op.add_column(
"spot_forecast",
sa.Column("for_date", TZTimeStamp(), autoincrement=False, nullable=True),
)
op.drop_column("spot_forecast", "expires_at")
op.drop_column("spot_forecast", "issued_at")

# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""SMURFI distribution groups

Revision ID: 63b52513243e
Revises: c2a830d3218e
Create Date: 2026-05-26 00:00:00.000000

"""

import sqlalchemy as sa
from alembic import op
from wps_shared.db.models.common import TZTimeStamp

# revision identifiers, used by Alembic.
revision = "63b52513243e"
down_revision = "c2a830d3218e"
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
"smurfi_distribution_group",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("emails", sa.ARRAY(sa.String()), nullable=False),
sa.Column("owner_idir", sa.String(), nullable=False),
sa.Column("created_at", TZTimeStamp(), nullable=False),
sa.Column("updated_at", TZTimeStamp(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
comment="Named distribution groups for spot forecast email notifications.",
)
op.create_table(
"spot_request_distribution_group",
sa.Column("spot_request_id", sa.Integer(), nullable=False),
sa.Column("distribution_group_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["distribution_group_id"], ["smurfi_distribution_group.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["spot_request_id"], ["spot_request_base.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("spot_request_id", "distribution_group_id"),
)


def downgrade():
op.drop_table("spot_request_distribution_group")
op.drop_table("smurfi_distribution_group")
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
"""split smurfi request instances

Revision ID: 6f1d8d4c2a90
Revises: 3b9310ff54f5
Create Date: 2026-05-26 10:24:00.000000

"""

import sqlalchemy as sa
from alembic import op
from geoalchemy2 import Geometry
from wps_shared.db.models.common import TZTimeStamp

# revision identifiers, used by Alembic.
revision = "6f1d8d4c2a90"
down_revision = "3b9310ff54f5"
branch_labels = None
depends_on = None


def upgrade():
op.rename_table("spot_request", "spot_request_base")

op.create_table(
"spot_request_instance",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("spot_request_base_id", sa.Integer(), nullable=False),
sa.Column("latitude", sa.Float(), nullable=False),
sa.Column("longitude", sa.Float(), nullable=False),
sa.Column(
"geom",
Geometry(
geometry_type="POINT",
srid=3005,
dimension=2,
spatial_index=False,
from_text="ST_GeomFromEWKT",
name="geometry",
),
nullable=False,
),
sa.Column("geographic_description", sa.String(), nullable=False),
sa.Column("aspect", sa.String(), nullable=True),
sa.Column("elevation", sa.Integer(), nullable=True),
sa.Column("valley", sa.String(), nullable=True),
sa.Column("created_at", TZTimeStamp(), nullable=False),
sa.Column("updated_at", TZTimeStamp(), nullable=False),
sa.ForeignKeyConstraint(["spot_request_base_id"], ["spot_request_base.id"]),
sa.PrimaryKeyConstraint("id"),
comment="Tracks geographic instances used by spot requests and forecasts.",
)
op.create_index(
op.f("ix_spot_request_instance_spot_request_base_id"),
"spot_request_instance",
["spot_request_base_id"],
unique=False,
)

op.execute(
"""
INSERT INTO spot_request_instance (
spot_request_base_id,
latitude,
longitude,
geom,
geographic_description,
aspect,
elevation,
valley,
created_at,
updated_at
)
SELECT
id,
ST_Y(ST_Transform(geom, 4326)),
ST_X(ST_Transform(geom, 4326)),
geom,
geographic_description,
aspect,
elevation,
NULL,
created_at,
updated_at
FROM spot_request_base
"""
)

op.drop_constraint("spot_forecast_spot_request_id_fkey", "spot_forecast", type_="foreignkey")
op.drop_index(op.f("ix_spot_forecast_spot_request_id"), table_name="spot_forecast")
op.execute(
"""
ALTER TABLE spot_forecast
ALTER COLUMN fire_size TYPE FLOAT[]
USING CASE
WHEN fire_size IS NULL THEN NULL
ELSE ARRAY[fire_size]
END
"""
)
op.add_column("spot_forecast", sa.Column("spot_request_base_id", sa.Integer(), nullable=True))
op.add_column(
"spot_forecast", sa.Column("spot_request_instance_id", sa.Integer(), nullable=True)
)
op.execute(
"""
UPDATE spot_forecast forecast
SET
spot_request_base_id = forecast.spot_request_id,
spot_request_instance_id = instance.id
FROM spot_request_instance instance
WHERE instance.spot_request_base_id = forecast.spot_request_id
"""
)
op.alter_column("spot_forecast", "spot_request_base_id", nullable=False)
op.alter_column("spot_forecast", "spot_request_instance_id", nullable=False)
op.create_foreign_key(
"spot_forecast_spot_request_base_id_fkey",
"spot_forecast",
"spot_request_base",
["spot_request_base_id"],
["id"],
)
op.create_foreign_key(
"spot_forecast_spot_request_instance_id_fkey",
"spot_forecast",
"spot_request_instance",
["spot_request_instance_id"],
["id"],
)
op.create_index(
op.f("ix_spot_forecast_spot_request_base_id"),
"spot_forecast",
["spot_request_base_id"],
unique=False,
)
op.create_index(
op.f("ix_spot_forecast_spot_request_instance_id"),
"spot_forecast",
["spot_request_instance_id"],
unique=False,
)
op.drop_column("spot_forecast", "spot_request_id")

op.drop_constraint(
"spot_subscriber_spot_request_id_fkey", "spot_subscriber", type_="foreignkey"
)
op.drop_index(op.f("ix_spot_subscriber_spot_request_id"), table_name="spot_subscriber")
op.alter_column("spot_subscriber", "spot_request_id", new_column_name="spot_request_base_id")
op.create_foreign_key(
"spot_subscriber_spot_request_base_id_fkey",
"spot_subscriber",
"spot_request_base",
["spot_request_base_id"],
["id"],
)
op.create_index(
op.f("ix_spot_subscriber_spot_request_base_id"),
"spot_subscriber",
["spot_request_base_id"],
unique=False,
)

op.drop_constraint("chk_aspect_spot_request", "spot_request_base", type_="check")
op.drop_column("spot_request_base", "geom")
op.drop_column("spot_request_base", "geographic_description")
op.drop_column("spot_request_base", "elevation")
op.drop_column("spot_request_base", "aspect")


def downgrade():
op.add_column("spot_request_base", sa.Column("aspect", sa.String(), nullable=True))
op.add_column("spot_request_base", sa.Column("elevation", sa.Integer(), nullable=True))
op.add_column(
"spot_request_base",
sa.Column("geographic_description", sa.String(), nullable=True),
)
op.add_column(
"spot_request_base",
sa.Column(
"geom",
Geometry(
geometry_type="POINT",
srid=3005,
dimension=2,
spatial_index=False,
from_text="ST_GeomFromEWKT",
name="geometry",
),
nullable=True,
),
)
op.execute(
"""
UPDATE spot_request_base base
SET
aspect = instance.aspect,
elevation = instance.elevation,
geographic_description = instance.geographic_description,
geom = instance.geom
FROM spot_request_instance instance
WHERE instance.spot_request_base_id = base.id
"""
)
op.alter_column("spot_request_base", "geographic_description", nullable=False)
op.alter_column("spot_request_base", "geom", nullable=False)

op.drop_index(op.f("ix_spot_subscriber_spot_request_base_id"), table_name="spot_subscriber")
op.drop_constraint(
"spot_subscriber_spot_request_base_id_fkey", "spot_subscriber", type_="foreignkey"
)
op.alter_column("spot_subscriber", "spot_request_base_id", new_column_name="spot_request_id")
op.create_foreign_key(
"spot_subscriber_spot_request_id_fkey",
"spot_subscriber",
"spot_request_base",
["spot_request_id"],
["id"],
)
op.create_index(
op.f("ix_spot_subscriber_spot_request_id"),
"spot_subscriber",
["spot_request_id"],
unique=False,
)

op.add_column("spot_forecast", sa.Column("spot_request_id", sa.Integer(), nullable=True))
op.execute("UPDATE spot_forecast SET spot_request_id = spot_request_base_id")
op.alter_column("spot_forecast", "spot_request_id", nullable=False)
op.execute(
"""
ALTER TABLE spot_forecast
ALTER COLUMN fire_size TYPE FLOAT
USING fire_size[1]
"""
)
op.drop_index(op.f("ix_spot_forecast_spot_request_instance_id"), table_name="spot_forecast")
op.drop_index(op.f("ix_spot_forecast_spot_request_base_id"), table_name="spot_forecast")
op.drop_constraint(
"spot_forecast_spot_request_instance_id_fkey", "spot_forecast", type_="foreignkey"
)
op.drop_constraint(
"spot_forecast_spot_request_base_id_fkey", "spot_forecast", type_="foreignkey"
)
op.drop_column("spot_forecast", "spot_request_instance_id")
op.drop_column("spot_forecast", "spot_request_base_id")
op.create_foreign_key(
"spot_forecast_spot_request_id_fkey",
"spot_forecast",
"spot_request_base",
["spot_request_id"],
["id"],
)
op.create_index(
op.f("ix_spot_forecast_spot_request_id"),
"spot_forecast",
["spot_request_id"],
unique=False,
)

op.drop_index(
op.f("ix_spot_request_instance_spot_request_base_id"),
table_name="spot_request_instance",
)
op.drop_table("spot_request_instance")
op.rename_table("spot_request_base", "spot_request")
Loading
Loading