diff --git a/.gitignore b/.gitignore index 9c24fab..4d9aa5d 100644 --- a/.gitignore +++ b/.gitignore @@ -129,5 +129,5 @@ dmypy.json .pyre/ *.swp -dcc/media +dcc/storage arduino/CommandStation-EX/build/ diff --git a/dcc/consist/migrations/0001_initial.py b/dcc/consist/migrations/0001_initial.py index df46d20..9ded3fe 100644 --- a/dcc/consist/migrations/0001_initial.py +++ b/dcc/consist/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.0.2 on 2022-04-02 14:25 +# Generated by Django 4.0.3 on 2022-04-07 09:25 from django.db import migrations, models import django.db.models.deletion @@ -10,8 +10,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('roster', '0001_initial'), ('metadata', '0001_initial'), + ('roster', '0001_initial'), ] operations = [ @@ -20,13 +20,13 @@ class Migration(migrations.Migration): fields=[ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('identifier', models.CharField(max_length=128)), - ('address', models.SmallIntegerField(blank=True, default=None, null=True)), - ('epoch', models.CharField(blank=True, max_length=32)), + ('consist_address', models.SmallIntegerField(blank=True, default=None, null=True)), + ('era', models.CharField(blank=True, max_length=32)), ('notes', models.TextField(blank=True)), ('creation_time', models.DateTimeField(auto_now_add=True)), ('updated_time', models.DateTimeField(auto_now=True)), ('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.company')), - ('tags', models.ManyToManyField(blank=True, related_name='consist', to='metadata.Tag')), + ('tags', models.ManyToManyField(blank=True, related_name='consist', to='metadata.tag')), ], ), migrations.CreateModel( @@ -34,7 +34,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('order', models.PositiveIntegerField(default=0)), - ('consist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='consist.consist')), + ('consist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consist_item', to='consist.consist')), ('rolling_stock', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='roster.rollingstock')), ], options={ diff --git a/dcc/consist/migrations/0002_rename_address_consist_consist_address_and_more.py b/dcc/consist/migrations/0002_rename_address_consist_consist_address_and_more.py deleted file mode 100644 index db729ca..0000000 --- a/dcc/consist/migrations/0002_rename_address_consist_consist_address_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 16:18 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('consist', '0001_initial'), - ] - - operations = [ - migrations.RenameField( - model_name='consist', - old_name='address', - new_name='consist_address', - ), - migrations.RenameField( - model_name='consist', - old_name='epoch', - new_name='era', - ), - ] diff --git a/dcc/consist/migrations/0003_alter_consistitem_consist.py b/dcc/consist/migrations/0003_alter_consistitem_consist.py deleted file mode 100644 index 6a2420f..0000000 --- a/dcc/consist/migrations/0003_alter_consistitem_consist.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.0.3 on 2022-04-02 21:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('consist', '0002_rename_address_consist_consist_address_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='consistitem', - name='consist', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consist_item', to='consist.consist'), - ), - ] diff --git a/dcc/dcc/settings.py b/dcc/dcc/settings.py index a87be4f..885329a 100644 --- a/dcc/dcc/settings.py +++ b/dcc/dcc/settings.py @@ -15,7 +15,7 @@ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - +STORAGE_DIR = BASE_DIR / "storage" # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ @@ -90,7 +90,7 @@ WSGI_APPLICATION = "dcc.wsgi.application" DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", + "NAME": STORAGE_DIR / "db.sqlite3", } } @@ -137,7 +137,7 @@ STATIC_URL = "static/" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" MEDIA_URL = "media/" -MEDIA_ROOT = os.path.join(BASE_DIR, "media") +MEDIA_ROOT = STORAGE_DIR / "media" COUNTRIES_OVERRIDE = { "ZZ": "Freelance", @@ -151,6 +151,11 @@ DECODER_INTERFACES = [ (5, "Next18/Next18S"), ] +MANUFACTURER_TYPES = [ + ("model", "Model"), + ("real", "Real") +] + ROLLING_STOCK_TYPES = [ ("engine", "Engine"), ("car", "Car"), diff --git a/dcc/driver/migrations/0001_initial.py b/dcc/driver/migrations/0001_initial.py index ca9c7bf..d05aaff 100644 --- a/dcc/driver/migrations/0001_initial.py +++ b/dcc/driver/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.0.2 on 2022-04-01 20:25 +# Generated by Django 4.0.3 on 2022-04-07 09:25 from django.db import migrations, models diff --git a/dcc/metadata/admin.py b/dcc/metadata/admin.py index fdedd1a..b0d0922 100644 --- a/dcc/metadata/admin.py +++ b/dcc/metadata/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin from metadata.models import ( + Property, Decoder, Scale, Manufacturer, @@ -9,17 +10,24 @@ from metadata.models import ( ) +@admin.register(Property) +class PropertyAdmin(admin.ModelAdmin): + search_fields = ("name",) + + @admin.register(Decoder) class DecoderAdmin(admin.ModelAdmin): readonly_fields = ("image_thumbnail",) list_display = ("__str__", "interface") list_filter = ("manufacturer", "interface") + search_fields = ("__str__",) @admin.register(Scale) class ScaleAdmin(admin.ModelAdmin): list_display = ("scale", "ratio", "gauge") list_filter = ("ratio", "gauge") + search_fields = list_display @admin.register(Company) @@ -27,20 +35,26 @@ class CompanyAdmin(admin.ModelAdmin): readonly_fields = ("logo_thumbnail",) list_display = ("name", "country") list_filter = list_display + search_fields = ("name",) @admin.register(Manufacturer) class ManufacturerAdmin(admin.ModelAdmin): readonly_fields = ("logo_thumbnail",) + list_display = ("name", "category") + list_filter = ("category",) + search_fields = ("name",) @admin.register(Tag) class TagAdmin(admin.ModelAdmin): readonly_fields = ("slug",) list_display = ("name", "slug") + search_fields = ("name",) @admin.register(RollingStockType) class RollingStockTypeAdmin(admin.ModelAdmin): list_display = ("__str__",) list_filter = ("type", "category") + search_fields = list_display diff --git a/dcc/metadata/migrations/0001_initial.py b/dcc/metadata/migrations/0001_initial.py index 09ced5a..450e6d9 100644 --- a/dcc/metadata/migrations/0001_initial.py +++ b/dcc/metadata/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.0.2 on 2022-04-02 14:25 +# Generated by Django 4.0.3 on 2022-04-07 09:25 from django.db import migrations, models import django.db.models.deletion @@ -18,12 +18,14 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=64, unique=True)), - ('extended_name', models.CharField(max_length=128, unique=True)), + ('extended_name', models.CharField(blank=True, max_length=128)), ('country', django_countries.fields.CountryField(max_length=2)), + ('freelance', models.BooleanField(default=False)), ('logo', models.ImageField(blank=True, null=True, upload_to='images/')), ], options={ 'verbose_name_plural': 'Companies', + 'ordering': ['name'], }, ), migrations.CreateModel( @@ -31,8 +33,36 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=128, unique=True)), + ('category', models.CharField(choices=[('model', 'Model'), ('real', 'Real')], max_length=64)), + ('website', models.URLField(blank=True)), ('logo', models.ImageField(blank=True, null=True, upload_to='images/')), ], + options={ + 'ordering': ['category', 'name'], + }, + ), + migrations.CreateModel( + name='Property', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, unique=True)), + ], + options={ + 'verbose_name_plural': 'Properties', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Scale', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('scale', models.CharField(max_length=32, unique=True)), + ('ratio', models.CharField(blank=True, max_length=16)), + ('gauge', models.CharField(blank=True, max_length=16)), + ], + options={ + 'ordering': ['scale'], + }, ), migrations.CreateModel( name='Tag', @@ -47,7 +77,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('type', models.CharField(max_length=64)), - ('category', models.CharField(choices=[('engine', 'Engine'), ('car', 'Car'), ('equipment', 'Equipment'), ('other', 'Other')], max_length=64)), + ('category', models.CharField(choices=[('engine', 'Engine'), ('car', 'Car'), ('railcar', 'Railcar'), ('equipment', 'Equipment'), ('other', 'Other')], max_length=64)), ], options={ 'unique_together': {('category', 'type')}, @@ -60,6 +90,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=128, unique=True)), ('version', models.CharField(blank=True, max_length=64)), ('interface', models.PositiveSmallIntegerField(blank=True, choices=[(1, 'NEM651'), (2, 'NEM652'), (3, 'PluX'), (4, '21MTC'), (5, 'Next18/Next18S')], null=True)), + ('sound', models.BooleanField(default=False)), ('image', models.ImageField(blank=True, null=True, upload_to='images/')), ('manufacturer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='metadata.manufacturer')), ], diff --git a/dcc/metadata/migrations/0002_scale_manufacturer_website_and_more.py b/dcc/metadata/migrations/0002_scale_manufacturer_website_and_more.py deleted file mode 100644 index 8e289ad..0000000 --- a/dcc/metadata/migrations/0002_scale_manufacturer_website_and_more.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 16:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('metadata', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Scale', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('scale', models.CharField(max_length=32, unique=True)), - ('ratio', models.CharField(blank=True, max_length=16)), - ('gauge', models.CharField(blank=True, max_length=16)), - ], - ), - migrations.AddField( - model_name='manufacturer', - name='website', - field=models.URLField(blank=True), - ), - migrations.AlterField( - model_name='company', - name='extended_name', - field=models.CharField(blank=True, max_length=128), - ), - ] diff --git a/dcc/metadata/migrations/0003_alter_company_options_alter_scale_options_and_more.py b/dcc/metadata/migrations/0003_alter_company_options_alter_scale_options_and_more.py deleted file mode 100644 index 9bd9674..0000000 --- a/dcc/metadata/migrations/0003_alter_company_options_alter_scale_options_and_more.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 17:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('metadata', '0002_scale_manufacturer_website_and_more'), - ] - - operations = [ - migrations.AlterModelOptions( - name='company', - options={'ordering': ['name'], 'verbose_name_plural': 'Companies'}, - ), - migrations.AlterModelOptions( - name='scale', - options={'ordering': ['scale']}, - ), - migrations.AlterField( - model_name='rollingstocktype', - name='category', - field=models.CharField(choices=[('engine', 'Engine'), ('car', 'Car'), ('railcar', 'Railcar'), ('equipment', 'Equipment'), ('other', 'Other')], max_length=64), - ), - ] diff --git a/dcc/metadata/migrations/0004_company_freelance_decoder_sound.py b/dcc/metadata/migrations/0004_company_freelance_decoder_sound.py deleted file mode 100644 index 2d32053..0000000 --- a/dcc/metadata/migrations/0004_company_freelance_decoder_sound.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 17:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('metadata', '0003_alter_company_options_alter_scale_options_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='freelance', - field=models.BooleanField(blank=True, default=False, null=True), - ), - migrations.AddField( - model_name='decoder', - name='sound', - field=models.BooleanField(blank=True, default=False, null=True), - ), - ] diff --git a/dcc/metadata/migrations/0005_alter_company_freelance_alter_decoder_sound.py b/dcc/metadata/migrations/0005_alter_company_freelance_alter_decoder_sound.py deleted file mode 100644 index 215e18d..0000000 --- a/dcc/metadata/migrations/0005_alter_company_freelance_alter_decoder_sound.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 17:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('metadata', '0004_company_freelance_decoder_sound'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='freelance', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='decoder', - name='sound', - field=models.BooleanField(default=False), - ), - ] diff --git a/dcc/metadata/models.py b/dcc/metadata/models.py index 58ed46d..7ffa75b 100644 --- a/dcc/metadata/models.py +++ b/dcc/metadata/models.py @@ -6,11 +6,28 @@ from django_countries.fields import CountryField from dcc.utils import get_image_preview, slugify +class Property(models.Model): + name = models.CharField(max_length=128, unique=True) + + class Meta: + verbose_name_plural = "Properties" + ordering = ["name"] + + def __str__(self): + return self.name + + class Manufacturer(models.Model): name = models.CharField(max_length=128, unique=True) + category = models.CharField( + max_length=64, choices=settings.MANUFACTURER_TYPES + ) website = models.URLField(blank=True) logo = models.ImageField(upload_to="images/", null=True, blank=True) + class Meta: + ordering = ["category", "name"] + def __str__(self): return self.name diff --git a/dcc/roster/admin.py b/dcc/roster/admin.py index 37aff2d..cb58ec8 100644 --- a/dcc/roster/admin.py +++ b/dcc/roster/admin.py @@ -1,14 +1,23 @@ from django.contrib import admin from roster.models import ( RollingClass, + RollingClassProperty, RollingStock, RollingStockImage, RollingStockDocument, + RollingStockProperty, ) +class RollingClassPropertyInline(admin.TabularInline): + model = RollingClassProperty + min_num = 0 + extra = 0 + + @admin.register(RollingClass) class RollingClass(admin.ModelAdmin): + inlines = (RollingClassPropertyInline,) list_display = ("__str__", "type", "company") list_filter = ("company", "type__category", "type") search_fields = list_display @@ -27,9 +36,19 @@ class RollingStockImageInline(admin.TabularInline): readonly_fields = ("image_thumbnail",) +class RollingStockPropertyInline(admin.TabularInline): + model = RollingStockProperty + min_num = 0 + extra = 0 + + @admin.register(RollingStock) class RollingStockAdmin(admin.ModelAdmin): - inlines = (RollingStockImageInline, RollingStockDocInline) + inlines = ( + RollingStockPropertyInline, + RollingStockImageInline, + RollingStockDocInline, + ) readonly_fields = ("creation_time", "updated_time") list_display = ( "__str__", @@ -55,11 +74,9 @@ class RollingStockAdmin(admin.ModelAdmin): "fields": ( "rolling_class", "road_number", - "manufacturer", "scale", + "manufacturer", "sku", - "decoder", - "address", "era", "production_year", "purchase_date", @@ -68,6 +85,15 @@ class RollingStockAdmin(admin.ModelAdmin): ) }, ), + ( + "DCC", + { + "fields": ( + "decoder", + "address", + ) + }, + ), ( "Audit", { diff --git a/dcc/roster/migrations/0001_initial.py b/dcc/roster/migrations/0001_initial.py index 9ad5c6e..9bcbb1e 100644 --- a/dcc/roster/migrations/0001_initial.py +++ b/dcc/roster/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.0.2 on 2022-04-02 14:25 +# Generated by Django 4.0.3 on 2022-04-07 09:25 from django.db import migrations, models import django.db.models.deletion @@ -15,34 +15,67 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Class', + name='RollingClass', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('identifier', models.CharField(max_length=128)), + ('description', models.CharField(blank=True, max_length=256)), ('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.company')), + ('manufacturer', models.ForeignKey(blank=True, limit_choices_to={'category': 'real'}, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.manufacturer')), ('type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.rollingstocktype')), ], + options={ + 'verbose_name': 'Class', + 'verbose_name_plural': 'Classes', + 'ordering': ['company', 'identifier'], + }, ), migrations.CreateModel( name='RollingStock', fields=[ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('road_number', models.CharField(max_length=128)), - ('address', models.SmallIntegerField(blank=True, default=None, null=True)), ('sku', models.CharField(blank=True, max_length=32)), - ('epoch', models.CharField(blank=True, max_length=32)), + ('address', models.SmallIntegerField(blank=True, default=None, null=True)), + ('era', models.CharField(blank=True, max_length=32)), ('production_year', models.SmallIntegerField(blank=True, null=True)), ('purchase_date', models.DateField(blank=True, null=True)), ('notes', models.TextField(blank=True)), ('creation_time', models.DateTimeField(auto_now_add=True)), ('updated_time', models.DateTimeField(auto_now=True)), - ('_class', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='roster.class')), ('decoder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.decoder')), - ('manufacturer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.manufacturer')), - ('tags', models.ManyToManyField(blank=True, related_name='rolling_stock', to='metadata.Tag')), + ('manufacturer', models.ForeignKey(blank=True, limit_choices_to={'category': 'model'}, null=True, on_delete=django.db.models.deletion.CASCADE, to='metadata.manufacturer')), + ('rolling_class', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='roster.rollingclass', verbose_name='Class')), + ('scale', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='metadata.scale')), + ('tags', models.ManyToManyField(blank=True, related_name='rolling_stock', to='metadata.tag')), ], options={ 'verbose_name_plural': 'Rolling stock', + 'ordering': ['rolling_class', 'road_number'], + }, + ), + migrations.CreateModel( + name='RollingStockProperty', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.CharField(max_length=256)), + ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='metadata.property')), + ('rolling_stock', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='roster.rollingstock')), + ], + options={ + 'verbose_name_plural': 'Properties', + }, + ), + migrations.CreateModel( + name='RollingClassProperty', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.CharField(max_length=256)), + ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='metadata.property')), + ('rolling_class', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='roster.rollingclass', verbose_name='Class')), + ], + options={ + 'verbose_name_plural': 'Properties', }, ), migrations.CreateModel( diff --git a/dcc/roster/migrations/0002_alter_rollingstockimage_unique_together_and_more.py b/dcc/roster/migrations/0002_alter_rollingstockimage_unique_together_and_more.py new file mode 100644 index 0000000..ed9c2da --- /dev/null +++ b/dcc/roster/migrations/0002_alter_rollingstockimage_unique_together_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.3 on 2022-04-08 21:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('roster', '0001_initial'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='rollingstockimage', + unique_together=set(), + ), + migrations.AddField( + model_name='rollingstockimage', + name='is_thumbnail', + field=models.BooleanField(default=False), + preserve_default=False, + ), + ] diff --git a/dcc/roster/migrations/0002_rename_class_rollingclass_alter_rollingclass_options_and_more.py b/dcc/roster/migrations/0002_rename_class_rollingclass_alter_rollingclass_options_and_more.py deleted file mode 100644 index f1cbb85..0000000 --- a/dcc/roster/migrations/0002_rename_class_rollingclass_alter_rollingclass_options_and_more.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 16:18 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('metadata', '0002_scale_manufacturer_website_and_more'), - ('roster', '0001_initial'), - ] - - operations = [ - migrations.RenameModel( - old_name='Class', - new_name='RollingClass', - ), - migrations.AlterModelOptions( - name='rollingclass', - options={'ordering': ['company', 'identifier'], 'verbose_name': 'Class', 'verbose_name_plural': 'Classes'}, - ), - migrations.AlterModelOptions( - name='rollingstock', - options={'ordering': ['rolling_class', 'road_number'], 'verbose_name_plural': 'Rolling stock'}, - ), - migrations.RenameField( - model_name='rollingstock', - old_name='epoch', - new_name='era', - ), - migrations.RemoveField( - model_name='rollingstock', - name='_class', - ), - migrations.AddField( - model_name='rollingstock', - name='rolling_class', - field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='roster.rollingclass', verbose_name='Class'), - preserve_default=False, - ), - migrations.AddField( - model_name='rollingstock', - name='scale', - field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='metadata.scale'), - preserve_default=False, - ), - ] diff --git a/dcc/roster/migrations/0003_rollingstockimage_description.py b/dcc/roster/migrations/0003_rollingstockimage_description.py deleted file mode 100644 index 7d991e1..0000000 --- a/dcc/roster/migrations/0003_rollingstockimage_description.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 17:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('roster', '0002_rename_class_rollingclass_alter_rollingclass_options_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='rollingstockimage', - name='description', - field=models.CharField(blank=True, max_length=256), - ), - ] diff --git a/dcc/roster/migrations/0004_remove_rollingstockimage_description_and_more.py b/dcc/roster/migrations/0004_remove_rollingstockimage_description_and_more.py deleted file mode 100644 index ca7dd75..0000000 --- a/dcc/roster/migrations/0004_remove_rollingstockimage_description_and_more.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.0.2 on 2022-04-02 17:17 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('roster', '0003_rollingstockimage_description'), - ] - - operations = [ - migrations.RemoveField( - model_name='rollingstockimage', - name='description', - ), - migrations.AddField( - model_name='rollingclass', - name='description', - field=models.CharField(blank=True, max_length=256), - ), - ] diff --git a/dcc/roster/models.py b/dcc/roster/models.py index 5c99b03..6417c80 100644 --- a/dcc/roster/models.py +++ b/dcc/roster/models.py @@ -1,13 +1,13 @@ import os from uuid import uuid4 from django.db import models -from django.urls import reverse # from django.core.files.storage import FileSystemStorage # from django.dispatch import receiver from dcc.utils import get_image_preview from metadata.models import ( + Property, Scale, Manufacturer, Decoder, @@ -27,10 +27,14 @@ class RollingClass(models.Model): type = models.ForeignKey( RollingStockType, on_delete=models.CASCADE, null=True, blank=True ) - description = models.CharField(max_length=256, blank=True) company = models.ForeignKey( Company, on_delete=models.CASCADE, null=True, blank=True ) + description = models.CharField(max_length=256, blank=True) + manufacturer = models.ForeignKey( + Manufacturer, on_delete=models.CASCADE, null=True, blank=True, + limit_choices_to={"category": "real"} + ) class Meta: ordering = ["company", "identifier"] @@ -41,6 +45,24 @@ class RollingClass(models.Model): return "{0} {1}".format(self.company, self.identifier) +class RollingClassProperty(models.Model): + rolling_class = models.ForeignKey( + RollingClass, + on_delete=models.CASCADE, + null=False, + blank=False, + verbose_name="Class", + ) + property = models.ForeignKey(Property, on_delete=models.CASCADE) + value = models.CharField(max_length=256) + + def __str__(self): + return self.property.name + + class Meta: + verbose_name_plural = "Properties" + + class RollingStock(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid4, editable=False) rolling_class = models.ForeignKey( @@ -52,7 +74,8 @@ class RollingStock(models.Model): ) road_number = models.CharField(max_length=128, unique=False) manufacturer = models.ForeignKey( - Manufacturer, on_delete=models.CASCADE, null=True, blank=True + Manufacturer, on_delete=models.CASCADE, null=True, blank=True, + limit_choices_to={"category": "model"} ) scale = models.ForeignKey(Scale, on_delete=models.CASCADE) sku = models.CharField(max_length=32, blank=True) @@ -100,18 +123,39 @@ class RollingStockDocument(models.Model): class RollingStockImage(models.Model): rolling_stock = models.ForeignKey(RollingStock, on_delete=models.CASCADE) image = models.ImageField(upload_to="images/", null=True, blank=True) + is_thumbnail = models.BooleanField() def image_thumbnail(self): return get_image_preview(self.image.url) image_thumbnail.short_description = "Preview" - class Meta(object): - unique_together = ("rolling_stock", "image") - def __str__(self): return "{0}".format(os.path.basename(self.image.name)) + def save(self, **kwargs): + if self.is_thumbnail: + RollingStockImage.objects.filter( + rolling_stock=self.rolling_stock).update(is_thumbnail=False) + super().save(**kwargs) + + +class RollingStockProperty(models.Model): + rolling_stock = models.ForeignKey( + RollingStock, + on_delete=models.CASCADE, + null=False, + blank=False + ) + property = models.ForeignKey(Property, on_delete=models.CASCADE) + value = models.CharField(max_length=256) + + def __str__(self): + return self.property.name + + class Meta: + verbose_name_plural = "Properties" + # @receiver(models.signals.post_delete, sender=Cab) # def post_save_image(sender, instance, *args, **kwargs):