From 7673f0514a8f2656f8ca2a375cbc500ea071e315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Thu, 1 May 2025 23:49:22 +0200 Subject: [PATCH] Fix filter by scale counters and add consist constrains Still to be improved, see FIXME --- ram/consist/admin.py | 4 +- ram/consist/migrations/0017_consist_scale.py | 42 ++++++++++++++++++++ ram/consist/models.py | 36 ++++++++++++++++- ram/portal/views.py | 32 ++++++++++----- ram/ram/__init__.py | 2 +- 5 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 ram/consist/migrations/0017_consist_scale.py diff --git a/ram/consist/admin.py b/ram/consist/admin.py index f898b4f..2ec67ef 100644 --- a/ram/consist/admin.py +++ b/ram/consist/admin.py @@ -25,10 +25,11 @@ class ConsistItemInline(SortableInlineAdminMixin, admin.TabularInline): class ConsistAdmin(SortableAdminBase, admin.ModelAdmin): inlines = (ConsistItemInline,) readonly_fields = ( + "scale", "creation_time", "updated_time", ) - list_filter = ("company__name", "era", "published") + list_filter = ("company__name", "era", "scale", "published") list_display = ("__str__",) + list_filter + ("country_flag",) search_fields = ("identifier",) + list_filter save_as = True @@ -49,6 +50,7 @@ class ConsistAdmin(SortableAdminBase, admin.ModelAdmin): "consist_address", "company", "era", + "scale", "description", "image", "tags", diff --git a/ram/consist/migrations/0017_consist_scale.py b/ram/consist/migrations/0017_consist_scale.py new file mode 100644 index 0000000..4e2fcaf --- /dev/null +++ b/ram/consist/migrations/0017_consist_scale.py @@ -0,0 +1,42 @@ +# Generated by Django 5.1.4 on 2025-05-01 09:51 + +import django.db.models.deletion +from django.db import migrations, models + + +def set_scale(apps, schema_editor): + Consist = apps.get_model("consist", "Consist") + + for consist in Consist.objects.all(): + try: + consist.scale = consist.consist_item.first().rolling_stock.scale + consist.save() + except AttributeError: + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ("consist", "0016_alter_consistitem_order"), + ( + "metadata", + "0024_remove_genericdocument_tags_delete_decoderdocument_and_more", + ), + ] + + operations = [ + migrations.AddField( + model_name="consist", + name="scale", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="metadata.scale", + ), + ), + migrations.RunPython( + set_scale, + reverse_code=migrations.RunPython.noop + ), + ] diff --git a/ram/consist/models.py b/ram/consist/models.py index e6467aa..9a7f61f 100644 --- a/ram/consist/models.py +++ b/ram/consist/models.py @@ -7,7 +7,7 @@ from django.core.exceptions import ValidationError from ram.models import BaseModel from ram.utils import DeduplicatedStorage -from metadata.models import Company, Tag +from metadata.models import Company, Scale, Tag from roster.models import RollingStock @@ -26,6 +26,7 @@ class Consist(BaseModel): blank=True, help_text="Era or epoch of the consist", ) + scale = models.ForeignKey(Scale, null=True, on_delete=models.CASCADE) image = models.ImageField( upload_to=os.path.join("images", "consists"), storage=DeduplicatedStorage, @@ -81,8 +82,39 @@ class ConsistItem(models.Model): def __str__(self): return "{0}".format(self.rolling_stock) + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + if self.consist.scale != self.rolling_stock.scale: + self.consist.scale = self.rolling_stock.scale + self.consist.save() + + def delete(self, *args, **kwargs): + super().delete(*args, **kwargs) + if not self.consist.consist_item.exists(): + self.consist.scale = None + self.consist.save() + def clean(self): - if self.consist.published and not self.rolling_stock.published: + rolling_stock = getattr(self, "rolling_stock", False) + if not rolling_stock: + return # exit if no inline are present + + # FIXME this does not work when creating a new consist, + # because the consist is not saved yet and it must be moved + # to the admin form validation via InlineFormSet.clean() + consist = self.consist + items = consist.consist_item + if ( + consist.pk # if we are not creating a new consist + and items.exists() # if there's at least one item + and self != items.first() # if we are not changing the first item + # if scale is different from the first item + and rolling_stock.scale != items.first().rolling_stock.scale + ): + raise ValidationError( + "The rolling stock and consist must be of the same scale." + ) + if self.consist.published and not rolling_stock.published: raise ValidationError( "You must unpublish the the consist before using this item." ) diff --git a/ram/portal/views.py b/ram/portal/views.py index 9437d1b..4685f5a 100644 --- a/ram/portal/views.py +++ b/ram/portal/views.py @@ -551,16 +551,30 @@ class Scales(GetData): item_type = "scale" def get_data(self, request): - return Scale.objects.annotate( - num_items=Count( - "rollingstock", - filter=Q( - rollingstock__in=RollingStock.objects.get_published( - request.user - ) + return ( + Scale.objects.annotate( + num_rollingstock=Count( + "rollingstock", + filter=Q( + rollingstock__in=RollingStock.objects.get_published( + request.user + ) + ), + distinct=True, ), - ), - ).order_by("-ratio_int", "-tracks", "scale") + num_consists=Count( + "consist", + filter=Q( + consist__in=Consist.objects.get_published( + request.user + ) + ), + distinct=True, + ), + ) + .annotate(num_items=F("num_rollingstock") + F("num_consists")) + .order_by("-ratio_int", "-tracks", "scale") + ) class Types(GetData): diff --git a/ram/ram/__init__.py b/ram/ram/__init__.py index bbf4658..30051e6 100644 --- a/ram/ram/__init__.py +++ b/ram/ram/__init__.py @@ -1,4 +1,4 @@ from ram.utils import git_suffix -__version__ = "0.17.6" +__version__ = "0.17.7" __version__ += git_suffix(__file__)