6 Commits

27 changed files with 171 additions and 44 deletions

View File

@@ -21,7 +21,7 @@ it has been developed with a commitment of few minutes a day;
it lacks any kind of documentation, code review, architectural review, it lacks any kind of documentation, code review, architectural review,
security assesment, pentest, ISO certification, etc. security assesment, pentest, ISO certification, etc.
This project probably doesn't match you needs nor expectations. Be aware. This project probably doesn't match your needs nor expectations. Be aware.
Your model train may also catch fire while using this software. Your model train may also catch fire while using this software.

View File

@@ -0,0 +1,17 @@
# Generated by Django 4.1.3 on 2023-01-02 15:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("consist", "0007_alter_consist_image"),
]
operations = [
migrations.AlterModelOptions(
name="consist",
options={"ordering": ["company", "-creation_time"]},
),
]

View File

@@ -32,7 +32,7 @@ class Consist(models.Model):
return reverse("consist", kwargs={"uuid": self.uuid}) return reverse("consist", kwargs={"uuid": self.uuid})
class Meta: class Meta:
ordering = ["creation_time"] ordering = ["company", "-creation_time"]
class ConsistItem(models.Model): class ConsistItem(models.Model):

View File

@@ -3,7 +3,34 @@ from solo.admin import SingletonModelAdmin
from portal.models import SiteConfiguration, Flatpage from portal.models import SiteConfiguration, Flatpage
admin.site.register(SiteConfiguration, SingletonModelAdmin) @admin.register(SiteConfiguration)
class SiteConfigurationAdmin(SingletonModelAdmin):
fieldsets = (
(
None,
{
"fields": (
"site_name",
"site_author",
"about",
"items_per_page",
"items_ordering",
"footer",
"footer_extended",
)
},
),
(
"Advanced",
{
"classes": ("collapse",),
"fields": (
"show_version",
"extra_head",
),
},
),
)
@admin.register(Flatpage) @admin.register(Flatpage)

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1.3 on 2022-12-28 22:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("portal", "0013_remove_flatpage_draft_flatpage_published"),
]
operations = [
migrations.AddField(
model_name="siteconfiguration",
name="extra_head",
field=models.TextField(blank=True),
),
]

View File

@@ -35,6 +35,7 @@ class SiteConfiguration(SingletonModel):
footer = RichTextField(blank=True) footer = RichTextField(blank=True)
footer_extended = RichTextField(blank=True) footer_extended = RichTextField(blank=True)
show_version = models.BooleanField(default=True) show_version = models.BooleanField(default=True)
extra_head = models.TextField(blank=True)
class Meta: class Meta:
verbose_name = "Site Configuration" verbose_name = "Site Configuration"

View File

@@ -12,7 +12,7 @@
<meta name="description" content="{{ site_conf.about}}"> <meta name="description" content="{{ site_conf.about}}">
<meta name="author" content="{{ site_conf.site_author }}"> <meta name="author" content="{{ site_conf.site_author }}">
<meta name="generator" content="Django Framework"> <meta name="generator" content="Django Framework">
<title>{{ site_conf.site_name }}</title> <title>{% block title %}{{ title }}{% endblock %} - {{ site_conf.site_name }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap-dark-5@1.1.3/dist/css/bootstrap-nightshade.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap-dark-5@1.1.3/dist/css/bootstrap-nightshade.min.css" rel="stylesheet">
<link href="{% static "css/main.css" %}" rel="stylesheet"> <link href="{% static "css/main.css" %}" rel="stylesheet">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
@@ -34,6 +34,10 @@
html.dark .d-light-inline { display: none !important; } html.dark .d-light-inline { display: none !important; }
html.dark .d-dark-inline { display: inline !important; } html.dark .d-dark-inline { display: inline !important; }
</style> </style>
{% block extra_head %}
{{ site_conf.extra_head | safe }}
{% endblock %}
</head> </head>
<body> <body>
<header> <header>
@@ -81,7 +85,9 @@
<section class="py-4 text-center container"> <section class="py-4 text-center container">
<div class="row"> <div class="row">
<div class="mx-auto"> <div class="mx-auto">
{% block header %}{% endblock %} <h1 class="fw-light">{{ title }}</h1>
{% block header %}
{% endblock %}
</div> </div>
</div> </div>
</section> </section>
@@ -94,7 +100,7 @@
<div class="col"> <div class="col">
<div class="card shadow-sm"> <div class="card shadow-sm">
{% for i in r.image.all %} {% for i in r.image.all %}
{% if i.is_thumbnail %}<a href="{{r.get_absolute_url}}"><img src="{{ i.image.url }}" alt="Card image cap"></a>{% endif %} {% if forloop.first %}<a href="{{r.get_absolute_url}}"><img src="{{ i.image.url }}" alt="Card image cap"></a>{% endif %}
{% endfor %} {% endfor %}
<div class="card-body"> <div class="card-body">
<p class="card-text" style="position: relative;"> <p class="card-text" style="position: relative;">

View File

@@ -1,8 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block header %}
<h1 class="fw-light">Companies</h1>
{% endblock %}
{% block cards %} {% block cards %}
{% for c in company %} {% for c in company %}
<div class="col"> <div class="col">

