mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-07 14:47:49 +02:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
ffad964373
|
|||
538dc0bd80
|
|||
8bd2635c28
|
|||
feda1f6cb4
|
|||
2c851b2822
|
|||
e5ba2cfaec | |||
091f426242
|
|||
f603fd3e2d
|
|||
a3b2112e03
|
|||
055b0bab59
|
|||
3aea2ae340 | |||
242fe6814d
|
|||
90ffadb2ab
|
|||
21bf09687a
|
Submodule arduino/CommandStation-EX updated: c47dd101a5...aca9c9c941
Submodule arduino/WebThrottle-EX updated: ca33f6a1e3...c67e4080d0
Submodule arduino/arduino-cli updated: c1b10f562f...76251df924
Submodule arduino/dcc-ex.github.io updated: a4367c9df0...d1e5c92c7b
Submodule arduino/vim-arduino updated: bf371b9690...4651b71223
@@ -21,6 +21,7 @@ class ConsistAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||
list_display = ("identifier", "company", "era")
|
||||
list_filter = list_display
|
||||
search_fields = list_display
|
||||
save_as = True
|
||||
|
||||
fieldsets = (
|
||||
(
|
||||
|
24
ram/consist/migrations/0007_alter_consist_image.py
Normal file
24
ram/consist/migrations/0007_alter_consist_image.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 4.1.2 on 2022-11-27 00:10
|
||||
|
||||
from django.db import migrations, models
|
||||
import ram.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("consist", "0006_md_to_html"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="consist",
|
||||
name="image",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
storage=ram.utils.DeduplicatedStorage,
|
||||
upload_to="images/",
|
||||
),
|
||||
),
|
||||
]
|
@@ -4,6 +4,7 @@ from django.urls import reverse
|
||||
|
||||
from ckeditor_uploader.fields import RichTextUploadingField
|
||||
|
||||
from ram.utils import DeduplicatedStorage
|
||||
from metadata.models import Company, Tag
|
||||
from roster.models import RollingStock
|
||||
|
||||
@@ -17,7 +18,9 @@ class Consist(models.Model):
|
||||
)
|
||||
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
||||
era = models.CharField(max_length=32, blank=True)
|
||||
image = models.ImageField(upload_to="images/", null=True, blank=True)
|
||||
image = models.ImageField(
|
||||
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||
)
|
||||
notes = RichTextUploadingField(blank=True)
|
||||
creation_time = models.DateTimeField(auto_now_add=True)
|
||||
updated_time = models.DateTimeField(auto_now=True)
|
||||
|
@@ -20,8 +20,8 @@ class PropertyAdmin(admin.ModelAdmin):
|
||||
@admin.register(Decoder)
|
||||
class DecoderAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ("image_thumbnail",)
|
||||
list_display = ("__str__", "interface")
|
||||
list_filter = ("manufacturer", "interface")
|
||||
list_display = ("__str__", "sound")
|
||||
list_filter = ("manufacturer", "sound")
|
||||
search_fields = ("name", "manufacturer__name")
|
||||
|
||||
|
||||
|
18
ram/metadata/migrations/0008_remove_decoder_interface.py
Normal file
18
ram/metadata/migrations/0008_remove_decoder_interface.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.1.2 on 2022-10-31 23:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("metadata", "0007_rename_track_scale_tracks"),
|
||||
("roster", "0013_rollingstock_decoder_interface"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="decoder",
|
||||
name="interface",
|
||||
),
|
||||
]
|
@@ -0,0 +1,44 @@
|
||||
# Generated by Django 4.1.2 on 2022-11-27 00:10
|
||||
|
||||
from django.db import migrations, models
|
||||
import ram.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("metadata", "0008_remove_decoder_interface"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="company",
|
||||
name="logo",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
storage=ram.utils.DeduplicatedStorage,
|
||||
upload_to="images/",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="decoder",
|
||||
name="image",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
storage=ram.utils.DeduplicatedStorage,
|
||||
upload_to="images/",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="manufacturer",
|
||||
name="logo",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
storage=ram.utils.DeduplicatedStorage,
|
||||
upload_to="images/",
|
||||
),
|
||||
),
|
||||
]
|
@@ -3,7 +3,7 @@ from django.conf import settings
|
||||
from django.dispatch.dispatcher import receiver
|
||||
from django_countries.fields import CountryField
|
||||
|
||||
from ram.utils import get_image_preview, slugify
|
||||
from ram.utils import DeduplicatedStorage, get_image_preview, slugify
|
||||
|
||||
|
||||
class Property(models.Model):
|
||||
@@ -24,7 +24,9 @@ class Manufacturer(models.Model):
|
||||
max_length=64, choices=settings.MANUFACTURER_TYPES
|
||||
)
|
||||
website = models.URLField(blank=True)
|
||||
logo = models.ImageField(upload_to="images/", null=True, blank=True)
|
||||
logo = models.ImageField(
|
||||
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ["category", "name"]
|
||||
@@ -43,7 +45,9 @@ class Company(models.Model):
|
||||
extended_name = models.CharField(max_length=128, blank=True)
|
||||
country = CountryField()
|
||||
freelance = models.BooleanField(default=False)
|
||||
logo = models.ImageField(upload_to="images/", null=True, blank=True)
|
||||
logo = models.ImageField(
|
||||
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Companies"
|
||||
@@ -66,11 +70,10 @@ class Decoder(models.Model):
|
||||
limit_choices_to={"category": "model"},
|
||||
)
|
||||
version = models.CharField(max_length=64, blank=True)
|
||||
interface = models.PositiveSmallIntegerField(
|
||||
choices=settings.DECODER_INTERFACES, null=True, blank=True
|
||||
)
|
||||
sound = models.BooleanField(default=False)
|
||||
image = models.ImageField(upload_to="images/", null=True, blank=True)
|
||||
image = models.ImageField(
|
||||
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "{0} - {1}".format(self.manufacturer, self.name)
|
||||
|
@@ -3,7 +3,34 @@ from solo.admin import SingletonModelAdmin
|
||||
|
||||
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)
|
||||
|
18
ram/portal/migrations/0014_siteconfiguration_extra_head.py
Normal file
18
ram/portal/migrations/0014_siteconfiguration_extra_head.py
Normal 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),
|
||||
),
|
||||
]
|
@@ -35,6 +35,7 @@ class SiteConfiguration(SingletonModel):
|
||||
footer = RichTextField(blank=True)
|
||||
footer_extended = RichTextField(blank=True)
|
||||
show_version = models.BooleanField(default=True)
|
||||
extra_head = models.TextField(blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Site Configuration"
|
||||
|
@@ -23,6 +23,15 @@ a.badge, a.badge:hover {
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
#nav-journal ul, #nav-journal ol {
|
||||
margin: 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
#nav-journal p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#footer > p {
|
||||
display: inline;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<meta name="description" content="{{ site_conf.about}}">
|
||||
<meta name="author" content="{{ site_conf.site_author }}">
|
||||
<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="{% static "css/main.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-dark-inline { display: inline !important; }
|
||||
</style>
|
||||
{% block extra_head %}
|
||||
{{ site_conf.extra_head | safe }}
|
||||
{% endblock %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
@@ -81,7 +85,9 @@
|
||||
<section class="py-4 text-center container">
|
||||
<div class="row">
|
||||
<div class="mx-auto">
|
||||
{% block header %}{% endblock %}
|
||||
<h1 class="fw-light">{{ title }}</h1>
|
||||
{% block header %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@@ -1,8 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">Companies</h1>
|
||||
{% endblock %}
|
||||
{% block cards %}
|
||||
{% for c in company %}
|
||||
<div class="col">
|
||||
@@ -59,7 +56,7 @@
|
||||
<ul class="pagination justify-content-center mt-4 mb-0">
|
||||
{% if company.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{% url 'company_pagination' page=company.previous_page_number %}#rolling-stock" tabindex="-1">Previous</a>
|
||||
<a class="page-link" href="{% url 'companies_pagination' page=company.previous_page_number %}#rolling-stock" tabindex="-1">Previous</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
@@ -75,13 +72,13 @@
|
||||
{% if i == company.paginator.ELLIPSIS %}
|
||||
<li class="page-item"><span class="page-link">{{ i }}</span></li>
|
||||
{% else %}
|
||||
<li class="page-item"><a class="page-link" href="{% url 'company_pagination' page=i %}#rolling-stock">{{ i }}</a></li>
|
||||
<li class="page-item"><a class="page-link" href="{% url 'companies_pagination' page=i %}#rolling-stock">{{ i }}</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if company.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{% url 'company_pagination' page=company.next_page_number %}#rolling-stock" tabindex="-1">Next</a>
|
||||
<a class="page-link" href="{% url 'companies_pagination' page=company.next_page_number %}#rolling-stock" tabindex="-1">Next</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
|
@@ -1,7 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">{{ consist }}</h1>
|
||||
{% if consist.tags.all %}
|
||||
<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">
|
||||
|
@@ -1,8 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">Consists</h1>
|
||||
{% endblock %}
|
||||
{% block cards %}
|
||||
{% for c in consist %}
|
||||
<div class="col">
|
||||
|
@@ -1,7 +1,6 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">{{ flatpage.name }}</h1>
|
||||
<small class="text-muted">Updated {{ flatpage.updated_time | date:"M d, Y H:i" }}</small>
|
||||
{% endblock %}
|
||||
{% block extra_content %}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
{% if site_conf.about %}<h1 class="fw-light">About</h1>{% endif %}
|
||||
<p class="lead text-muted">{{ site_conf.about | safe }}</p>
|
||||
{% endblock %}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">{{ rolling_stock }}</h1>
|
||||
{% if rolling_stock.tags.all %}
|
||||
<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">
|
||||
@@ -102,7 +101,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% if rolling_stock.decoder %}
|
||||
{% if rolling_stock.decoder or rolling_stock.decoder_interface %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -111,13 +110,19 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="35%" scope="row">Decoder</th>
|
||||
<th width="35%" scope="row">Interface</th>
|
||||
<td>{{ rolling_stock.get_decoder_interface_display }}</td>
|
||||
</tr>
|
||||
{% if rolling_stock.decoder %}
|
||||
<tr>
|
||||
<th scope="row">Decoder</th>
|
||||
<td>{{ rolling_stock.decoder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Address</th>
|
||||
<td>{{ rolling_stock.address }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
@@ -230,6 +235,10 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Interface</th>
|
||||
<td>{{ rolling_stock.get_decoder_interface_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th width="35%" scope="row">Address</th>
|
||||
<td>{{ rolling_stock.address }}</td>
|
||||
@@ -246,10 +255,6 @@
|
||||
<th scope="row">Version</th>
|
||||
<td>{{ rolling_stock.decoder.version }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Interface</th>
|
||||
<td>{{ rolling_stock.decoder.get_interface_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Sound</th>
|
||||
<td>{{ rolling_stock.decoder.sound | yesno:"Yes,No" }}</td>
|
||||
|
@@ -1,8 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">Scales</h1>
|
||||
{% endblock %}
|
||||
{% block cards %}
|
||||
{% for s in scale %}
|
||||
<div class="col">
|
||||
|
@@ -1,7 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
<h1 class="fw-light">{{ filter | default_if_none:"Search" | title }}: {{ search }}</h1>
|
||||
<p class="lead text-muted">Results found: {{ matches }}</p>
|
||||
{% endblock %}
|
||||
{% block pagination %}
|
||||
|
@@ -46,7 +46,11 @@ class GetHome(View):
|
||||
return render(
|
||||
request,
|
||||
"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)
|
||||
else:
|
||||
raise Http404
|
||||
rolling_stock = RollingStock.objects.filter(query).order_by(
|
||||
*order_by_fields()
|
||||
rolling_stock = (
|
||||
RollingStock.objects.filter(query)
|
||||
.distinct()
|
||||
.order_by(*order_by_fields())
|
||||
)
|
||||
matches = len(rolling_stock)
|
||||
|
||||
@@ -105,6 +111,7 @@ class GetHomeFiltered(View):
|
||||
request,
|
||||
"search.html",
|
||||
{
|
||||
"title": "{0}: {1}".format(_filter.capitalize(), search),
|
||||
"search": search,
|
||||
"filter": _filter,
|
||||
"matches": matches,
|
||||
@@ -125,6 +132,7 @@ class GetHomeFiltered(View):
|
||||
request,
|
||||
"search.html",
|
||||
{
|
||||
"title": "{0}: {1}".format(_filter.capitalize(), search),
|
||||
"search": search,
|
||||
"filter": _filter,
|
||||
"matches": matches,
|
||||
@@ -164,6 +172,7 @@ class GetRollingStock(View):
|
||||
request,
|
||||
"page.html",
|
||||
{
|
||||
"title": rolling_stock,
|
||||
"rolling_stock": rolling_stock,
|
||||
"class_properties": class_properties,
|
||||
"rolling_stock_properties": rolling_stock_properties,
|
||||
@@ -186,7 +195,11 @@ class Consists(View):
|
||||
return render(
|
||||
request,
|
||||
"consists.html",
|
||||
{"consist": consist, "page_range": page_range},
|
||||
{
|
||||
"title": "Consists",
|
||||
"consist": consist,
|
||||
"page_range": page_range,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -209,6 +222,7 @@ class GetConsist(View):
|
||||
request,
|
||||
"consist.html",
|
||||
{
|
||||
"title": consist,
|
||||
"consist": consist,
|
||||
"rolling_stock": rolling_stock,
|
||||
"page_range": page_range,
|
||||
@@ -230,7 +244,11 @@ class Companies(View):
|
||||
return render(
|
||||
request,
|
||||
"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(
|
||||
request,
|
||||
"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(
|
||||
request,
|
||||
"flatpage.html",
|
||||
{"flatpage": flatpage},
|
||||
{"title": flatpage.name, "flatpage": flatpage},
|
||||
)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from ram.utils import git_suffix
|
||||
|
||||
__version__ = "0.0.18"
|
||||
__version__ = "0.0.23"
|
||||
__version__ += git_suffix(__file__)
|
||||
|
@@ -1,8 +1,29 @@
|
||||
import os
|
||||
import hashlib
|
||||
import subprocess
|
||||
|
||||
from django.utils.html import format_html
|
||||
from django.utils.text import slugify as django_slugify
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
|
||||
|
||||
class DeduplicatedStorage(FileSystemStorage):
|
||||
"""
|
||||
A derived FileSystemStorage class that compares already existing files
|
||||
(with the same name) with new uploaded ones and stores new file only if
|
||||
sha256 hash on is content is different
|
||||
"""
|
||||
|
||||
def save(self, name, content, max_length=None):
|
||||
if super().exists(name):
|
||||
new = hashlib.sha256(content.file.getbuffer()).hexdigest()
|
||||
with open(super().path(name), "rb") as file:
|
||||
file_binary = file.read()
|
||||
old = hashlib.sha256(file_binary).hexdigest()
|
||||
if old == new:
|
||||
return name
|
||||
|
||||
return super().save(name, content, max_length)
|
||||
|
||||
|
||||
def git_suffix(fname):
|
||||
|
@@ -125,6 +125,7 @@ class RollingStockAdmin(admin.ModelAdmin):
|
||||
"address",
|
||||
"sku",
|
||||
)
|
||||
save_as = True
|
||||
|
||||
fieldsets = (
|
||||
(
|
||||
@@ -148,6 +149,7 @@ class RollingStockAdmin(admin.ModelAdmin):
|
||||
"DCC",
|
||||
{
|
||||
"fields": (
|
||||
"decoder_interface",
|
||||
"decoder",
|
||||
"address",
|
||||
)
|
||||
|
42
ram/roster/migrations/0013_rollingstock_decoder_interface.py
Normal file
42
ram/roster/migrations/0013_rollingstock_decoder_interface.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 4.1 on 2022-10-31 22:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def meta_to_roster(apps, schema_editor):
|
||||
model = apps.get_model("roster", "RollingStock")
|
||||
|
||||
for row in model.objects.all():
|
||||
if row.decoder:
|
||||
decoder_interface = row.decoder.interface
|
||||
row.__dict__["decoder_interface"] = decoder_interface
|
||||
row.save(update_fields=["decoder_interface"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("roster", "0012_rollingstockjournal"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="rollingstock",
|
||||
name="decoder_interface",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(1, "NEM651"),
|
||||
(2, "NEM652"),
|
||||
(3, "PluX"),
|
||||
(4, "21MTC"),
|
||||
(5, "Next18/Next18S"),
|
||||
],
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
meta_to_roster,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
]
|
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 4.1.2 on 2022-11-27 00:10
|
||||
|
||||
from django.db import migrations, models
|
||||
import ram.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("roster", "0013_rollingstock_decoder_interface"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="rollingstockdocument",
|
||||
name="file",
|
||||
field=models.FileField(
|
||||
blank=True,
|
||||
null=True,
|
||||
storage=ram.utils.DeduplicatedStorage(),
|
||||
upload_to="files/",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="rollingstockimage",
|
||||
name="image",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
storage=ram.utils.DeduplicatedStorage,
|
||||
upload_to="images/",
|
||||
),
|
||||
),
|
||||
]
|
@@ -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"]},
|
||||
),
|
||||
]
|
@@ -3,12 +3,13 @@ import re
|
||||
from uuid import uuid4
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.dispatch import receiver
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ckeditor_uploader.fields import RichTextUploadingField
|
||||
|
||||
from ram.utils import get_image_preview
|
||||
from ram.utils import DeduplicatedStorage, get_image_preview
|
||||
from metadata.models import (
|
||||
Property,
|
||||
Scale,
|
||||
@@ -19,11 +20,6 @@ from metadata.models import (
|
||||
RollingStockType,
|
||||
)
|
||||
|
||||
# class OverwriteMixin(FileSystemStorage):
|
||||
# def get_available_name(self, name, max_length):
|
||||
# self.delete(name)
|
||||
# return name
|
||||
|
||||
|
||||
class RollingClass(models.Model):
|
||||
identifier = models.CharField(max_length=128, unique=False)
|
||||
@@ -87,6 +83,9 @@ class RollingStock(models.Model):
|
||||
)
|
||||
scale = models.ForeignKey(Scale, on_delete=models.CASCADE)
|
||||
sku = models.CharField(max_length=32, blank=True)
|
||||
decoder_interface = models.PositiveSmallIntegerField(
|
||||
choices=settings.DECODER_INTERFACES, null=True, blank=True
|
||||
)
|
||||
decoder = models.ForeignKey(
|
||||
Decoder, on_delete=models.CASCADE, null=True, blank=True
|
||||
)
|
||||
@@ -133,7 +132,12 @@ class RollingStockDocument(models.Model):
|
||||
RollingStock, on_delete=models.CASCADE, related_name="document"
|
||||
)
|
||||
description = models.CharField(max_length=128, blank=True)
|
||||
file = models.FileField(upload_to="files/", null=True, blank=True)
|
||||
file = models.FileField(
|
||||
upload_to="files/",
|
||||
storage=DeduplicatedStorage(),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
unique_together = ("rolling_stock", "file")
|
||||
@@ -154,7 +158,9 @@ class RollingStockImage(models.Model):
|
||||
rolling_stock = models.ForeignKey(
|
||||
RollingStock, on_delete=models.CASCADE, related_name="image"
|
||||
)
|
||||
image = models.ImageField(upload_to="images/", null=True, blank=True)
|
||||
image = models.ImageField(
|
||||
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||
)
|
||||
is_thumbnail = models.BooleanField()
|
||||
|
||||
def image_thumbnail(self):
|
||||
@@ -172,6 +178,9 @@ class RollingStockImage(models.Model):
|
||||
).update(is_thumbnail=False)
|
||||
super().save(**kwargs)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-is_thumbnail"]
|
||||
|
||||
|
||||
class RollingStockProperty(models.Model):
|
||||
rolling_stock = models.ForeignKey(
|
||||
|
Reference in New Issue
Block a user