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
17 changes: 17 additions & 0 deletions app/db/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sqlalchemy.orm import Query

from app.db.model import Entity
from app.schemas.auth import UserContext


def constrain_to_accessible_entities[Q: Query | Select](
Expand All @@ -25,6 +26,22 @@ def constrain_to_accessible_entities[Q: Query | Select](
return query


def constrain_to_private_entities[Q: Query | Select](
query: Q,
user_context: UserContext,
db_model_class: Any = Entity,
) -> Q:
"""Ensure a query is filtered to private rows that are viewable by the user."""
return query.where(
and_(
db_model_class.authorized_public == false(),
db_model_class.authorized_project_id.in_(user_context.user_project_ids)
if user_context.user_project_ids
else false(),
)
)


def constrain_entity_query_to_project[Q: Query | Select | Delete](query: Q, project_id: UUID4) -> Q:
"""Ensure a query is filtered to rows in the user's project."""
return query.where(Entity.authorized_project_id == project_id)
Expand Down
34 changes: 34 additions & 0 deletions app/queries/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from app.db.auth import (
constrain_to_accessible_entities,
constrain_to_private_entities,
select_unauthorized_entities,
)
from app.db.model import Activity, Agent, Generation, Identifiable, Person, Usage
Expand Down Expand Up @@ -329,6 +330,39 @@ def router_read_many[T: BaseModel, I: Identifiable]( # noqa: PLR0913
)


def router_update_one[T: BaseModel, I: Identifiable](
*,
id_: uuid.UUID,
db: Session,
db_model_class: type[I],
user_context: UserContext,
json_model: BaseModel,
response_schema_class: type[T],
apply_operations: ApplyOperations | None = None,
):
query = (
sa.select(db_model_class).where(db_model_class.id == id_).with_for_update(of=db_model_class)
)
if id_model_class := get_declaring_class(db_model_class, "authorized_project_id"):
query = constrain_to_private_entities(query, user_context, db_model_class=id_model_class)
if apply_operations:
query = apply_operations(query)

with ensure_result(error_message=f"{db_model_class.__name__} not found"):
obj = db.execute(query).unique().scalar_one()

# remove attributes with NOT_SET sentinel and leave only user set ones
update_data = json_model.model_dump(exclude_defaults=True)

for key, value in update_data.items():
setattr(obj, key, value)

db.flush()
db.refresh(obj)

return response_schema_class.model_validate(obj)


def router_delete_one[T: BaseModel, I: Identifiable](
*,
id_: uuid.UUID,
Expand Down
1 change: 1 addition & 0 deletions app/routers/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
read_many = router.get("")(app.service.circuit.read_many)
read_one = router.get("/{id_}")(app.service.circuit.read_one)
create_one = router.post("")(app.service.circuit.create_one)
update_one = router.patch("/{id_}")(app.service.circuit.update_one)
1 change: 1 addition & 0 deletions app/routers/electrical_cell_recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.electrical_cell_recording.read_many)
read_one = router.get("/{id_}")(app.service.electrical_cell_recording.read_one)
create_one = router.post("")(app.service.electrical_cell_recording.create_one)
update_one = router.patch("/{id_}")(app.service.electrical_cell_recording.update_one)
1 change: 1 addition & 0 deletions app/routers/electrical_recording_stimulus.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.electrical_recording_stimulus.read_many)
read_one = router.get("/{id_}")(app.service.electrical_recording_stimulus.read_one)
create_one = router.post("")(app.service.electrical_recording_stimulus.create_one)
update_one = router.patch("/{id_}")(app.service.electrical_recording_stimulus.update_one)
1 change: 1 addition & 0 deletions app/routers/emodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
read_many = router.get("")(app.service.emodel.read_many)
read_one = router.get("/{id_}")(app.service.emodel.read_one)
create_one = router.post("")(app.service.emodel.create_one)
update_one = router.patch("/{id_}")(app.service.emodel.update_one)
3 changes: 2 additions & 1 deletion app/routers/experimental_bouton_density.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
)