View File

@@ -1,7 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block header %} {% block header %}
<h1 class="fw-light">{{ consist }}</h1>
{% if consist.tags.all %} {% if consist.tags.all %}
<p><small>Tags:</small> <p><small>Tags:</small>
{% for t in consist.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary"> {% for t in consist.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
@@ -16,7 +15,7 @@
<div class="col"> <div class="col">
<div class="card shadow-sm"> <div class="card shadow-sm">
{% for i in r.rolling_stock.image.all %} {% for i in r.rolling_stock.image.all %}
{% if i.is_thumbnail %}<a href="{{r.rolling_stock.get_absolute_url}}"><img src="{{ i.image.url }}" alt="Card image cap"></a>{% endif %} {% if forloop.first %}<a href="{{r.rolling_stock.get_absolute_url}}"><img src="{{ i.image.url }}" alt="Card image cap"></a>{% endif %}
{% endfor %} {% endfor %}
<div class="card-body"> <div class="card-body">
<p class="card-text" style="position: relative;"> <p class="card-text" style="position: relative;">

View File

@@ -1,8 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block header %}
<h1 class="fw-light">Consists</h1>
{% endblock %}
{% block cards %} {% block cards %}
{% for c in consist %} {% for c in consist %}
<div class="col"> <div class="col">
@@ -13,7 +10,7 @@
{% else %} {% else %}
{% with c.consist_item.first.rolling_stock as r %} {% with c.consist_item.first.rolling_stock as r %}
{% for i in r.image.all %} {% for i in r.image.all %}
{% if i.is_thumbnail %}<img src="{{ i.image.url }}" alt="Card image cap">{% endif %} {% if forloop.first %}<img src="{{ i.image.url }}" alt="Card image cap">{% endif %}
{% endfor %} {% endfor %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}

View File

@@ -1,7 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block header %} {% block header %}
<h1 class="fw-light">{{ flatpage.name }}</h1>
<small class="text-muted">Updated {{ flatpage.updated_time | date:"M d, Y H:i" }}</small> <small class="text-muted">Updated {{ flatpage.updated_time | date:"M d, Y H:i" }}</small>
{% endblock %} {% endblock %}
{% block extra_content %} {% block extra_content %}

View File

@@ -1,7 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block header %} {% block header %}
{% if site_conf.about %}<h1 class="fw-light">About</h1>{% endif %}
<p class="lead text-muted">{{ site_conf.about | safe }}</p> <p class="lead text-muted">{{ site_conf.about | safe }}</p>
{% endblock %} {% endblock %}

View File

@@ -1,7 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block header %} {% block header %}
<h1 class="fw-light">{{ rolling_stock }}</h1>
{% if rolling_stock.tags.all %} {% if rolling_stock.tags.all %}
<p><small>Tags:</small> <p><small>Tags:</small>
{% for t in rolling_stock.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary"> {% for t in rolling_stock.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">

View File

@@ -1,8 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block header %}
<h1 class="fw-light">Scales</h1>
{% endblock %}
{% block cards %} {% block cards %}
{% for s in scale %} {% for s in scale %}
<div class="col"> <div class="col">

View File

@@ -1,7 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block header %} {% block header %}
<h1 class="fw-light">{{ filter | default_if_none:"Search" | title }}: {{ search }}</h1>
<p class="lead text-muted">Results found: {{ matches }}</p> <p class="lead text-muted">Results found: {{ matches }}</p>
{% endblock %} {% endblock %}
{% block pagination %} {% block pagination %}

View File

