From 089be35c70b469c8b42fe40bb625fdb49cc8cab3 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 10:58:47 -0600 Subject: [PATCH 01/13] Create migration 0002 --- ...n_location_sample_sensor_thing_and_more.py | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py diff --git a/geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py b/geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py new file mode 100644 index 0000000..adbb230 --- /dev/null +++ b/geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py @@ -0,0 +1,213 @@ +# Generated by Django 5.2.3 on 2025-07-11 02:53 + +import django.contrib.gis.db.models.fields +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('samplelocations', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Datastream', + fields=[ + ('datastream_id', models.BigAutoField(primary_key=True, serialize=False)), + ('observed_property', models.CharField(max_length=100)), + ('release_status', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='Observation', + fields=[ + ('observation_id', models.BigAutoField(primary_key=True, serialize=False)), + ('observed_value', models.FloatField(help_text='The value of the observation')), + ('release_status', models.BooleanField(default=False)), + ('datastream', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='observations', to='samplelocations.datastream', verbose_name='related datastream')), + ], + ), + migrations.CreateModel( + name='Location', + fields=[ + ('location_id', models.BigAutoField(primary_key=True, serialize=False)), + ('coordinate', django.contrib.gis.db.models.fields.PointField(dim=3, srid=4326)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'verbose_name': 'Location', + 'verbose_name_plural': 'Locations', + 'db_table_comment': "This table stores point locations on the earth's surface", + }, + ), + migrations.CreateModel( + name='Sample', + fields=[ + ('sample_id', models.BigAutoField(primary_key=True, serialize=False)), + ('sample_date', models.DateTimeField()), + ('sample_notes', models.TextField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='Sensor', + fields=[ + ('sensor_id', models.BigAutoField(primary_key=True, serialize=False)), + ('serial_number', models.CharField(blank=True, max_length=50, null=True)), + ('install_date', models.DateTimeField(blank=True, null=True)), + ('model', models.CharField(blank=True, max_length=50, null=True)), + ('notes', models.TextField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='Thing', + fields=[ + ('thing_id', models.BigAutoField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100, unique=True)), + ('release_status', models.BooleanField(default=False)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'verbose_name': 'Thing', + 'verbose_name_plural': 'Things', + }, + ), + migrations.RemoveField( + model_name='owner', + name='contact', + ), + migrations.RemoveField( + model_name='equipment', + name='location', + ), + migrations.RemoveField( + model_name='well', + name='well_type', + ), + migrations.RemoveField( + model_name='wellscreen', + name='screen_type', + ), + migrations.RemoveField( + model_name='well', + name='formation_zone', + ), + migrations.RemoveField( + model_name='samplelocation', + name='owner', + ), + migrations.RemoveField( + model_name='spring', + name='location', + ), + migrations.RemoveField( + model_name='well', + name='location', + ), + migrations.RemoveField( + model_name='wellscreen', + name='well', + ), + migrations.CreateModel( + name='GroundwaterLevelObservation', + fields=[ + ('groundwater_level_observation_id', models.BigAutoField(primary_key=True, serialize=False)), + ('observation_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='groundwater_level_observations', to='samplelocations.observation', verbose_name='related observation')), + ], + bases=('samplelocations.observation',), + ), + migrations.CreateModel( + name='Location_Thing_Junction', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('effective_start', models.DateTimeField()), + ('effective_end', models.DateTimeField()), + ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations_things', to='samplelocations.location', verbose_name='related location')), + ], + options={ + 'db_table_comment': 'Junction table linking Location and Thing models', + }, + ), + migrations.AddField( + model_name='observation', + name='sample', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='observations', to='samplelocations.sample', verbose_name='related sample'), + ), + migrations.AddField( + model_name='datastream', + name='sensor_id', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='datastreams', to='samplelocations.sensor', verbose_name='related sensor'), + ), + migrations.CreateModel( + name='SpringThing', + fields=[ + ('springthing_id', models.BigAutoField(primary_key=True, serialize=False)), + ('thing_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='springthings', to='samplelocations.thing', verbose_name='related thing')), + ('description', models.CharField(blank=True, max_length=255, null=True)), + ], + bases=('samplelocations.thing',), + ), + migrations.CreateModel( + name='WellThing', + fields=[ + ('wellthing_id', models.BigAutoField(primary_key=True, serialize=False)), + ('thing_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='wellthings', to='samplelocations.thing', verbose_name='related thing')), + ('well_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), + ('hole_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), + ('casing_diameter', models.FloatField(blank=True, help_text='inches', null=True)), + ('casing_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), + ('casing_description', models.CharField(blank=True, max_length=50, null=True)), + ('construction_notes', models.TextField(blank=True, null=True)), + ], + bases=('samplelocations.thing',), + ), + migrations.AddField( + model_name='thing', + name='location', + field=models.ManyToManyField(related_name='things', through='samplelocations.Location_Thing_Junction', to='samplelocations.location', verbose_name='related location'), + ), + migrations.AddField( + model_name='sample', + name='thing', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='samples', to='samplelocations.thing', verbose_name='related thing'), + ), + migrations.AddField( + model_name='location_thing_junction', + name='thing', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations_things', to='samplelocations.thing', verbose_name='related thing'), + ), + migrations.AddField( + model_name='datastream', + name='thing', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='datastreams', to='samplelocations.thing', verbose_name='related thing'), + ), + migrations.DeleteModel( + name='Contact', + ), + migrations.DeleteModel( + name='Equipment', + ), + migrations.DeleteModel( + name='Lexicon', + ), + migrations.DeleteModel( + name='Owner', + ), + migrations.DeleteModel( + name='Spring', + ), + migrations.DeleteModel( + name='SampleLocation', + ), + migrations.DeleteModel( + name='Well', + ), + migrations.DeleteModel( + name='WellScreen', + ), + migrations.AddConstraint( + model_name='location_thing_junction', + constraint=models.UniqueConstraint(fields=('location', 'thing'), name='unique_location_thing'), + ), + ] From 19a8ba133932b447da45531e02a2eddab763597c Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 16:06:12 -0600 Subject: [PATCH 02/13] refactor: Add `thing_type` field and choices for `thing_type` field. --- geodjango/samplelocations/models.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 13be40f..6860383 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -37,8 +37,22 @@ class Meta: class Thing(models.Model): """A base model representing a generic monitoring station (Thing)""" + + # Define class-based choices for the 'thing_type' field. + # This allows for a more structured way to define and use choices in Django models. + # The format is CHOICE = " database value", "human-readable or display name" + class ThingType(models.TextChoices): + WELL = "W", "Well" + SPRING = "S", "Spring" + thing_id = models.BigAutoField(primary_key=True) name = models.CharField(max_length=100, unique=True) + thing_type = models.CharField( + max_length=2, + choices=ThingType.choices, # Use the choices defined in the ThingType class. + default=ThingType.WELL, # Set a default value for the field. + verbose_name="type of thing" # Human-readable label for user interfaces like forms and admin panel. + ) release_status = models.BooleanField(default=False) date_created = models.DateTimeField(auto_now_add=True) # The 'location' field sets up the M:M relationship and specifies From 9f7e395373ef7834c277470859dd8e5ae6247704 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 16:25:51 -0600 Subject: [PATCH 03/13] refactor: Add fields specific to a WELL. Add fields specific to a SPRING. --- geodjango/samplelocations/models.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 6860383..6617328 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -64,6 +64,17 @@ class ThingType(models.TextChoices): verbose_name= "related location" # Human-readable label for user interfaces like forms and the admin panel. ) + #Fields specific to a WELL + well_depth_ft = models.FloatField(blank=True, null=True, help_text="well depth feet below ground surface") + hole_depth_ft = models.FloatField(blank=True, null=True, help_text="hole depth feet below ground surface") + casing_diameter_ft = models.FloatField(blank=True, null=True, help_text="casing diameter in ft") + casing_depth_ft = models.FloatField(blank=True, null=True, help_text="casing depth feet below ground surface") + casing_description = models.CharField(max_length=50, blank=True, null=True) + construction_notes = models.TextField(blank=True, null=True) # Use TextField over CharField for long-form text of variable length without a predefined limit. + + #Fields specific to a SPRING + spring_type = models.CharField(max_length=255, blank=True, null=True) # e.g. "artesian", "subartesian", "thermal", etc. + def __str__(self): return f"Thing object with id {self.thing_id} and name {self.name}" From 89c54677b6e0cb2dea4a75e7fe91ac6dc24e30c9 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 16:31:21 -0600 Subject: [PATCH 04/13] refactor: Update __str__ method for Thing to include type and name --- geodjango/samplelocations/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 6617328..598dbfc 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -76,7 +76,7 @@ class ThingType(models.TextChoices): spring_type = models.CharField(max_length=255, blank=True, null=True) # e.g. "artesian", "subartesian", "thermal", etc. def __str__(self): - return f"Thing object with id {self.thing_id} and name {self.name}" + return f"Thing object is a {self.thing_type} with name {self.name}" class Meta: verbose_name = "Thing" From 88dd1ae595d21c5a15c910b7db694f53f653bc3a Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 16:40:37 -0600 Subject: [PATCH 05/13] refactor: Remove WellThing model- it has been merged into Thing model. --- geodjango/samplelocations/models.py | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 598dbfc..8f7c9c9 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -74,6 +74,7 @@ class ThingType(models.TextChoices): #Fields specific to a SPRING spring_type = models.CharField(max_length=255, blank=True, null=True) # e.g. "artesian", "subartesian", "thermal", etc. + description_spring = models.CharField(max_length=255, blank=True, null=True) def __str__(self): return f"Thing object is a {self.thing_type} with name {self.name}" @@ -118,31 +119,6 @@ class Meta: db_table_comment = "Junction table linking Location and Thing models" -#--------WellThing model. Inherits all fields from Thing model ----------- - -class WellThing(Thing): - """ A specific type of monitoring station (Thing) representing a well.""" - wellthing_id = models.BigAutoField(primary_key=True) - # This field creates the inheritance link from WellThing back to Thing. - # The name 'thing_ptr' is a conventional naming choice in Django for the parent link field, - thing_ptr= models.OneToOneField( - Thing, - on_delete=models.CASCADE, - parent_link = True, - related_name='wellthings', - verbose_name="related thing" - ) - well_depth_ft = models.FloatField(blank=True, null=True, help_text="well depth feet below ground surface") - hole_depth_ft = models.FloatField(blank=True, null=True, help_text="hole depth feet below ground surface") - casing_diameter_ft = models.FloatField(blank=True, null=True, help_text="casing diameter in ft") - casing_depth_ft = models.FloatField(blank=True, null=True, help_text="casing depth feet below ground surface") - casing_description = models.CharField(max_length=50, blank=True, null=True) - construction_notes = models.TextField(blank=True, null=True) # Use TextField over CharField for long-form text of variable length without a predefined limit. - - def __str__(self): - return f"{self.name} (Well)" - - #--------SpringThing model. Inherits all fields from Thing model ----------- class SpringThing(Thing): From 235f28f7e14c8714c5b21a16836e3b24650971b6 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 16:45:05 -0600 Subject: [PATCH 06/13] refactor: Remove SpringThing model- it has been merged into Thing model. --- geodjango/samplelocations/models.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 8f7c9c9..530588d 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -119,26 +119,6 @@ class Meta: db_table_comment = "Junction table linking Location and Thing models" -#--------SpringThing model. Inherits all fields from Thing model ----------- - -class SpringThing(Thing): - """ A specific type of monitoring station (Thing) representing a spring.""" - springthing_id = models.BigAutoField(primary_key=True) - # This field creates the inheritance link from SpringThing back to Thing. - # The name 'thing_ptr' is a conventional naming choice in Django for the parent link field, - thing_ptr = models.OneToOneField( - Thing, - on_delete=models.CASCADE, - parent_link=True, - related_name='springthings', - verbose_name="related thing" - ) - description = models.CharField(max_length=255, blank=True, null=True) - - def __str__(self): - return f"{self.name} (Spring)" - - #--------Sensor model----------- #TODO: add a 'name' field to this model. class Sensor(models.Model): From 12b4c90a6a8337e4964864aec4fbd31e0bdbd080 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Thu, 24 Jul 2025 16:49:05 -0600 Subject: [PATCH 07/13] refactor: De-register WellThing and SpringThing models --- geodjango/samplelocations/admin.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/geodjango/samplelocations/admin.py b/geodjango/samplelocations/admin.py index 4458283..7ad3bef 100644 --- a/geodjango/samplelocations/admin.py +++ b/geodjango/samplelocations/admin.py @@ -29,14 +29,6 @@ class LocationAdmin(GISModelAdmin): class ThingAdmin(ModelAdmin): pass -@admin.register(WellThing) -class WellThingAdmin(ModelAdmin): - pass - -@admin.register(SpringThing) -class SpringThingAdmin(ModelAdmin): - pass - @admin.register(Location_Thing_Junction) class LocationThingJunctionAdmin(ModelAdmin): pass @@ -61,6 +53,14 @@ class GroundwaterLevelObservationAdmin(ModelAdmin): class SampleAdmin(ModelAdmin): pass +# @admin.register(WellThing) +# class WellThingAdmin(ModelAdmin): +# pass + +# @admin.register(SpringThing) +# class SpringThingAdmin(ModelAdmin): +# pass + #admin.site.register(Lexicon) #admin.site.register(WellScreen) #admin.site.register(Equipment) From ad6b9a7464d9b8eb477c5b6a8189e3ba2ea755dd Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 30 Jul 2025 09:45:57 -0600 Subject: [PATCH 08/13] refactor: Move ThingType choices outside of Thing model --- geodjango/samplelocations/models.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 530588d..0e18388 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -34,17 +34,16 @@ class Meta: #--------Thing model ----------- +# Define class-based choices for the 'thing_type' field. +# This allows for a more structured way to define and use choices in Django models. +# The format is CHOICE = " database value", "human-readable or display name" +class ThingType(models.TextChoices): + WELL = "W", "Well" + SPRING = "S", "Spring" class Thing(models.Model): """A base model representing a generic monitoring station (Thing)""" - # Define class-based choices for the 'thing_type' field. - # This allows for a more structured way to define and use choices in Django models. - # The format is CHOICE = " database value", "human-readable or display name" - class ThingType(models.TextChoices): - WELL = "W", "Well" - SPRING = "S", "Spring" - thing_id = models.BigAutoField(primary_key=True) name = models.CharField(max_length=100, unique=True) thing_type = models.CharField( @@ -74,7 +73,7 @@ class ThingType(models.TextChoices): #Fields specific to a SPRING spring_type = models.CharField(max_length=255, blank=True, null=True) # e.g. "artesian", "subartesian", "thermal", etc. - description_spring = models.CharField(max_length=255, blank=True, null=True) + def __str__(self): return f"Thing object is a {self.thing_type} with name {self.name}" From 1eb2d67eadc33ee4ef14f5e08ff16220b0efabeb Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 30 Jul 2025 09:51:54 -0600 Subject: [PATCH 09/13] refactor: Add sample_matrix field to Sample model. Add sample matrix choices --- geodjango/samplelocations/models.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 0e18388..652d99d 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -148,11 +148,22 @@ def __str__(self): #--------Sample model----------- +# Define choices for thhe 'sample_matrix' field. +# The format is CHOICE = " database value", "human-readable or display name" +class SampleMatrix(models.TextChoices): + GROUNDWATER = "GW", "Groundwater" + SOIL = "S", "Soil" class Sample(models.Model): """Represents a sample collected from a Thing""" sample_id = models.BigAutoField(primary_key=True) thing_id = models.ForeignKey(Thing, on_delete=models.CASCADE, related_name="samples", verbose_name="related thing") + sample_matrix - models.CharField( + max_length=2, + choices=SampleMatrix.choices, # Use the choices defined in the SampleMatrix class + default=SampleMatrix.GROUNDWATER, # Set a default value for the field. + verbose_name="type of sample" # Human-readable label for user interfaces like forms and the admin panel. + ) sample_date = models.DateTimeField() sample_notes = models.TextField(blank=True, null=True) From fb15065d0d683ea8e28326f893e8f62cdf65bebe Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 30 Jul 2025 11:59:11 -0600 Subject: [PATCH 10/13] style: Correct sample_matrix field formatting --- geodjango/samplelocations/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geodjango/samplelocations/models.py b/geodjango/samplelocations/models.py index 652d99d..7b989ea 100644 --- a/geodjango/samplelocations/models.py +++ b/geodjango/samplelocations/models.py @@ -158,7 +158,7 @@ class Sample(models.Model): """Represents a sample collected from a Thing""" sample_id = models.BigAutoField(primary_key=True) thing_id = models.ForeignKey(Thing, on_delete=models.CASCADE, related_name="samples", verbose_name="related thing") - sample_matrix - models.CharField( + sample_matrix = models.CharField( max_length=2, choices=SampleMatrix.choices, # Use the choices defined in the SampleMatrix class default=SampleMatrix.GROUNDWATER, # Set a default value for the field. From 266f88bb554029698b44d355cc20628e60f7b7fb Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 30 Jul 2025 11:59:44 -0600 Subject: [PATCH 11/13] refactor: remove previous migrations --- .../migrations/0001_initial.py | 127 ----------- ...n_location_sample_sensor_thing_and_more.py | 213 ------------------ ...junction_unique_location_thing_and_more.py | 87 ------- .../migrations/0004_sensor_name.py | 18 -- 4 files changed, 445 deletions(-) delete mode 100644 geodjango/samplelocations/migrations/0001_initial.py delete mode 100644 geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py delete mode 100644 geodjango/samplelocations/migrations/0003_remove_location_thing_junction_unique_location_thing_and_more.py delete mode 100644 geodjango/samplelocations/migrations/0004_sensor_name.py diff --git a/geodjango/samplelocations/migrations/0001_initial.py b/geodjango/samplelocations/migrations/0001_initial.py deleted file mode 100644 index d6c175a..0000000 --- a/geodjango/samplelocations/migrations/0001_initial.py +++ /dev/null @@ -1,127 +0,0 @@ -# Generated by Django 5.2.3 on 2025-06-26 15:14 - -import django.contrib.gis.db.models.fields -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Contact', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('email', models.EmailField(max_length=254)), - ('phone', models.CharField(blank=True, max_length=20, null=True)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'verbose_name': 'Contact', - 'verbose_name_plural': 'Contacts', - 'ordering': ['name'], - }, - ), - migrations.CreateModel( - name='Lexicon', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100, unique=True)), - ('description', models.CharField(blank=True, max_length=255, null=True)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'verbose_name': 'Lexicon', - 'verbose_name_plural': 'Lexicons', - 'ordering': ['name'], - }, - ), - migrations.CreateModel( - name='Owner', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('description', models.CharField(blank=True, max_length=255, null=True)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owners', to='samplelocations.contact')), - ], - options={ - 'verbose_name': 'Owner', - 'verbose_name_plural': 'Owners', - 'ordering': ['name'], - }, - ), - migrations.CreateModel( - name='SampleLocation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('description', models.CharField(blank=True, max_length=255, null=True)), - ('visible', models.BooleanField(default=False)), - ('point', django.contrib.gis.db.models.fields.PointField(srid=4326)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='samplelocations', to='samplelocations.owner')), - ], - options={ - 'verbose_name': 'Sample Location', - 'verbose_name_plural': 'Sample Locations', - 'ordering': ['name'], - }, - ), - migrations.CreateModel( - name='Equipment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('equipment_type', models.CharField(max_length=50)), - ('model', models.CharField(max_length=50)), - ('serial_no', models.CharField(max_length=50)), - ('date_installed', models.DateTimeField(blank=True, null=True)), - ('date_removed', models.DateTimeField(blank=True, null=True)), - ('recording_interval', models.IntegerField(blank=True, null=True)), - ('equipment_notes', models.CharField(blank=True, max_length=50, null=True)), - ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipment', to='samplelocations.samplelocation')), - ], - ), - migrations.CreateModel( - name='Spring', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('description', models.CharField(blank=True, max_length=255, null=True)), - ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='springs', to='samplelocations.samplelocation')), - ], - ), - migrations.CreateModel( - name='Well', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ose_pod_id', models.CharField(blank=True, max_length=50, null=True)), - ('api_id', models.CharField(blank=True, default='', max_length=50)), - ('usgs_id', models.CharField(blank=True, max_length=50, null=True)), - ('well_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), - ('hole_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), - ('casing_diameter', models.FloatField(blank=True, help_text='inches', null=True)), - ('casing_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), - ('casing_description', models.CharField(blank=True, max_length=50, null=True)), - ('construction_notes', models.CharField(blank=True, max_length=250, null=True)), - ('formation_zone', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wells_by_formation', to='samplelocations.lexicon')), - ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wells', to='samplelocations.samplelocation')), - ('well_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wells_by_type', to='samplelocations.lexicon')), - ], - ), - migrations.CreateModel( - name='WellScreen', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('screen_depth_top', models.FloatField(help_text='feet below ground surface')), - ('screen_depth_bottom', models.FloatField(help_text='feet below ground surface')), - ('screen_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='well_screens_by_type', to='samplelocations.lexicon')), - ('well', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='screens', to='samplelocations.well')), - ], - ), - ] diff --git a/geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py b/geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py deleted file mode 100644 index 1c34efa..0000000 --- a/geodjango/samplelocations/migrations/0002_datastream_observation_location_sample_sensor_thing_and_more.py +++ /dev/null @@ -1,213 +0,0 @@ -# Generated by Django 5.2.3 on 2025-07-11 16:53 - -import django.contrib.gis.db.models.fields -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('samplelocations', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Datastream', - fields=[ - ('datastream_id', models.BigAutoField(primary_key=True, serialize=False)), - ('observed_property', models.CharField(max_length=100)), - ('release_status', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='Observation', - fields=[ - ('observation_id', models.BigAutoField(primary_key=True, serialize=False)), - ('observed_value', models.FloatField(help_text='The value of the observation')), - ('release_status', models.BooleanField(default=False)), - ('datastream', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='observations', to='samplelocations.datastream', verbose_name='related datastream')), - ], - ), - migrations.CreateModel( - name='Location', - fields=[ - ('location_id', models.BigAutoField(primary_key=True, serialize=False)), - ('coordinate', django.contrib.gis.db.models.fields.PointField(dim=3, srid=4326)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'verbose_name': 'Location', - 'verbose_name_plural': 'Locations', - 'db_table_comment': "This table stores point locations on the earth's surface", - }, - ), - migrations.CreateModel( - name='Sample', - fields=[ - ('sample_id', models.BigAutoField(primary_key=True, serialize=False)), - ('sample_date', models.DateTimeField()), - ('sample_notes', models.TextField(blank=True, null=True)), - ], - ), - migrations.CreateModel( - name='Sensor', - fields=[ - ('sensor_id', models.BigAutoField(primary_key=True, serialize=False)), - ('serial_number', models.CharField(blank=True, max_length=50, null=True)), - ('install_date', models.DateTimeField(blank=True, null=True)), - ('model', models.CharField(blank=True, max_length=50, null=True)), - ('notes', models.TextField(blank=True, null=True)), - ], - ), - migrations.CreateModel( - name='Thing', - fields=[ - ('thing_id', models.BigAutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=100, unique=True)), - ('release_status', models.BooleanField(default=False)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'verbose_name': 'Thing', - 'verbose_name_plural': 'Things', - }, - ), - migrations.RemoveField( - model_name='owner', - name='contact', - ), - migrations.RemoveField( - model_name='equipment', - name='location', - ), - migrations.RemoveField( - model_name='well', - name='well_type', - ), - migrations.RemoveField( - model_name='wellscreen', - name='screen_type', - ), - migrations.RemoveField( - model_name='well', - name='formation_zone', - ), - migrations.RemoveField( - model_name='samplelocation', - name='owner', - ), - migrations.RemoveField( - model_name='well', - name='location', - ), - migrations.RemoveField( - model_name='spring', - name='location', - ), - migrations.RemoveField( - model_name='wellscreen', - name='well', - ), - migrations.CreateModel( - name='GroundwaterLevelObservation', - fields=[ - ('groundwater_level_observation_id', models.BigAutoField(primary_key=True, serialize=False)), - ('observation_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='groundwater_level_observations', to='samplelocations.observation', verbose_name='related observation')), - ], - bases=('samplelocations.observation',), - ), - migrations.CreateModel( - name='Location_Thing_Junction', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('effective_start', models.DateTimeField()), - ('effective_end', models.DateTimeField()), - ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations_things', to='samplelocations.location', verbose_name='related location')), - ], - options={ - 'db_table_comment': 'Junction table linking Location and Thing models', - }, - ), - migrations.AddField( - model_name='observation', - name='sample', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='observations', to='samplelocations.sample', verbose_name='related sample'), - ), - migrations.AddField( - model_name='datastream', - name='sensor_id', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='datastreams', to='samplelocations.sensor', verbose_name='related sensor'), - ), - migrations.CreateModel( - name='SpringThing', - fields=[ - ('springthing_id', models.BigAutoField(primary_key=True, serialize=False)), - ('thing_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='springthings', to='samplelocations.thing', verbose_name='related thing')), - ('description', models.CharField(blank=True, max_length=255, null=True)), - ], - bases=('samplelocations.thing',), - ), - migrations.CreateModel( - name='WellThing', - fields=[ - ('wellthing_id', models.BigAutoField(primary_key=True, serialize=False)), - ('thing_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='wellthings', to='samplelocations.thing', verbose_name='related thing')), - ('well_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), - ('hole_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), - ('casing_diameter', models.FloatField(blank=True, help_text='inches', null=True)), - ('casing_depth', models.FloatField(blank=True, help_text='feet below ground surface', null=True)), - ('casing_description', models.CharField(blank=True, max_length=50, null=True)), - ('construction_notes', models.TextField(blank=True, null=True)), - ], - bases=('samplelocations.thing',), - ), - migrations.AddField( - model_name='thing', - name='location', - field=models.ManyToManyField(related_name='things', through='samplelocations.Location_Thing_Junction', to='samplelocations.location', verbose_name='related location'), - ), - migrations.AddField( - model_name='sample', - name='thing', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='samples', to='samplelocations.thing', verbose_name='related thing'), - ), - migrations.AddField( - model_name='location_thing_junction', - name='thing', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations_things', to='samplelocations.thing', verbose_name='related thing'), - ), - migrations.AddField( - model_name='datastream', - name='thing', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='datastreams', to='samplelocations.thing', verbose_name='related thing'), - ), - migrations.DeleteModel( - name='Contact', - ), - migrations.DeleteModel( - name='Equipment', - ), - migrations.DeleteModel( - name='Lexicon', - ), - migrations.DeleteModel( - name='Owner', - ), - migrations.DeleteModel( - name='SampleLocation', - ), - migrations.DeleteModel( - name='Spring', - ), - migrations.DeleteModel( - name='Well', - ), - migrations.DeleteModel( - name='WellScreen', - ), - migrations.AddConstraint( - model_name='location_thing_junction', - constraint=models.UniqueConstraint(fields=('location', 'thing'), name='unique_location_thing'), - ), - ] diff --git a/geodjango/samplelocations/migrations/0003_remove_location_thing_junction_unique_location_thing_and_more.py b/geodjango/samplelocations/migrations/0003_remove_location_thing_junction_unique_location_thing_and_more.py deleted file mode 100644 index a9337d4..0000000 --- a/geodjango/samplelocations/migrations/0003_remove_location_thing_junction_unique_location_thing_and_more.py +++ /dev/null @@ -1,87 +0,0 @@ -# Generated by Django 5.2.3 on 2025-07-11 17:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('samplelocations', '0002_datastream_observation_location_sample_sensor_thing_and_more'), - ] - - operations = [ - migrations.RemoveConstraint( - model_name='location_thing_junction', - name='unique_location_thing', - ), - migrations.RenameField( - model_name='datastream', - old_name='thing', - new_name='thing_id', - ), - migrations.RenameField( - model_name='location_thing_junction', - old_name='location', - new_name='location_id', - ), - migrations.RenameField( - model_name='location_thing_junction', - old_name='thing', - new_name='thing_id', - ), - migrations.RenameField( - model_name='observation', - old_name='datastream', - new_name='datastream_id', - ), - migrations.RenameField( - model_name='sample', - old_name='thing', - new_name='thing_id', - ), - migrations.RenameField( - model_name='thing', - old_name='location', - new_name='location_id', - ), - migrations.RemoveField( - model_name='wellthing', - name='casing_depth', - ), - migrations.RemoveField( - model_name='wellthing', - name='casing_diameter', - ), - migrations.RemoveField( - model_name='wellthing', - name='hole_depth', - ), - migrations.RemoveField( - model_name='wellthing', - name='well_depth', - ), - migrations.AddField( - model_name='wellthing', - name='casing_depth_ft', - field=models.FloatField(blank=True, help_text='casing depth feet below ground surface', null=True), - ), - migrations.AddField( - model_name='wellthing', - name='casing_diameter_ft', - field=models.FloatField(blank=True, help_text='casing diameter in ft', null=True), - ), - migrations.AddField( - model_name='wellthing', - name='hole_depth_ft', - field=models.FloatField(blank=True, help_text='hole depth feet below ground surface', null=True), - ), - migrations.AddField( - model_name='wellthing', - name='well_depth_ft', - field=models.FloatField(blank=True, help_text='well depth feet below ground surface', null=True), - ), - migrations.AddConstraint( - model_name='location_thing_junction', - constraint=models.UniqueConstraint(fields=('location_id', 'thing_id'), name='unique_location_thing'), - ), - ] diff --git a/geodjango/samplelocations/migrations/0004_sensor_name.py b/geodjango/samplelocations/migrations/0004_sensor_name.py deleted file mode 100644 index 901f494..0000000 --- a/geodjango/samplelocations/migrations/0004_sensor_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.3 on 2025-07-11 18:50 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('samplelocations', '0003_remove_location_thing_junction_unique_location_thing_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='sensor', - name='name', - field=models.CharField(blank=True, max_length=100, null=True), - ), - ] From 22204b95fe3b7be30d254a75f95a47d8240e9367 Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 30 Jul 2025 12:02:40 -0600 Subject: [PATCH 12/13] refactor: Remove import of WellThing and SpringThing models --- geodjango/samplelocations/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geodjango/samplelocations/admin.py b/geodjango/samplelocations/admin.py index 7ad3bef..9f126f5 100644 --- a/geodjango/samplelocations/admin.py +++ b/geodjango/samplelocations/admin.py @@ -3,7 +3,7 @@ from django import forms from django.contrib.gis.admin import GISModelAdmin from django.contrib.gis.geos import Point -from samplelocations.models import Location, Thing, WellThing, SpringThing, Location_Thing_Junction, Sensor, Datastream, Observation, \ +from samplelocations.models import Location, Thing, Location_Thing_Junction, Sensor, Datastream, Observation, \ GroundwaterLevelObservation, Sample class LocationForm(forms.ModelForm): From 12d14bbee2c3ef67f17bdfbe59ab3122184b32bf Mon Sep 17 00:00:00 2001 From: ksmuczynski Date: Wed, 30 Jul 2025 12:12:25 -0600 Subject: [PATCH 13/13] Perform new initial migration (2025-07-30) --- .../migrations/0001_initial.py | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 geodjango/samplelocations/migrations/0001_initial.py diff --git a/geodjango/samplelocations/migrations/0001_initial.py b/geodjango/samplelocations/migrations/0001_initial.py new file mode 100644 index 0000000..fafbb0d --- /dev/null +++ b/geodjango/samplelocations/migrations/0001_initial.py @@ -0,0 +1,137 @@ +# Generated by Django 5.2.3 on 2025-07-30 18:04 + +import django.contrib.gis.db.models.fields +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Datastream', + fields=[ + ('datastream_id', models.BigAutoField(primary_key=True, serialize=False)), + ('observed_property', models.CharField(max_length=100)), + ('release_status', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='Observation', + fields=[ + ('observation_id', models.BigAutoField(primary_key=True, serialize=False)), + ('observed_value', models.FloatField(help_text='The value of the observation')), + ('release_status', models.BooleanField(default=False)), + ('datastream_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='observations', to='samplelocations.datastream', verbose_name='related datastream')), + ], + ), + migrations.CreateModel( + name='Location', + fields=[ + ('location_id', models.BigAutoField(primary_key=True, serialize=False)), + ('coordinate', django.contrib.gis.db.models.fields.PointField(dim=3, srid=4326)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'verbose_name': 'Location', + 'verbose_name_plural': 'Locations', + 'db_table_comment': "This table stores point locations on the earth's surface", + }, + ), + migrations.CreateModel( + name='Sample', + fields=[ + ('sample_id', models.BigAutoField(primary_key=True, serialize=False)), + ('sample_matrix', models.CharField(choices=[('GW', 'Groundwater'), ('S', 'Soil')], default='GW', max_length=2, verbose_name='type of sample')), + ('sample_date', models.DateTimeField()), + ('sample_notes', models.TextField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='Sensor', + fields=[ + ('sensor_id', models.BigAutoField(primary_key=True, serialize=False)), + ('name', models.CharField(blank=True, max_length=100, null=True)), + ('serial_number', models.CharField(blank=True, max_length=50, null=True)), + ('install_date', models.DateTimeField(blank=True, null=True)), + ('model', models.CharField(blank=True, max_length=50, null=True)), + ('notes', models.TextField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='GroundwaterLevelObservation', + fields=[ + ('groundwater_level_observation_id', models.BigAutoField(primary_key=True, serialize=False)), + ('observation_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, related_name='groundwater_level_observations', to='samplelocations.observation', verbose_name='related observation')), + ], + bases=('samplelocations.observation',), + ), + migrations.CreateModel( + name='Location_Thing_Junction', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('effective_start', models.DateTimeField()), + ('effective_end', models.DateTimeField()), + ('location_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations_things', to='samplelocations.location', verbose_name='related location')), + ], + options={ + 'db_table_comment': 'Junction table linking Location and Thing models', + }, + ), + migrations.AddField( + model_name='observation', + name='sample', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='observations', to='samplelocations.sample', verbose_name='related sample'), + ), + migrations.AddField( + model_name='datastream', + name='sensor_id', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='datastreams', to='samplelocations.sensor', verbose_name='related sensor'), + ), + migrations.CreateModel( + name='Thing', + fields=[ + ('thing_id', models.BigAutoField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100, unique=True)), + ('thing_type', models.CharField(choices=[('W', 'Well'), ('S', 'Spring')], default='W', max_length=2, verbose_name='type of thing')), + ('release_status', models.BooleanField(default=False)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('well_depth_ft', models.FloatField(blank=True, help_text='well depth feet below ground surface', null=True)), + ('hole_depth_ft', models.FloatField(blank=True, help_text='hole depth feet below ground surface', null=True)), + ('casing_diameter_ft', models.FloatField(blank=True, help_text='casing diameter in ft', null=True)), + ('casing_depth_ft', models.FloatField(blank=True, help_text='casing depth feet below ground surface', null=True)), + ('casing_description', models.CharField(blank=True, max_length=50, null=True)), + ('construction_notes', models.TextField(blank=True, null=True)), + ('spring_type', models.CharField(blank=True, max_length=255, null=True)), + ('location_id', models.ManyToManyField(related_name='things', through='samplelocations.Location_Thing_Junction', to='samplelocations.location', verbose_name='related location')), + ], + options={ + 'verbose_name': 'Thing', + 'verbose_name_plural': 'Things', + }, + ), + migrations.AddField( + model_name='sample', + name='thing_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='samples', to='samplelocations.thing', verbose_name='related thing'), + ), + migrations.AddField( + model_name='location_thing_junction', + name='thing_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations_things', to='samplelocations.thing', verbose_name='related thing'), + ), + migrations.AddField( + model_name='datastream', + name='thing_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='datastreams', to='samplelocations.thing', verbose_name='related thing'), + ), + migrations.AddConstraint( + model_name='location_thing_junction', + constraint=models.UniqueConstraint(fields=('location_id', 'thing_id'), name='unique_location_thing'), + ), + ]