read_many = router.get("")(app.service.experimental_bouton_density.read_many)
read_onw = router.get("/{id_}")(app.service.experimental_bouton_density.read_one)
read_one = router.get("/{id_}")(app.service.experimental_bouton_density.read_one)
create_one = router.post("")(app.service.experimental_bouton_density.create_one)
update_one = router.patch("/{id_}")(app.service.experimental_bouton_density.update_one)
1 change: 1 addition & 0 deletions app/routers/experimental_neuron_density.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.experimental_neuron_density.read_many)
read_one = router.get("/{id_}")(app.service.experimental_neuron_density.read_one)
create_one = router.post("")(app.service.experimental_neuron_density.create_one)
update_one = router.patch("/{id_}")(app.service.experimental_neuron_density.update_one)
1 change: 1 addition & 0 deletions app/routers/experimental_synapses_per_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.experimental_synapses_per_connection.read_many)
read_one = router.get("/{id_}")(app.service.experimental_synapses_per_connection.read_one)
create_one = router.post("")(app.service.experimental_synapses_per_connection.create_one)
update_one = router.patch("/{id_}")(app.service.experimental_synapses_per_connection.update_one)
1 change: 1 addition & 0 deletions app/routers/ion_channel_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.ion_channel_model.read_many)
read_one = router.get("/{id_}")(app.service.ion_channel_model.read_one)
create_one = router.post("")(app.service.ion_channel_model.create_one)
update_one = router.patch("/{id_}")(app.service.ion_channel_model.update_one)
1 change: 1 addition & 0 deletions app/routers/memodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.memodel.read_many)
read_one = router.get("/{id_}")(app.service.memodel.read_one)
create_one = router.post("")(app.service.memodel.create_one)
update_one = router.patch("/{id_}")(app.service.memodel.update_one)
2 changes: 2 additions & 0 deletions app/routers/memodel_calibration_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
prefix="/memodel-calibration-result",
tags=["memodel-calibration-result"],
)

read_many = router.get("")(app.service.memodel_calibration_result.read_many)
read_one = router.get("/{id_}")(app.service.memodel_calibration_result.read_one)
create_one = router.post("")(app.service.memodel_calibration_result.create_one)
update_one = router.patch("/{id_}")(app.service.memodel_calibration_result.update_one)
1 change: 1 addition & 0 deletions app/routers/morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.morphology.read_many)
read_one = router.get("/{id_}")(app.service.morphology.read_one)
create_one = router.post("")(app.service.morphology.create_one)
update_one = router.patch("/{id_}")(app.service.morphology.update_one)
1 change: 1 addition & 0 deletions app/routers/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.simulation.read_many)
read_one = router.get("/{id_}")(app.service.simulation.read_one)
create_one = router.post("")(app.service.simulation.create_one)
update_one = router.patch("/{id_}")(app.service.simulation.update_one)
1 change: 1 addition & 0 deletions app/routers/simulation_campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.simulation_campaign.read_many)
read_one = router.get("/{id_}")(app.service.simulation_campaign.read_one)
create_one = router.post("")(app.service.simulation_campaign.create_one)
update_one = router.patch("/{id_}")(app.service.simulation_campaign.update_one)
1 change: 1 addition & 0 deletions app/routers/simulation_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.simulation_result.read_many)
read_one = router.get("/{id_}")(app.service.simulation_result.read_one)
create_one = router.post("")(app.service.simulation_result.create_one)
update_one = router.patch("/{id_}")(app.service.simulation_result.update_one)
1 change: 1 addition & 0 deletions app/routers/single_neuron_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.single_neuron_simulation.read_many)
read_one = router.get("/{id_}")(app.service.single_neuron_simulation.read_one)
create_one = router.post("")(app.service.single_neuron_simulation.create_one)
update_one = router.patch("/{id_}")(app.service.single_neuron_simulation.update_one)
1 change: 1 addition & 0 deletions app/routers/single_neuron_synaptome.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.single_neuron_synaptome.read_many)
read_one = router.get("/{id_}")(app.service.single_neuron_synaptome.read_one)
create_one = router.post("")(app.service.single_neuron_synaptome.create_one)
update_one = router.patch("/{id_}")(app.service.single_neuron_synaptome.update_one)
1 change: 1 addition & 0 deletions app/routers/single_neuron_synaptome_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.single_neuron_synaptome_simulation.read_many)
read_one = router.get("/{id_}")(app.service.single_neuron_synaptome_simulation.read_one)
create_one = router.post("")(app.service.single_neuron_synaptome_simulation.create_one)
update_one = router.patch("/{id_}")(app.service.single_neuron_synaptome_simulation.update_one)
1 change: 1 addition & 0 deletions app/routers/subject.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.subject.read_many)
read_one = router.get("/{id_}")(app.service.subject.read_one)
create_one = router.post("")(app.service.subject.create_one)
update_one = router.patch("/{id_}")(app.service.subject.update_one)
1 change: 1 addition & 0 deletions app/routers/validation_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
read_many = router.get("")(app.service.validation_result.read_many)
read_one = router.get("/{id_}")(app.service.validation_result.read_one)
create_one = router.post("")(app.service.validation_result.create_one)
update_one = router.patch("/{id_}")(app.service.validation_result.update_one)
4 changes: 4 additions & 0 deletions app/schemas/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from app.db.types import CircuitBuildCategory, CircuitScale
from app.schemas.scientific_artifact import ScientificArtifactCreate, ScientificArtifactRead
from app.schemas.utils import make_update_schema


