mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
Improve user experience in admin and UI (#45)
This commit is contained in:
@@ -2,7 +2,7 @@ import html
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import format_html, strip_tags
|
||||||
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
||||||
|
|
||||||
from ram.admin import publish, unpublish
|
from ram.admin import publish, unpublish
|
||||||
@@ -93,12 +93,7 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Notes",
|
"Notes",
|
||||||
{
|
{"classes": ("collapse",), "fields": ("notes",)},
|
||||||
"classes": ("collapse",),
|
|
||||||
"fields": (
|
|
||||||
"notes",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Audit",
|
"Audit",
|
||||||
@@ -150,23 +145,25 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"{}:{}".format(property.property.name, property.value)
|
"{}:{}".format(property.property.name, property.value)
|
||||||
for property in obj.property.all()
|
for property in obj.property.all()
|
||||||
)
|
)
|
||||||
data.append([
|
data.append(
|
||||||
obj.title,
|
[
|
||||||
obj.authors_list.replace(",", settings.CSV_SEPARATOR_ALT),
|
obj.title,
|
||||||
obj.publisher.name,
|
obj.authors_list.replace(",", settings.CSV_SEPARATOR_ALT),
|
||||||
obj.ISBN,
|
obj.publisher.name,
|
||||||
dict(settings.LANGUAGES)[obj.language],
|
obj.ISBN,
|
||||||
obj.number_of_pages,
|
dict(settings.LANGUAGES)[obj.language],
|
||||||
obj.publication_year,
|
obj.number_of_pages,
|
||||||
html.unescape(strip_tags(obj.description)),
|
obj.publication_year,
|
||||||
settings.CSV_SEPARATOR_ALT.join(
|
html.unescape(strip_tags(obj.description)),
|
||||||
t.name for t in obj.tags.all()
|
settings.CSV_SEPARATOR_ALT.join(
|
||||||
),
|
t.name for t in obj.tags.all()
|
||||||
obj.purchase_date,
|
),
|
||||||
obj.price,
|
obj.purchase_date,
|
||||||
html.unescape(strip_tags(obj.notes)),
|
obj.price,
|
||||||
properties,
|
html.unescape(strip_tags(obj.notes)),
|
||||||
])
|
properties,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return generate_csv(header, data, "bookshelf_books.csv")
|
return generate_csv(header, data, "bookshelf_books.csv")
|
||||||
|
|
||||||
@@ -185,9 +182,15 @@ class AuthorAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(Publisher)
|
@admin.register(Publisher)
|
||||||
class PublisherAdmin(admin.ModelAdmin):
|
class PublisherAdmin(admin.ModelAdmin):
|
||||||
list_display = ("name", "country")
|
list_display = ("name", "country_flag")
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
@admin.display(description="Country")
|
||||||
|
def country_flag(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" /> {}'.format(obj.country.flag, obj.country.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Catalog)
|
@admin.register(Catalog)
|
||||||
class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||||
@@ -237,12 +240,7 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Notes",
|
"Notes",
|
||||||
{
|
{"classes": ("collapse",), "fields": ("notes",)},
|
||||||
"classes": ("collapse",),
|
|
||||||
"fields": (
|
|
||||||
"notes",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Audit",
|
"Audit",
|
||||||
@@ -287,24 +285,26 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"{}:{}".format(property.property.name, property.value)
|
"{}:{}".format(property.property.name, property.value)
|
||||||
for property in obj.property.all()
|
for property in obj.property.all()
|
||||||
)
|
)
|
||||||
data.append([
|
data.append(
|
||||||
obj.__str__(),
|
[
|
||||||
obj.manufacturer.name,
|
obj.__str__(),
|
||||||
obj.years,
|
obj.manufacturer.name,
|
||||||
obj.get_scales(),
|
obj.years,
|
||||||
obj.ISBN,
|
obj.get_scales(),
|
||||||
dict(settings.LANGUAGES)[obj.language],
|
obj.ISBN,
|
||||||
obj.number_of_pages,
|
dict(settings.LANGUAGES)[obj.language],
|
||||||
obj.publication_year,
|
obj.number_of_pages,
|
||||||
html.unescape(strip_tags(obj.description)),
|
obj.publication_year,
|
||||||
settings.CSV_SEPARATOR_ALT.join(
|
html.unescape(strip_tags(obj.description)),
|
||||||
t.name for t in obj.tags.all()
|
settings.CSV_SEPARATOR_ALT.join(
|
||||||
),
|
t.name for t in obj.tags.all()
|
||||||
obj.purchase_date,
|
),
|
||||||
obj.price,
|
obj.purchase_date,
|
||||||
html.unescape(strip_tags(obj.notes)),
|
obj.price,
|
||||||
properties,
|
html.unescape(strip_tags(obj.notes)),
|
||||||
])
|
properties,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return generate_csv(header, data, "bookshelf_catalogs.csv")
|
return generate_csv(header, data, "bookshelf_catalogs.csv")
|
||||||
|
|
||||||
|
@@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2025-01-18 11:20
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookshelf", "0020_alter_basebookdocument_unique_together_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="basebookdocument",
|
||||||
|
name="creation_time",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
auto_now_add=True, default=django.utils.timezone.now
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="basebookdocument",
|
||||||
|
name="updated_time",
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="basebookdocument",
|
||||||
|
name="private",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, help_text="Document will be visible only to logged users"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@@ -1,4 +1,5 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.utils.html import format_html
|
||||||
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
||||||
|
|
||||||
from ram.admin import publish, unpublish
|
from ram.admin import publish, unpublish
|
||||||
@@ -28,10 +29,16 @@ class ConsistAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"updated_time",
|
"updated_time",
|
||||||
)
|
)
|
||||||
list_filter = ("company", "era", "published")
|
list_filter = ("company", "era", "published")
|
||||||
list_display = ("__str__",) + list_filter
|
list_display = ("__str__",) + list_filter + ("country_flag",)
|
||||||
search_fields = ("identifier",) + list_filter
|
search_fields = ("identifier",) + list_filter
|
||||||
save_as = True
|
save_as = True
|
||||||
|
|
||||||
|
@admin.display(description="Country")
|
||||||
|
def country_flag(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" /> {}'.format(obj.country.flag, obj.country)
|
||||||
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
@@ -39,6 +39,10 @@ class Consist(BaseModel):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("consist", kwargs={"uuid": self.uuid})
|
return reverse("consist", kwargs={"uuid": self.uuid})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def country(self):
|
||||||
|
return self.company.country
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.consist_item.filter(rolling_stock__published=False).exists():
|
if self.consist_item.filter(rolling_stock__published=False).exists():
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.utils.html import format_html
|
||||||
from adminsortable2.admin import SortableAdminMixin
|
from adminsortable2.admin import SortableAdminMixin
|
||||||
|
|
||||||
from ram.admin import publish, unpublish
|
from ram.admin import publish, unpublish
|
||||||
@@ -47,18 +48,30 @@ class ScaleAdmin(admin.ModelAdmin):
|
|||||||
@admin.register(Company)
|
@admin.register(Company)
|
||||||
class CompanyAdmin(admin.ModelAdmin):
|
class CompanyAdmin(admin.ModelAdmin):
|
||||||
readonly_fields = ("logo_thumbnail",)
|
readonly_fields = ("logo_thumbnail",)
|
||||||
list_display = ("name", "country")
|
list_display = ("name", "country_flag")
|
||||||
list_filter = list_display
|
list_filter = ("name", "country")
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
@admin.display(description="Country")
|
||||||
|
def country_flag(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" /> {}'.format(obj.country.flag, obj.country.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Manufacturer)
|
@admin.register(Manufacturer)
|
||||||
class ManufacturerAdmin(admin.ModelAdmin):
|
class ManufacturerAdmin(admin.ModelAdmin):
|
||||||
readonly_fields = ("logo_thumbnail",)
|
readonly_fields = ("logo_thumbnail",)
|
||||||
list_display = ("name", "category")
|
list_display = ("name", "category", "country_flag")
|
||||||
list_filter = ("category",)
|
list_filter = ("category",)
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
@admin.display(description="Country")
|
||||||
|
def country_flag(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" /> {}'.format(obj.country.flag, obj.country.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Tag)
|
@admin.register(Tag)
|
||||||
class TagAdmin(admin.ModelAdmin):
|
class TagAdmin(admin.ModelAdmin):
|
||||||
@@ -76,7 +89,7 @@ class RollingStockTypeAdmin(SortableAdminMixin, admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(GenericDocument)
|
@admin.register(GenericDocument)
|
||||||
class GenericDocumentAdmin(admin.ModelAdmin):
|
class GenericDocumentAdmin(admin.ModelAdmin):
|
||||||
readonly_fields = ("size",)
|
readonly_fields = ("size", "creation_time", "updated_time")
|
||||||
list_display = (
|
list_display = (
|
||||||
"__str__",
|
"__str__",
|
||||||
"description",
|
"description",
|
||||||
@@ -88,4 +101,32 @@ class GenericDocumentAdmin(admin.ModelAdmin):
|
|||||||
"description",
|
"description",
|
||||||
"file",
|
"file",
|
||||||
)
|
)
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"private",
|
||||||
|
"description",
|
||||||
|
"file",
|
||||||
|
"size",
|
||||||
|
"tags",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Notes",
|
||||||
|
{"classes": ("collapse",), "fields": ("notes",)},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Audit",
|
||||||
|
{
|
||||||
|
"classes": ("collapse",),
|
||||||
|
"fields": (
|
||||||
|
"creation_time",
|
||||||
|
"updated_time",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
actions = [publish, unpublish]
|
actions = [publish, unpublish]
|
||||||
|
@@ -0,0 +1,66 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2025-01-18 11:20
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
import django_countries.fields
|
||||||
|
import tinymce.models
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("metadata", "0021_genericdocument"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="decoderdocument",
|
||||||
|
name="creation_time",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
auto_now_add=True, default=django.utils.timezone.now
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="decoderdocument",
|
||||||
|
name="updated_time",
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="genericdocument",
|
||||||
|
name="creation_time",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
auto_now_add=True, default=django.utils.timezone.now
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="genericdocument",
|
||||||
|
name="notes",
|
||||||
|
field=tinymce.models.HTMLField(blank=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="genericdocument",
|
||||||
|
name="updated_time",
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="manufacturer",
|
||||||
|
name="country",
|
||||||
|
field=django_countries.fields.CountryField(blank=True, max_length=2),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="decoderdocument",
|
||||||
|
name="private",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, help_text="Document will be visible only to logged users"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="genericdocument",
|
||||||
|
name="private",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, help_text="Document will be visible only to logged users"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@@ -6,6 +6,8 @@ from django.dispatch.dispatcher import receiver
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django_countries.fields import CountryField
|
from django_countries.fields import CountryField
|
||||||
|
|
||||||
|
from tinymce import models as tinymce
|
||||||
|
|
||||||
from ram.models import Document
|
from ram.models import Document
|
||||||
from ram.utils import DeduplicatedStorage, get_image_preview, slugify
|
from ram.utils import DeduplicatedStorage, get_image_preview, slugify
|
||||||
from ram.managers import PublicManager
|
from ram.managers import PublicManager
|
||||||
@@ -34,6 +36,7 @@ class Manufacturer(models.Model):
|
|||||||
category = models.CharField(
|
category = models.CharField(
|
||||||
max_length=64, choices=settings.MANUFACTURER_TYPES
|
max_length=64, choices=settings.MANUFACTURER_TYPES
|
||||||
)
|
)
|
||||||
|
country = CountryField(blank=True)
|
||||||
website = models.URLField(blank=True)
|
website = models.URLField(blank=True)
|
||||||
logo = models.ImageField(
|
logo = models.ImageField(
|
||||||
upload_to=os.path.join("images", "manufacturers"),
|
upload_to=os.path.join("images", "manufacturers"),
|
||||||
@@ -237,6 +240,7 @@ class Tag(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class GenericDocument(Document):
|
class GenericDocument(Document):
|
||||||
|
notes = tinymce.HTMLField(blank=True)
|
||||||
tags = models.ManyToManyField(Tag, blank=True)
|
tags = models.ManyToManyField(Tag, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@@ -7,7 +7,10 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Company</th>
|
<th scope="row">Company</th>
|
||||||
|
{% if d.item.freelance %}
|
||||||
|
<th class="text-end" scope="row"><span class="badge text-bg-secondary">Freelance</span></th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
@@ -27,7 +30,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Country</th>
|
<th class="w-33" scope="row">Country</th>
|
||||||
<td>{{ d.item.country.name }} <img src="{{ d.item.country.flag }}" alt="{{ d.item.country }}">
|
<td><img src="{{ d.item.country.flag }}" alt="{{ d.item.country }}"> {{ d.item.country.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if d.item.freelance %}
|
{% if d.item.freelance %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@@ -24,7 +24,10 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Consist</th>
|
<th scope="row">Consist</th>
|
||||||
|
{% if d.item.company.freelance %}
|
||||||
|
<th class="text-end" scope="row"><span class="badge text-bg-secondary">Freelance</span></th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
|
@@ -22,7 +22,10 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Rolling stock</th>
|
<th scope="row">Rolling stock</th>
|
||||||
|
{% if d.item.rolling_class.company.freelance %}
|
||||||
|
<th class="text-end" scope="row"><span class="badge text-bg-secondary">Freelance</span></th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
|
@@ -79,7 +79,10 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Data</th>
|
<th scope="row">Consist</th>
|
||||||
|
{% if consist.company.freelance %}
|
||||||
|
<th class="text-end" scope="row"><span class="badge text-bg-secondary">Freelance</span></th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
|
@@ -73,7 +73,10 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Rolling stock</th>
|
<th scope="row">Rolling stock</th>
|
||||||
|
{% if company.freelance %}
|
||||||
|
<th class="text-end" scope="row"><span class="badge text-bg-secondary">Freelance</span></th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
@@ -84,7 +87,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Company</th>
|
<th scope="row">Company</th>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'filtered' _filter="company" search=company.slug %}">{{ company }}</a> {{ company.extended_name_pp }}
|
<a href="{% url 'filtered' _filter="company" search=company.slug %}">{{ company }}</a> {{ company.extended_name_pp }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Country</th>
|
||||||
|
<td>
|
||||||
|
<img src="{{ company.country.flag }}" alt="{{ company.country }}"> {{ company.country.name }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -281,7 +290,10 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Company data</th>
|
<th scope="row">Company data</th>
|
||||||
|
{% if company.freelance %}
|
||||||
|
<th class="text-end" scope="row"><span class="badge text-bg-secondary">Freelance</th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
@@ -293,18 +305,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Name</th>
|
<th class="w-33" scope="row">Name</th>
|
||||||
<td><a href="{% url 'filtered' _filter="company" search=company.slug %}">{{ company.name }}</a> {{ company.extended_name_pp }}</td>
|
<td>
|
||||||
|
<a href="{% url 'filtered' _filter="company" search=company.slug %}">{{ company.name }}</a> {{ company.extended_name_pp }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Country</th>
|
<th class="w-33" scope="row">Country</th>
|
||||||
<td>{{ company.country.name }} <img src="{{ company.country.flag }}" alt="{{ company.country }}">
|
<td>
|
||||||
|
<img src="{{ company.country.flag }}" alt="{{ company.country }}"> {{ company.country.name }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if company.freelance %}
|
|
||||||
<tr>
|
|
||||||
<th class="w-33" scope="row">Notes</th>
|
|
||||||
<td>A <em>freelance</em> company</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from ram.utils import git_suffix
|
from ram.utils import git_suffix
|
||||||
|
|
||||||
__version__ = "0.16.0"
|
__version__ = "0.16.1"
|
||||||
__version__ += git_suffix(__file__)
|
__version__ += git_suffix(__file__)
|
||||||
|
@@ -28,7 +28,12 @@ class Document(models.Model):
|
|||||||
upload_to="files/",
|
upload_to="files/",
|
||||||
storage=DeduplicatedStorage(),
|
storage=DeduplicatedStorage(),
|
||||||
)
|
)
|
||||||
private = models.BooleanField(default=False)
|
private = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Document will be visible only to logged users",
|
||||||
|
)
|
||||||
|
creation_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_time = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
@@ -2,7 +2,7 @@ import html
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import format_html, strip_tags
|
||||||
|
|
||||||
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ class RollingClassPropertyInline(admin.TabularInline):
|
|||||||
class RollingClass(admin.ModelAdmin):
|
class RollingClass(admin.ModelAdmin):
|
||||||
inlines = (RollingClassPropertyInline,)
|
inlines = (RollingClassPropertyInline,)
|
||||||
autocomplete_fields = ("manufacturer",)
|
autocomplete_fields = ("manufacturer",)
|
||||||
list_display = ("__str__", "type", "company")
|
list_display = ("__str__", "type", "company", "country_flag")
|
||||||
list_filter = ("company", "type__category", "type")
|
list_filter = ("company", "type__category", "type")
|
||||||
search_fields = (
|
search_fields = (
|
||||||
"identifier",
|
"identifier",
|
||||||
@@ -40,6 +40,12 @@ class RollingClass(admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
save_as = True
|
save_as = True
|
||||||
|
|
||||||
|
@admin.display(description="Country")
|
||||||
|
def country_flag(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" /> {}'.format(obj.country.flag, obj.country)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RollingStockDocInline(admin.TabularInline):
|
class RollingStockDocInline(admin.TabularInline):
|
||||||
model = RollingStockDocument
|
model = RollingStockDocument
|
||||||
@@ -87,6 +93,21 @@ class RollingStockDocumentAdmin(admin.ModelAdmin):
|
|||||||
"description",
|
"description",
|
||||||
"file",
|
"file",
|
||||||
)
|
)
|
||||||
|
autocomplete_fields = ("rolling_stock",)
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"private",
|
||||||
|
"rolling_stock",
|
||||||
|
"description",
|
||||||
|
"file",
|
||||||
|
"size",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(RollingStockJournal)
|
@admin.register(RollingStockJournal)
|
||||||
@@ -94,19 +115,32 @@ class RollingJournalDocumentAdmin(admin.ModelAdmin):
|
|||||||
list_display = (
|
list_display = (
|
||||||
"__str__",
|
"__str__",
|
||||||
"date",
|
"date",
|
||||||
"rolling_stock",
|
|
||||||
"private",
|
"private",
|
||||||
)
|
)
|
||||||
list_filter = (
|
list_filter = (
|
||||||
"date",
|
"date",
|
||||||
"private",
|
"private",
|
||||||
)
|
)
|
||||||
|
autocomplete_fields = ("rolling_stock",)
|
||||||
search_fields = (
|
search_fields = (
|
||||||
"rolling_stock__rolling_class__identifier",
|
"rolling_stock__rolling_class__identifier",
|
||||||
"rolling_stock__road_number",
|
"rolling_stock__road_number",
|
||||||
"rolling_stock__item_number",
|
"rolling_stock__item_number",
|
||||||
"log",
|
"log",
|
||||||
)
|
)
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"private",
|
||||||
|
"rolling_stock",
|
||||||
|
"log",
|
||||||
|
"date",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(RollingStock)
|
@admin.register(RollingStock)
|
||||||
@@ -126,7 +160,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"scale",
|
"scale",
|
||||||
"item_number",
|
"item_number",
|
||||||
"company",
|
"company",
|
||||||
"country",
|
"country_flag",
|
||||||
"published",
|
"published",
|
||||||
)
|
)
|
||||||
list_filter = (
|
list_filter = (
|
||||||
@@ -146,6 +180,12 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
save_as = True
|
save_as = True
|
||||||
|
|
||||||
|
@admin.display(description="Country")
|
||||||
|
def country_flag(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<img src="{}" /> {}'.format(obj.country.flag, obj.country)
|
||||||
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
@@ -187,12 +227,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Notes",
|
"Notes",
|
||||||
{
|
{"classes": ("collapse",), "fields": ("notes",)},
|
||||||
"classes": ("collapse",),
|
|
||||||
"fields": (
|
|
||||||
"notes",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Audit",
|
"Audit",
|
||||||
@@ -241,29 +276,31 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"{}:{}".format(property.property.name, property.value)
|
"{}:{}".format(property.property.name, property.value)
|
||||||
for property in obj.property.all()
|
for property in obj.property.all()
|
||||||
)
|
)
|
||||||
data.append([
|
data.append(
|
||||||
obj.__str__(),
|
[
|
||||||
obj.rolling_class.company.name,
|
obj.__str__(),
|
||||||
obj.rolling_class.identifier,
|
obj.rolling_class.company.name,
|
||||||
obj.road_number,
|
obj.rolling_class.identifier,
|
||||||
obj.manufacturer.name,
|
obj.road_number,
|
||||||
obj.scale.scale,
|
obj.manufacturer.name,
|
||||||
obj.item_number,
|
obj.scale.scale,
|
||||||
obj.set,
|
obj.item_number,
|
||||||
obj.era,
|
obj.set,
|
||||||
html.unescape(strip_tags(obj.description)),
|
obj.era,
|
||||||
obj.production_year,
|
html.unescape(strip_tags(obj.description)),
|
||||||
html.unescape(strip_tags(obj.notes)),
|
obj.production_year,
|
||||||
settings.CSV_SEPARATOR_ALT.join(
|
html.unescape(strip_tags(obj.notes)),
|
||||||
t.name for t in obj.tags.all()
|
settings.CSV_SEPARATOR_ALT.join(
|
||||||
),
|
t.name for t in obj.tags.all()
|
||||||
obj.decoder_interface,
|
),
|
||||||
obj.decoder,
|
obj.decoder_interface,
|
||||||
obj.address,
|
obj.decoder,
|
||||||
obj.purchase_date,
|
obj.address,
|
||||||
obj.price,
|
obj.purchase_date,
|
||||||
properties,
|
obj.price,
|
||||||
])
|
properties,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return generate_csv(header, data, "rolling_stock.csv")
|
return generate_csv(header, data, "rolling_stock.csv")
|
||||||
|
|
||||||
|
@@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2025-01-18 11:20
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("roster", "0031_alter_rollingstockdocument_unique_together_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="rollingstockdocument",
|
||||||
|
name="creation_time",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
auto_now_add=True, default=django.utils.timezone.now
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="rollingstockdocument",
|
||||||
|
name="updated_time",
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="rollingstockdocument",
|
||||||
|
name="private",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, help_text="Document will be visible only to logged users"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@@ -42,6 +42,10 @@ class RollingClass(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0} {1}".format(self.company, self.identifier)
|
return "{0} {1}".format(self.company, self.identifier)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def country(self):
|
||||||
|
return self.company.country
|
||||||
|
|
||||||
|
|
||||||
class RollingClassProperty(PropertyInstance):
|
class RollingClassProperty(PropertyInstance):
|
||||||
rolling_class = models.ForeignKey(
|
rolling_class = models.ForeignKey(
|
||||||
@@ -127,7 +131,7 @@ class RollingStock(BaseModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def country(self):
|
def country(self):
|
||||||
return str(self.rolling_class.company.country)
|
return self.rolling_class.company.country
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def company(self):
|
def company(self):
|
||||||
|
Reference in New Issue
Block a user