Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Add simulation recording_arrays

Revision ID: 7bb31ca36dbe
Revises: 79dbeada57f3
Create Date: 2026-07-28 23:27:49.412975

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "7bb31ca36dbe"
down_revision: Union[str, None] = "79dbeada57f3"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"simulatable_extracellular_recording_array__simulation",
sa.Column("simulatable_extracellular_recording_array_id", sa.Uuid(), nullable=False),
sa.Column("simulation_id", sa.Uuid(), nullable=False),
sa.ForeignKeyConstraint(
["simulatable_extracellular_recording_array_id"],
["simulatable_extracellular_recording_array.id"],
name=op.f(
"fk_simulatable_extracellular_recording_array__simulation_simulatable_extracellular_recording_array_id_simulatable_extracellular_recording_array"
),
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["simulation_id"],
["simulation.id"],
name=op.f(
"fk_simulatable_extracellular_recording_array__simulation_simulation_id_simulation"
),
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint(
"simulatable_extracellular_recording_array_id",
"simulation_id",
name=op.f("pk_simulatable_extracellular_recording_array__simulation"),
),
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("simulatable_extracellular_recording_array__simulation")
# ### end Alembic commands ###
23 changes: 23 additions & 0 deletions app/db/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,21 @@ class SimulationCampaign(
}


class SimulatableExtracellularRecordingArrayToSimulation(Base):
__tablename__ = "simulatable_extracellular_recording_array__simulation"

simulatable_extracellular_recording_array_id: Mapped[uuid.UUID] = mapped_column(
ForeignKey(
f"{EntityType.simulatable_extracellular_recording_array}.id", ondelete="CASCADE"
),
primary_key=True,
)
simulation_id: Mapped[uuid.UUID] = mapped_column(
ForeignKey(f"{EntityType.simulation}.id", ondelete="CASCADE"),
primary_key=True,
)


class Simulation(Entity, NameDescriptionVectorMixin):
"""Represents a simulation entity in the database.

Expand All @@ -1669,6 +1684,7 @@ class Simulation(Entity, NameDescriptionVectorMixin):
entity (Entity): The entity this simulation is associated with.
number_neurons (int): Number of neurons to be simulated
scan_parameters (JSON_DICT): Scan parameters for the simulation.
recording_arrays: Simulatable extracellular recording arrays used by this simulation.
"""

__tablename__ = EntityType.simulation.value
Expand All @@ -1693,6 +1709,13 @@ class Simulation(Entity, NameDescriptionVectorMixin):
nullable=False,
server_default="{}",
)
recording_arrays: Mapped[list["SimulatableExtracellularRecordingArray"]] = relationship(
"SimulatableExtracellularRecordingArray",
primaryjoin=(
"Simulation.id == SimulatableExtracellularRecordingArrayToSimulation.simulation_id"
),
secondary="simulatable_extracellular_recording_array__simulation",
)

__mapper_args__ = { # ruff:ignore[mutable-class-default]
"polymorphic_identity": __tablename__,
Expand Down
14 changes: 14 additions & 0 deletions app/queries/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
IonChannelModelingCampaign,
IonChannelModelToEModel,
IonChannelRecordingToIonChannelModelingCampaign,
SimulatableExtracellularRecordingArrayToSimulation,
Simulation,
SkeletonizationCampaign,
TaskConfig,
Usage,
Expand Down Expand Up @@ -73,4 +75,16 @@
"nested_id_getter": lambda *, items: [item.id for item in items],
},
},
Simulation: {
"recording_arrays": {
"relationship_name": "recording_arrays",
"db_model_factory": lambda *, parent_id, child_id: (
SimulatableExtracellularRecordingArrayToSimulation(
simulation_id=parent_id,
simulatable_extracellular_recording_array_id=child_id,
)
),
"nested_id_getter": lambda *, items: [item.id for item in items],
},
},
}
9 changes: 6 additions & 3 deletions app/schemas/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
NameDescriptionMixin,
)
from app.schemas.brain_region import BrainRegionCreateMixin, BrainRegionReadMixin
from app.schemas.entity import EntityCreate, EntityRead, NestedEntityRead
from app.schemas.entity import EntityCreate, EntityRead, NestedEntityCreate, NestedEntityRead
from app.schemas.me_model import NestedMEModel
from app.schemas.simulatable_extracellular_recording_array import (
NestedSimulatableExtracellularRecordingArrayRead,
)
from app.schemas.synaptome import NestedSynaptome
from app.schemas.utils import make_update_schema

Expand Down Expand Up @@ -79,7 +82,7 @@ class SimulationBaseMixin(NameDescriptionMixin):


class SimulationCreate(SimulationBaseMixin, EntityCreate):
pass
recording_arrays: list[NestedEntityCreate] = [] # ruff:ignore[mutable-class-default]


