diff --git a/backend/flowcell/serializers.py b/backend/flowcell/serializers.py index 25ec4b0a3..f54603dca 100644 --- a/backend/flowcell/serializers.py +++ b/backend/flowcell/serializers.py @@ -57,7 +57,6 @@ class LaneSerializer(ModelSerializer): read_length_name = SerializerMethodField() index_i7_show = SerializerMethodField() index_i5_show = SerializerMethodField() - equal_representation = SerializerMethodField() quality_check = CharField(required=False) request = SerializerMethodField() protocol = SerializerMethodField() @@ -73,7 +72,6 @@ class Meta: "read_length_name", "index_i7_show", "index_i5_show", - "equal_representation", "loading_concentration", "phix", "quality_check", @@ -168,13 +166,6 @@ def get_index_i5_show(self, obj): return "" # return None - def get_equal_representation(self, obj): - records = list( - itertools.chain(obj.pool.libraries.all(), obj.pool.samples.all()) - ) - ern = [x.equal_representation_nucleotides for x in records].count(True) - return len(records) == ern - class FlowcellListSerializer(ModelSerializer): flowcell = SerializerMethodField() diff --git a/backend/flowcell/views.py b/backend/flowcell/views.py index 7659420f9..3de2d99cb 100644 --- a/backend/flowcell/views.py +++ b/backend/flowcell/views.py @@ -117,13 +117,13 @@ def get_queryset(self): libraries_qs = ( Library.objects.filter(~Q(status=-1)) .prefetch_related("read_length", "index_type") - .only("read_length", "index_type", "equal_representation_nucleotides") + .only("read_length", "index_type") ) samples_qs = ( Sample.objects.filter(~Q(status=-1)) .prefetch_related("read_length", "index_type") - .only("read_length", "index_type", "equal_representation_nucleotides") + .only("read_length", "index_type") ) lanes_qs = ( diff --git a/backend/incoming_libraries/serializers.py b/backend/incoming_libraries/serializers.py index 3fb6e723d..b9d7baa99 100644 --- a/backend/incoming_libraries/serializers.py +++ b/backend/incoming_libraries/serializers.py @@ -48,11 +48,9 @@ class Meta: "barcode", "record_type", "library_protocol", - "concentration", - "concentration_method", + "measuring_unit", + "measured_value", "dilution_factor", - "concentration_facility", - "concentration_method_facility", "sample_volume_facility", "amount_facility", "quality_check", @@ -60,14 +58,18 @@ class Meta: "comments_facility", "sequencing_depth", "library_protocol_name", + "measuring_unit_facility", + "measured_value_facility", ) extra_kwargs = { "name": {"required": False}, "barcode": {"required": False}, "library_protocol": {"required": False}, - "concentration": {"required": False}, - "concentration_method": {"required": False}, "sequencing_depth": {"required": False}, + "measuring_unit": {"required": False}, + "measured_value": {"required": False}, + "measuring_unit_facility": {"required": False}, + "measured_value_facility": {"required": False}, } def get_record_type(self, obj): @@ -80,15 +82,11 @@ def get_library_protocol_name(self, obj): class LibrarySerializer(BaseSerializer): class Meta(BaseSerializer.Meta): model = Library - fields = BaseSerializer.Meta.fields + ( - "qpcr_result", - "qpcr_result_facility", - "mean_fragment_size", - ) + fields = BaseSerializer.Meta.fields + ("mean_fragment_size",) + extra_kwargs = { **BaseSerializer.Meta.extra_kwargs, **{ - "qpcr_result": {"required": False}, "mean_fragment_size": {"required": False}, }, } diff --git a/backend/incoming_libraries/views.py b/backend/incoming_libraries/views.py index 83a18691e..aa9840977 100644 --- a/backend/incoming_libraries/views.py +++ b/backend/incoming_libraries/views.py @@ -28,11 +28,9 @@ def list(self, request): """Get the list of all incoming libraries and samples.""" libraries_qs = Library.objects.select_related( "library_protocol", - "concentration_method", ).filter(status=1) samples_qs = Sample.objects.select_related( "library_protocol", - "concentration_method", "nucleic_acid_type", ).filter(status=1) diff --git a/backend/library/admin.py b/backend/library/admin.py index 8aae8c602..b37c1c2eb 100644 --- a/backend/library/admin.py +++ b/backend/library/admin.py @@ -34,7 +34,6 @@ class LibraryAdmin(admin.ModelAdmin): list_filter = ( ("library_protocol", RelatedDropdownFilter), ("library_type", RelatedDropdownFilter), - ("concentration_method", RelatedDropdownFilter), ("organism", RelatedDropdownFilter), ("read_length", RelatedDropdownFilter), ("index_type", RelatedDropdownFilter), @@ -58,19 +57,17 @@ class LibraryAdmin(admin.ModelAdmin): "fields": ( "library_protocol", "library_type", - "concentration", - "concentration_method", - "organism", + "measuring_unit", + "measured_value", + "mean_fragment_size", + "volume", "read_length", "sequencing_depth", - "mean_fragment_size", - "equal_representation_nucleotides", "index_type", "index_reads", "index_i7", "index_i5", - "qpcr_result", - "amplification_cycles", + "organism", "comments", ), }, @@ -79,13 +76,12 @@ class LibraryAdmin(admin.ModelAdmin): "Determined by Facility", { "fields": ( + "measuring_unit_facility", + "measured_value_facility", "dilution_factor", - "concentration_facility", - "concentration_method_facility", "sample_volume_facility", "amount_facility", "size_distribution_facility", - "qpcr_result_facility", "comments_facility", ), }, diff --git a/backend/library/migrations/0005_rename_amplification_cycles_library_removed_amplification_cycles_and_more.py b/backend/library/migrations/0005_rename_amplification_cycles_library_removed_amplification_cycles_and_more.py new file mode 100644 index 000000000..df086b976 --- /dev/null +++ b/backend/library/migrations/0005_rename_amplification_cycles_library_removed_amplification_cycles_and_more.py @@ -0,0 +1,91 @@ +# Generated by Django 4.2.16 on 2024-10-23 01:27 + +import django.core.validators +import library_sample_shared.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "library_sample_shared", + "0011_alter_indexi5_number_alter_indexi5_prefix_and_more", + ), + ("library", "0004_archived_feature"), + ] + + operations = [ + migrations.RenameField( + model_name="library", + old_name="amplification_cycles", + new_name="removed_amplification_cycles", + ), + migrations.RenameField( + model_name="library", + old_name="concentration_facility", + new_name="measured_value_facility", + ), + migrations.RenameField( + model_name="library", + old_name="concentration_method_facility", + new_name="removed_concentration_method_facility", + ), + migrations.RenameField( + model_name="library", + old_name="qpcr_result", + new_name="removed_qpcr_result", + ), + migrations.RenameField( + model_name="library", + old_name="qpcr_result_facility", + new_name="removed_qpcr_result_facility", + ), + migrations.RenameField( + model_name="library", + old_name="concentration", + new_name="measured_value", + ), + migrations.RenameField( + model_name="library", + old_name="concentration_method", + new_name="removed_concentration_method", + ), + migrations.RenameField( + model_name="library", + old_name="equal_representation_nucleotides", + new_name="removed_equal_representation_nucleotides", + ), + migrations.AddField( + model_name="library", + name="measuring_unit", + field=models.CharField( + blank=True, + choices=[("concentration", "Concentration (ng/µl)"), ("-", "Unknown")], + max_length=50, + null=True, + verbose_name="Measuring Unit", + ), + ), + migrations.AddField( + model_name="library", + name="measuring_unit_facility", + field=models.CharField( + blank=True, + choices=[("concentration", "Concentration (ng/µl)"), ("-", "Unknown")], + max_length=50, + null=True, + verbose_name="Measuring Unit (facility)", + ), + ), + migrations.AddField( + model_name="library", + name="volume", + field=models.FloatField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(10)], + verbose_name="Volume", + ), + ), + ] diff --git a/backend/library/migrations/0006_alter_library_measured_value_and_more.py b/backend/library/migrations/0006_alter_library_measured_value_and_more.py new file mode 100644 index 000000000..fbb6f3228 --- /dev/null +++ b/backend/library/migrations/0006_alter_library_measured_value_and_more.py @@ -0,0 +1,64 @@ +# Generated by Django 4.2.16 on 2024-10-23 01:52 + +import django.core.validators +import library_sample_shared.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "library_sample_shared", + "0011_alter_indexi5_number_alter_indexi5_prefix_and_more", + ), + ( + "library", + "0005_rename_amplification_cycles_library_removed_amplification_cycles_and_more", + ), + ] + + operations = [ + migrations.AlterField( + model_name="library", + name="measured_value", + field=models.FloatField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(-1)], + verbose_name="Measured Value", + ), + ), + migrations.AlterField( + model_name="library", + name="measured_value_facility", + field=models.FloatField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(-1)], + verbose_name="Measured Value (facility)", + ), + ), + migrations.AlterField( + model_name="library", + name="removed_concentration_method", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=models.SET( + library_sample_shared.models.get_removed_concentrationmethod + ), + to="library_sample_shared.concentrationmethod", + verbose_name="Concentration Method", + ), + ), + migrations.AlterField( + model_name="library", + name="removed_equal_representation_nucleotides", + field=models.BooleanField( + blank=True, + default=False, + verbose_name="Equal Representation of Nucleotides", + ), + ), + ] diff --git a/backend/library/migrations/max_migration.txt b/backend/library/migrations/max_migration.txt index 719cf5a12..9001660e3 100644 --- a/backend/library/migrations/max_migration.txt +++ b/backend/library/migrations/max_migration.txt @@ -1 +1 @@ -0004_archived_feature +0006_alter_library_measured_value_and_more diff --git a/backend/library/models.py b/backend/library/models.py index 0574e6cda..ebf745f6e 100644 --- a/backend/library/models.py +++ b/backend/library/models.py @@ -1,25 +1,54 @@ +from django.core.validators import MinValueValidator from django.db import models from library_sample_shared.models import GenericLibrarySample class Library(GenericLibrarySample): + MEASURING_UNIT_CHOICES = [ + ("Concentration (ng/µl)", "concentration", "Concentration"), + ("Unknown", "-", "Unknown"), + ] + + measuring_unit = models.CharField( + "Measuring Unit", + max_length=50, + choices=[ + (unit, display_name) + for display_name, unit, input_type in MEASURING_UNIT_CHOICES + ], + null=True, + blank=True, + ) + mean_fragment_size = models.PositiveIntegerField( "Mean Fragment Size", null=True, blank=True, ) - qpcr_result = models.FloatField("qPCR Result", null=True, blank=True) + removed_qpcr_result = models.FloatField( + "qPCR Result", null=True, blank=True + ) # This field is not in use - # Quality Control - qpcr_result_facility = models.FloatField( + removed_qpcr_result_facility = models.FloatField( "qPCR Result (facility)", null=True, blank=True, - ) + ) # This field is not in use archived = models.BooleanField("Archived", default=False) + measuring_unit_facility = models.CharField( + "Measuring Unit (facility)", + max_length=50, + choices=[ + (unit, display_name) + for display_name, unit, input_type in MEASURING_UNIT_CHOICES + ], + null=True, + blank=True, + ) + class Meta: verbose_name = "Library" verbose_name_plural = "Libraries" diff --git a/backend/library/serializers.py b/backend/library/serializers.py index 4d56931ca..3f99a38b9 100644 --- a/backend/library/serializers.py +++ b/backend/library/serializers.py @@ -29,7 +29,8 @@ class Meta(LibrarySampleBaseSerializer.Meta): "index_i7", "index_i5", "mean_fragment_size", - "qpcr_result", + "measuring_unit", + "measured_value", ) def get_record_type(self, obj): diff --git a/backend/library/tests.py b/backend/library/tests.py index ed7cdb1ae..0e80a34d9 100644 --- a/backend/library/tests.py +++ b/backend/library/tests.py @@ -25,9 +25,6 @@ def create_library(name, status=0, save=True, read_length=None, index_type=None) organism = Organism(name="Organism") organism.save() - concentration_method = ConcentrationMethod(name="Concentration Method") - concentration_method.save() - if read_length is None: read_length = ReadLength(name="Read Length") read_length.save() @@ -56,12 +53,10 @@ def create_library(name, status=0, save=True, read_length=None, index_type=None) status=status, organism_id=organism.pk, concentration=1.0, - concentration_method_id=concentration_method.pk, read_length_id=read_length.pk, sequencing_depth=1, library_protocol_id=library_protocol.pk, library_type_id=library_type.pk, - amplification_cycles=1, index_type_id=index_type.pk, index_reads=0, mean_fragment_size=1, @@ -214,12 +209,10 @@ def test_add_library(self): "name": name, "organism": library.organism.pk, "concentration": 1.0, - "concentration_method": library.concentration_method.pk, "read_length": library.read_length.pk, "sequencing_depth": 1, "library_protocol": library.library_protocol.pk, "library_type": library.library_type.pk, - "amplification_cycles": 1, "index_type": library.index_type.pk, "index_reads": 0, "mean_fragment_size": 1, @@ -246,12 +239,10 @@ def test_add_library_contains_invalid(self): "name": name, "organism": self.library.organism.pk, "concentration": 1.0, - "concentration_method": self.library.concentration_method.pk, "read_length": self.library.read_length.pk, "sequencing_depth": 1, "library_protocol": self.library.library_protocol.pk, "library_type": self.library.library_type.pk, - "amplification_cycles": 1, "index_type": self.library.index_type.pk, "index_reads": 0, "mean_fragment_size": 1, @@ -260,7 +251,6 @@ def test_add_library_contains_invalid(self): "name": self._get_random_name(), "concentration": 1.0, "sequencing_depth": 1, - "amplification_cycles": 1, "index_reads": 0, "mean_fragment_size": 1, }, @@ -317,12 +307,10 @@ def test_update_library(self): "name": new_name, "organism": library.organism.pk, "concentration": 1.0, - "concentration_method": library.concentration_method.pk, "read_length": library.read_length.pk, "sequencing_depth": 1, "library_protocol": library.library_protocol.pk, "library_type": library.library_type.pk, - "amplification_cycles": 1, "index_type": library.index_type.pk, "index_reads": 0, "mean_fragment_size": 1, @@ -352,12 +340,10 @@ def test_update_library_contains_invalid(self): "name": new_name1, "organism": library1.organism.pk, "concentration": 1.0, - "concentration_method": library1.concentration_method.pk, "read_length": library1.read_length.pk, "sequencing_depth": 1, "library_protocol": library1.library_protocol.pk, "library_type": library1.library_type.pk, - "amplification_cycles": 1, "index_type": library1.index_type.pk, "index_reads": 0, "mean_fragment_size": 1, @@ -367,7 +353,6 @@ def test_update_library_contains_invalid(self): "name": new_name2, "concentration": 2.0, "sequencing_depth": 2, - "amplification_cycles": 2, "index_reads": 0, "mean_fragment_size": 2, }, diff --git a/backend/library_preparation/migrations/0005_rename_qpcr_result_librarypreparation_removed_qpcr_result.py b/backend/library_preparation/migrations/0005_rename_qpcr_result_librarypreparation_removed_qpcr_result.py new file mode 100644 index 000000000..58fa911ce --- /dev/null +++ b/backend/library_preparation/migrations/0005_rename_qpcr_result_librarypreparation_removed_qpcr_result.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.16 on 2024-10-23 01:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("library_preparation", "0004_librarypreparation_smear_analysis"), + ] + + operations = [ + migrations.RenameField( + model_name="librarypreparation", + old_name="qpcr_result", + new_name="removed_qpcr_result", + ), + ] diff --git a/backend/library_preparation/migrations/max_migration.txt b/backend/library_preparation/migrations/max_migration.txt index 9dfac2c79..0983993f8 100644 --- a/backend/library_preparation/migrations/max_migration.txt +++ b/backend/library_preparation/migrations/max_migration.txt @@ -1 +1 @@ -0004_librarypreparation_smear_analysis +0005_rename_qpcr_result_librarypreparation_removed_qpcr_result diff --git a/backend/library_preparation/models.py b/backend/library_preparation/models.py index 95901a7cf..f81d8e4ea 100644 --- a/backend/library_preparation/models.py +++ b/backend/library_preparation/models.py @@ -51,11 +51,11 @@ class LibraryPreparation(DateTimeMixin): blank=True, ) - qpcr_result = models.FloatField( + removed_qpcr_result = models.FloatField( "qPCR Result", null=True, blank=True, - ) + ) # This field is not in use comments = models.TextField( "Comments", diff --git a/backend/library_sample_shared/models.py b/backend/library_sample_shared/models.py index 0bf04d371..57ba131e5 100644 --- a/backend/library_sample_shared/models.py +++ b/backend/library_sample_shared/models.py @@ -310,17 +310,26 @@ class GenericLibrarySample(DateTimeMixin): Organism, verbose_name="Organism", on_delete=models.SET_NULL, null=True ) - concentration = models.FloatField("Concentration") + measured_value = models.FloatField( + "Measured Value", validators=[MinValueValidator(-1)], null=True, blank=True + ) - concentration_method = models.ForeignKey( + removed_concentration_method = models.ForeignKey( ConcentrationMethod, verbose_name="Concentration Method", + null=True, + blank=True, on_delete=models.SET(get_removed_concentrationmethod), - ) + ) # This field is not in use - equal_representation_nucleotides = models.BooleanField( + removed_equal_representation_nucleotides = models.BooleanField( "Equal Representation of Nucleotides", - default=True, + blank=True, + default=False, + ) # This field is not in use + + volume = models.FloatField( + "Volume", validators=[MinValueValidator(10)], null=True, blank=True ) read_length = models.ForeignKey( @@ -362,11 +371,11 @@ class GenericLibrarySample(DateTimeMixin): blank=True, ) - amplification_cycles = models.PositiveIntegerField( + removed_amplification_cycles = models.PositiveIntegerField( "Amplification cycles", null=True, blank=True, - ) + ) # This field is not in use @property def index_i7_id(self): @@ -388,20 +397,14 @@ def index_i5_id(self): blank=True, ) - concentration_facility = models.FloatField( - "Concentration", - null=True, - blank=True, - ) - - concentration_method_facility = models.ForeignKey( + removed_concentration_method_facility = models.ForeignKey( ConcentrationMethod, related_name="+", verbose_name="Concentration Method", null=True, blank=True, on_delete=models.SET_NULL, - ) + ) # This field is not in use sample_volume_facility = models.PositiveIntegerField( "Sample Volume", @@ -428,6 +431,13 @@ def index_i5_id(self): blank=True, ) + measured_value_facility = models.FloatField( + "Measured Value (facility)", + validators=[MinValueValidator(-1)], + null=True, + blank=True, + ) + class Meta: abstract = True @@ -443,6 +453,12 @@ def generate_barcode(self): self.barcode = barcode self.save(update_fields=["barcode"]) + def get_measuring_unit_details(self): + for display_name, unit, input_type in self.MEASURING_UNIT_CHOICES: + if display_name == self.measuring_unit: + return display_name, unit, input_type + return None, None, None + def save(self, *args, **kwargs): created = self.pk is None super().save(*args, **kwargs) diff --git a/backend/library_sample_shared/serializers.py b/backend/library_sample_shared/serializers.py index cec7147d2..2499a2d84 100644 --- a/backend/library_sample_shared/serializers.py +++ b/backend/library_sample_shared/serializers.py @@ -127,7 +127,6 @@ class LibrarySampleBaseSerializer(ModelSerializer): request_name = SerializerMethodField() library_protocol_name = SerializerMethodField() library_type_name = SerializerMethodField() - concentration_method_name = SerializerMethodField() read_length_name = SerializerMethodField() organism_name = SerializerMethodField() @@ -144,16 +143,13 @@ class Meta: "library_protocol_name", "library_type", "library_type_name", + "volume", "organism", - "equal_representation_nucleotides", - "concentration", - "concentration_method", "read_length", "read_length_name", "sequencing_depth", - "comments", - "amplification_cycles", "organism_name", + "comments", ) extra_kwargs = {"barcode": {"required": False}} @@ -169,9 +165,6 @@ def get_library_protocol_name(self, obj): def get_library_type_name(self, obj): return obj.library_type.name - def get_concentration_method_name(self, obj): - return obj.concentration_method.name - def get_read_length_name(self, obj): return obj.read_length.name diff --git a/backend/library_sample_shared/tests.py b/backend/library_sample_shared/tests.py index b10d83d77..3488827f4 100644 --- a/backend/library_sample_shared/tests.py +++ b/backend/library_sample_shared/tests.py @@ -66,15 +66,6 @@ def test_organism_name(self): self.assertEqual(self.organism.__str__(), self.organism.name) -class ConcentrationMethodTest(TestCase): - def setUp(self): - self.method = ConcentrationMethod(name=get_random_name()) - - def test_concentration_method_name(self): - self.assertTrue(isinstance(self.method, ConcentrationMethod)) - self.assertEqual(self.method.__str__(), self.method.name) - - class ReadLengthTest(TestCase): def setUp(self): self.read_length = ReadLength(name=get_random_name()) @@ -229,23 +220,6 @@ def test_organisms_list(self): self.assertIn(self.read_length.name, read_lengths) -class TestConcentrationMethods(BaseTestCase): - def setUp(self): - self.create_user("foo@bar.io", "foo-foo") - self.client.login(email="foo@bar.io", password="foo-foo") - - self.concentration_method = ConcentrationMethod(name=self._get_random_name()) - self.concentration_method.save() - - def test_organisms_list(self): - """Ensure get concentration methods behaves correctly.""" - response = self.client.get(reverse("concentration-method-list")) - data = response.json() - concentration_methods = [x["name"] for x in data] - self.assertEqual(response.status_code, 200) - self.assertIn(self.concentration_method.name, concentration_methods) - - class TestIndexTypes(BaseTestCase): def setUp(self): self.create_user("foo@bar.io", "foo-foo") diff --git a/backend/request/views.py b/backend/request/views.py index e7cf5115d..fb42e92c9 100644 --- a/backend/request/views.py +++ b/backend/request/views.py @@ -1092,15 +1092,6 @@ def export_request(request): raise PermissionDenied() dataset = Dataset() dataset.headers = ( - # The following are not submitted by user... - # id barcode create_time update_time status concentration concentration_method - # equal_representation_nucleotides comments is_pooled amplification_cycles - # dilution_factor concentration_facility concentration_method_facility archived - # sample_volume_facility amount_facility size_distribution_facility comments_facility - ##libraries-exclusively: - # qpcr_result qpcr_result_facility - ##sample-exclusively: - # is_converted "id", "name", "barcode", diff --git a/backend/sample/admin.py b/backend/sample/admin.py index 1b47ed728..6cce96dd9 100644 --- a/backend/sample/admin.py +++ b/backend/sample/admin.py @@ -57,7 +57,6 @@ class SampleAdmin(admin.ModelAdmin): ("library_protocol", RelatedDropdownFilter), ("library_type", RelatedDropdownFilter), ("nucleic_acid_type", RelatedDropdownFilter), - ("concentration_method", RelatedDropdownFilter), ("organism", RelatedDropdownFilter), ("read_length", RelatedDropdownFilter), ("index_type", RelatedDropdownFilter), @@ -84,18 +83,17 @@ class SampleAdmin(admin.ModelAdmin): "library_protocol", "library_type", "nucleic_acid_type", - "concentration", - "concentration_method", - "organism", + "measuring_unit", + "measured_value", + "volume", "read_length", "sequencing_depth", - "equal_representation_nucleotides", "index_type", "index_i7", "index_i5", - "rna_quality", - "amplification_cycles", - "comments", + "organism", + "biosafety_level", + "gmo", ), }, ), @@ -103,13 +101,13 @@ class SampleAdmin(admin.ModelAdmin): "Determined by Facility", { "fields": ( + "measuring_unit_facility", + "measured_value_facility", "dilution_factor", "concentration_facility", - "concentration_method_facility", "sample_volume_facility", "amount_facility", "size_distribution_facility", - "rna_quality_facility", "comments_facility", ), }, diff --git a/backend/sample/migrations/0007_alter_nucleicacidtype_options_and_more.py b/backend/sample/migrations/0007_alter_nucleicacidtype_options_and_more.py new file mode 100644 index 000000000..0020de56f --- /dev/null +++ b/backend/sample/migrations/0007_alter_nucleicacidtype_options_and_more.py @@ -0,0 +1,124 @@ +# Generated by Django 4.2.16 on 2024-10-23 01:27 + +import django.core.validators +import django.db.models.deletion +import library_sample_shared.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "library_sample_shared", + "0011_alter_indexi5_number_alter_indexi5_prefix_and_more", + ), + ("sample", "0006_alter_nucleicacidtype_type"), + ] + + operations = [ + migrations.AlterModelOptions( + name="nucleicacidtype", + options={ + "verbose_name": "Input Type", + "verbose_name_plural": "Input Types", + }, + ), + migrations.RenameField( + model_name="sample", + old_name="amplification_cycles", + new_name="removed_amplification_cycles", + ), + migrations.RenameField( + model_name="sample", + old_name="concentration", + new_name="measured_value", + ), + migrations.RenameField( + model_name="sample", + old_name="concentration_method", + new_name="removed_concentration_method", + ), + migrations.RenameField( + model_name="sample", + old_name="equal_representation_nucleotides", + new_name="removed_equal_representation_nucleotides", + ), + migrations.RenameField( + model_name="sample", + old_name="concentration_facility", + new_name="measured_value_facility", + ), + migrations.RenameField( + model_name="sample", + old_name="concentration_method_facility", + new_name="removed_concentration_method_facility", + ), + migrations.AddField( + model_name="sample", + name="biosafety_level", + field=models.CharField( + choices=[("bsl1", "BSL1"), ("bsl2", "BSL2")], + max_length=50, + null=True, + verbose_name="Biosafety Level", + ), + ), + migrations.AddField( + model_name="sample", + name="gmo", + field=models.BooleanField( + blank=True, null=True, verbose_name="Genetically Modified Organism" + ), + ), + migrations.AddField( + model_name="sample", + name="measuring_unit", + field=models.CharField( + blank=True, + choices=[ + ("ng/µl", "ng/µl (Concentration)"), + ("M", "M (Cells)"), + ("-", "Unknown"), + ], + max_length=50, + null=True, + verbose_name="Measuring Unit", + ), + ), + migrations.AddField( + model_name="sample", + name="measuring_unit_facility", + field=models.CharField( + blank=True, + choices=[ + ("ng/µl", "ng/µl (Concentration)"), + ("M", "M (Cells)"), + ("-", "Unknown"), + ], + max_length=50, + null=True, + verbose_name="Measuring Unit (facility)", + ), + ), + migrations.AddField( + model_name="sample", + name="volume", + field=models.FloatField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(10)], + verbose_name="Volume", + ), + ), + migrations.AlterField( + model_name="sample", + name="nucleic_acid_type", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="sample.nucleicacidtype", + verbose_name="Input Type", + ), + ), + ] diff --git a/backend/sample/migrations/0008_alter_sample_measured_value_and_more.py b/backend/sample/migrations/0008_alter_sample_measured_value_and_more.py new file mode 100644 index 000000000..28e9aa36e --- /dev/null +++ b/backend/sample/migrations/0008_alter_sample_measured_value_and_more.py @@ -0,0 +1,61 @@ +# Generated by Django 4.2.16 on 2024-10-23 01:38 + +import django.core.validators +import library_sample_shared.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "library_sample_shared", + "0011_alter_indexi5_number_alter_indexi5_prefix_and_more", + ), + ("sample", "0007_alter_nucleicacidtype_options_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="sample", + name="measured_value", + field=models.FloatField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(-1)], + verbose_name="Measured Value", + ), + ), + migrations.AlterField( + model_name="sample", + name="measured_value_facility", + field=models.FloatField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(-1)], + verbose_name="Measured Value (facility)", + ), + ), + migrations.AlterField( + model_name="sample", + name="removed_concentration_method", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=models.SET( + library_sample_shared.models.get_removed_concentrationmethod + ), + to="library_sample_shared.concentrationmethod", + verbose_name="Concentration Method", + ), + ), + migrations.AlterField( + model_name="sample", + name="removed_equal_representation_nucleotides", + field=models.BooleanField( + blank=True, + default=False, + verbose_name="Equal Representation of Nucleotides", + ), + ), + ] diff --git a/backend/sample/migrations/max_migration.txt b/backend/sample/migrations/max_migration.txt index 23b67bb98..3da3624e4 100644 --- a/backend/sample/migrations/max_migration.txt +++ b/backend/sample/migrations/max_migration.txt @@ -1 +1 @@ -0006_alter_nucleicacidtype_type +0008_alter_sample_measured_value_and_more diff --git a/backend/sample/models.py b/backend/sample/models.py index 0eb7dcc2b..13e6bc4b4 100644 --- a/backend/sample/models.py +++ b/backend/sample/models.py @@ -18,21 +18,40 @@ class NucleicAcidType(models.Model): archived = models.BooleanField("Archived", default=False) class Meta: - verbose_name = "Nucleic Acid Type" - verbose_name_plural = "Nucleic Acid Types" + verbose_name = "Input Type" + verbose_name_plural = "Input Types" def __str__(self): return self.name class Sample(GenericLibrarySample): + MEASURING_UNIT_CHOICES = [ + ("ng/µl (Concentration)", "ng/µl", "Concentration"), + ("M (Cells)", "M", "Cells"), + ("Unknown", "-", "Unknown"), + ] + + BIOSAFETY_LEVEL_CHOICES = [("BSL1", "bsl1"), ("BSL2", "bsl2")] + nucleic_acid_type = models.ForeignKey( NucleicAcidType, - verbose_name="Nucleic Acid Type", + verbose_name="Input Type", on_delete=models.SET_NULL, null=True, ) + measuring_unit = models.CharField( + "Measuring Unit", + max_length=50, + choices=[ + (unit, display_name) + for display_name, unit, input_type in MEASURING_UNIT_CHOICES + ], + null=True, + blank=True, + ) + rna_quality = models.FloatField( "RNA Quality", validators=[MinValueValidator(0.0), MaxValueValidator(11.0)], @@ -42,7 +61,6 @@ class Sample(GenericLibrarySample): is_converted = models.BooleanField("Converted", default=False) - # Quality Control rna_quality_facility = models.FloatField( "RNA Quality (facility)", validators=[MinValueValidator(0.0), MaxValueValidator(11.0)], @@ -50,6 +68,31 @@ class Sample(GenericLibrarySample): blank=True, ) + biosafety_level = models.CharField( + "Biosafety Level", + max_length=50, + choices=[ + (biosafety_level, display_name) + for display_name, biosafety_level in BIOSAFETY_LEVEL_CHOICES + ], + null=True, + ) + + gmo = models.BooleanField("Genetically Modified Organism", null=True, blank=True) + + # Facility + + measuring_unit_facility = models.CharField( + "Measuring Unit (facility)", + max_length=50, + choices=[ + (unit, display_name) + for display_name, unit, input_type in MEASURING_UNIT_CHOICES + ], + null=True, + blank=True, + ) + archived = models.BooleanField("Archived", default=False) class Meta: diff --git a/backend/sample/serializers.py b/backend/sample/serializers.py index f852006d6..69ccb2bc5 100644 --- a/backend/sample/serializers.py +++ b/backend/sample/serializers.py @@ -29,6 +29,10 @@ class Meta(LibrarySampleBaseSerializer.Meta): "record_type", "is_converted", "rna_quality", + "measuring_unit", + "measured_value", + "gmo", + "biosafety_level", "nucleic_acid_type", "nucleic_acid_type_name", ) diff --git a/backend/sample/tests.py b/backend/sample/tests.py index 93f025535..cc77c0a6d 100644 --- a/backend/sample/tests.py +++ b/backend/sample/tests.py @@ -25,9 +25,6 @@ def create_sample(name, status=0, save=True, read_length=None, index_type=None): organism = Organism(name="Organism") organism.save() - concentration_method = ConcentrationMethod(name="Concentration Method") - concentration_method.save() - if read_length is None: read_length = ReadLength(name="Read Length") read_length.save() @@ -55,7 +52,6 @@ def create_sample(name, status=0, save=True, read_length=None, index_type=None): status=status, organism_id=organism.pk, concentration=1.0, - concentration_method_id=concentration_method.pk, read_length_id=read_length.pk, sequencing_depth=1, library_protocol_id=library_protocol.pk, @@ -216,7 +212,6 @@ def test_add_sample(self): "name": name, "organism": self.sample.organism.pk, "concentration": 1.0, - "concentration_method": self.sample.concentration_method.pk, "read_length": self.sample.read_length.pk, "sequencing_depth": 1, "library_protocol": self.sample.library_protocol.pk, @@ -245,7 +240,6 @@ def test_add_sample_contains_invalid(self): "name": name, "organism": self.sample.organism.pk, "concentration": 1.0, - "concentration_method": self.sample.concentration_method.pk, "read_length": self.sample.read_length.pk, "sequencing_depth": 1, "library_protocol": self.sample.library_protocol.pk, @@ -310,7 +304,6 @@ def test_update_sample(self): "name": new_name, "organism": sample.organism.pk, "concentration": 1.0, - "concentration_method": sample.concentration_method.pk, "read_length": sample.read_length.pk, "sequencing_depth": 1, "library_protocol": sample.library_protocol.pk, @@ -342,7 +335,6 @@ def test_update_sample_contains_invalid(self): "name": new_name1, "organism": sample1.organism.pk, "concentration": 1.0, - "concentration_method": sample1.concentration_method.pk, "read_length": sample1.read_length.pk, "sequencing_depth": 1, "library_protocol": sample1.library_protocol.pk, diff --git a/backend/static/main-hub/app/components/BaseGridController.js b/backend/static/main-hub/app/components/BaseGridController.js index 842f25772..64c29cbc9 100644 --- a/backend/static/main-hub/app/components/BaseGridController.js +++ b/backend/static/main-hub/app/components/BaseGridController.js @@ -312,7 +312,9 @@ Ext.define("MainHub.components.BaseGridController", { }, gridCellTooltipRenderer: function (value, meta) { - meta.tdAttr = Ext.String.format('data-qtip="{0}"', value); + if (value) { + meta.tdAttr = 'data-qtip="' + value + '"'; + } else meta.tdAttr = "data-qtip=Empty"; return value; }, diff --git a/backend/static/main-hub/app/model/incominglibraries/IncomingLibraries.js b/backend/static/main-hub/app/model/incominglibraries/IncomingLibraries.js index 00431ffc6..d6ef451cd 100644 --- a/backend/static/main-hub/app/model/incominglibraries/IncomingLibraries.js +++ b/backend/static/main-hub/app/model/incominglibraries/IncomingLibraries.js @@ -34,20 +34,15 @@ Ext.define("MainHub.model.incominglibraries.IncomingLibraries", { allowNull: true }, { - name: "qpcr_result_facility", - type: "float", - allowNull: true + name: "measuring_unit_facility", + type: "string" }, { - name: "rna_quality_facility", + name: "measured_value_facility", type: "float", allowNull: true, defaultValue: null }, - { - name: "comments_facility", - type: "string" - }, { name: "samples_submitted", type: "bool" diff --git a/backend/static/main-hub/app/model/libraries/BatchAdd/Common.js b/backend/static/main-hub/app/model/libraries/BatchAdd/Common.js index 5dde405b1..349c869c8 100644 --- a/backend/static/main-hub/app/model/libraries/BatchAdd/Common.js +++ b/backend/static/main-hub/app/model/libraries/BatchAdd/Common.js @@ -41,6 +41,16 @@ Ext.define("MainHub.model.libraries.BatchAdd.Common", { type: "string", name: "name" }, + { + name: "measuring_unit", + type: "string" + }, + { + name: "measured_value", + type: "float", + allowNull: true, + defaultValue: null + }, { type: "int", name: "library_protocol", @@ -59,25 +69,10 @@ Ext.define("MainHub.model.libraries.BatchAdd.Common", { defaultValue: null }, { + name: "volume", type: "float", - name: "concentration", defaultValue: null }, - { - type: "int", - name: "concentration_method", - allowNull: true, - defaultValue: null - }, - { - type: "int", - name: "amplification_cycles", - defaultValue: null - }, - { - type: "bool", - name: "equal_representation_nucleotides" - }, { type: "int", name: "read_length", @@ -116,13 +111,12 @@ Ext.define("MainHub.model.libraries.BatchAdd.Common", { dataIndex: "name" } ], + measuring_unit: "presence", library_protocol: "presence", library_type: "presence", - concentration: "presence", + volume: "greaterthanten", read_length: "presence", sequencing_depth: "greaterthanten", - // amplification_cycles: 'presence', - // concentration_method: 'presence', organism: "presence" } }); diff --git a/backend/static/main-hub/app/model/libraries/BatchAdd/Library.js b/backend/static/main-hub/app/model/libraries/BatchAdd/Library.js index 0defa4dd4..19ebccb3b 100644 --- a/backend/static/main-hub/app/model/libraries/BatchAdd/Library.js +++ b/backend/static/main-hub/app/model/libraries/BatchAdd/Library.js @@ -52,12 +52,6 @@ Ext.define("MainHub.model.libraries.BatchAdd.Library", { { type: "string", name: "index_i5" - }, - { - type: "int", - name: "qpcr_result", - allowNull: true, - defaultValue: null } ], diff --git a/backend/static/main-hub/app/model/libraries/BatchAdd/Sample.js b/backend/static/main-hub/app/model/libraries/BatchAdd/Sample.js index bc546a952..31fb061eb 100644 --- a/backend/static/main-hub/app/model/libraries/BatchAdd/Sample.js +++ b/backend/static/main-hub/app/model/libraries/BatchAdd/Sample.js @@ -33,6 +33,14 @@ Ext.define("MainHub.model.libraries.BatchAdd.Sample", { name: "rna_quality", allowNull: true, defaultValue: null + }, + { + name: "biosafety_level", + type: "string" + }, + { + name: "gmo", + type: "bool" } ], diff --git a/backend/static/main-hub/app/model/libraries/Library.js b/backend/static/main-hub/app/model/libraries/Library.js index 9dc189c3d..90c825c23 100644 --- a/backend/static/main-hub/app/model/libraries/Library.js +++ b/backend/static/main-hub/app/model/libraries/Library.js @@ -32,11 +32,6 @@ Ext.define("MainHub.model.libraries.Library", { name: "library_type_name", type: "string" }, - { - name: "amplification_cycles", - type: "int", - allowNull: true - }, { name: "organism", type: "int" @@ -67,32 +62,19 @@ Ext.define("MainHub.model.libraries.Library", { type: "string" }, { - name: "equal_representation_nucleotides", - type: "bool", - allowNull: true - }, - { - name: "concentration", - type: "float", + name: "mean_fragment_size", + type: "int", allowNull: true }, { - name: "concentration_method", - type: "int" - }, - { - name: "concentration_method_name", + name: "measuring_unit", type: "string" }, { - name: "mean_fragment_size", - type: "int", - allowNull: true - }, - { - name: "qpcr_result", + name: "measured_value", type: "float", - allowNull: true + allowNull: true, + defaultValue: null }, { name: "read_length", @@ -119,6 +101,12 @@ Ext.define("MainHub.model.libraries.Library", { name: "nucleic_acid_type", type: "int" }, + { + name: "volume", + type: "float", + allowNull: true, + defaultValue: null + }, { name: "rna_quality", type: "float", @@ -129,6 +117,14 @@ Ext.define("MainHub.model.libraries.Library", { name: "pool", type: "string", defaultValue: "" + }, + { + name: "biosafety_level", + type: "string" + }, + { + name: "gmo", + type: "bool" } ] }); diff --git a/backend/static/main-hub/app/model/librarypreparation/LibraryPreparation.js b/backend/static/main-hub/app/model/librarypreparation/LibraryPreparation.js index e176bf03f..1abbe967b 100644 --- a/backend/static/main-hub/app/model/librarypreparation/LibraryPreparation.js +++ b/backend/static/main-hub/app/model/librarypreparation/LibraryPreparation.js @@ -102,11 +102,6 @@ Ext.define("MainHub.model.librarypreparation.LibraryPreparation", { type: "float", allowNull: true }, - { - name: "qpcr_result", - type: "float", - allowNull: true - }, { name: "dilution_factor", type: "int", diff --git a/backend/static/main-hub/app/view/incominglibraries/IncomingLibrariesController.js b/backend/static/main-hub/app/view/incominglibraries/IncomingLibrariesController.js index 536f6cfd9..fc913b2e5 100644 --- a/backend/static/main-hub/app/view/incominglibraries/IncomingLibrariesController.js +++ b/backend/static/main-hub/app/view/incominglibraries/IncomingLibrariesController.js @@ -95,13 +95,12 @@ Ext.define("MainHub.view.incominglibraries.IncomingLibrariesController", { var store = gridView.grid.getStore(); var allowedColumns = [ "dilution_factor", - "concentration_facility", - "concentration_method_facility", + "measuring_unit_facility", + "measured_value_facility", "sample_volume_facility", "amount_facility", "size_distribution_facility", "comments_facility", - "qpcr_result_facility", "rna_quality_facility" ]; var ngFormulaDataIndices = [ diff --git a/backend/static/main-hub/app/view/invoicing/InvoicingController.js b/backend/static/main-hub/app/view/invoicing/InvoicingController.js index 18a9ac453..84b0ef5ff 100644 --- a/backend/static/main-hub/app/view/invoicing/InvoicingController.js +++ b/backend/static/main-hub/app/view/invoicing/InvoicingController.js @@ -117,7 +117,9 @@ Ext.define("MainHub.view.invoicing.InvoicingController", { }, gridCellTooltipRenderer: function (value, meta) { - meta.tdAttr = Ext.String.format('data-qtip="{0}"', value); + if (value) { + meta.tdAttr = 'data-qtip="' + value + '"'; + } else meta.tdAttr = "data-qtip=Empty"; return value; }, diff --git a/backend/static/main-hub/app/view/libraries/BatchAddWindowController.js b/backend/static/main-hub/app/view/libraries/BatchAddWindowController.js index 7114ad612..4d2a45f91 100644 --- a/backend/static/main-hub/app/view/libraries/BatchAddWindowController.js +++ b/backend/static/main-hub/app/view/libraries/BatchAddWindowController.js @@ -22,6 +22,12 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { "#create-empty-records-button": { click: "createEmptyRecords" }, + "#save-button": { + click: "save" + }, + "#download-sample-form": { + click: "downloadSampleForm" + }, // Libraries only "#indexTypeEditor": { @@ -39,12 +45,9 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { select: "selectLibraryProtocol" }, - "#save-button": { - click: "save" - }, - - "#download-sample-form": { - click: "downloadSampleForm" + // Both Libraries and Samples + "#measuringUnitEditor": { + select: "selectMeasuringUnit" } } }, @@ -59,7 +62,7 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { } Ext.apply(Ext.tip.QuickTipManager.getQuickTip(), { - dismissDelay: 10000 // hide after 10 seconds + dismissDelay: 10000 // Hide after 10 seconds }); }, @@ -74,8 +77,6 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { wnd.getDockedItems('toolbar[dock="bottom"]')[0].show(); if (wnd.mode === "add") { wnd.getDockedItems('toolbar[dock="top"]')[0].show(); - } else { - // wnd.down('#cancel-button').show(); } layout.setActiveItem(1); @@ -102,15 +103,11 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { configuration[0].add(wnd.records); } - wnd.maximize(); // auto fullscreen + wnd.maximize(); // Auto fullscreen var grid = Ext.getCmp("batch-add-grid"); grid.reconfigure(configuration[0], configuration[1]); - // Load stores - // Ext.getStore('libraryProtocolsStore').reload(); - // Ext.getStore('libraryTypesStore').reload(); - // Flash hint text $("#edit-hint") .delay(100) @@ -278,20 +275,6 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { }); } - // RNA Quality should be applied only when Input Type is RNA - else if (dataIndex === "rna_quality") { - var nat = Ext.getStore("nucleicAcidTypesStore").findRecord( - "id", - item.get("nucleic_acid_type") - ); - - if (nat && nat.get("type") === "RNA") { - item.set(dataIndex, record.get(dataIndex)); - } - } else { - item.set(dataIndex, record.get(dataIndex)); - } - item.commit(); } }); @@ -311,7 +294,8 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { var libraryProtocolEditor = Ext.getCmp("libraryProtocolEditor"); var libraryProtocolsStore = Ext.getStore("libraryProtocolsStore"); var libraryTypeEditor = Ext.getCmp("libraryTypeEditor"); - var rnaQualityEditor = Ext.getCmp("rnaQualityEditor"); + var measuringUnitEditor = Ext.getCmp("measuringUnitEditor"); + var measuredValueEditor = Ext.getCmp("measuredValueEditor"); var record = context.record; // Toggle Library Type @@ -350,6 +334,22 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { indexI7Editor.disable(); indexI5Editor.disable(); } + + // Reset Measured Value if Measuring Unit is changed + if (!measuringUnitEditor.getValue()) { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", null); + } else { + if (measuringUnitEditor.getValue() === "-") { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", -1); + } else { + measuredValueEditor.setValue(null); + measuredValueEditor.enable(); + } + } } // Samples @@ -369,15 +369,20 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { } } - // Toggle RNA Quality - var nat = nucleicAcidTypesStore.findRecord( - "id", - record.get("nucleic_acid_type") - ); - if (nat && nat.get("type") === "RNA") { - rnaQualityEditor.enable(); + // Reset Measured Value if Measuring Unit is changed + if (!measuringUnitEditor.getValue()) { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", null); } else { - rnaQualityEditor.disable(); + if (measuringUnitEditor.getValue() === "-") { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", -1); + } else { + measuredValueEditor.setValue(null); + measuredValueEditor.enable(); + } } } }, @@ -412,22 +417,26 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { record.set({ index_i7: "", index_i5: "" }); } - // Reset RNA Quality if Input Type has changed - var nat = Ext.getStore("nucleicAcidTypesStore").findRecord( - "id", - record.get("nucleic_acid_type") - ); - if ( - nat !== null && - nat.get("type") === "DNA" && - record.get("rna_quality") > 0 - ) { - record.set("rna_quality", null); + // Reset Measured Value if Measuring Unit is empty + var measuringUnitEditor = Ext.getCmp("measuringUnitEditor"); + var measuredValueEditor = Ext.getCmp("measuredValueEditor"); + if (!measuringUnitEditor.getValue()) { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", null); + } else { + if (measuringUnitEditor.getValue() === "-") { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", -1); + } else { + measuredValueEditor.setValue(null); + measuredValueEditor.enable(); + } } record.commit(); - // Validate the record after editing and refresh the grid this.validateRecord(record); grid.getView().refresh(); }, @@ -454,13 +463,11 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { indexReadsEditor.getStore().add({ num: i }); } - // Remove values before loading new stores indexI7Editor.setValue(null); indexI5Editor.setValue(null); indexI7Editor.disable(); indexI5Editor.disable(); - // Reload stores indexI7Store.reload({ params: { index_type_id: record.get("id") } }); @@ -492,19 +499,31 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { var libraryProtocolEditor = Ext.getCmp("libraryProtocolEditor"); var libraryProtocolsStore = Ext.getStore("libraryProtocolsStore"); var libraryTypeEditor = Ext.getCmp("libraryTypeEditor"); - var rnaQualityEditor = Ext.getCmp("rnaQualityEditor"); libraryTypeEditor.setValue(null); libraryTypeEditor.disable(); this.filterLibraryProtocols(libraryProtocolsStore, record.get("type")); libraryProtocolEditor.enable(); + }, - if (record.get("type") === "RNA") { - rnaQualityEditor.enable(); + selectMeasuringUnit: function (fld, record) { + // Reset Measured Value if Measuring Unit is changed + var measuringUnitEditor = Ext.getCmp("measuringUnitEditor"); + var measuredValueEditor = Ext.getCmp("measuredValueEditor"); + if (!measuringUnitEditor.getValue()) { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", null); } else { - rnaQualityEditor.setValue(null); - rnaQualityEditor.disable(); + if (measuringUnitEditor.getValue() === "-") { + measuredValueEditor.setValue(null); + measuredValueEditor.disable(); + record.set("measured_value", -1); + } else { + measuredValueEditor.setValue(null); + measuredValueEditor.enable(); + } } }, @@ -534,11 +553,50 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { text: "Size (bp)", dataIndex: "mean_fragment_size", tooltip: "Mean Fragment Size", - width: 100, editor: { xtype: "numberfield", minValue: 0 }, + renderer: this.errorRenderer, + width: 100 + }, + { + text: "Measuring Unit", + dataIndex: "measuring_unit", + tooltip: "Measuring Unit", + width: 120, + editor: { + id: "measuringUnitEditor", + itemId: "measuringUnitEditor", + xtype: "combobox", + queryMode: "local", + valueField: "id", + displayField: "name", + store: { + fields: ["id", "name"], + data: [ + { id: "ng/µl", name: "ng/µl (Concentration)" }, + { id: "-", name: "Unknown" } + ] + }, + forceSelection: true, + listConfig: { + minWidth: 200 + } + }, + renderer: this.comboboxErrorRenderer + }, + { + text: "Measured Value", + dataIndex: "measured_value", + tooltip: "Measured Value", + width: 120, + editor: { + id: "measuredValueEditor", + itemId: "measuredValueEditor", + xtype: "numberfield", + minValue: 0 + }, renderer: this.errorRenderer }, { @@ -562,7 +620,7 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { { text: "# of Index Reads", dataIndex: "index_reads", - tooltip: "Index Type", + tooltip: "Number of Index Reads", width: 130, editor: { xtype: "combobox", @@ -616,7 +674,7 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { store: "indexI7Store", regex: new RegExp("^(?=(?:.{6}|.{8}|.{10}|.{12}|.{24})$)[ATCG]+$"), regexText: - "Only A, T, C and G (uppercase) are allowed. Index length must be 6, 8, 10, 12 or 24.", + "Only A, T, C, and G (uppercase) are allowed. Index length must be 6, 8, 10, 12, or 24.", matchFieldWidth: false }, renderer: this.errorRenderer @@ -642,44 +700,42 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { store: "indexI5Store", regex: new RegExp("^(?=(?:.{6}|.{8}|.{10}|.{12}|.{24})$)[ATCG]+$"), regexText: - "Only A, T, C and G (uppercase) are allowed. Index length must be 6, 8, 10, 12 or 24.", + "Only A, T, C, and G (uppercase) are allowed. Index length must be 6, 8, 10, 12, or 24.", matchFieldWidth: false }, renderer: this.errorRenderer + }, + { + text: "Comment Library", + dataIndex: "comments", + tooltip: "Comments", + width: 200, + editor: { + xtype: "textfield", + allowBlank: true + } } - // { - // text: 'qPCR (nM)', - // dataIndex: 'qpcr_result', - // tooltip: 'qPCR Result (nM)', - // width: 85, - // editor: { - // xtype: 'numberfield', - // allowBlank: true, - // minValue: 0 - // } - // } ]); - // Sort columns + // Sorting the columns var order = [ "numberer", "name", "barcode", "library_protocol", - "library_type", - "concentration", + "comments", + "measuring_unit", + "measured_value", "mean_fragment_size", + "volume", + "library_type", + "read_length", + "sequencing_depth", "index_type", "index_reads", "index_i7", "index_i5", - "read_length", - "sequencing_depth", - // 'amplification_cycles', 'equal_representation_nucleotides', 'qpcr_result', - "sample_volume", - // 'concentration_method', - "organism", - "comments" + "organism" ]; columns = this.sortColumns(columns, order); @@ -693,6 +749,46 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { }); var columns = Ext.Array.merge(this.getCommonColumns(mode), [ + { + text: "Measuring Unit", + dataIndex: "measuring_unit", + tooltip: "Measuring Unit", + width: 120, + editor: { + id: "measuringUnitEditor", + itemId: "measuringUnitEditor", + xtype: "combobox", + queryMode: "local", + valueField: "id", + displayField: "name", + store: { + fields: ["id", "name"], + data: [ + { id: "ng/µl", name: "ng/µl (Concentration)" }, + { id: "M", name: "M (Cells)" }, + { id: "-", name: "Unknown" } + ] + }, + forceSelection: true, + listConfig: { + minWidth: 200 + } + }, + renderer: this.comboboxErrorRenderer + }, + { + text: "Measured Value", + dataIndex: "measured_value", + tooltip: "Measured Value", + width: 120, + editor: { + id: "measuredValueEditor", + itemId: "measuredValueEditor", + xtype: "numberfield", + minValue: 0 + }, + renderer: this.errorRenderer + }, { text: "Input Type", dataIndex: "nucleic_acid_type", @@ -712,42 +808,76 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { renderer: this.comboboxErrorRenderer }, { - text: "RQN", - dataIndex: "rna_quality", - tooltip: "RNA Quality", - width: 80, + text: "GMO", + dataIndex: "gmo", + tooltip: "Genetically Modified Organism", + width: 90, editor: { xtype: "combobox", - id: "rnaQualityEditor", queryMode: "local", valueField: "value", displayField: "name", - displayTpl: Ext.create("Ext.XTemplate", '{value}'), - store: "rnaQualityStore", - regex: new RegExp("^(11|10|[1-9]?(.[0-9]+)?|.[0-9]+)$"), - regexText: "Only values between 1 and 10 are allowed." + store: { + fields: ["id", "name"], + data: [ + { value: true, name: "Yes" }, + { value: false, name: "No" } + ] + }, + forceSelection: true }, - renderer: this.errorRenderer + renderer: this.comboboxErrorRenderer + }, + { + text: "Biosafety Level", + dataIndex: "biosafety_level", + tooltip: "Biosafety Level", + width: 120, + editor: { + xtype: "combobox", + queryMode: "local", + valueField: "id", + displayField: "name", + store: { + fields: ["id", "name"], + data: [ + { id: "bsl1", name: "BSL1" }, + { id: "bsl2", name: "BSL2" } + ] + }, + forceSelection: true + }, + renderer: this.comboboxErrorRenderer + }, + { + text: "Comment Input", + dataIndex: "comments", + tooltip: "Comments", + width: 200, + editor: { + xtype: "textfield", + allowBlank: true + } } ]); - // Sort columns + // Sorting the columns var order = [ "numberer", "name", "barcode", - "nucleic_acid_type", + "comments", + "measuring_unit", + "measured_value", + "volume", "library_protocol", "library_type", - "concentration", - "rna_quality", + "nucleic_acid_type", "read_length", "sequencing_depth", - // 'amplification_cycles', 'equal_representation_nucleotides', - "sample_volume", - // 'concentration_method', "organism", - "comments" + "biosafety_level", + "gmo" ]; columns = this.sortColumns(columns, order); @@ -830,27 +960,27 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { displayField: "name", valueField: "id", store: "libraryTypesStore", - // matchFieldWidth: false, forceSelection: true }, renderer: this.comboboxErrorRenderer }, { - text: "ng/μl", - dataIndex: "concentration", - tooltip: "Concentration", + text: "Volume (µl)", + dataIndex: "volume", + tooltip: "Volume", width: 90, editor: { xtype: "numberfield", - minValue: 0 + minValue: 0, + step: 10 }, renderer: this.errorRenderer }, { - text: "Length", + text: "Read Length", dataIndex: "read_length", tooltip: "Read Length", - width: 70, + width: 100, editor: { xtype: "combobox", queryMode: "local", @@ -866,7 +996,7 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { text: "Depth (M)", dataIndex: "sequencing_depth", tooltip: "Sequencing Depth", - width: 85, + width: 90, editor: { xtype: "numberfield", minValue: 0, @@ -874,71 +1004,23 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { }, renderer: this.errorRenderer }, - // { - // text: 'Amplification', - // tooltip: 'Amplification cycles', - // dataIndex: 'amplification_cycles', - // width: 105, - // editor: { - // xtype: 'numberfield', - // minValue: 0, - // allowDecimals: false, - // allowBlank: true - // }, - // renderer: this.errorRenderer - // }, - // { - // xtype: 'checkcolumn', - // text: 'Equal nucl.', - // tooltip: 'Equal Representation of Nucleotides: check = Yes, no check = No', - // dataIndex: 'equal_representation_nucleotides', - // width: 95, - // editor: { - // xtype: 'checkbox', - // cls: 'x-grid-checkheader-editor' - // } - // }, - // { - // text: 'F/S', - // dataIndex: 'concentration_method', - // tooltip: 'Concentration Determined by', - // width: 80, - // editor: { - // xtype: 'combobox', - // queryMode: 'local', - // valueField: 'id', - // displayField: 'name', - // store: 'concentrationMethodsStore', - // matchFieldWidth: false, - // forceSelection: true - // }, - // renderer: this.comboboxErrorRenderer - // }, { text: "Organism", dataIndex: "organism", tooltip: "Organism", - width: 200, + width: 120, editor: { xtype: "combobox", queryMode: "local", valueField: "id", displayField: "name", store: "organismsStore", - // matchFieldWidth: false, - forceSelection: true + forceSelection: true, + listConfig: { + minWidth: 200 + } }, renderer: this.comboboxErrorRenderer - }, - { - text: "Comments", - dataIndex: "comments", - tooltip: "Comments", - width: 200, - editor: { - xtype: "textfield", - allowBlank: true - } } ]; @@ -1054,7 +1136,6 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { Ext.Ajax.request({ url: url, method: "POST", - // timeout: 1000000, scope: this, params: { data: Ext.JSON.encode(Ext.Array.pluck(store.data.items, "data")) @@ -1105,25 +1186,16 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { var grid = Ext.getCmp("batch-add-grid"); var store = grid.getStore(); - // Validate all records store.each(function (record) { me.validateRecord(record, url); }); - // Refresh the grid grid.getView().refresh(); }, validateRecord: function (record, url) { var grid = Ext.getCmp("batch-add-grid"); var store = grid.getStore(); - - // Passing default values to the API for the removed variables which are still required in the request object - record.data.amplification_cycles = 0; - record.data.concentration_method = 4; - record.data.equal_representation_nucleotides = false; - if (url == "api/libraries/") record.data.qpcr_result = 0; - var validation = record.getValidation(true).data; var invalid = false; var errors = {}; @@ -1165,8 +1237,8 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { } } - if (dataIndex === "rna_quality" && value === 11) { - return "Determined by Facility"; + if (dataIndex === "measured_value" && value === -1) { + return ""; } return value; @@ -1181,11 +1253,14 @@ Ext.define("MainHub.view.libraries.BatchAddWindowController", { meta.tdAttr = 'data-qtip="' + record.get("errors")[dataIndex] + '"'; } + if (dataIndex === "gmo") { + var item = store.findRecord("value", value, 0, false, false, true); + return item ? item.get("name") : ""; + } + store.clearFilter(); - // Exact match var item = store.findRecord("id", value, 0, false, false, true); - return item ? item.get("name") : ""; }, diff --git a/backend/static/main-hub/app/view/libraries/Libraries.js b/backend/static/main-hub/app/view/libraries/Libraries.js index 8f2524f0d..499fa04f3 100644 --- a/backend/static/main-hub/app/view/libraries/Libraries.js +++ b/backend/static/main-hub/app/view/libraries/Libraries.js @@ -437,6 +437,18 @@ Ext.define("MainHub.view.libraries.Libraries", { return meta.record.getPoolPaths(); } }, + { + text: "GMO", + tooltip: "Genetically Modified Organism", + dataIndex: "gmo", + renderer: function (value, meta, record) { + if (record.get("leaf")) { + return value ? "Yes" : "No"; + } else { + return ""; + } + } + }, { text: "Date", dataIndex: "create_time", @@ -461,33 +473,66 @@ Ext.define("MainHub.view.libraries.Libraries", { renderer: "gridCellTooltipRenderer" }, { - text: "ng/μl", - tooltip: "Concentration", - dataIndex: "concentration" + text: "bp", + tooltip: "Mean Fragment Size", + dataIndex: "mean_fragment_size" }, { - text: "RQN", - tooltip: "RNA Quality", - dataIndex: "rna_quality", - width: 55, - renderer: function (value) { - return value === 11 ? "Determined by Facility" : value; + text: "Measurement", + tooltip: "Measured value along with its unit", + renderer: function (value, meta, record) { + var measuringUnit = record.get("measuring_unit"); + var measuredValue = record.get("measured_value"); + + if (measuringUnit === "-") { + return "Requires Measurement"; + } else + return Ext.String.format( + "{0}{1}", + measuredValue, + measuringUnit + ); } }, { - text: "bp", - tooltip: "Mean Fragment Size", - dataIndex: "mean_fragment_size" + text: "Starting Amount", + tooltip: "Starting Amount", + dataIndex: "amount_facility", + renderer: "gridCellTooltipRenderer" }, { - text: "Index Type", - dataIndex: "index_type_name", + text: "PCR Cycles", + tooltip: "PCR Cycles", + dataIndex: "pcr_cycles", renderer: "gridCellTooltipRenderer" }, { - text: "Index Reads", - tooltip: "# of Index Reads", - dataIndex: "index_reads" + text: "ng/μl (Output)", + tooltip: "ng/μl (Output)", + dataIndex: "concentration_facility", + renderer: "gridCellTooltipRenderer" + }, + { + text: "Measurement (Output)", + tooltip: "Measured value along with its unit", + renderer: function (value, meta, record) { + var measuringUnit = record.get("measuring_unit_facility"); + var measuredValue = record.get("measured_value_facility"); + + if (measuringUnit === "-") { + return "Requires Measurement"; + } else + return Ext.String.format( + "{0}{1}", + measuredValue, + measuringUnit + ); + } + }, + { + text: "Index Type", + dataIndex: "index_type_name", + renderer: "gridCellTooltipRenderer" }, { text: "I7", @@ -508,53 +553,6 @@ Ext.define("MainHub.view.libraries.Libraries", { text: "Depth (M)", tooltip: "Sequencing Depth", dataIndex: "sequencing_depth" - }, - // { - // text: 'Amplification', - // tooltip: 'Amplification Cycles', - // dataIndex: 'amplification_cycles' - // }, - // { - // text: 'Equal nucl.', - // tooltip: 'Equal Representation of Nucleotides', - // dataIndex: 'equal_representation_nucleotides', - // width: 90, - // renderer: function (value, meta) { - // if (meta.record.get('leaf')) { - // return value ? 'Yes' : 'No'; - // } - // } - // }, - // { - // text: 'qPCR (nM)', - // tooltip: 'qPCR Result', - // dataIndex: 'qpcr_result' - // }, - // { - // text: 'F/S', - // tooltip: 'Concentration Determined by', - // dataIndex: 'concentration_method', - // width: 50, - // renderer: function (value, meta) { - // if (meta.record.get('leaf')) { - // var store = Ext.getStore('concentrationMethodsStore'); - // var record = store.findRecord('id', value); - // var name = record.get('name'); - // meta.tdAttr = Ext.String.format('data-qtip="{0}"', name); - // return name.charAt(0); - // } - // } - // }, - { - text: "Organism", - dataIndex: "organism_name", - width: 150 - }, - { - text: "Comments", - dataIndex: "comments", - renderer: "gridCellTooltipRenderer", - width: 150 } ] } diff --git a/backend/static/main-hub/app/view/libraries/LibrariesController.js b/backend/static/main-hub/app/view/libraries/LibrariesController.js index 10895d498..45a8b69b8 100644 --- a/backend/static/main-hub/app/view/libraries/LibrariesController.js +++ b/backend/static/main-hub/app/view/libraries/LibrariesController.js @@ -115,7 +115,9 @@ Ext.define("MainHub.view.libraries.LibrariesController", { }, gridCellTooltipRenderer: function (value, meta) { - meta.tdAttr = 'data-qtip="' + value + '"'; + if (value) { + meta.tdAttr = 'data-qtip="' + value + '"'; + } else meta.tdAttr = "data-qtip=Empty"; return value; } }); diff --git a/backend/static/main-hub/app/view/libraries/LibraryWindow.js b/backend/static/main-hub/app/view/libraries/LibraryWindow.js index 7bd660285..45049150a 100644 --- a/backend/static/main-hub/app/view/libraries/LibraryWindow.js +++ b/backend/static/main-hub/app/view/libraries/LibraryWindow.js @@ -142,13 +142,6 @@ Ext.define("MainHub.view.libraries.LibraryWindow", { forceSelection: true, disabled: true }, - { - xtype: "numberfield", - name: "concentration", - fieldLabel: "Concentration (ng/µl)", - emptyText: "Concentration (ng/µl)", - minValue: 0 - }, { xtype: "numberfield", name: "mean_fragment_size", @@ -266,63 +259,6 @@ Ext.define("MainHub.view.libraries.LibraryWindow", { minValue: 1, allowDecimals: false }, - { - xtype: "numberfield", - name: "amplification_cycles", - fieldLabel: - 'Number of amplification cycles [?]', - emptyText: "Number of amplification cycles", - allowDecimals: false, - minValue: 0 - }, - { - xtype: "fieldcontainer", - id: "equalRepresentation", - fieldLabel: - 'Equal Representation of Nucleotides [?]', - defaultType: "radiofield", - defaults: { - // flex: 1 - }, - layout: "hbox", - items: [ - { - boxLabel: "Yes", - name: "equal_representation_nucleotides", - inputValue: true, - id: "equalRepresentationRadio1", - checked: true, - margin: "0 15px 0 0" - }, - { - boxLabel: "No", - name: "equal_representation_nucleotides", - inputValue: false, - id: "equalRepresentationRadio2" - } - ] - }, - { - xtype: "numberfield", - name: "qpcr_result", - fieldLabel: - 'qPCR Result (nM) [?]', - emptyText: "qPCR Result (nM)", - allowBlank: true, - minValue: 1 - }, - { - xtype: "combobox", - id: "concentrationMethodField", - queryMode: "local", - displayField: "name", - valueField: "id", - name: "concentration_method", - fieldLabel: "Concentration Determined by", - emptyText: "Concentration Determined by", - store: "concentrationMethodsStore", - forceSelection: true - }, { xtype: "combobox", id: "organismField", @@ -443,13 +379,6 @@ Ext.define("MainHub.view.libraries.LibraryWindow", { forceSelection: true, disabled: true }, - { - xtype: "numberfield", - name: "concentration", - fieldLabel: "Concentration (ng/µl)", - emptyText: "Concentration (ng/µl)", - minValue: 0 - }, { xtype: "combobox", id: "rnaQualityField", @@ -491,52 +420,6 @@ Ext.define("MainHub.view.libraries.LibraryWindow", { minValue: 1, allowDecimals: false }, - { - xtype: "numberfield", - name: "amplification_cycles", - fieldLabel: - 'Sample amplification (cycles) [?]', - emptyText: "Sample amplification (cycles)", - allowDecimals: false, - minValue: 0, - allowBlank: true - }, - { - xtype: "fieldcontainer", - id: "equalRepresentationSample", - fieldLabel: - 'Equal Representation of Nucleotides [?]', - defaultType: "radiofield", - layout: "hbox", - items: [ - { - boxLabel: "Yes", - name: "equal_representation_nucleotides", - inputValue: true, - id: "equalRepresentationRadio3", - checked: true, - margin: "0 15px 0 0" - }, - { - boxLabel: "No", - name: "equal_representation_nucleotides", - inputValue: false, - id: "equalRepresentationRadio4" - } - ] - }, - { - xtype: "combobox", - id: "concentrationSampleMethodField", - queryMode: "local", - displayField: "name", - valueField: "id", - name: "concentration_method", - fieldLabel: "Concentration Determined by", - emptyText: "Concentration Determined by", - store: "concentrationMethodsStore", - forceSelection: true - }, { xtype: "combobox", id: "organismSampleField", diff --git a/backend/static/main-hub/app/view/libraries/LibraryWindowController.js b/backend/static/main-hub/app/view/libraries/LibraryWindowController.js index 723116390..855665c14 100644 --- a/backend/static/main-hub/app/view/libraries/LibraryWindowController.js +++ b/backend/static/main-hub/app/view/libraries/LibraryWindowController.js @@ -139,15 +139,6 @@ Ext.define("MainHub.view.libraries.LibraryWindowController", { true ); - // Set Concentration Method - var concentrationMethodField = Ext.getCmp("concentrationMethodField"); - concentrationMethodField.select(record.concentration_method); - concentrationMethodField.fireEvent( - "select", - concentrationMethodField, - concentrationMethodField.findRecordByValue(record.concentration_method) - ); - // Set Read Length var readLengthField = Ext.getCmp("readLengthField"); readLengthField.select(record.readLengthId); @@ -337,19 +328,6 @@ Ext.define("MainHub.view.libraries.LibraryWindowController", { organismSampleField.findRecordByValue(record.organism) ); - // Set concentration method - var concentrationSampleMethodField = Ext.getCmp( - "concentrationSampleMethodField" - ); - concentrationSampleMethodField.select(record.concentration_method); - concentrationSampleMethodField.fireEvent( - "select", - concentrationSampleMethodField, - concentrationSampleMethodField.findRecordByValue( - record.concentration_method - ) - ); - // Set read length var readLengthSampleField = Ext.getCmp("readLengthSampleField"); readLengthSampleField.select(record.readLengthId);