From 9f3cf4fae76419b6763259ea70b99da7747c1e28 Mon Sep 17 00:00:00 2001 From: Fragariaa Date: Fri, 24 Oct 2025 22:04:59 +0200 Subject: [PATCH] Changed handling of texture and colour data --- .idea/hellforge.iml | 12 +++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ .idea/workspace.xml | 47 ++++++++++ main/migrations/0174_colours.py | 21 +++++ ...sell_colourname_delete_colours_and_more.py | 37 ++++++++ ..._colour_hex_alter_layer_colour_and_more.py | 28 ++++++ ...layer_colour_alter_layer_colour_munsell.py | 24 +++++ ...layer_colour_alter_layer_colour_munsell.py | 24 +++++ ...layer_colour_alter_layer_colour_munsell.py | 24 +++++ .../0180_texturecategory_texturekeyword.py | 29 ++++++ main/models.py | 55 ++++++++--- main/signals.py | 16 +++- main/tools/colours_textures.py | 92 +++++++++++++++++++ 16 files changed, 417 insertions(+), 16 deletions(-) create mode 100644 .idea/hellforge.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 main/migrations/0174_colours.py create mode 100644 main/migrations/0175_colourmunsell_colourname_delete_colours_and_more.py create mode 100644 main/migrations/0176_remove_layer_colour_hex_alter_layer_colour_and_more.py create mode 100644 main/migrations/0177_alter_layer_colour_alter_layer_colour_munsell.py create mode 100644 main/migrations/0178_alter_layer_colour_alter_layer_colour_munsell.py create mode 100644 main/migrations/0179_alter_layer_colour_alter_layer_colour_munsell.py create mode 100644 main/migrations/0180_texturecategory_texturekeyword.py create mode 100644 main/tools/colours_textures.py diff --git a/.idea/hellforge.iml b/.idea/hellforge.iml new file mode 100644 index 0000000..b60d2ff --- /dev/null +++ b/.idea/hellforge.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..eece9a8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..32438c3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..8136602 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + 1761336038698 + + + + \ No newline at end of file diff --git a/main/migrations/0174_colours.py b/main/migrations/0174_colours.py new file mode 100644 index 0000000..b1b5a85 --- /dev/null +++ b/main/migrations/0174_colours.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.3 on 2025-09-19 19:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0173_project_parameters'), + ] + + operations = [ + migrations.CreateModel( + name='Colours', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('colour_name', models.CharField(max_length=64)), + ('colour_munsell', models.CharField(max_length=32)), + ], + ), + ] diff --git a/main/migrations/0175_colourmunsell_colourname_delete_colours_and_more.py b/main/migrations/0175_colourmunsell_colourname_delete_colours_and_more.py new file mode 100644 index 0000000..212ff2d --- /dev/null +++ b/main/migrations/0175_colourmunsell_colourname_delete_colours_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 5.1.3 on 2025-09-23 19:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0174_colours'), + ] + + operations = [ + migrations.CreateModel( + name='ColourMunsell', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('colour_munsell', models.CharField(max_length=32)), + ('is_default', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='ColourName', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=64)), + ('is_default', models.BooleanField(default=False)), + ], + ), + migrations.DeleteModel( + name='Colours', + ), + migrations.AddField( + model_name='colourmunsell', + name='colour_name', + field=models.ManyToManyField(related_name='munsell_value', to='main.colourname'), + ), + ] diff --git a/main/migrations/0176_remove_layer_colour_hex_alter_layer_colour_and_more.py b/main/migrations/0176_remove_layer_colour_hex_alter_layer_colour_and_more.py new file mode 100644 index 0000000..1f522b2 --- /dev/null +++ b/main/migrations/0176_remove_layer_colour_hex_alter_layer_colour_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.3 on 2025-09-26 20:42 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0175_colourmunsell_colourname_delete_colours_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='layer', + name='colour_hex', + ), + migrations.AlterField( + model_name='layer', + name='colour', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='colour', to='main.colourname'), + ), + migrations.AlterField( + model_name='layer', + name='colour_munsell', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='munsell', to='main.colourmunsell'), + ), + ] diff --git a/main/migrations/0177_alter_layer_colour_alter_layer_colour_munsell.py b/main/migrations/0177_alter_layer_colour_alter_layer_colour_munsell.py new file mode 100644 index 0000000..2c5ae7a --- /dev/null +++ b/main/migrations/0177_alter_layer_colour_alter_layer_colour_munsell.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.3 on 2025-09-26 21:13 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0176_remove_layer_colour_hex_alter_layer_colour_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='layer', + name='colour', + field=models.CharField(blank=True, max_length=200, null=True, verbose_name='colour'), + ), + migrations.AlterField( + model_name='layer', + name='colour_munsell', + field=models.CharField(blank=True, max_length=200, null=True, validators=[django.core.validators.RegexValidator(code='invalid_registration', message='Enter a valid Munsell Number in the format 8.75YR 4.5/3', regex='^[0-9]+(\\.[0-9]+)?[A-Z]+\\s+[1-9](\\.[0-9]+)?/[0-9]+(\\.[0-9]+)?$')], verbose_name='colour_munsell'), + ), + ] diff --git a/main/migrations/0178_alter_layer_colour_alter_layer_colour_munsell.py b/main/migrations/0178_alter_layer_colour_alter_layer_colour_munsell.py new file mode 100644 index 0000000..1270327 --- /dev/null +++ b/main/migrations/0178_alter_layer_colour_alter_layer_colour_munsell.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.3 on 2025-09-27 16:01 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0177_alter_layer_colour_alter_layer_colour_munsell'), + ] + + operations = [ + migrations.AlterField( + model_name='layer', + name='colour', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='colour', to='main.colourname'), + ), + migrations.AlterField( + model_name='layer', + name='colour_munsell', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='munsell', to='main.colourmunsell'), + ), + ] diff --git a/main/migrations/0179_alter_layer_colour_alter_layer_colour_munsell.py b/main/migrations/0179_alter_layer_colour_alter_layer_colour_munsell.py new file mode 100644 index 0000000..e27a294 --- /dev/null +++ b/main/migrations/0179_alter_layer_colour_alter_layer_colour_munsell.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.3 on 2025-09-29 20:21 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0178_alter_layer_colour_alter_layer_colour_munsell'), + ] + + operations = [ + migrations.AlterField( + model_name='layer', + name='colour', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='colour', to='main.colourname'), + ), + migrations.AlterField( + model_name='layer', + name='colour_munsell', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='munsell', to='main.colourmunsell'), + ), + ] diff --git a/main/migrations/0180_texturecategory_texturekeyword.py b/main/migrations/0180_texturecategory_texturekeyword.py new file mode 100644 index 0000000..6066ef8 --- /dev/null +++ b/main/migrations/0180_texturecategory_texturekeyword.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.3 on 2025-09-30 20:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0179_alter_layer_colour_alter_layer_colour_munsell'), + ] + + operations = [ + migrations.CreateModel( + name='TextureCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('category', models.CharField(max_length=32)), + ], + ), + migrations.CreateModel( + name='TextureKeyword', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('texture', models.CharField(max_length=64)), + ('texture_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='texture_category', to='main.texturecategory')), + ], + ), + ] diff --git a/main/models.py b/main/models.py index 3880839..5808dce 100644 --- a/main/models.py +++ b/main/models.py @@ -1,7 +1,7 @@ import json from django.db import models from django.urls import reverse -from django.db.models import Q +from django.db.models import Q, CASCADE from django.core.validators import RegexValidator from django.contrib.contenttypes.fields import GenericForeignKey # for the description from django.contrib.contenttypes.models import ContentType # for the description @@ -1083,6 +1083,21 @@ def get_data(self, **kwargs): 'Silt' ]} +class ColourName(models.Model): + name = models.CharField(max_length=64) + is_default = models.BooleanField(default=False) + +class ColourMunsell(models.Model): + colour_munsell = models.CharField(max_length=32) + colour_name = models.ManyToManyField(ColourName, related_name="munsell_value") + is_default = models.BooleanField(default=False) + +class TextureCategory(models.Model): + category = models.CharField(max_length=32) + +class TextureKeyword(models.Model): + texture = models.CharField(max_length=64) + texture_category = models.ForeignKey(TextureCategory, on_delete=models.CASCADE, related_name="texture_category") class Layer(Dateable): name = models.CharField("name", max_length=200) @@ -1130,21 +1145,24 @@ class Layer(Dateable): null=True, ) # fields related to geological sediment properties - colour = models.CharField("colour",max_length=200, blank=True, null=True) # this is the "informal" name - colour_munsell = models.CharField( - "colour_munsell", - max_length=200, - validators=[ - RegexValidator( - regex=r'^[0-9]+(\.[0-9]+)?[A-Z]+\s+[1-9](\.[0-9]+)?/[0-9]+(\.[0-9]+)?$', - message="Enter a valid Munsell Number in the format 8.75YR 4.5/3", - code="invalid_registration", - ), - ], - blank=True, - null=True) # The Munsell Color Code - colour_hex = models.CharField("colour_rgb", max_length=200, blank=True, null=True) # The hex value calculated from Munsell + #colour = models.CharField("colour",max_length=200, blank=True, null=True) # this is the "informal" name + #colour_munsell = models.CharField( + # "colour_munsell", + # max_length=200, + # validators=[ + # RegexValidator( + # regex=r'^[0-9]+(\.[0-9]+)?[A-Z]+\s+[1-9](\.[0-9]+)?/[0-9]+(\.[0-9]+)?$', + # message="Enter a valid Munsell Number in the format 8.75YR 4.5/3", + # code="invalid_registration", + # ), + # ], + # blank=True, + # null=True) # The Munsell Color Code + colour = models.ForeignKey(ColourName, blank=True, null=True, on_delete=models.SET_NULL, related_name="colour") + colour_munsell = models.ForeignKey(ColourMunsell, blank=True, null=True, on_delete=models.SET_NULL, related_name="munsell") + #colour_hex = models.CharField("colour_rgb", max_length=200, blank=True, null=True) # The hex value calculated from Munsell texture = models.CharField("texture", max_length=200, blank=True, null=True, choices=texture_choices) + texture_keyword = models.ForeignKey(TextureKeyword, blank=True, null=True, on_delete=models.SET_NULL, related_name="texture") # ## References @@ -1775,6 +1793,13 @@ def get_data(self, **kwargs): new_data = {(f"MM_{k}" if k in duplicates else k):v for k,v in data.items() } # preserve column names in data return new_data +# +# +# Classes for Colour and Texture Databases +# +# + + models = { "site": Site, diff --git a/main/signals.py b/main/signals.py index c3d83be..bc67fc6 100644 --- a/main/signals.py +++ b/main/signals.py @@ -12,7 +12,9 @@ FaunalResults, Reference, SampleBatch, - Gallery + Gallery, + ColourName, + ColourMunsell ) from main.tools import dating import json @@ -189,6 +191,18 @@ def update_order(sender, instance, **kwargs): @receiver(pre_save, sender=Layer) def update_colour(sender, instance, **kwargs): + if instance.colour_munsell and not instance.colour: + colour_link = instance.colour_munsell.colour_name.filter(is_default=True) + if colour_link.exists(): + instance.colour = colour_link.first() + else: + instance.colour = instance.colour_munsell.colour_name.first() + elif instance.colour and not instance.colour_munsell: + munsell_link = instance.colour.munsell_value.filter(is_default=True) + if munsell_link.exists(): + instance.colour_munsell = munsell_link.first() + else: + instance.colour_munsell = instance.colour.munsell_value.first() # unset the hex-colour if instance.colour_munsell == "" or not instance.colour_munsell: instance.colour_hex = None diff --git a/main/tools/colours_textures.py b/main/tools/colours_textures.py new file mode 100644 index 0000000..cb1a899 --- /dev/null +++ b/main/tools/colours_textures.py @@ -0,0 +1,92 @@ +#from colour.examples.notation.examples_munsell import munsell_colour +#from weasyprint.css.computed_values import length +from unicodedata import category + +from main.models import ColourName, ColourMunsell, Layer, Site, TextureCategory, TextureKeyword +from main.forms import ReferenceForm, LayerColourForm +import csv + +#add input string cleanup before creation! +def import_colours_from_csv(path): + with open(path) as file: + reader = csv.reader(file, delimiter="\t") + for row in reader: + name_new, created = ColourName.objects.get_or_create( + name=row[0] + ) + if created: + name_new.is_default = False if len(row) < 4 else row[3].strip().lower()=="true" + name_new.save() + _ ,created_m = ColourMunsell.objects.get_or_create( + colour_munsell=row[1], + ) + _.is_default = False if len(row) < 3 else row[2].strip().lower()=="true" + _.colour_name.add(name_new) + _.save() + +def update_layer_colours_new(path): + with open(path) as file: + reader = csv.reader(file, delimiter="\t") + for row in reader: + colour_keyword = row[13] + if colour_keyword != "": + site = Site.objects.filter(name=row[0]) + if site.exists(): + layer = Layer.objects.filter(name=row[2], site=site[0]) + if layer.exists(): + layer = layer.first() + if colour_keyword[0].isdigit(): # is colour keyword a munsell value? + new_colour_munsell = ColourMunsell.objects.filter(colour_munsell=colour_keyword) + if new_colour_munsell.exists(): + new_colour_munsell_entry = new_colour_munsell[0] + layer.colour_munsell = new_colour_munsell_entry + layer.save() + else: + print("Munsell value " + colour_keyword + " does not exist.") + continue + else: + new_colour_name = ColourName.objects.filter(name=colour_keyword) + if new_colour_name.exists(): + new_colour_name_entry = new_colour_name[0] + layer.colour = new_colour_name_entry + layer.save() + else: + print("Colour " + colour_keyword + " does not exist.") + continue + else: + print("Layer " + row[2] + " of site " + row[0] + " does not exist.") + continue + else: + print("Site " + row[0] + " does not exist.") + continue + +#for initially creating category database entries +def make_texture_db(): + categories = ["silty clay", + "sandy clay loam", + "silty clay loam", + "clay loam", + "sandy loam", + "silt loam", + "loam", + "silt", + "loamy sand", + "sand", + "clay", + "sandy clay"] + for entry in categories: + new_cat, created = TextureCategory.objects.get_or_create(category=entry) + new_cat.save() + +#for adding keywords and pairing to categories +def update_textures(path): + with open(path) as file: + reader = csv.reader(file, delimiter="\t") + for row in reader: + print(row) + new_keyword, created = TextureKeyword.objects.get_or_create( + texture = row[0], + texture_category = TextureCategory.objects.get(category=row[1].lower()) + ) + if created: + new_keyword.save() \ No newline at end of file