SimulationUserUpdate = make_update_schema(SimulationCreate, "SimulationUserUpdate") # pyright: ignore [reportInvalidTypeForm]
Expand All @@ -99,4 +102,4 @@ class SimulationRead(
SimulationBaseMixin,
EntityRead,
):
pass
recording_arrays: list[NestedSimulatableExtracellularRecordingArrayRead]
1 change: 1 addition & 0 deletions app/service/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def _load(query: sa.Select):
joinedload(Simulation.updated_by),
selectinload(Simulation.assets),
selectinload(Simulation.contributions),
selectinload(Simulation.recording_arrays),
raiseload("*"),
)

Expand Down
6 changes: 4 additions & 2 deletions scripts/export/build_database_archive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Automatically generated, do not edit!
set -euo pipefail
SCRIPT_VERSION="1"
SCRIPT_DB_VERSION="79dbeada57f3"
SCRIPT_DB_VERSION="7bb31ca36dbe"
echo "DB dump (version $SCRIPT_VERSION for db version $SCRIPT_DB_VERSION)"


Expand Down Expand Up @@ -211,6 +211,8 @@ SET TRANSACTION READ ONLY;
\copy (SELECT t0.* FROM scientific_artifact_publication_link AS t0 JOIN entity AS t1 ON t1.id=t0.scientific_artifact_id WHERE t1.authorized_public IS NOT false) TO '$DATA_DIR/scientific_artifact_publication_link.csv' WITH CSV HEADER;
\echo Dumping table simulatable_extracellular_recording_array
\copy (SELECT t0.* FROM simulatable_extracellular_recording_array AS t0 JOIN entity AS t1 ON t1.id=t0.id JOIN entity AS t2 ON t2.id=t0.circuit_id WHERE t1.authorized_public IS NOT false AND t2.authorized_public IS NOT false) TO '$DATA_DIR/simulatable_extracellular_recording_array.csv' WITH CSV HEADER;
\echo Dumping table simulatable_extracellular_recording_array__simulation
\copy (SELECT t0.* FROM simulatable_extracellular_recording_array__simulation AS t0 JOIN entity AS t1 ON t1.id=t0.simulatable_extracellular_recording_array_id JOIN entity AS t2 ON t2.id=t0.simulation_id WHERE t1.authorized_public IS NOT false AND t2.authorized_public IS NOT false) TO '$DATA_DIR/simulatable_extracellular_recording_array__simulation.csv' WITH CSV HEADER;
\echo Dumping table simulation
\copy (SELECT t0.* FROM simulation AS t0 JOIN entity AS t1 ON t1.id=t0.id JOIN entity AS t2 ON t2.id=t0.simulation_campaign_id JOIN entity AS t3 ON t3.id=t0.entity_id WHERE t1.authorized_public IS NOT false AND t2.authorized_public IS NOT false AND t3.authorized_public IS NOT false) TO '$DATA_DIR/simulation.csv' WITH CSV HEADER;
\echo Dumping table simulation_campaign
Expand Down Expand Up @@ -265,7 +267,7 @@ install -m 755 /dev/stdin "$WORK_DIR/load.sh" <<'EOF_LOAD_SCRIPT'
# Automatically generated, do not edit!
set -euo pipefail
SCRIPT_VERSION="1"
SCRIPT_DB_VERSION="79dbeada57f3"
SCRIPT_DB_VERSION="7bb31ca36dbe"
echo "DB load (version $SCRIPT_VERSION for db version $SCRIPT_DB_VERSION)"


Expand Down
26 changes: 25 additions & 1 deletion tests/test_simulation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pytest

from app.db.model import Simulation, SimulationCampaign
from app.db.types import EntityType
from app.db.types import ElectrodeType, EntityType
from app.types import EntityRoute

from .utils import (
PROJECT_ID,
Expand Down Expand Up @@ -50,6 +51,7 @@ def _assert_read_response(data, json_data):
assert "authorized_public" in data
assert "authorized_project_id" in data
assert "assets" in data
assert "recording_arrays" in data
assert data["name"] == json_data["name"]
assert data["description"] == json_data["description"]
assert data["type"] == EntityType.simulation
Expand All @@ -60,6 +62,28 @@ def _assert_read_response(data, json_data):
def test_create_one(client, json_data):
data = assert_request(client.post, url=ROUTE, json=json_data).json()
_assert_read_response(data, json_data)
assert data["recording_arrays"] == []


def test_create_one_with_recording_arrays(client, json_data, root_circuit):
array_route = f"/{EntityRoute.simulatable_extracellular_recording_array}"
arrays = [
assert_request(
client.post,
url=array_route,
json={
"name": f"array-{i}",
"description": f"array-description-{i}",
"electrode_type": ElectrodeType.custom,
"circuit_id": str(root_circuit.id),
},
).json()
for i in range(2)
]
payload = json_data | {"recording_arrays": [{"id": array["id"]} for array in arrays]}
data = assert_request(client.post, url=ROUTE, json=payload).json()
_assert_read_response(data, payload)
assert {array["id"] for array in data["recording_arrays"]} == {array["id"] for array in arrays}


def test_update_one(clients, public_json_data):
Expand Down