class CircuitBase(BaseModel):
Expand Down Expand Up @@ -34,3 +35,6 @@ class CircuitRead(CircuitBase, ScientificArtifactRead):

class CircuitCreate(CircuitBase, ScientificArtifactCreate):
pass


CircuitUpdate = make_update_schema(CircuitCreate, "CircuitUpdate") # pyright: ignore [reportInvalidTypeForm]
14 changes: 14 additions & 0 deletions app/schemas/density.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from app.schemas.contribution import ContributionReadWithoutEntityMixin
from app.schemas.subject import SubjectReadMixin
from app.schemas.utils import make_update_schema


class MeasurementRead(BaseModel):
Expand Down Expand Up @@ -73,6 +74,19 @@ class ExperimentalSynapsesPerConnectionCreate(ExperimentalDensityCreate):
post_region_id: uuid.UUID


ExperimentalSynapsesPerConnectionUpdate = make_update_schema(
ExperimentalSynapsesPerConnectionCreate, "ExperimentalSynapsesPerConnectionUpdate"
) # pyright: ignore [reportInvalidTypeForm]

ExperimentalBoutonDensityUpdate = make_update_schema(
ExperimentalBoutonDensityCreate, "ExperimentalBoutonDensityUpdate"
) # pyright: ignore [reportInvalidTypeForm]

ExperimentalNeuronDensityUpdate = make_update_schema(
ExperimentalNeuronDensityCreate, "ExperimentalNeuronDensityUpdate"
) # pyright: ignore [reportInvalidTypeForm]


class ExperimentalNeuronDensityRead(ExperimentalDensityRead):
mtypes: list[MTypeClassRead] | None
etypes: list[ETypeClassRead] | None
Expand Down
6 changes: 6 additions & 0 deletions app/schemas/electrical_cell_recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
ScientificArtifactCreate,
ScientificArtifactRead,
)
from app.schemas.utils import make_update_schema


