Skip to content

Commit

Permalink
#48 - Fix ordering of data for fear conditioning (#42)
Browse files Browse the repository at this point in the history
* Fix ordering of data for fear conditioning

* Fix tests

* Update sorting to take module sortorder into account
  • Loading branch information
mixxorz authored Dec 15, 2020
1 parent 692a9b7 commit 939a5eb
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 13 deletions.
18 changes: 18 additions & 0 deletions flare_portal/experiments/migrations/0020_cs_verbose_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2020-12-14 08:53

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("experiments", "0019_trial_unique_together_constraint"),
]

operations = [
migrations.AlterField(
model_name="fearconditioningdata",
name="conditional_stimulus",
field=models.CharField(max_length=24, verbose_name="CS/GS"),
),
]
2 changes: 1 addition & 1 deletion flare_portal/experiments/models/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class FearConditioningData(BaseData):
)
trial = models.PositiveIntegerField()
rating = models.PositiveIntegerField()
conditional_stimulus = models.CharField(max_length=24, verbose_name="CS")
conditional_stimulus = models.CharField(max_length=24, verbose_name="CS/GS")
unconditional_stimulus = models.BooleanField(verbose_name="US")
trial_started_at = models.DateTimeField()
response_recorded_at = models.DateTimeField()
Expand Down
42 changes: 33 additions & 9 deletions flare_portal/experiments/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,23 +193,28 @@ class CreateMeta:


class DataViewMixin:
data_type: BaseData
data_type: Type[BaseData]

def get_data_type(self) -> Type[BaseData]:
return self.data_type

def dispatch(self, *args: Any, **kwargs: Any) -> HttpResponse:
self.experiment = get_object_or_404(Experiment, pk=kwargs["experiment_pk"])
return super().dispatch(*args, **kwargs) # type: ignore

def get_queryset(self) -> QuerySet[BaseData]:
data_type = self.get_data_type()
return (
self.data_type.objects.filter(module__experiment=self.experiment)
data_type.objects.filter(module__experiment=self.experiment)
.order_by("pk")
.select_related("participant", "module")
)

def get_context_data(self, **kwargs: Any) -> dict:
data_type = self.get_data_type()
context = super().get_context_data(**kwargs) # type: ignore
context["experiment"] = self.experiment
context["data_type"] = self.data_type
context["data_type"] = data_type
return context


Expand Down Expand Up @@ -265,18 +270,24 @@ def __init__(self) -> None:
self.urls: List[URLPattern] = []
self.views: Dict[str, Callable] = {}

def register(self, data_model: Type[BaseData]) -> None:
def register(
self, data_model: Type[BaseData], list_view_class: Type[DataListView] = None
) -> None:
module_camel_case = data_model.get_module_camel_case()

self.data_models.append(data_model)

# ListView
list_view_class: ListView = type( # type: ignore
f"{module_camel_case}ListView", (DataListView,), {"data_type": data_model},
)
list_view_name = data_model.get_list_path_name()
if list_view_class is not None:
self.views[list_view_name] = list_view_class.as_view()
else:
self.views[list_view_name]: DataListView = type( # type: ignore
f"{module_camel_case}ListView",
(DataListView,),
{"data_type": data_model},
).as_view()

self.views[list_view_name] = list_view_class.as_view()
self.urls.append(
path(
data_model.get_list_path(),
Expand All @@ -303,7 +314,20 @@ def register(self, data_model: Type[BaseData]) -> None:
)


class FearConditioningDataListView(DataListView):
data_type = FearConditioningData

def get_queryset(self) -> QuerySet[BaseData]:
return (
super()
.get_queryset()
.order_by("participant_id", "module__sortorder", "trial")
)


data_viewset_registry = DataViewsetRegistry()

data_viewset_registry.register(BasicInfoData)
data_viewset_registry.register(FearConditioningData)
data_viewset_registry.register(
FearConditioningData, list_view_class=FearConditioningDataListView
)
6 changes: 3 additions & 3 deletions flare_portal/experiments/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def test_data_values(self) -> None:
("phase", module.get_phase_display()),
("trial", data.trial),
("rating", data.rating),
("CS", data.conditional_stimulus),
("CS/GS", data.conditional_stimulus),
("US", data.unconditional_stimulus),
("trial started at", data.trial_started_at),
("response recorded at", data.response_recorded_at),
Expand All @@ -154,7 +154,7 @@ def test_list_display_columns(self) -> None:
columns = FearConditioningData.get_list_display_columns()

self.assertEqual(
columns, ["participant", "phase", "trial", "CS", "US", "rating"]
columns, ["participant", "phase", "trial", "CS/GS", "US", "rating"]
)

def test_list_display_values(self) -> None:
Expand All @@ -180,7 +180,7 @@ def test_list_display_values(self) -> None:
("participant", data.participant),
("phase", module.get_phase_display()),
("trial", data.trial),
("CS", data.conditional_stimulus),
("CS/GS", data.conditional_stimulus),
("US", data.unconditional_stimulus),
("rating", data.rating),
],
Expand Down
65 changes: 65 additions & 0 deletions flare_portal/experiments/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,71 @@ def test_filter(self) -> None:
self.assertEqual(participant, resp.context["participant"])
self.assertEqual(list(resp.context["data"]), participant_data)

def test_data_list_view_override(self) -> None:
# Should be able to override the pregenerated DataListView
project: Project = ProjectFactory()
experiment: Experiment = ExperimentFactory(project=project)
module_1: FearConditioningModule = FearConditioningModuleFactory(
experiment=experiment, sortorder=2,
)
module_2: FearConditioningModule = FearConditioningModuleFactory(
experiment=experiment, sortorder=1,
)
participant_1: Participant = ParticipantFactory(experiment=experiment)
participant_2: Participant = ParticipantFactory(experiment=experiment)
data = sorted(
[
FearConditioningDataFactory(
trial=4, module=module_1, participant=participant_1
),
FearConditioningDataFactory(
trial=2, module=module_1, participant=participant_1
),
FearConditioningDataFactory(
trial=3, module=module_1, participant=participant_1
),
FearConditioningDataFactory(
trial=1, module=module_1, participant=participant_1
),
FearConditioningDataFactory(
trial=4, module=module_2, participant=participant_2
),
FearConditioningDataFactory(
trial=2, module=module_2, participant=participant_2
),
FearConditioningDataFactory(
trial=3, module=module_2, participant=participant_2
),
FearConditioningDataFactory(
trial=1, module=module_2, participant=participant_2
),
FearConditioningDataFactory(
trial=4, module=module_1, participant=participant_2
),
FearConditioningDataFactory(
trial=2, module=module_1, participant=participant_2
),
FearConditioningDataFactory(
trial=3, module=module_1, participant=participant_2
),
FearConditioningDataFactory(
trial=1, module=module_1, participant=participant_2
),
],
key=lambda d: (d.participant_id, d.module.sortorder, d.trial),
)
url = reverse(
"experiments:data:fear_conditioning_data_list",
kwargs={"project_pk": project.pk, "experiment_pk": experiment.pk},
)

resp = self.client.get(url)

self.assertEqual(resp.status_code, 200)

self.assertEqual(resp.context["data_type"], FearConditioningData)
self.assertEqual(list(resp.context["data"]), data)


class DataDetailViewTest(TestCase):
def setUp(self) -> None:
Expand Down

0 comments on commit 939a5eb

Please sign in to comment.