Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move column settings to querysettings (#2042) #2050

Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 0 additions & 4 deletions backend/seqvars/factory_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
66 changes: 66 additions & 0 deletions backend/seqvars/migrations/0012_auto_20241024_0823.py
Original file line number Diff line number Diff line change
@@ -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",
),
]
65 changes: 36 additions & 29 deletions backend/seqvars/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -903,6 +906,7 @@ class SeqvarsQuerySettings(model_clone.CloneMixin, BaseModel):
"phenotypeprio",
"quality",
"clinvar",
"columns",
]

#: Custom manager with ``from_predefinedquery()``.
Expand Down Expand Up @@ -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):
Expand All @@ -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

Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -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``."""

Expand All @@ -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):
Expand Down Expand Up @@ -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


Expand Down
39 changes: 21 additions & 18 deletions backend/seqvars/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
SeqvarsPredefinedQuery,
SeqvarsPrioServicePydantic,
SeqvarsQuery,
SeqvarsQueryColumnsConfig,
SeqvarsQueryExecution,
SeqvarsQueryPresetsClinvar,
SeqvarsQueryPresetsColumns,
Expand All @@ -45,6 +44,7 @@
SeqvarsQuerySettings,
SeqvarsQuerySettingsCategoryBase,
SeqvarsQuerySettingsClinvar,
SeqvarsQuerySettingsColumns,
SeqvarsQuerySettingsConsequence,
SeqvarsQuerySettingsFrequency,
SeqvarsQuerySettingsGenotype,
Expand Down Expand Up @@ -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``."""

Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -963,6 +979,7 @@ class Meta:
"phenotypeprio",
"variantprio",
"clinvar",
"columns",
]
read_only_fields = fields

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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``."""

Expand Down Expand Up @@ -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
Expand Down
23 changes: 15 additions & 8 deletions backend/seqvars/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
SeqvarsPredefinedQuery,
SeqvarsPrioServicePydantic,
SeqvarsQuery,
SeqvarsQueryColumnsConfig,
SeqvarsQueryExecution,
SeqvarsQueryPresetsClinvar,
SeqvarsQueryPresetsColumns,
Expand All @@ -36,6 +35,7 @@
SeqvarsQueryPresetsVariantPrio,
SeqvarsQuerySettings,
SeqvarsQuerySettingsClinvar,
SeqvarsQuerySettingsColumns,
SeqvarsQuerySettingsConsequence,
SeqvarsQuerySettingsFrequency,
SeqvarsQuerySettingsGenotype,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -499,25 +503,28 @@ 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
label = factory.Sequence(lambda n: f"query-{n}")

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
Expand Down
Loading
Loading