From 3c3587c4ed8608c290d919d3bbc17836145981d4 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 2 Feb 2024 14:01:17 +0530 Subject: [PATCH 01/13] #250 add null property to models to laterality, projection, and circuit_type --- backend/composer/enums.py | 3 --- backend/composer/models.py | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/backend/composer/enums.py b/backend/composer/enums.py index 5ea1b830..f9fb0f3b 100644 --- a/backend/composer/enums.py +++ b/backend/composer/enums.py @@ -7,14 +7,12 @@ class Laterality(models.TextChoices): RIGHT = "RIGHT", "Right" LEFT = "LEFT", "Left" - UNKNOWN = "UNKNOWN", "Not specified" class Projection(models.TextChoices): IPSI = "IPSI", "Ipsilateral" CONTRAT = "CONTRAT", "Contralateral" BI = "BI", "Bilateral" - UNKNOWN = "UNKNOWN", "Not specified" # todo: motor and sensory should move to phenotype options per csv @@ -25,7 +23,6 @@ class CircuitType(models.TextChoices): INTRINSIC = "INTRINSIC", "Intrinsic" PROJECTION = "PROJECTION", "Projection" ANAXONIC = "ANAXONIC", "Anaxonic" - UNKNOWN = "UNKNOWN", "Not specified" class ViaType(models.TextChoices): diff --git a/backend/composer/models.py b/backend/composer/models.py index 424fcf9a..375c1138 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -386,14 +386,15 @@ class ConnectivityStatement(models.Model): owner = models.ForeignKey( User, verbose_name="Curator", on_delete=models.SET_NULL, null=True, blank=True ) + laterality = models.CharField( - max_length=20, default=Laterality.UNKNOWN, choices=Laterality.choices + max_length=20, choices=Laterality.choices, null=True ) projection = models.CharField( - max_length=20, default=Projection.UNKNOWN, choices=Projection.choices + max_length=20, choices=Projection.choices, null=True ) circuit_type = models.CharField( - max_length=20, default=CircuitType.UNKNOWN, choices=CircuitType.choices + max_length=20, choices=CircuitType.choices, null=True ) # TODO for next releases we could have only 1 field for phenotype + an intermediate table with the phenotype's categories such as circuit_type, laterality, projection, functional_circuit_role, projection_phenotype among others phenotype = models.ForeignKey( @@ -525,7 +526,6 @@ def get_laterality_description(self): laterality_map = { Laterality.RIGHT.value: "on the right side of the body", Laterality.LEFT.value: "on the left side of the body", - Laterality.UNKNOWN.value: "", } return laterality_map.get(self.laterality, "") From 5f7f9cc6c06885221d2f2bb8bbfc789d74e28b38 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 2 Feb 2024 14:02:14 +0530 Subject: [PATCH 02/13] #250 migrations merged - add null true -> convert unknown to null -> remove unknown option from enums --- ...ectivitystatement_circuit_type_and_more.py | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py diff --git a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py b/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py new file mode 100644 index 00000000..44b0b291 --- /dev/null +++ b/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py @@ -0,0 +1,149 @@ +# Generated by Django 4.1.4 on 2024-02-01 13:26 + +from django.db import migrations, models + + +def change_not_specified_to_null(apps, schema_editor): + ConnectivityStatement = apps.get_model('composer', 'ConnectivityStatement') + ConnectivityStatement.objects.filter(laterality='UNKNOWN').update(laterality=None) + ConnectivityStatement.objects.filter(projection='UNKNOWN').update(projection=None) + ConnectivityStatement.objects.filter(circuit_type='UNKNOWN').update(circuit_type=None) + + +class Migration(migrations.Migration): + dependencies = [ + ("composer", "0032_alter_connectivitystatement_projection"), + ] + + operations = [ + # ADD the null=True option to the circuit_type, laterality and projection fields + migrations.AlterField( + model_name="connectivitystatement", + name="circuit_type", + field=models.CharField( + choices=[ + ("SENSORY", "Sensory"), + ("MOTOR", "Motor"), + ("INTRINSIC", "Intrinsic"), + ("PROJECTION", "Projection"), + ("ANAXONIC", "Anaxonic"), + ("UNKNOWN", "Not specified"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="laterality", + field=models.CharField( + choices=[ + ("RIGHT", "Right"), + ("LEFT", "Left"), + ("UNKNOWN", "Not specified"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="projection", + field=models.CharField( + choices=[ + ("IPSI", "Ipsilateral"), + ("CONTRAT", "Contralateral"), + ("BI", "Bilateral"), + ("UNKNOWN", "Not specified"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="species", + field=models.ManyToManyField( + blank=True, null=True, to="composer.specie", verbose_name="Species" + ), + ), + + # CONVERT UNKNOWN TO NULL + migrations.RunPython(change_not_specified_to_null), + + + # Remove the UNKNOWN option from the circuit_type, laterality and projection fields + migrations.RemoveConstraint( + model_name="connectivitystatement", + name="circuit_type_valid", + ), + migrations.RemoveConstraint( + model_name="connectivitystatement", + name="laterality_valid", + ), + migrations.RemoveConstraint( + model_name="connectivitystatement", + name="projection_valid", + ), + migrations.AlterField( + model_name="connectivitystatement", + name="circuit_type", + field=models.CharField( + choices=[ + ("SENSORY", "Sensory"), + ("MOTOR", "Motor"), + ("INTRINSIC", "Intrinsic"), + ("PROJECTION", "Projection"), + ("ANAXONIC", "Anaxonic"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="laterality", + field=models.CharField( + choices=[("RIGHT", "Right"), ("LEFT", "Left")], max_length=20, null=True + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="projection", + field=models.CharField( + choices=[ + ("IPSI", "Ipsilateral"), + ("CONTRAT", "Contralateral"), + ("BI", "Bilateral"), + ], + max_length=20, + null=True, + ), + ), + migrations.AddConstraint( + model_name="connectivitystatement", + constraint=models.CheckConstraint( + check=models.Q(("laterality__in", ["RIGHT", "LEFT"])), + name="laterality_valid", + ), + ), + migrations.AddConstraint( + model_name="connectivitystatement", + constraint=models.CheckConstraint( + check=models.Q( + ( + "circuit_type__in", + ["SENSORY", "MOTOR", "INTRINSIC", "PROJECTION", "ANAXONIC"], + ) + ), + name="circuit_type_valid", + ), + ), + migrations.AddConstraint( + model_name="connectivitystatement", + constraint=models.CheckConstraint( + check=models.Q(("projection__in", ["IPSI", "CONTRAT", "BI"])), + name="projection_valid", + ), + ), + ] From f7380b4fb964c05ebc58492f586cd09793218c1a Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 2 Feb 2024 14:06:12 +0530 Subject: [PATCH 03/13] #250 FE changes to get null field and placeholder --- frontend/src/components/Forms/StatementForm.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/frontend/src/components/Forms/StatementForm.tsx b/frontend/src/components/Forms/StatementForm.tsx index 20a2ff0d..0da5c70b 100644 --- a/frontend/src/components/Forms/StatementForm.tsx +++ b/frontend/src/components/Forms/StatementForm.tsx @@ -61,6 +61,7 @@ const StatementForm = (props: any) => { "ui:options": { label: "Circuit Type", classNames: "col-xs-12 col-md-6", + placeholder: "Enter Circuit Type", }, }; @@ -69,6 +70,7 @@ const StatementForm = (props: any) => { "ui:options": { label: "Laterality", classNames: "col-xs-12 col-md-6", + placeholder: "Enter Laterality", }, }; @@ -77,6 +79,7 @@ const StatementForm = (props: any) => { "ui:options": { label: "Projection", classNames: "col-xs-12 col-md-6", + placeholder: "Enter Projection", }, }; @@ -638,6 +641,20 @@ const StatementForm = (props: any) => { }, }; + // Add null option to the fields which have null type in dropdown. + Object.keys(copiedSchema.properties).forEach((key) => { + if (copiedSchema.properties[key].type.includes("null") && copiedSchema.properties[key]?.enum && copiedSchema.properties[key]?.enumNames) { + copiedSchema.properties[key].enum.push(null); + copiedSchema.properties[key].enumNames.push("---------"); + } + }); + + Object.keys(copiedUISchema).forEach((key) => { + if (copiedUISchema[key]["ui:options"] && copiedUISchema[key]["ui:options"].data) { + copiedUISchema[key]["ui:options"].data.push({ label: "---------", value: null }) + } + }); + const widgets = { AnatomicalEntitiesField, CustomSingleSelect, From 17ffb7f9556c2e202bf61d0bd530d13008ef937a Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 2 Feb 2024 16:49:34 +0530 Subject: [PATCH 04/13] #250 improve statement preview sentences - for null values adjustments --- backend/composer/api/serializers.py | 33 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index 3096746e..2bb25fdd 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -486,25 +486,39 @@ def get_statement_preview(self, instance): self.context['journey'] = instance.get_journey() return self.create_statement_preview(instance, self.context['journey']) + def get_property_or_empty_string(self, instance, property_name, sub_property_name=None): + if hasattr(instance, property_name): + property_attr = getattr(instance, property_name) + if property_attr: + if sub_property_name and hasattr(property_attr, sub_property_name): + subproperty_attr = getattr(property_attr, sub_property_name) + return subproperty_attr() if callable(subproperty_attr) else subproperty_attr + + if callable(property_attr): + return property_attr() if property_attr() else "" + else: + return property_attr + return "" + + def create_statement_preview(self, instance, journey): - sex = instance.sex.name if instance.sex else "" + sex = self.get_property_or_empty_string(instance, 'sex', 'name') + species_list = [specie.name for specie in instance.species.all()] species = join_entities(species_list) if not species: species = "" - phenotype = instance.phenotype.name.lower() if instance.phenotype else "" + phenotype = self.get_property_or_empty_string(instance, 'phenotype', 'name').lower() origin_names = [origin.name for origin in instance.origins.all()] origins = join_entities(origin_names) if not origins: origins = "" - circuit_type = instance.get_circuit_type_display().lower() if instance.circuit_type else "" - projection = instance.get_projection_display().lower() if instance.projection else "" + circuit_type = self.get_property_or_empty_string(instance, 'get_circuit_type_display').lower() + projection = self.get_property_or_empty_string(instance, 'get_projection_display').lower() - laterality_description = instance.get_laterality_description() - if not laterality_description: - laterality_description = "" + laterality_description = self.get_property_or_empty_string(instance, 'get_laterality_description') apinatomy = instance.apinatomy_model if instance.apinatomy_model else "" journey_sentence = ', '.join(journey) @@ -514,10 +528,11 @@ def create_statement_preview(self, instance, journey): statement = f"In {sex} {species}, the {phenotype} connection goes {journey_sentence}.\n" else: statement = f"A {phenotype} connection goes {journey_sentence}.\n" + statement += f"This " - if projection != "not specified": + if projection != "": statement += f"{projection} " - if circuit_type != "not specified": + if circuit_type != "": statement += f"{circuit_type} " statement += f"connection projects from the {origins}." From 0d3d3fad9b9ad597de8a35fc7c7649fa6f598898 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 2 Feb 2024 16:54:28 +0530 Subject: [PATCH 05/13] #250 remove the species null true --- ...33_alter_connectivitystatement_circuit_type_and_more.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py b/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py index 44b0b291..29ca444b 100644 --- a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py +++ b/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py @@ -60,13 +60,6 @@ class Migration(migrations.Migration): null=True, ), ), - migrations.AlterField( - model_name="connectivitystatement", - name="species", - field=models.ManyToManyField( - blank=True, null=True, to="composer.specie", verbose_name="Species" - ), - ), # CONVERT UNKNOWN TO NULL migrations.RunPython(change_not_specified_to_null), From 10539844b23ee080160ee5cb774f262421b18bc9 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Thu, 8 Feb 2024 18:27:30 +0530 Subject: [PATCH 06/13] #250 adding blank true to fields in connectivity statements --- ...ectivitystatement_circuit_type_and_more.py | 44 +++++++++++++++++++ backend/composer/models.py | 6 +-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py b/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py index 29ca444b..914561d1 100644 --- a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py +++ b/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py @@ -139,4 +139,48 @@ class Migration(migrations.Migration): name="projection_valid", ), ), + + + # add blank=True to Laterality, Projections, Circuit Type + migrations.AlterField( + model_name="connectivitystatement", + name="circuit_type", + field=models.CharField( + blank=True, + choices=[ + ("SENSORY", "Sensory"), + ("MOTOR", "Motor"), + ("INTRINSIC", "Intrinsic"), + ("PROJECTION", "Projection"), + ("ANAXONIC", "Anaxonic"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="laterality", + field=models.CharField( + blank=True, + choices=[("RIGHT", "Right"), ("LEFT", "Left")], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="projection", + field=models.CharField( + blank=True, + choices=[ + ("IPSI", "Ipsilateral"), + ("CONTRAT", "Contralateral"), + ("BI", "Bilateral"), + ], + max_length=20, + null=True, + ), + ), + ] diff --git a/backend/composer/models.py b/backend/composer/models.py index 375c1138..24d80633 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -388,13 +388,13 @@ class ConnectivityStatement(models.Model): ) laterality = models.CharField( - max_length=20, choices=Laterality.choices, null=True + max_length=20, choices=Laterality.choices, null=True, blank=True ) projection = models.CharField( - max_length=20, choices=Projection.choices, null=True + max_length=20, choices=Projection.choices, null=True, blank=True ) circuit_type = models.CharField( - max_length=20, choices=CircuitType.choices, null=True + max_length=20, choices=CircuitType.choices, null=True, blank=True ) # TODO for next releases we could have only 1 field for phenotype + an intermediate table with the phenotype's categories such as circuit_type, laterality, projection, functional_circuit_role, projection_phenotype among others phenotype = models.ForeignKey( From ae979213b1a4d21c4eae8ff6f8fb8b9d6f8c869c Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Sun, 11 Feb 2024 19:18:27 +0530 Subject: [PATCH 07/13] #250 improve get_method_value and get_property_value --- backend/composer/api/serializers.py | 44 ++++++++++------------------- backend/composer/models.py | 2 +- backend/composer/utils.py | 14 +++++++++ 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index 2bb25fdd..fe38fd58 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -24,7 +24,7 @@ from ..services.connections_service import get_complete_from_entities_for_destination, \ get_complete_from_entities_for_via from ..services.errors_service import get_connectivity_errors -from ..utils import join_entities +from ..utils import join_entities, get_property_value, get_method_value # MixIns @@ -486,63 +486,49 @@ def get_statement_preview(self, instance): self.context['journey'] = instance.get_journey() return self.create_statement_preview(instance, self.context['journey']) - def get_property_or_empty_string(self, instance, property_name, sub_property_name=None): - if hasattr(instance, property_name): - property_attr = getattr(instance, property_name) - if property_attr: - if sub_property_name and hasattr(property_attr, sub_property_name): - subproperty_attr = getattr(property_attr, sub_property_name) - return subproperty_attr() if callable(subproperty_attr) else subproperty_attr - - if callable(property_attr): - return property_attr() if property_attr() else "" - else: - return property_attr - return "" - def create_statement_preview(self, instance, journey): - sex = self.get_property_or_empty_string(instance, 'sex', 'name') + sex = get_property_value(instance, 'sex', 'name') species_list = [specie.name for specie in instance.species.all()] species = join_entities(species_list) if not species: species = "" - phenotype = self.get_property_or_empty_string(instance, 'phenotype', 'name').lower() + phenotype = get_property_value(instance, 'phenotype', 'name') origin_names = [origin.name for origin in instance.origins.all()] origins = join_entities(origin_names) if not origins: origins = "" - circuit_type = self.get_property_or_empty_string(instance, 'get_circuit_type_display').lower() - projection = self.get_property_or_empty_string(instance, 'get_projection_display').lower() + circuit_type = get_method_value(instance, 'get_circuit_type_display') + projection = get_method_value(instance, 'get_projection_display') - laterality_description = self.get_property_or_empty_string(instance, 'get_laterality_description') + laterality_description = get_method_value(instance, 'get_laterality_description') apinatomy = instance.apinatomy_model if instance.apinatomy_model else "" journey_sentence = ', '.join(journey) # Creating the statement - if sex != "" or species != "": - statement = f"In {sex} {species}, the {phenotype} connection goes {journey_sentence}.\n" + if sex is not None or species != "": + statement = f"In {sex or ''} {species}, the {phenotype.lower() if phenotype else '' } connection goes {journey_sentence}.\n" else: statement = f"A {phenotype} connection goes {journey_sentence}.\n" statement += f"This " - if projection != "": - statement += f"{projection} " - if circuit_type != "": - statement += f"{circuit_type} " + if projection is not None: + statement += f"{projection.lower() or ''} " + if circuit_type is not None: + statement += f"{circuit_type.lower() or ''} " statement += f"connection projects from the {origins}." - if laterality_description != "": - statement = statement[:-1] + f" and is found {laterality_description}.\n" + if laterality_description is not None: + statement = statement[:-1] + f" and is found {laterality_description or ''}.\n" if apinatomy: statement += f" It is described in {apinatomy} model." - return statement.strip() + return statement.strip().replace(" ", " ") def get_errors(self, instance) -> List: return get_connectivity_errors(instance) diff --git a/backend/composer/models.py b/backend/composer/models.py index 24d80633..56d63959 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -527,7 +527,7 @@ def get_laterality_description(self): Laterality.RIGHT.value: "on the right side of the body", Laterality.LEFT.value: "on the left side of the body", } - return laterality_map.get(self.laterality, "") + return laterality_map.get(self.laterality, None) def assign_owner(self, request): if ConnectivityStatementService(self).should_set_owner(request): diff --git a/backend/composer/utils.py b/backend/composer/utils.py index 9ff9db91..543d0510 100644 --- a/backend/composer/utils.py +++ b/backend/composer/utils.py @@ -22,3 +22,17 @@ def join_entities(entities): elif entities_list: return entities_list[0] return '' + +def get_property_value(instance, property_name, sub_property_name=None): + """ + Get the value of a sub-property of an instance property or the value of the property itself. + """ + if sub_property_name: + return getattr(getattr(instance, property_name, None), sub_property_name, None) + return getattr(instance, property_name, None) + +def get_method_value(instance, method_name): + """ + Get the value of a method of an instance. + """ + return getattr(instance, method_name, None)() From 285dd03f5a7ffc86e672284871eeb5b85bdf112d Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Sun, 11 Feb 2024 19:45:50 +0530 Subject: [PATCH 08/13] #250 cleanup serializers - remove "is not None" --- backend/composer/api/serializers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index fe38fd58..35a1183c 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -510,19 +510,19 @@ def create_statement_preview(self, instance, journey): journey_sentence = ', '.join(journey) # Creating the statement - if sex is not None or species != "": + if sex or species != "": statement = f"In {sex or ''} {species}, the {phenotype.lower() if phenotype else '' } connection goes {journey_sentence}.\n" else: statement = f"A {phenotype} connection goes {journey_sentence}.\n" statement += f"This " - if projection is not None: + if projection: statement += f"{projection.lower() or ''} " - if circuit_type is not None: + if circuit_type: statement += f"{circuit_type.lower() or ''} " statement += f"connection projects from the {origins}." - if laterality_description is not None: + if laterality_description: statement = statement[:-1] + f" and is found {laterality_description or ''}.\n" if apinatomy: From b0cd5f64a663e6bbc8cc620b91dccc5e7d1e6640 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Sun, 11 Feb 2024 19:59:20 +0530 Subject: [PATCH 09/13] #250 migration file update - rename after merging develop branch --- ...> 0035_alter_connectivitystatement_circuit_type_and_more.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/composer/migrations/{0033_alter_connectivitystatement_circuit_type_and_more.py => 0035_alter_connectivitystatement_circuit_type_and_more.py} (98%) diff --git a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py b/backend/composer/migrations/0035_alter_connectivitystatement_circuit_type_and_more.py similarity index 98% rename from backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py rename to backend/composer/migrations/0035_alter_connectivitystatement_circuit_type_and_more.py index 914561d1..daca27b1 100644 --- a/backend/composer/migrations/0033_alter_connectivitystatement_circuit_type_and_more.py +++ b/backend/composer/migrations/0035_alter_connectivitystatement_circuit_type_and_more.py @@ -12,7 +12,7 @@ def change_not_specified_to_null(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("composer", "0032_alter_connectivitystatement_projection"), + ("composer", "0034_remove_connectivitystatement_state_valid_and_more"), ] operations = [ From 3593127c5191288b2c1c8cab334705560314dd76 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Sun, 11 Feb 2024 20:14:13 +0530 Subject: [PATCH 10/13] #250 removing utils functions to add @property for strings of sex and phenotype --- backend/composer/api/serializers.py | 6 +++--- backend/composer/models.py | 8 ++++++++ backend/composer/utils.py | 14 -------------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index 35a1183c..1644e9d5 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -24,7 +24,7 @@ from ..services.connections_service import get_complete_from_entities_for_destination, \ get_complete_from_entities_for_via from ..services.errors_service import get_connectivity_errors -from ..utils import join_entities, get_property_value, get_method_value +from ..utils import join_entities # MixIns @@ -488,14 +488,14 @@ def get_statement_preview(self, instance): def create_statement_preview(self, instance, journey): - sex = get_property_value(instance, 'sex', 'name') + sex = instance.sex.sex_str if instance.sex else None species_list = [specie.name for specie in instance.species.all()] species = join_entities(species_list) if not species: species = "" - phenotype = get_property_value(instance, 'phenotype', 'name') + phenotype = instance.phenotype.phenotype_str if instance.phenotype else None origin_names = [origin.name for origin in instance.origins.all()] origins = join_entities(origin_names) if not origins: diff --git a/backend/composer/models.py b/backend/composer/models.py index 3d6294a5..006d77bd 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -154,6 +154,10 @@ class Phenotype(models.Model): def __str__(self): return self.name + + @property + def phenotype_str(self): + return str(self.name) if self.name else '' class Meta: ordering = ["name"] @@ -182,6 +186,10 @@ class Sex(models.Model): def __str__(self): return self.name + + @property + def sex_str(self): + return str(self.name) if self.name else '' class Meta: ordering = ["name"] diff --git a/backend/composer/utils.py b/backend/composer/utils.py index 543d0510..9ff9db91 100644 --- a/backend/composer/utils.py +++ b/backend/composer/utils.py @@ -22,17 +22,3 @@ def join_entities(entities): elif entities_list: return entities_list[0] return '' - -def get_property_value(instance, property_name, sub_property_name=None): - """ - Get the value of a sub-property of an instance property or the value of the property itself. - """ - if sub_property_name: - return getattr(getattr(instance, property_name, None), sub_property_name, None) - return getattr(instance, property_name, None) - -def get_method_value(instance, method_name): - """ - Get the value of a method of an instance. - """ - return getattr(instance, method_name, None)() From 7a8d044fe4771220aa37c1d60a04c3ef7c9f5b4d Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Sun, 11 Feb 2024 20:15:05 +0530 Subject: [PATCH 11/13] #250 remove util for callable methods and replace with inline logic --- backend/composer/api/serializers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index 1644e9d5..bf6817d6 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -501,10 +501,10 @@ def create_statement_preview(self, instance, journey): if not origins: origins = "" - circuit_type = get_method_value(instance, 'get_circuit_type_display') - projection = get_method_value(instance, 'get_projection_display') + circuit_type = instance.get_circuit_type_display() if instance.circuit_type else None + projection = instance.get_projection_display() if instance.projection else None - laterality_description = get_method_value(instance, 'get_laterality_description') + laterality_description = instance.get_laterality_description() apinatomy = instance.apinatomy_model if instance.apinatomy_model else "" journey_sentence = ', '.join(journey) From c7be135741fda938d86f75a6a1ef9071aac59625 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Sun, 11 Feb 2024 20:31:19 +0530 Subject: [PATCH 12/13] #250 improve statement logic --- backend/composer/api/serializers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index bf6817d6..123b67c3 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -495,7 +495,7 @@ def create_statement_preview(self, instance, journey): if not species: species = "" - phenotype = instance.phenotype.phenotype_str if instance.phenotype else None + phenotype = instance.phenotype.phenotype_str if instance.phenotype else '' origin_names = [origin.name for origin in instance.origins.all()] origins = join_entities(origin_names) if not origins: @@ -511,19 +511,19 @@ def create_statement_preview(self, instance, journey): # Creating the statement if sex or species != "": - statement = f"In {sex or ''} {species}, the {phenotype.lower() if phenotype else '' } connection goes {journey_sentence}.\n" + statement = f"In {sex or ''} {species}, the {phenotype.lower()} connection goes {journey_sentence}.\n" else: - statement = f"A {phenotype} connection goes {journey_sentence}.\n" + statement = f"A {phenotype.lower()} connection goes {journey_sentence}.\n" statement += f"This " if projection: - statement += f"{projection.lower() or ''} " + statement += f"{projection.lower()} " if circuit_type: - statement += f"{circuit_type.lower() or ''} " + statement += f"{circuit_type.lower()} " statement += f"connection projects from the {origins}." if laterality_description: - statement = statement[:-1] + f" and is found {laterality_description or ''}.\n" + statement = statement[:-1] + f" and is found {laterality_description}.\n" if apinatomy: statement += f" It is described in {apinatomy} model." From 84bed1a7fcede6222e952692b4a81ae140cf299c Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Mon, 12 Feb 2024 16:30:24 +0530 Subject: [PATCH 13/13] #250 remove eslint warnings --- frontend/src/components/StatementWithProvenances.tsx | 2 +- frontend/src/components/Widgets/CustomEntitiesDropdown.tsx | 1 - frontend/src/components/Widgets/TextfieldWithChips.tsx | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/src/components/StatementWithProvenances.tsx b/frontend/src/components/StatementWithProvenances.tsx index bd2ef5c1..da03c339 100644 --- a/frontend/src/components/StatementWithProvenances.tsx +++ b/frontend/src/components/StatementWithProvenances.tsx @@ -1,7 +1,7 @@ import React from "react"; import {Box} from "@mui/material"; import StatementForm from "./Forms/StatementForm"; -import ProvenancesForm from "./Forms/ProvenanceForm"; +// import ProvenancesForm from "./Forms/ProvenanceForm"; const StatementWithProvenances = ({ statement, background = "#fff", refreshStatement, isDisabled } : any) => { diff --git a/frontend/src/components/Widgets/CustomEntitiesDropdown.tsx b/frontend/src/components/Widgets/CustomEntitiesDropdown.tsx index fb887e0b..0c227c6f 100644 --- a/frontend/src/components/Widgets/CustomEntitiesDropdown.tsx +++ b/frontend/src/components/Widgets/CustomEntitiesDropdown.tsx @@ -18,7 +18,6 @@ import { } from "@mui/material"; import {CheckedItemIcon, CheckedItemIconBG, UncheckedItemIcon} from "../icons"; import HoveredOptionContent from "./HoveredOptionContent"; -import ClearOutlinedIcon from "@mui/icons-material/ClearOutlined"; import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp"; import theme from "../../theme/Theme"; diff --git a/frontend/src/components/Widgets/TextfieldWithChips.tsx b/frontend/src/components/Widgets/TextfieldWithChips.tsx index 87ce8174..6266e883 100644 --- a/frontend/src/components/Widgets/TextfieldWithChips.tsx +++ b/frontend/src/components/Widgets/TextfieldWithChips.tsx @@ -2,9 +2,6 @@ import React from "react"; import TextField from "@mui/material/TextField"; import FormControl from "@mui/material/FormControl"; import { Autocomplete, styled } from "@mui/material"; -import Chip from "@mui/material/Chip"; -import ClearOutlinedIcon from "@mui/icons-material/ClearOutlined"; -import { vars } from "../../theme/variables"; import Box from "@mui/material/Box"; import CustomTextFieldChip from "./CustomTextFieldChip";