@@ -46,7 +46,11 @@ class GetHome(View):
return render( return render(
request, request,
"home.html", "home.html",
{"rolling_stock": rolling_stock, "page_range": page_range}, {
"title": "Home",
"rolling_stock": rolling_stock,
"page_range": page_range,
},
) )
@@ -83,8 +87,10 @@ class GetHomeFiltered(View):
query = Q(tags__slug__iexact=search) query = Q(tags__slug__iexact=search)
else: else:
raise Http404 raise Http404
rolling_stock = RollingStock.objects.filter(query).distinct().order_by( rolling_stock = (
*order_by_fields() RollingStock.objects.filter(query)
.distinct()
.order_by(*order_by_fields())
) )
matches = len(rolling_stock) matches = len(rolling_stock)
@@ -105,6 +111,7 @@ class GetHomeFiltered(View):
request, request,
"search.html", "search.html",
{ {
"title": "{0}: {1}".format(_filter.capitalize(), search),
"search": search, "search": search,
"filter": _filter, "filter": _filter,
"matches": matches, "matches": matches,
@@ -125,6 +132,7 @@ class GetHomeFiltered(View):
request, request,
"search.html", "search.html",
{ {
"title": "{0}: {1}".format(_filter.capitalize(), search),
"search": search, "search": search,
"filter": _filter, "filter": _filter,
"matches": matches, "matches": matches,
@@ -164,6 +172,7 @@ class GetRollingStock(View):
request, request,
"page.html", "page.html",
{ {
"title": rolling_stock,
"rolling_stock": rolling_stock, "rolling_stock": rolling_stock,
"class_properties": class_properties, "class_properties": class_properties,
"rolling_stock_properties": rolling_stock_properties, "rolling_stock_properties": rolling_stock_properties,
@@ -186,7 +195,11 @@ class Consists(View):
return render( return render(
request, request,
"consists.html", "consists.html",
{"consist": consist, "page_range": page_range}, {
"title": "Consists",
"consist": consist,
"page_range": page_range,
},
) )
@@ -209,6 +222,7 @@ class GetConsist(View):
request, request,
"consist.html", "consist.html",
{ {
"title": consist,
"consist": consist, "consist": consist,
"rolling_stock": rolling_stock, "rolling_stock": rolling_stock,
"page_range": page_range, "page_range": page_range,
@@ -230,7 +244,11 @@ class Companies(View):
return render( return render(
request, request,
"companies.html", "companies.html",
{"company": company, "page_range": page_range}, {
"title": "Companies",
"company": company,
"page_range": page_range,
},
) )
@@ -248,7 +266,7 @@ class Scales(View):
return render( return render(
request, request,
"scales.html", "scales.html",
{"scale": scale, "page_range": page_range}, {"title": "Scales", "scale": scale, "page_range": page_range},
) )
@@ -264,5 +282,5 @@ class GetFlatpage(View):
return render( return render(
request, request,
"flatpage.html", "flatpage.html",
{"flatpage": flatpage}, {"title": flatpage.name, "flatpage": flatpage},
) )

View File

@@ -1,4 +1,4 @@
from ram.utils import git_suffix from ram.utils import git_suffix
__version__ = "0.0.21" __version__ = "0.0.24"
__version__ += git_suffix(__file__) __version__ += git_suffix(__file__)

View File

@@ -1,4 +1,6 @@
from django.contrib import admin from django.contrib import admin
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
from roster.models import ( from roster.models import (
RollingClass, RollingClass,
RollingClassProperty, RollingClassProperty,
@@ -35,7 +37,7 @@ class RollingStockDocInline(admin.TabularInline):
classes = ["collapse"] classes = ["collapse"]
class RollingStockImageInline(admin.TabularInline): class RollingStockImageInline(SortableInlineAdminMixin, admin.TabularInline):
model = RollingStockImage model = RollingStockImage
min_num = 0 min_num = 0
extra = 0 extra = 0
@@ -93,7 +95,7 @@ class RollingJournalDocumentAdmin(admin.ModelAdmin):
@admin.register(RollingStock) @admin.register(RollingStock)
class RollingStockAdmin(admin.ModelAdmin): class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
inlines = ( inlines = (
RollingStockPropertyInline, RollingStockPropertyInline,
RollingStockImageInline, RollingStockImageInline,

View File

@@ -0,0 +1,17 @@
# Generated by Django 4.1.3 on 2022-12-28 21:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("roster", "0014_alter_rollingstockdocument_file_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="rollingstockimage",
options={"ordering": ["-is_thumbnail"]},
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 4.1.3 on 2023-01-02 12:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("roster", "0015_alter_rollingstockimage_options"),
]
operations = [
migrations.AlterModelOptions(
name="rollingstockimage",
options={"ordering": ["order"]},
),
migrations.AddField(
model_name="rollingstockimage",
name="order",
field=models.PositiveIntegerField(default=0),
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 4.1.3 on 2023-01-02 15:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("roster", "0016_alter_rollingstockimage_options_and_more"),
]
operations = [
migrations.RemoveField(
model_name="rollingstockimage",
name="is_thumbnail",
),
]

View File

@@ -155,13 +155,13 @@ class RollingStockDocument(models.Model):
class RollingStockImage(models.Model): class RollingStockImage(models.Model):
order = models.PositiveIntegerField(default=0, blank=False, null=False)
rolling_stock = models.ForeignKey( rolling_stock = models.ForeignKey(
RollingStock, on_delete=models.CASCADE, related_name="image" RollingStock, on_delete=models.CASCADE, related_name="image"
) )
image = models.ImageField( image = models.ImageField(
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
) )
is_thumbnail = models.BooleanField()
def image_thumbnail(self): def image_thumbnail(self):
return get_image_preview(self.image.url) return get_image_preview(self.image.url)
@@ -171,12 +171,8 @@ class RollingStockImage(models.Model):
def __str__(self): def __str__(self):
return "{0}".format(os.path.basename(self.image.name)) return "{0}".format(os.path.basename(self.image.name))
def save(self, **kwargs): class Meta:
if self.is_thumbnail: ordering = ["order"]
RollingStockImage.objects.filter(
rolling_stock=self.rolling_stock
).update(is_thumbnail=False)
super().save(**kwargs)
class RollingStockProperty(models.Model): class RollingStockProperty(models.Model):