diff --git a/backend/seqvars/factory_defaults.py b/backend/seqvars/factory_defaults.py index 6dcea367e..661a46d75 100644 --- a/backend/seqvars/factory_defaults.py +++ b/backend/seqvars/factory_defaults.py @@ -1195,9 +1195,7 @@ def create_seqvarsquerypresetscolumns(faker: Faker) -> list[SeqvarsQueryPresetsC # default column names COLUMNS_DEFAULT = ( "index", - "__chrom_pos__", "payload.variant_annotation.gene.identity.gene_symbol", - "payload.variant_annotation.gene.consequences.hgvs_p", "__gene_flags__", "__effect__", "payload.variant_annotation.gene.consequences.consequences", @@ -1219,9 +1217,7 @@ def create_seqvarsquerypresetscolumns(faker: Faker) -> list[SeqvarsQueryPresetsC # clinvar filter column names COLUMNS_CLINVAR = ( "index", - "__chrom_pos__", "payload.variant_annotation.gene.identity.gene_symbol", - "payload.variant_annotation.gene.consequences.hgvs_p", "__gene_flags__", "__effect__", "payload.variant_annotation.gene.consequences.consequences", diff --git a/backend/seqvars/migrations/0012_auto_20241024_0823.py b/backend/seqvars/migrations/0012_auto_20241024_0823.py new file mode 100644 index 000000000..696a9f0a2 --- /dev/null +++ b/backend/seqvars/migrations/0012_auto_20241024_0823.py @@ -0,0 +1,66 @@ +# Generated by Django 3.2.25 on 2024-10-24 08:23 + +import uuid + +import django.core.serializers.json +from django.db import migrations, models +import django.db.models.deletion +import django_pydantic_field.compat.django +import django_pydantic_field.fields +import model_clone.mixin + +import seqvars.models.base + + +class Migration(migrations.Migration): + + dependencies = [ + ("seqvars", "0011_alter_seqvarsqueryexecution_options"), + ] + + operations = [ + migrations.CreateModel( + name="SeqvarsQuerySettingsColumns", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("sodar_uuid", models.UUIDField(default=uuid.uuid4, unique=True)), + ("date_created", models.DateTimeField(auto_now_add=True)), + ("date_modified", models.DateTimeField(auto_now=True)), + ( + "column_settings", + django_pydantic_field.fields.PydanticSchemaField( + config=None, + default=list, + encoder=django.core.serializers.json.DjangoJSONEncoder, + schema=django_pydantic_field.compat.django.GenericContainer( + list, (seqvars.models.base.SeqvarsColumnConfigPydantic,) + ), + ), + ), + ( + "querysettings", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="columns", + to="seqvars.seqvarsquerysettings", + ), + ), + ], + options={ + "abstract": False, + }, + bases=(model_clone.mixin.CloneMixin, models.Model), + ), + migrations.RemoveField( + model_name="seqvarsquery", + name="columnsconfig", + ), + migrations.DeleteModel( + name="SeqvarsQueryColumnsConfig", + ), + ] diff --git a/backend/seqvars/models/base.py b/backend/seqvars/models/base.py index 3882b791f..c2970174f 100644 --- a/backend/seqvars/models/base.py +++ b/backend/seqvars/models/base.py @@ -885,6 +885,9 @@ def from_predefinedquery( SeqvarsQuerySettingsClinvar.objects.from_presets( querysettings=querysettings, clinvarpresets=predefinedquery.clinvar ) + SeqvarsQuerySettingsColumns.objects.from_presets( + querysettings=querysettings, columnspresets=predefinedquery.columns + ) return querysettings @@ -903,6 +906,7 @@ class SeqvarsQuerySettings(model_clone.CloneMixin, BaseModel): "phenotypeprio", "quality", "clinvar", + "columns", ] #: Custom manager with ``from_predefinedquery()``. @@ -1601,38 +1605,37 @@ def __str__(self): return f"SeqvarsQuerySettingsClinvar '{self.sodar_uuid}'" -class SeqvarsQueryColumnsConfigManager(models.Manager): - """Manager for ``SeqvarsQueryColumnsConfig``.""" +class SeqvarsQuerySettingsColumnsManager(models.Manager): + """Manager for ``SeqvarsQuerySettingsColumns``.""" - def from_predefinedquery( + def from_presets( self, *, - predefinedquery: SeqvarsPredefinedQuery, - ) -> "SeqvarsQueryColumnsConfig": - if predefinedquery.columns.column_settings: - return super().create( - column_settings=predefinedquery.columns.column_settings, - ) - else: - return super().create(column_settings=[]) + querysettings: SeqvarsQuerySettings, + columnspresets: SeqvarsQueryPresetsColumns, + ) -> "SeqvarsQuerySettingsColumns": + return super().create( + querysettings=querysettings, + column_settings=columnspresets.column_settings, + ) -class SeqvarsQueryColumnsConfig(model_clone.CloneMixin, SeqvarsColumnsSettingsBase, BaseModel): - """Per-query (not execution) configuration of columns. - - This will be copied over from the presets to the query and not the query - settings. Thus, it will not be persisted by query execution but is - editable after query execution. - """ +class SeqvarsQuerySettingsColumns(model_clone.CloneMixin, SeqvarsColumnsSettingsBase, BaseModel): + """Configuration of columns.""" #: Let the ``sodar_uuid`` value be re-created when cloning. _clone_excluded_fields = ["sodar_uuid"] #: Custom manager with ``from_predefinedquery()``. - objects = SeqvarsQueryColumnsConfigManager() + objects = SeqvarsQuerySettingsColumnsManager() + + #: The owning ``QuerySettings``. + querysettings = models.OneToOneField( + SeqvarsQuerySettings, on_delete=models.CASCADE, related_name="columns" + ) def __str__(self): - return f"SeqvarsQueryColumnsConfig '{self.sodar_uuid}'" + return f"SeqvarsQuerySettingsColumns '{self.sodar_uuid}'" class SeqvarsQueryManager(models.Manager): @@ -1658,9 +1661,6 @@ def from_predefinedquery( settings=SeqvarsQuerySettings.objects.from_predefinedquery( session=session, predefinedquery=predefinedquery ), - columnsconfig=SeqvarsQueryColumnsConfig.objects.from_predefinedquery( - predefinedquery=predefinedquery - ), ) return query @@ -1691,8 +1691,6 @@ class SeqvarsQuery(model_clone.CloneMixin, BaseModel): session = models.ForeignKey(CaseAnalysisSession, on_delete=models.CASCADE) #: Query settings to be edited in the next query execution. settings = models.OneToOneField(SeqvarsQuerySettings, on_delete=models.CASCADE) - #: The columns configuration of the query. - columnsconfig = models.OneToOneField(SeqvarsQueryColumnsConfig, on_delete=models.PROTECT) @property def case(self) -> typing.Optional[Case]: @@ -1908,6 +1906,13 @@ class SeqvarsQuerySettingsClinvarPydantic(pydantic.BaseModel): allow_conflicting_interpretations: bool = False +class SeqvarsQuerySettingsColumnsPydantic(pydantic.BaseModel): + """Pydantic representation of ``SeqvarsQuerySettingsColumns``.""" + + #: List of columns with their widths. + column_settings: typing.List[SeqvarsColumnConfigPydantic] = [] + + class SeqvarsCaseQueryPydantic(pydantic.BaseModel): """Pydantic representation of ``SeqvarsCaseQuery``.""" @@ -1923,6 +1928,8 @@ class SeqvarsCaseQueryPydantic(pydantic.BaseModel): locus: typing.Optional[SeqvarsQuerySettingsLocusPydantic] = None #: ClinVar query settings. clinvar: typing.Optional[SeqvarsQuerySettingsClinvarPydantic] = None + #: Column settings. + columns: typing.Optional[SeqvarsQuerySettingsColumnsPydantic] = None class DataSourceInfoPydantic(pydantic.BaseModel): @@ -1992,13 +1999,13 @@ def values(cls) -> list[str]: class SeqvarsVariantScoreColumnPydantic(pydantic.BaseModel): """Store information about the variant score columns in the output.""" - #: Name of the scolumn. + #: Name of the column. name: str - #: Label for the scolumn. + #: Label for the column. label: str - #: Description of the scolumn. + #: Description of the column. description: typing.Optional[str] = None - #: Type of the scolumn. + #: Type of the column. type: SeqvarsVariantScoreColumnTypeChoice diff --git a/backend/seqvars/serializers.py b/backend/seqvars/serializers.py index 47ca2a35d..0e041df4f 100644 --- a/backend/seqvars/serializers.py +++ b/backend/seqvars/serializers.py @@ -30,7 +30,6 @@ SeqvarsPredefinedQuery, SeqvarsPrioServicePydantic, SeqvarsQuery, - SeqvarsQueryColumnsConfig, SeqvarsQueryExecution, SeqvarsQueryPresetsClinvar, SeqvarsQueryPresetsColumns, @@ -45,6 +44,7 @@ SeqvarsQuerySettings, SeqvarsQuerySettingsCategoryBase, SeqvarsQuerySettingsClinvar, + SeqvarsQuerySettingsColumns, SeqvarsQuerySettingsConsequence, SeqvarsQuerySettingsFrequency, SeqvarsQuerySettingsGenotype, @@ -866,6 +866,20 @@ class Meta: read_only_fields = fields +class SeqvarsQuerySettingsColumnsSerializer( + ColumnsSettingsBaseSerializer, SeqvarsQuerySettingsBaseSerializer +): + """Serializer for ``QuerySettingsColumns``.""" + + class Meta: + model = SeqvarsQuerySettingsColumns + fields = ( + ColumnsSettingsBaseSerializer.Meta.fields + + SeqvarsQuerySettingsBaseSerializer.Meta.fields + ) + read_only_fields = fields + + class SeqvarsQuerySettingsSerializer(BaseModelSerializer): """Serializer for ``QuerySettings``.""" @@ -933,6 +947,8 @@ class SeqvarsQuerySettingsSerializer(BaseModelSerializer): variantprio = serializers.ReadOnlyField(source="variantprio.sodar_uuid") #: Serialize ``clinvar`` as its ``sodar_uuid``. clinvar = serializers.ReadOnlyField(source="clinvar.sodar_uuid") + #: Serialize ``columns`` as its ``sodar_uuid``. + columns = serializers.ReadOnlyField(source="columns.sodar_uuid") def validate(self, attrs): """Augment the attributes by the case from context.""" @@ -963,6 +979,7 @@ class Meta: "phenotypeprio", "variantprio", "clinvar", + "columns", ] read_only_fields = fields @@ -1025,6 +1042,8 @@ class SeqvarsQuerySettingsDetailsSerializer( variantprio = SeqvarsQuerySettingsVariantPrioSerializer() #: Nested serialization of the clinvar settings. clinvar = SeqvarsQuerySettingsClinvarSerializer() + #: Nested serialization of the columns settings. + columns = SeqvarsQuerySettingsColumnsSerializer() def validate(self, data): data = super().validate(data) @@ -1091,25 +1110,11 @@ class Meta: "phenotypeprio", "variantprio", "clinvar", + "columns", ] read_only_fields = fields -class SeqvarsQueryColumnsConfigSerializer(ColumnsSettingsBaseSerializer, BaseModelSerializer): - """Serializer for ``QueryColumnsConfig``.""" - - def validate(self, attrs): - """Augment the attributes by the case from context.""" - if "query" in self.context: - attrs["query"] = self.context["query"] - return attrs - - class Meta: - model = SeqvarsQueryColumnsConfig - fields = BaseModelSerializer.Meta.fields + ColumnsSettingsBaseSerializer.Meta.fields - read_only_fields = fields - - class SeqvarsQueryCreateFromSerializer(serializers.Serializer): """Serializer used for drf-spectacular arguments for ``SeqvarsQuerySettingsViewSet.create_from``.""" @@ -1159,8 +1164,6 @@ class SeqvarsQueryDetailsSerializer(SeqvarsQuerySerializer, SodarUuidWritableNes #: For the details serializer, we use a nested details serializer. settings = SeqvarsQuerySettingsDetailsSerializer() - #: Render the columns configuration here. - columnsconfig = SeqvarsQueryColumnsConfigSerializer() class Meta: model = SeqvarsQuery diff --git a/backend/seqvars/tests/factories.py b/backend/seqvars/tests/factories.py index d66631854..9c576de6b 100644 --- a/backend/seqvars/tests/factories.py +++ b/backend/seqvars/tests/factories.py @@ -22,7 +22,6 @@ SeqvarsPredefinedQuery, SeqvarsPrioServicePydantic, SeqvarsQuery, - SeqvarsQueryColumnsConfig, SeqvarsQueryExecution, SeqvarsQueryPresetsClinvar, SeqvarsQueryPresetsColumns, @@ -36,6 +35,7 @@ SeqvarsQueryPresetsVariantPrio, SeqvarsQuerySettings, SeqvarsQuerySettingsClinvar, + SeqvarsQuerySettingsColumns, SeqvarsQuerySettingsConsequence, SeqvarsQuerySettingsFrequency, SeqvarsQuerySettingsGenotype, @@ -405,6 +405,10 @@ class SeqvarsQuerySettingsFactory(BaseModelFactory): "seqvars.tests.factories.SeqvarsQuerySettingsClinvarFactory", factory_related_name="querysettings", ) + columns = factory.RelatedFactory( + "seqvars.tests.factories.SeqvarsQuerySettingsColumnsFactory", + factory_related_name="querysettings", + ) class Meta: model = SeqvarsQuerySettings @@ -499,6 +503,16 @@ class Meta: model = SeqvarsQuerySettingsClinvar +class SeqvarsQuerySettingsColumnsFactory(ColumnsSettingsBaseFactory, BaseModelFactory): + + # We pass in columns=None to prevent creation of a second + # ``QuerySettingsColumns``. + querysettings = factory.SubFactory(SeqvarsQuerySettingsFactory, columns=None) + + class Meta: + model = SeqvarsQuerySettingsColumns + + class SeqvarsQueryFactory(BaseModelFactory): rank = 1 @@ -506,18 +520,11 @@ class SeqvarsQueryFactory(BaseModelFactory): session = factory.SubFactory(CaseAnalysisSessionFactory) settings = factory.SubFactory(SeqvarsQuerySettingsFactory) - columnsconfig = factory.SubFactory("seqvars.tests.factories.SeqvarsQueryColumnsConfigFactory") class Meta: model = SeqvarsQuery -class SeqvarsQueryColumnsConfigFactory(ColumnsSettingsBaseFactory, BaseModelFactory): - - class Meta: - model = SeqvarsQueryColumnsConfig - - class SeqvarsQueryExecutionFactory(BaseModelFactory): state = SeqvarsQueryExecution.STATE_DONE diff --git a/backend/seqvars/tests/snapshots/snap_test_factory_defaults.py b/backend/seqvars/tests/snapshots/snap_test_factory_defaults.py index 80879889d..e9eac1385 100644 --- a/backend/seqvars/tests/snapshots/snap_test_factory_defaults.py +++ b/backend/seqvars/tests/snapshots/snap_test_factory_defaults.py @@ -285,7 +285,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -327,7 +327,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { @@ -849,7 +849,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -891,7 +891,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { @@ -2942,7 +2942,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -2984,7 +2984,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { @@ -3506,7 +3506,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -3548,7 +3548,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { @@ -5599,7 +5599,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -5641,7 +5641,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { @@ -6163,7 +6163,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -6205,7 +6205,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { diff --git a/backend/seqvars/tests/snapshots/snap_test_models.py b/backend/seqvars/tests/snapshots/snap_test_models.py index 3b3344d78..5f534f16b 100644 --- a/backend/seqvars/tests/snapshots/snap_test_models.py +++ b/backend/seqvars/tests/snapshots/snap_test_models.py @@ -2,7 +2,7 @@ # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals -from snapshottest import Snapshot +from snapshottest import GenericRepr, Snapshot snapshots = Snapshot() @@ -19,6 +19,12 @@ snapshots["TestSeqvarsQuerySettingsClinvar::test_from_presets clinvar_presence_required"] = False +snapshots["TestSeqvarsQuerySettingsColumns::test_from_presets column_settings"] = [ + GenericRepr( + "SeqvarsColumnConfigPydantic(name='chromosome', label='Chromosome', description=None, width=300, visible=True)" + ) +] + snapshots["TestSeqvarsQuerySettingsConsequence::test_from_presets max_distance_to_exon"] = 50 snapshots["TestSeqvarsQuerySettingsConsequence::test_from_presets transcript_types"] = [ diff --git a/backend/seqvars/tests/snapshots/snap_test_views_api.py b/backend/seqvars/tests/snapshots/snap_test_views_api.py index e2ae3eae0..6b1a7e752 100644 --- a/backend/seqvars/tests/snapshots/snap_test_views_api.py +++ b/backend/seqvars/tests/snapshots/snap_test_views_api.py @@ -317,7 +317,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -359,7 +359,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { @@ -881,7 +881,7 @@ "description": "Chromosome and position", "label": "chrom/pos", "name": "__chrom_pos__", - "visible": True, + "visible": False, "width": 100, }, { @@ -923,7 +923,7 @@ "description": "HGVS description at protein level", "label": "HGVS(p)", "name": "payload.variant_annotation.gene.consequences.hgvs_p", - "visible": True, + "visible": False, "width": 100, }, { diff --git a/backend/seqvars/tests/test_models.py b/backend/seqvars/tests/test_models.py index fbd508b23..336e89a4b 100644 --- a/backend/seqvars/tests/test_models.py +++ b/backend/seqvars/tests/test_models.py @@ -13,7 +13,6 @@ SeqvarsGenotypePresetsPydantic, SeqvarsPredefinedQuery, SeqvarsQuery, - SeqvarsQueryColumnsConfig, SeqvarsQueryExecution, SeqvarsQueryPresetsClinvar, SeqvarsQueryPresetsColumns, @@ -27,6 +26,7 @@ SeqvarsQueryPresetsVariantPrio, SeqvarsQuerySettings, SeqvarsQuerySettingsClinvar, + SeqvarsQuerySettingsColumns, SeqvarsQuerySettingsConsequence, SeqvarsQuerySettingsFrequency, SeqvarsQuerySettingsGenotype, @@ -40,7 +40,6 @@ ) from seqvars.tests.factories import ( SeqvarsPredefinedQueryFactory, - SeqvarsQueryColumnsConfigFactory, SeqvarsQueryExecutionFactory, SeqvarsQueryFactory, SeqvarsQueryPresetsClinvarFactory, @@ -54,6 +53,7 @@ SeqvarsQueryPresetsSetVersionFactory, SeqvarsQueryPresetsVariantPrioFactory, SeqvarsQuerySettingsClinvarFactory, + SeqvarsQuerySettingsColumnsFactory, SeqvarsQuerySettingsConsequenceFactory, SeqvarsQuerySettingsFactory, SeqvarsQuerySettingsFrequencyFactory, @@ -293,6 +293,7 @@ def test_from_predefinedquery(self): self.assertEqual(SeqvarsQuerySettingsPhenotypePrio.objects.count(), 0) self.assertEqual(SeqvarsQuerySettingsQuality.objects.count(), 0) self.assertEqual(SeqvarsQuerySettingsClinvar.objects.count(), 0) + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 0) querysettings = SeqvarsQuerySettings.objects.from_predefinedquery( session=session, predefinedquery=predefinedquery, @@ -307,6 +308,7 @@ def test_from_predefinedquery(self): self.assertEqual(SeqvarsQuerySettingsPhenotypePrio.objects.count(), 1) self.assertEqual(SeqvarsQuerySettingsQuality.objects.count(), 1) self.assertEqual(SeqvarsQuerySettingsClinvar.objects.count(), 1) + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 1) def test_make_clone(self): predefinedquery = SeqvarsPredefinedQueryFactory() @@ -324,6 +326,7 @@ def test_make_clone(self): self.assertEqual(SeqvarsQuerySettingsPhenotypePrio.objects.count(), 1) self.assertEqual(SeqvarsQuerySettingsQuality.objects.count(), 1) self.assertEqual(SeqvarsQuerySettingsClinvar.objects.count(), 1) + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 1) cloned_querysettings = querysettings.make_clone() _ = cloned_querysettings @@ -336,6 +339,7 @@ def test_make_clone(self): self.assertEqual(SeqvarsQuerySettingsPhenotypePrio.objects.count(), 2) self.assertEqual(SeqvarsQuerySettingsQuality.objects.count(), 2) self.assertEqual(SeqvarsQuerySettingsClinvar.objects.count(), 2) + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 2) class TestSeqvarsQuerySettingsGenotype(TestCaseSnapshot, TestCase): @@ -622,6 +626,35 @@ def test_from_presets(self): self.assertMatchSnapshot(getattr(clinvarsettings, key), key) +class TestSeqvarsQuerySettingsColumns(TestCaseSnapshot, TestCase): + + def test_create(self): + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 0) + SeqvarsQuerySettingsColumnsFactory() + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 1) + + def test_str(self): + columnsconfig = SeqvarsQuerySettingsColumnsFactory() + self.assertEqual( + f"SeqvarsQuerySettingsColumns '{columnsconfig.sodar_uuid}'", + columnsconfig.__str__(), + ) + + def test_from_presets(self): + session = CaseAnalysisSessionFactory() + querysettings = SeqvarsQuerySettings.objects.create(session=session) + columnspresets = SeqvarsQueryPresetsColumnsFactory() + + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 0) + columnssettings = SeqvarsQuerySettingsColumns.objects.from_presets( + querysettings=querysettings, columnspresets=columnspresets + ) + + self.assertEqual(SeqvarsQuerySettingsColumns.objects.count(), 1) + for key in ("column_settings",): + self.assertMatchSnapshot(getattr(columnssettings, key), key) + + class TestSeqvarsPredefinedQuery(TestCase): def test_create(self): @@ -630,10 +663,10 @@ def test_create(self): self.assertEqual(SeqvarsPredefinedQuery.objects.count(), 1) def test_str(self): - clinvar = SeqvarsPredefinedQueryFactory() + predefinedquery = SeqvarsPredefinedQueryFactory() self.assertEqual( - f"SeqvarsPredefinedQuery '{clinvar.sodar_uuid}'", - clinvar.__str__(), + f"SeqvarsPredefinedQuery '{predefinedquery.sodar_uuid}'", + predefinedquery.__str__(), ) @@ -662,7 +695,6 @@ def test_from_predefinedquery(self): session = CaseAnalysisSessionFactory() predefinedquery = SeqvarsPredefinedQueryFactory() self.assertEqual(SeqvarsQuery.objects.count(), 0) - self.assertEqual(SeqvarsQueryColumnsConfig.objects.count(), 0) self.assertEqual(SeqvarsQuerySettings.objects.count(), 0) query = SeqvarsQuery.objects.from_predefinedquery( session=session, @@ -670,34 +702,9 @@ def test_from_predefinedquery(self): ) _ = query self.assertEqual(SeqvarsQuery.objects.count(), 1) - self.assertEqual(SeqvarsQueryColumnsConfig.objects.count(), 1) self.assertEqual(SeqvarsQuerySettings.objects.count(), 1) -class TestSeqvarsQueryColumnsConfig(TestCase): - - def test_create(self): - self.assertEqual(SeqvarsQueryColumnsConfig.objects.count(), 0) - SeqvarsQueryColumnsConfigFactory() - self.assertEqual(SeqvarsQueryColumnsConfig.objects.count(), 1) - - def test_str(self): - columnsconfig = SeqvarsQueryColumnsConfigFactory() - self.assertEqual( - f"SeqvarsQueryColumnsConfig '{columnsconfig.sodar_uuid}'", - columnsconfig.__str__(), - ) - - def test_from_predefinedquery(self): - predefinedquery = SeqvarsPredefinedQueryFactory() - self.assertEqual(SeqvarsQueryColumnsConfig.objects.count(), 0) - columnsconfig = SeqvarsQueryColumnsConfig.objects.from_predefinedquery( - predefinedquery=predefinedquery, - ) - _ = columnsconfig - self.assertEqual(SeqvarsQueryColumnsConfig.objects.count(), 1) - - class TestSeqvarsQueryExecution(TestCase): def test_create(self): diff --git a/backend/seqvars/tests/test_permissions_api.py b/backend/seqvars/tests/test_permissions_api.py index 713cb0954..cf1b6acf9 100644 --- a/backend/seqvars/tests/test_permissions_api.py +++ b/backend/seqvars/tests/test_permissions_api.py @@ -19,8 +19,8 @@ SeqvarsQuerySettings, ) from seqvars.serializers import ( - SeqvarsQueryColumnsConfigSerializer, SeqvarsQuerySettingsClinvarSerializer, + SeqvarsQuerySettingsColumnsSerializer, SeqvarsQuerySettingsConsequenceSerializer, SeqvarsQuerySettingsFrequencySerializer, SeqvarsQuerySettingsGenotypeSerializer, @@ -31,7 +31,6 @@ ) from seqvars.tests.factories import ( SeqvarsPredefinedQueryFactory, - SeqvarsQueryColumnsConfigFactory, SeqvarsQueryExecutionFactory, SeqvarsQueryFactory, SeqvarsQueryPresetsClinvarFactory, @@ -45,6 +44,7 @@ SeqvarsQueryPresetsSetVersionFactory, SeqvarsQueryPresetsVariantPrioFactory, SeqvarsQuerySettingsClinvarFactory, + SeqvarsQuerySettingsColumnsFactory, SeqvarsQuerySettingsConsequenceFactory, SeqvarsQuerySettingsFactory, SeqvarsQuerySettingsFrequencyFactory, @@ -1795,6 +1795,9 @@ def test_create(self): "clinvar": SeqvarsQuerySettingsClinvarSerializer( SeqvarsQuerySettingsClinvarFactory.build(querysettings=None) ).data, + "columns": SeqvarsQuerySettingsColumnsSerializer( + SeqvarsQuerySettingsColumnsFactory.build(querysettings=None) + ).data, } querysettings_uuid = self.querysettings.sodar_uuid @@ -1995,10 +1998,10 @@ def test_create(self): "clinvar": SeqvarsQuerySettingsClinvarSerializer( SeqvarsQuerySettingsClinvarFactory.build(querysettings=None) ).data, + "columns": SeqvarsQuerySettingsColumnsSerializer( + SeqvarsQuerySettingsColumnsFactory.build(querysettings=None) + ).data, }, - "columnsconfig": SeqvarsQueryColumnsConfigSerializer( - SeqvarsQueryColumnsConfigFactory.build(seqvarsquery=None) - ).data, } query_uuid = self.query.sodar_uuid diff --git a/backend/seqvars/tests/test_serializers.py b/backend/seqvars/tests/test_serializers.py index b24aaee83..3786e0ac3 100644 --- a/backend/seqvars/tests/test_serializers.py +++ b/backend/seqvars/tests/test_serializers.py @@ -7,7 +7,6 @@ from seqvars.models.base import SeqvarsQueryPresetsSetVersion from seqvars.serializers import ( SeqvarsPredefinedQuerySerializer, - SeqvarsQueryColumnsConfigSerializer, SeqvarsQueryDetailsSerializer, SeqvarsQueryExecutionDetailsSerializer, SeqvarsQueryExecutionSerializer, @@ -25,6 +24,7 @@ SeqvarsQueryPresetsVariantPrioSerializer, SeqvarsQuerySerializer, SeqvarsQuerySettingsClinvarSerializer, + SeqvarsQuerySettingsColumnsSerializer, SeqvarsQuerySettingsConsequenceSerializer, SeqvarsQuerySettingsDetailsSerializer, SeqvarsQuerySettingsFrequencySerializer, @@ -39,7 +39,6 @@ ) from seqvars.tests.factories import ( SeqvarsPredefinedQueryFactory, - SeqvarsQueryColumnsConfigFactory, SeqvarsQueryExecutionFactory, SeqvarsQueryFactory, SeqvarsQueryPresetsClinvarFactory, @@ -53,6 +52,7 @@ SeqvarsQueryPresetsSetVersionFactory, SeqvarsQueryPresetsVariantPrioFactory, SeqvarsQuerySettingsClinvarFactory, + SeqvarsQuerySettingsColumnsFactory, SeqvarsQuerySettingsConsequenceFactory, SeqvarsQuerySettingsFactory, SeqvarsQuerySettingsFrequencyFactory, @@ -968,6 +968,7 @@ def test_serialize_existing(self): "phenotypeprio", "variantprio", "clinvar", + "columns", ] expected = model_to_dict( self.querysettings, @@ -984,6 +985,7 @@ def test_serialize_existing(self): expected["phenotypeprio"] = self.querysettings.phenotypeprio.sodar_uuid expected["variantprio"] = self.querysettings.variantprio.sodar_uuid expected["clinvar"] = self.querysettings.clinvar.sodar_uuid + expected["columns"] = self.querysettings.columns.sodar_uuid # Create JSON dump where necessary. expected["genotypepresets"] = expected["genotypepresets"].model_dump(mode="json") # Note that "date_created", "date_modified" are ignored in model_to_dict as they @@ -1029,6 +1031,7 @@ def test_serialize_existing(self): "phenotypeprio", "variantprio", "clinvar", + "columns", ] expected = model_to_dict( self.querysettings, @@ -1060,6 +1063,7 @@ def test_serialize_existing(self): self.querysettings.variantprio ).data expected["clinvar"] = SeqvarsQuerySettingsClinvarSerializer(self.querysettings.clinvar).data + expected["columns"] = SeqvarsQuerySettingsColumnsSerializer(self.querysettings.columns).data # Create JSON dump where necessary. expected["genotypepresets"] = expected["genotypepresets"].model_dump(mode="json") @@ -1335,6 +1339,43 @@ def test_serialize_existing(self): self.assertDictEqual(dict(serializer.data), expected) +@freeze_time("2012-01-14 12:00:01") +class TestSeqvarsQuerySettingsColumnsSerializer(TestCase): + def setUp(self): + super().setUp() + self.columns = SeqvarsQuerySettingsColumnsFactory() + + def test_serialize_existing(self): + serializer = SeqvarsQuerySettingsColumnsSerializer(self.columns) + fields = [ + # BaseModel + "sodar_uuid", + "date_created", + "date_modified", + # QuerySettingsBase + "querysettings", + # ColumnsSettingsBase + "column_settings", + ] + expected = model_to_dict( + self.columns, + fields=fields, + ) + # We replace the related objects with their UUIDs. + expected["querysettings"] = self.columns.querysettings.sodar_uuid + # Note that "date_created", "date_modified" are ignored in model_to_dict as they + # are not editable. + expected["date_created"] = "2012-01-14T12:00:01Z" + expected["date_modified"] = "2012-01-14T12:00:01Z" + # Map the pydantic fields to their JSON value. + expected["column_settings"] = [ + x.model_dump(mode="json") for x in expected["column_settings"] + ] + + self.assertEqual(set(serializer.data.keys()), set(fields)) + self.assertDictEqual(dict(serializer.data), expected) + + @freeze_time("2012-01-14 12:00:01") class TestSeqvarsQuerySettingsClinvarSerializer(TestCase): def setUp(self): @@ -1375,7 +1416,6 @@ class TestSeqvarsQuerySerializer(TestCase): def setUp(self): super().setUp() self.query = SeqvarsQueryFactory() - self.columnsconfig = SeqvarsQueryColumnsConfigFactory(seqvarsquery=self.query) self.query.refresh_from_db() def test_serialize_existing(self): @@ -1390,7 +1430,6 @@ def test_serialize_existing(self): "label", "session", "settings", - "columnsconfig", ] expected = model_to_dict( self.query, @@ -1400,7 +1439,6 @@ def test_serialize_existing(self): expected["sodar_uuid"] = str(expected["sodar_uuid"]) expected["session"] = self.query.session.sodar_uuid expected["settings"] = self.query.settings.sodar_uuid - expected["columnsconfig"] = self.query.columnsconfig.sodar_uuid # Note that "date_created", "date_modified" are ignored in model_to_dict as they # are not editable. expected["date_created"] = "2012-01-14T12:00:01Z" @@ -1415,7 +1453,6 @@ class TestSeqvarsQueryDetailsSerializer(TestCase): def setUp(self): super().setUp() self.query = SeqvarsQueryFactory() - self.columnsconfig = SeqvarsQueryColumnsConfigFactory(seqvarsquery=self.query) self.query.refresh_from_db() def test_serialize_existing(self): @@ -1430,7 +1467,6 @@ def test_serialize_existing(self): "label", "session", "settings", - "columnsconfig", ] expected = model_to_dict( self.query, @@ -1445,9 +1481,6 @@ def test_serialize_existing(self): expected["date_modified"] = "2012-01-14T12:00:01Z" # The same is true for settings. expected["settings"] = SeqvarsQuerySettingsDetailsSerializer(self.query.settings).data - expected["columnsconfig"] = SeqvarsQueryColumnsConfigSerializer( - self.query.columnsconfig - ).data self.assertEqual(set(serializer.data.keys()), set(fields)) self.assertDictEqual(dict(serializer.data), expected) diff --git a/backend/seqvars/tests/test_views_api.py b/backend/seqvars/tests/test_views_api.py index 84e7c5db5..64686877c 100644 --- a/backend/seqvars/tests/test_views_api.py +++ b/backend/seqvars/tests/test_views_api.py @@ -29,7 +29,6 @@ ) from seqvars.serializers import ( SeqvarsPredefinedQuerySerializer, - SeqvarsQueryColumnsConfigSerializer, SeqvarsQueryDetailsSerializer, SeqvarsQueryExecutionDetailsSerializer, SeqvarsQueryExecutionSerializer, @@ -46,6 +45,7 @@ SeqvarsQueryPresetsVariantPrioSerializer, SeqvarsQuerySerializer, SeqvarsQuerySettingsClinvarSerializer, + SeqvarsQuerySettingsColumnsSerializer, SeqvarsQuerySettingsConsequenceSerializer, SeqvarsQuerySettingsDetailsSerializer, SeqvarsQuerySettingsFrequencySerializer, @@ -60,7 +60,6 @@ ) from seqvars.tests.factories import ( SeqvarsPredefinedQueryFactory, - SeqvarsQueryColumnsConfigFactory, SeqvarsQueryExecutionFactory, SeqvarsQueryFactory, SeqvarsQueryPresetsClinvarFactory, @@ -74,6 +73,7 @@ SeqvarsQueryPresetsSetVersionFactory, SeqvarsQueryPresetsVariantPrioFactory, SeqvarsQuerySettingsClinvarFactory, + SeqvarsQuerySettingsColumnsFactory, SeqvarsQuerySettingsConsequenceFactory, SeqvarsQuerySettingsFactory, SeqvarsQuerySettingsFrequencyFactory, @@ -1689,6 +1689,9 @@ def test_create(self): "clinvar": SeqvarsQuerySettingsClinvarSerializer( SeqvarsQuerySettingsClinvarFactory.build(querysettings=None) ).data, + "columns": SeqvarsQuerySettingsColumnsSerializer( + SeqvarsQuerySettingsColumnsFactory.build(querysettings=None) + ).data, }, format="json", ) @@ -1865,9 +1868,8 @@ def test_create(self): settings["clinvar"] = SeqvarsQuerySettingsClinvarSerializer( SeqvarsQuerySettingsClinvarFactory.build(querysettings=None) ).data - - columnsconfig = SeqvarsQueryColumnsConfigSerializer( - SeqvarsQueryColumnsConfigFactory.build(seqvarsquery=None) + settings["columns"] = SeqvarsQuerySettingsColumnsSerializer( + SeqvarsQuerySettingsColumnsFactory.build(querysettings=None) ).data response = self.client.post( @@ -1880,7 +1882,6 @@ def test_create(self): data={ "label": "test label", "settings": settings, - "columnsconfig": columnsconfig, }, format="json", ) @@ -1941,6 +1942,7 @@ def test_patch(self, data: dict[str, Any]): # noqa: C901 "phenotypeprio", "variantprio", "clinvar", + "columns", ] for key in keys: if key not in data["settings"]: diff --git a/backend/varfish/tests/drf_openapi_schema/varfish_api_schema.yaml b/backend/varfish/tests/drf_openapi_schema/varfish_api_schema.yaml index 3cdb665a2..109c911ae 100644 --- a/backend/varfish/tests/drf_openapi_schema/varfish_api_schema.yaml +++ b/backend/varfish/tests/drf_openapi_schema/varfish_api_schema.yaml @@ -8317,8 +8317,6 @@ components: maxLength: 128 settings: $ref: '#/components/schemas/SeqvarsQuerySettingsDetailsRequest' - columnsconfig: - $ref: '#/components/schemas/SeqvarsQueryColumnsConfigRequest' PatchedSeqvarsQueryPresetsClinvarRequest: type: object description: |- @@ -8791,6 +8789,8 @@ components: $ref: '#/components/schemas/SeqvarsQuerySettingsVariantPrioRequest' clinvar: $ref: '#/components/schemas/SeqvarsQuerySettingsClinvarRequest' + columns: + $ref: '#/components/schemas/SeqvarsQuerySettingsColumnsRequest' PatchedTargetBedFileRequest: type: object description: Serializer for ``TargetBedFile``. @@ -9943,8 +9943,41 @@ components: - $ref: '#/components/schemas/SeqvarsQuerySettingsClinvarPydantic' - type: 'null' default: null + columns: + anyOf: + - $ref: '#/components/schemas/SeqvarsQuerySettingsColumnsPydantic' + - type: 'null' + default: null title: SeqvarsCaseQueryPydantic type: object + SeqvarsColumnConfigPydantic: + description: Configuration for a single column in the result table. + properties: + name: + title: Name + type: string + label: + title: Label + type: string + description: + anyOf: + - type: string + - type: 'null' + default: null + title: Description + width: + default: 100 + title: Width + type: integer + visible: + default: false + title: Visible + type: boolean + required: + - name + - label + title: SeqvarsColumnConfigPydantic + type: object SeqvarsColumnConfigPydanticList: type: array items: @@ -10424,7 +10457,6 @@ components: readOnly: true columnsconfig: type: string - format: uuid readOnly: true required: - columnsconfig @@ -10434,34 +10466,6 @@ components: - session - settings - sodar_uuid - SeqvarsQueryColumnsConfig: - type: object - description: Serializer for ``QueryColumnsConfig``. - properties: - sodar_uuid: - type: string - format: uuid - readOnly: true - date_created: - type: string - format: date-time - readOnly: true - date_modified: - type: string - format: date-time - readOnly: true - column_settings: - $ref: '#/components/schemas/SeqvarsColumnConfigPydanticList' - required: - - date_created - - date_modified - - sodar_uuid - SeqvarsQueryColumnsConfigRequest: - type: object - description: Serializer for ``QueryColumnsConfig``. - properties: - column_settings: - $ref: '#/components/schemas/SeqvarsColumnConfigPydanticList' SeqvarsQueryCreateFromRequest: type: object description: Serializer used for drf-spectacular arguments for ``SeqvarsQuerySettingsViewSet.create_from``. @@ -10509,7 +10513,8 @@ components: settings: $ref: '#/components/schemas/SeqvarsQuerySettingsDetails' columnsconfig: - $ref: '#/components/schemas/SeqvarsQueryColumnsConfig' + type: string + readOnly: true required: - columnsconfig - date_created @@ -10535,10 +10540,7 @@ components: maxLength: 128 settings: $ref: '#/components/schemas/SeqvarsQuerySettingsDetailsRequest' - columnsconfig: - $ref: '#/components/schemas/SeqvarsQueryColumnsConfigRequest' required: - - columnsconfig - label - settings SeqvarsQueryExecution: @@ -11980,9 +11982,14 @@ components: type: string format: uuid readOnly: true + columns: + type: string + format: uuid + readOnly: true required: - clinvar - clinvarpresets + - columns - columnspresets - consequence - consequencepresets @@ -12068,6 +12075,50 @@ components: allow_conflicting_interpretations: type: boolean default: false + SeqvarsQuerySettingsColumns: + type: object + description: Serializer for ``QuerySettingsColumns``. + properties: + column_settings: + $ref: '#/components/schemas/SeqvarsColumnConfigPydanticList' + sodar_uuid: + type: string + format: uuid + readOnly: true + date_created: + type: string + format: date-time + readOnly: true + date_modified: + type: string + format: date-time + readOnly: true + querysettings: + type: string + format: uuid + readOnly: true + required: + - date_created + - date_modified + - querysettings + - sodar_uuid + SeqvarsQuerySettingsColumnsPydantic: + description: Pydantic representation of ``SeqvarsQuerySettingsColumns``. + properties: + column_settings: + default: [] + items: + $ref: '#/components/schemas/SeqvarsColumnConfigPydantic' + title: Column Settings + type: array + title: SeqvarsQuerySettingsColumnsPydantic + type: object + SeqvarsQuerySettingsColumnsRequest: + type: object + description: Serializer for ``QuerySettingsColumns``. + properties: + column_settings: + $ref: '#/components/schemas/SeqvarsColumnConfigPydanticList' SeqvarsQuerySettingsConsequence: type: object description: Serializer for ``QuerySettingsConsequence``. @@ -12237,8 +12288,11 @@ components: $ref: '#/components/schemas/SeqvarsQuerySettingsVariantPrio' clinvar: $ref: '#/components/schemas/SeqvarsQuerySettingsClinvar' + columns: + $ref: '#/components/schemas/SeqvarsQuerySettingsColumns' required: - clinvar + - columns - consequence - date_created - date_modified @@ -12321,8 +12375,11 @@ components: $ref: '#/components/schemas/SeqvarsQuerySettingsVariantPrioRequest' clinvar: $ref: '#/components/schemas/SeqvarsQuerySettingsClinvarRequest' + columns: + $ref: '#/components/schemas/SeqvarsQuerySettingsColumnsRequest' required: - clinvar + - columns - consequence - frequency - genotype diff --git a/frontend/ext/varfish-api/src/lib/schemas.gen.ts b/frontend/ext/varfish-api/src/lib/schemas.gen.ts index b618ba117..14254b2f8 100644 --- a/frontend/ext/varfish-api/src/lib/schemas.gen.ts +++ b/frontend/ext/varfish-api/src/lib/schemas.gen.ts @@ -3730,9 +3730,6 @@ in detail.`, }, settings: { '$ref': '#/components/schemas/SeqvarsQuerySettingsDetailsRequest' - }, - columnsconfig: { - '$ref': '#/components/schemas/SeqvarsQueryColumnsConfigRequest' } } } as const; @@ -4459,6 +4456,9 @@ owned category settings.`, }, clinvar: { '$ref': '#/components/schemas/SeqvarsQuerySettingsClinvarRequest' + }, + columns: { + '$ref': '#/components/schemas/SeqvarsQuerySettingsColumnsRequest' } } } as const; @@ -5880,12 +5880,62 @@ export const $SeqvarsCaseQueryPydantic = { } ], default: null + }, + columns: { + anyOf: [ + { + '$ref': '#/components/schemas/SeqvarsQuerySettingsColumnsPydantic' + }, + { + type: 'null' + } + ], + default: null } }, title: 'SeqvarsCaseQueryPydantic', type: 'object' } as const; +export const $SeqvarsColumnConfigPydantic = { + description: 'Configuration for a single column in the result table.', + properties: { + name: { + title: 'Name', + type: 'string' + }, + label: { + title: 'Label', + type: 'string' + }, + description: { + anyOf: [ + { + type: 'string' + }, + { + type: 'null' + } + ], + default: null, + title: 'Description' + }, + width: { + default: 100, + title: 'Width', + type: 'integer' + }, + visible: { + default: false, + title: 'Visible', + type: 'boolean' + } + }, + required: ['name', 'label'], + title: 'SeqvarsColumnConfigPydantic', + type: 'object' +} as const; + export const $SeqvarsColumnConfigPydanticList = { type: 'array', items: { @@ -6567,49 +6617,12 @@ export const $SeqvarsQuery = { }, columnsconfig: { type: 'string', - format: 'uuid', readOnly: true } }, required: ['columnsconfig', 'date_created', 'date_modified', 'label', 'session', 'settings', 'sodar_uuid'] } as const; -export const $SeqvarsQueryColumnsConfig = { - type: 'object', - description: 'Serializer for ``QueryColumnsConfig``.', - properties: { - sodar_uuid: { - type: 'string', - format: 'uuid', - readOnly: true - }, - date_created: { - type: 'string', - format: 'date-time', - readOnly: true - }, - date_modified: { - type: 'string', - format: 'date-time', - readOnly: true - }, - column_settings: { - '$ref': '#/components/schemas/SeqvarsColumnConfigPydanticList' - } - }, - required: ['date_created', 'date_modified', 'sodar_uuid'] -} as const; - -export const $SeqvarsQueryColumnsConfigRequest = { - type: 'object', - description: 'Serializer for ``QueryColumnsConfig``.', - properties: { - column_settings: { - '$ref': '#/components/schemas/SeqvarsColumnConfigPydanticList' - } - } -} as const; - export const $SeqvarsQueryCreateFromRequest = { type: 'object', description: 'Serializer used for drf-spectacular arguments for ``SeqvarsQuerySettingsViewSet.create_from``.', @@ -6666,7 +6679,8 @@ in detail.`, '$ref': '#/components/schemas/SeqvarsQuerySettingsDetails' }, columnsconfig: { - '$ref': '#/components/schemas/SeqvarsQueryColumnsConfig' + type: 'string', + readOnly: true } }, required: ['columnsconfig', 'date_created', 'date_modified', 'label', 'session', 'settings', 'sodar_uuid'] @@ -6690,12 +6704,9 @@ in detail.`, }, settings: { '$ref': '#/components/schemas/SeqvarsQuerySettingsDetailsRequest' - }, - columnsconfig: { - '$ref': '#/components/schemas/SeqvarsQueryColumnsConfigRequest' } }, - required: ['columnsconfig', 'label', 'settings'] + required: ['label', 'settings'] } as const; export const $SeqvarsQueryExecution = { @@ -8643,9 +8654,14 @@ export const $SeqvarsQuerySettings = { type: 'string', format: 'uuid', readOnly: true + }, + columns: { + type: 'string', + format: 'uuid', + readOnly: true } }, - required: ['clinvar', 'clinvarpresets', 'columnspresets', 'consequence', 'consequencepresets', 'date_created', 'date_modified', 'frequency', 'frequencypresets', 'genotype', 'locus', 'locuspresets', 'phenotypeprio', 'phenotypepriopresets', 'predefinedquery', 'presetssetversion', 'quality', 'qualitypresets', 'session', 'sodar_uuid', 'variantprio', 'variantpriopresets'] + required: ['clinvar', 'clinvarpresets', 'columns', 'columnspresets', 'consequence', 'consequencepresets', 'date_created', 'date_modified', 'frequency', 'frequencypresets', 'genotype', 'locus', 'locuspresets', 'phenotypeprio', 'phenotypepriopresets', 'predefinedquery', 'presetssetversion', 'quality', 'qualitypresets', 'session', 'sodar_uuid', 'variantprio', 'variantpriopresets'] } as const; export const $SeqvarsQuerySettingsClinvar = { @@ -8731,6 +8747,63 @@ export const $SeqvarsQuerySettingsClinvarRequest = { } } as const; +export const $SeqvarsQuerySettingsColumns = { + type: 'object', + description: 'Serializer for ``QuerySettingsColumns``.', + properties: { + column_settings: { + '$ref': '#/components/schemas/SeqvarsColumnConfigPydanticList' + }, + sodar_uuid: { + type: 'string', + format: 'uuid', + readOnly: true + }, + date_created: { + type: 'string', + format: 'date-time', + readOnly: true + }, + date_modified: { + type: 'string', + format: 'date-time', + readOnly: true + }, + querysettings: { + type: 'string', + format: 'uuid', + readOnly: true + } + }, + required: ['date_created', 'date_modified', 'querysettings', 'sodar_uuid'] +} as const; + +export const $SeqvarsQuerySettingsColumnsPydantic = { + description: 'Pydantic representation of ``SeqvarsQuerySettingsColumns``.', + properties: { + column_settings: { + default: [], + items: { + '$ref': '#/components/schemas/SeqvarsColumnConfigPydantic' + }, + title: 'Column Settings', + type: 'array' + } + }, + title: 'SeqvarsQuerySettingsColumnsPydantic', + type: 'object' +} as const; + +export const $SeqvarsQuerySettingsColumnsRequest = { + type: 'object', + description: 'Serializer for ``QuerySettingsColumns``.', + properties: { + column_settings: { + '$ref': '#/components/schemas/SeqvarsColumnConfigPydanticList' + } + } +} as const; + export const $SeqvarsQuerySettingsConsequence = { type: 'object', description: 'Serializer for ``QuerySettingsConsequence``.', @@ -8962,9 +9035,12 @@ owned category settings.`, }, clinvar: { '$ref': '#/components/schemas/SeqvarsQuerySettingsClinvar' + }, + columns: { + '$ref': '#/components/schemas/SeqvarsQuerySettingsColumns' } }, - required: ['clinvar', 'consequence', 'date_created', 'date_modified', 'frequency', 'genotype', 'locus', 'phenotypeprio', 'predefinedquery', 'presetssetversion', 'quality', 'session', 'sodar_uuid', 'variantprio'] + required: ['clinvar', 'columns', 'consequence', 'date_created', 'date_modified', 'frequency', 'genotype', 'locus', 'phenotypeprio', 'predefinedquery', 'presetssetversion', 'quality', 'session', 'sodar_uuid', 'variantprio'] } as const; export const $SeqvarsQuerySettingsDetailsRequest = { @@ -9063,9 +9139,12 @@ owned category settings.`, }, clinvar: { '$ref': '#/components/schemas/SeqvarsQuerySettingsClinvarRequest' + }, + columns: { + '$ref': '#/components/schemas/SeqvarsQuerySettingsColumnsRequest' } }, - required: ['clinvar', 'consequence', 'frequency', 'genotype', 'locus', 'phenotypeprio', 'quality', 'variantprio'] + required: ['clinvar', 'columns', 'consequence', 'frequency', 'genotype', 'locus', 'phenotypeprio', 'quality', 'variantprio'] } as const; export const $SeqvarsQuerySettingsFrequency = { diff --git a/frontend/ext/varfish-api/src/lib/types.gen.ts b/frontend/ext/varfish-api/src/lib/types.gen.ts index 15a39471b..afc577039 100644 --- a/frontend/ext/varfish-api/src/lib/types.gen.ts +++ b/frontend/ext/varfish-api/src/lib/types.gen.ts @@ -1266,7 +1266,6 @@ export type PatchedSeqvarsQueryDetailsRequest = { rank?: number; label?: string; settings?: SeqvarsQuerySettingsDetailsRequest; - columnsconfig?: SeqvarsQueryColumnsConfigRequest; }; /** @@ -1457,6 +1456,7 @@ export type PatchedSeqvarsQuerySettingsDetailsRequest = { phenotypeprio?: SeqvarsQuerySettingsPhenotypePrioRequest; variantprio?: SeqvarsQuerySettingsVariantPrioRequest; clinvar?: SeqvarsQuerySettingsClinvarRequest; + columns?: SeqvarsQuerySettingsColumnsRequest; }; /** @@ -1960,6 +1960,18 @@ export type SeqvarsCaseQueryPydantic = { consequence?: (SeqvarsQuerySettingsConsequencePydantic | null); locus?: (SeqvarsQuerySettingsLocusPydantic | null); clinvar?: (SeqvarsQuerySettingsClinvarPydantic | null); + columns?: (SeqvarsQuerySettingsColumnsPydantic | null); +}; + +/** + * Configuration for a single column in the result table. + */ +export type SeqvarsColumnConfigPydantic = { + name: string; + label: string; + description?: (string | null); + width?: number; + visible?: boolean; }; export type SeqvarsColumnConfigPydanticList = Array<{ @@ -2141,23 +2153,6 @@ export type SeqvarsQuery = { readonly columnsconfig: string; }; -/** - * Serializer for ``QueryColumnsConfig``. - */ -export type SeqvarsQueryColumnsConfig = { - readonly sodar_uuid: string; - readonly date_created: string; - readonly date_modified: string; - column_settings?: SeqvarsColumnConfigPydanticList; -}; - -/** - * Serializer for ``QueryColumnsConfig``. - */ -export type SeqvarsQueryColumnsConfigRequest = { - column_settings?: SeqvarsColumnConfigPydanticList; -}; - /** * Serializer used for drf-spectacular arguments for ``SeqvarsQuerySettingsViewSet.create_from``. */ @@ -2180,7 +2175,7 @@ export type SeqvarsQueryDetails = { label: string; readonly session: string; settings: SeqvarsQuerySettingsDetails; - columnsconfig: SeqvarsQueryColumnsConfig; + readonly columnsconfig: string; }; /** @@ -2193,7 +2188,6 @@ export type SeqvarsQueryDetailsRequest = { rank?: number; label: string; settings: SeqvarsQuerySettingsDetailsRequest; - columnsconfig: SeqvarsQueryColumnsConfigRequest; }; /** @@ -2697,6 +2691,7 @@ export type SeqvarsQuerySettings = { readonly phenotypeprio: string; readonly variantprio: string; readonly clinvar: string; + readonly columns: string; }; /** @@ -2730,6 +2725,31 @@ export type SeqvarsQuerySettingsClinvarRequest = { allow_conflicting_interpretations?: boolean; }; +/** + * Serializer for ``QuerySettingsColumns``. + */ +export type SeqvarsQuerySettingsColumns = { + column_settings?: SeqvarsColumnConfigPydanticList; + readonly sodar_uuid: string; + readonly date_created: string; + readonly date_modified: string; + readonly querysettings: string; +}; + +/** + * Pydantic representation of ``SeqvarsQuerySettingsColumns``. + */ +export type SeqvarsQuerySettingsColumnsPydantic = { + column_settings?: Array; +}; + +/** + * Serializer for ``QuerySettingsColumns``. + */ +export type SeqvarsQuerySettingsColumnsRequest = { + column_settings?: SeqvarsColumnConfigPydanticList; +}; + /** * Serializer for ``QuerySettingsConsequence``. */ @@ -2796,6 +2816,7 @@ export type SeqvarsQuerySettingsDetails = { phenotypeprio: SeqvarsQuerySettingsPhenotypePrio; variantprio: SeqvarsQuerySettingsVariantPrio; clinvar: SeqvarsQuerySettingsClinvar; + columns: SeqvarsQuerySettingsColumns; }; /** @@ -2824,6 +2845,7 @@ export type SeqvarsQuerySettingsDetailsRequest = { phenotypeprio: SeqvarsQuerySettingsPhenotypePrioRequest; variantprio: SeqvarsQuerySettingsVariantPrioRequest; clinvar: SeqvarsQuerySettingsClinvarRequest; + columns: SeqvarsQuerySettingsColumnsRequest; }; /** diff --git a/frontend/src/seqvars/components/QueryEditor/ColumnControls.vue b/frontend/src/seqvars/components/QueryEditor/ColumnsControls.vue similarity index 81% rename from frontend/src/seqvars/components/QueryEditor/ColumnControls.vue rename to frontend/src/seqvars/components/QueryEditor/ColumnsControls.vue index 42f31c3e4..2172ca491 100644 --- a/frontend/src/seqvars/components/QueryEditor/ColumnControls.vue +++ b/frontend/src/seqvars/components/QueryEditor/ColumnsControls.vue @@ -33,17 +33,20 @@ const seqvarQueryUpdate = useSeqvarQueryUpdateMutation() const applyMutation = async (data: { name: string; visible: boolean }) => { const newData = { ...props.modelValue, - columnsconfig: { - ...props.modelValue.columnsconfig, - column_settings: ( - props.modelValue.columnsconfig.column_settings || [] - ).map((column) => { - const res = { - ...column, - visible: data.name === column.name ? data.visible : column.visible, - } - return res - }), + settings: { + ...props.modelValue.settings, + columns: { + ...props.modelValue.settings.columns, + column_settings: ( + props.modelValue.settings.columns.column_settings || [] + ).map((column) => { + const res = { + ...column, + visible: data.name === column.name ? data.visible : column.visible, + } + return res + }), + }, }, } @@ -62,7 +65,7 @@ const applyMutation = async (data: { name: string; visible: boolean }) => { - - - - - diff --git a/frontend/src/seqvars/components/QueryEditor/groups.ts b/frontend/src/seqvars/components/QueryEditor/groups.ts index 416f0d4f4..f27ed0a46 100644 --- a/frontend/src/seqvars/components/QueryEditor/groups.ts +++ b/frontend/src/seqvars/components/QueryEditor/groups.ts @@ -15,6 +15,7 @@ import { PedigreeObj } from '@/cases/stores/caseDetails' import { isKeyOfObject } from '../utils' import ClinvarControls from './ClinvarControls.vue' +import ColumnsControls from './ColumnsControls.vue' import EffectsControls from './EffectsControls.vue' import FrequencyControls from './FrequencyControls.vue' import LocusControls from './LocusControls.vue' @@ -175,6 +176,13 @@ export const GROUPS = [ getCompareFields: (v) => [v.gene_panels, v.genes, v.genome_regions], Component: LocusControls, }), + + new FilterGroup({ + id: 'columns', + title: 'Columns', + getCompareFields: (v) => [v.column_settings], + Component: ColumnsControls, + }), ] export const createGenotypeFromPreset = ( diff --git a/frontend/src/seqvars/components/QueryResults/QueryResultsTable.vue b/frontend/src/seqvars/components/QueryResults/QueryResultsTable.vue index 935c1e7ae..efbbf43d9 100644 --- a/frontend/src/seqvars/components/QueryResults/QueryResultsTable.vue +++ b/frontend/src/seqvars/components/QueryResults/QueryResultsTable.vue @@ -154,7 +154,7 @@ const headers = computed(() => { const formatColumns = [] // Collect `INFO` headers. - for (const column of seqvarQueryRes.data.value?.columnsconfig + for (const column of seqvarQueryRes.data.value?.settings.columns .column_settings ?? []) { if (column.visible) { if (column.name.includes('__SAMPLE__')) { diff --git a/frontend/src/seqvars/queries/seqvarQuery.ts b/frontend/src/seqvars/queries/seqvarQuery.ts index 95dc70684..5358aa71e 100644 --- a/frontend/src/seqvars/queries/seqvarQuery.ts +++ b/frontend/src/seqvars/queries/seqvarQuery.ts @@ -230,12 +230,12 @@ export const useSeqvarQueryUpdateMutation = () => { : deepmergeCustom({ mergeArrays: false })(previousValue, data.body) if ( newValue !== undefined && - data.body.columnsconfig.column_settings !== undefined + data.body.settings.columns.column_settings !== undefined ) { // Need to manually clone the column settings as we disable merging of // arrays in `deepmergeCustom()` call. - newValue.columnsconfig.column_settings = structuredClone( - toRaw(data.body.columnsconfig.column_settings), + newValue.settings.columns.column_settings = structuredClone( + toRaw(data.body.settings.columns.column_settings), ) } queryClient.setQueryData(queryKey, newValue)