Skip to content

Commit b552fce

Browse files
Add model version to simulationresult
1 parent 4c9768f commit b552fce

8 files changed

Lines changed: 52 additions & 0 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Added model_version to simulation
2+
3+
Revision ID: 60afa15c63a4
4+
Revises: c35588effcc4
5+
Create Date: 2025-11-18 10:43:26.687119
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
from alembic import op
12+
import sqlalchemy as sa
13+
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "60afa15c63a4"
17+
down_revision: Union[str, Sequence[str], None] = "c35588effcc4"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
"""Upgrade schema."""
24+
# ### commands auto generated by Alembic - please adjust! ###
25+
op.add_column("results", sa.Column("model_version", sa.String(), nullable=True))
26+
# ### end Alembic commands ###
27+
28+
29+
def downgrade() -> None:
30+
"""Downgrade schema."""
31+
# ### commands auto generated by Alembic - please adjust! ###
32+
op.drop_column("results", "model_version")
33+
# ### end Alembic commands ###

backend/src/acidwatch_api/database.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Result(Base):
5555
__tablename__ = "results"
5656

5757
simulation_id: Mapped[UUID] = mapped_column(ForeignKey("simulations.id"))
58+
model_version: Mapped[str | None]
5859
concentrations: Mapped[dict[str, float]] = mapped_column(JSON)
5960
panels: Mapped[list[Any]] = mapped_column(JSON)
6061
python_exception: Mapped[BaseException | None] = mapped_column(PickleType)

backend/src/acidwatch_api/models/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ def __init_subclass__(cls) -> None:
274274

275275
model_id: Annotated[str, Doc("Unique model identifier")]
276276

277+
model_version: Annotated[str, Doc("Model version (can be set during run function)")]
278+
277279
display_name: Annotated[
278280
str, Doc("User-friendly model name which is displayed in the frontend")
279281
]

backend/src/acidwatch_api/models/datamodel.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class ModelInput(RunRequest):
2828
class RunResponse(_BaseModel):
2929
status: Literal["done", "pending"]
3030
model_input: ModelInput
31+
model_version: str | None = None
3132
final_concentrations: dict[str, int | float] = Field(default_factory=dict)
3233
panels: Iterable[AnyPanel] = ()
3334

backend/src/acidwatch_api/models/example_adapter.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ class ExampleAdapter(BaseAdapter):
2424

2525
model_id = "example"
2626

27+
# if a model is based on a python package one would typically do:
28+
# model_version = package_name.__version__
29+
#
30+
# It's also possible to modify this during the run function.
31+
# It will be used to provide meta information of any content returned
32+
# in the run function
33+
model_version = "1.0.0"
34+
2735
# Every model requires a human-readable model name. This text will be
2836
# displayed in the frontend.
2937
display_name = "Example"

backend/src/acidwatch_api/routes/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ async def _run_adapter(
8383

8484
result_obj = db.Result(
8585
simulation_id=simulation_id,
86+
model_version=adapter.model_version,
8687
concentrations=concs,
8788
panels=[p.model_dump(mode="json", by_alias=True) for p in panels],
8889
python_exception=None,
@@ -174,6 +175,7 @@ def get_result_for_simulation(
174175

175176
return RunResponse(
176177
status="done",
178+
model_version=result.model_version,
177179
model_input=model_input,
178180
final_concentrations=result.concentrations,
179181
panels=result.panels,

backend/tests/test_models_endpoints.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_get_models(client):
4141

4242
class DummyAdapter(base.BaseAdapter):
4343
model_id = "dummy"
44+
model_version = "0.0.0"
4445
display_name = "Dummy Model"
4546
description = ""
4647
category = "Primary"
@@ -80,6 +81,7 @@ def test_run_test_model(client, dummy_model):
8081

8182
assert response.json() == {
8283
"status": "done",
84+
"modelVersion": dummy_model.model_version,
8385
"modelInput": {
8486
"modelId": dummy_model.model_id,
8587
"parameters": {},
@@ -125,6 +127,7 @@ def test_dummy_model_only_valid_substances_are_present(
125127
response = client.get(f"/simulations/{simulation_id}/result")
126128
assert response.json() == {
127129
"status": "done",
130+
"modelVersion": dummy_model.model_version,
128131
"modelInput": {
129132
"modelId": dummy_model.model_id,
130133
"concentrations": concentrations,
@@ -300,6 +303,7 @@ async def run(self):
300303

301304
assert response.json() == {
302305
"status": "done",
306+
"modelVersion": dummy_model.model_version,
303307
"modelInput": {
304308
"modelId": dummy_model.model_id,
305309
"concentrations": {},

frontend/src/dto/SimulationResults.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ export const SimulationResults = z.object({
3434
modelInput: ModelInput,
3535
finalConcentrations: z.record(z.string(), z.number()),
3636
panels: z.array(Panel),
37+
modelVersion: z.string(),
3738
});
3839
export type SimulationResults = z.infer<typeof SimulationResults>;

0 commit comments

Comments
 (0)