class ElectricalCellRecordingBase(BaseModel):
Expand Down Expand Up @@ -71,6 +72,11 @@ class ElectricalCellRecordingCreate(ElectricalCellRecordingBase, ScientificArtif
pass


ElectricalCellRecordingUpdate = make_update_schema(
ElectricalCellRecordingCreate, "ElectricalCellRecordingUpdate"
) # pyright : ignore [reportInvalidTypeForm]


class ElectricalCellRecordingRead(
ElectricalCellRecordingBase,
ScientificArtifactRead,
Expand Down
6 changes: 6 additions & 0 deletions app/schemas/electrical_recording_stimulus.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
EntityTypeMixin,
IdentifiableMixin,
)
from app.schemas.utils import make_update_schema


class ElectricalRecordingStimulusBase(BaseModel):
Expand Down Expand Up @@ -46,3 +47,8 @@ class ElectricalRecordingStimulusCreate(
ElectricalRecordingStimulusBase, AuthorizationOptionalPublicMixin
):
pass


ElectricalRecordingStimulusUpdate = make_update_schema(
ElectricalRecordingStimulusCreate, "ElectricalRecordingStimulusUpdate"
) # pyright: ignore [reportInvalidTypeForm]
4 changes: 4 additions & 0 deletions app/schemas/emodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from app.schemas.ion_channel_model import IonChannelModelWAssets
from app.schemas.morphology import ReconstructionMorphologyBase
from app.schemas.species import NestedSpeciesRead, NestedStrainRead
from app.schemas.utils import make_update_schema


class ExemplarMorphology(CreationMixin, ReconstructionMorphologyBase, IdentifiableMixin):
Expand All @@ -39,6 +40,9 @@ class EModelCreate(EModelBase, AuthorizationOptionalPublicMixin):
exemplar_morphology_id: uuid.UUID


EModelUpdate = make_update_schema(EModelCreate, "EModelUpdate") # pyright: ignore [reportInvalidTypeForm]


class EModelRead(
EModelBase,
CreationMixin,
Expand Down
4 changes: 4 additions & 0 deletions app/schemas/ion_channel_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ScientificArtifactRead,
)
from app.schemas.subject import SubjectReadMixin
from app.schemas.utils import make_update_schema


class UseIon(BaseModel):
Expand Down Expand Up @@ -44,6 +45,9 @@ class IonChannelModelCreate(
pass


IonChannelModelUpdate = make_update_schema(IonChannelModelCreate, "IonChannelModelUpdate") # pyright: ignore [reportInvalidTypeForm]


class IonChannelModelRead(
IonChannelModelBase,
NestedScientificArtifactRead,
Expand Down
4 changes: 4 additions & 0 deletions app/schemas/me_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from app.schemas.memodel_calibration_result import MEModelCalibrationResultRead
from app.schemas.morphology import ReconstructionMorphologyRead
from app.schemas.species import NestedSpeciesRead, NestedStrainRead
from app.schemas.utils import make_update_schema


class MEModelBase(BaseModel):
Expand All @@ -41,6 +42,9 @@ class MEModelCreate(MEModelBase, AuthorizationOptionalPublicMixin):
strain_id: uuid.UUID | None = None


MEModelUpdate = make_update_schema(MEModelCreate, "MEModelUpdate") # pyright: ignore [reportInvalidTypeForm]


class MEModelRead(
MEModelBase,
CreationMixin,
Expand Down
6 changes: 6 additions & 0 deletions app/schemas/memodel_calibration_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
CreationMixin,
IdentifiableMixin,
)
from app.schemas.utils import make_update_schema


class MEModelCalibrationResultBase(BaseModel):
Expand All @@ -34,3 +35,8 @@ class MEModelCalibrationResultCreate(
MEModelCalibrationResultBase, AuthorizationOptionalPublicMixin
):
"""Create model for MEModel calibration results."""


MEModelCalibrationResultUpdate = make_update_schema(
MEModelCalibrationResultCreate, "MEModelCalibrationResultUpdate"
) # pyright: ignore [reportInvalidTypeForm]
6 changes: 6 additions & 0 deletions app/schemas/morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from app.schemas.contribution import ContributionReadWithoutEntityMixin
from app.schemas.measurement_annotation import MeasurementAnnotationRead
from app.schemas.species import NestedSpeciesRead, NestedStrainRead
from app.schemas.utils import make_update_schema


class ReconstructionMorphologyBase(BaseModel):
Expand All @@ -40,6 +41,11 @@ class ReconstructionMorphologyCreate(
legacy_id: list[str] | None = None


ReconstructionMorphologyUpdate = make_update_schema(
ReconstructionMorphologyCreate, "ReconstructionMorphologyUpdate"
) # pyright: ignore [reportInvalidTypeForm]


class ReconstructionMorphologyRead(
ReconstructionMorphologyBase,
CreationMixin,
Expand Down
14 changes: 14 additions & 0 deletions app/schemas/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from app.schemas.me_model import NestedMEModel
from app.schemas.synaptome import NestedSynaptome
from app.schemas.utils import make_update_schema


class SingleNeuronSimulationBase(BaseModel):
Expand All @@ -36,6 +37,11 @@ class SingleNeuronSimulationCreate(
me_model_id: uuid.UUID


SingleNeuronSimulationUpdate = make_update_schema(
SingleNeuronSimulationCreate, "SingleNeuronSimulationUpdate"
) # pyright: ignore [reportInvalidTypeForm]


class SingleNeuronSimulationRead(
SingleNeuronSimulationBase,
BrainRegionReadMixin,
Expand All @@ -57,6 +63,11 @@ class SingleNeuronSynaptomeSimulationCreate(
synaptome_id: uuid.UUID


SingleNeuronSynaptomeSimulationUpdate = make_update_schema(
SingleNeuronSynaptomeSimulationCreate, "SingleNeuronSynaptomeSimulationUpdate"
) # pyright: ignore [reportInvalidTypeForm]


class SingleNeuronSynaptomeSimulationRead(
SingleNeuronSimulationBase,
BrainRegionReadMixin,
Expand All @@ -83,6 +94,9 @@ class SimulationCreate(SimulationBase, AuthorizationOptionalPublicMixin):
pass


SimulationUpdate = make_update_schema(SimulationCreate, "SimulationUpdate") # pyright: ignore [reportInvalidTypeForm]


class NestedSimulationRead(SimulationBase, EntityTypeMixin, IdentifiableMixin):
pass

Expand Down
Loading