mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-07 22:57:50 +02:00
Compare commits
2 Commits
v0.12.2
...
fix-warnin
Author | SHA1 | Date | |
---|---|---|---|
9cb3fb1d8a
|
|||
ed8ffb5ece
|
2
.github/workflows/django.yml
vendored
2
.github/workflows/django.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
max-parallel: 2
|
max-parallel: 2
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.10', '3.11', '3.12']
|
python-version: ['3.9', '3.10', '3.11', '3.12']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ __pycache__/
|
|||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
|
dist/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
|
@@ -49,7 +49,7 @@ It has been developed with:
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Python 3.10+
|
- Python 3.9+
|
||||||
- A USB port when running Arduino hardware (and adaptors if you have a Mac)
|
- A USB port when running Arduino hardware (and adaptors if you have a Mac)
|
||||||
|
|
||||||
## Web portal installation
|
## Web portal installation
|
||||||
|
Submodule arduino/CommandStation-EX updated: 87073b0d36...2db2b0ecc6
@@ -1,7 +1,6 @@
|
|||||||
# Generated by Django 4.2.5 on 2023-10-01 20:16
|
# Generated by Django 4.2.5 on 2023-10-01 20:16
|
||||||
|
|
||||||
# ckeditor removal
|
import ckeditor_uploader.fields
|
||||||
# import ckeditor_uploader.fields
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import uuid
|
import uuid
|
||||||
@@ -48,8 +47,7 @@ class Migration(migrations.Migration):
|
|||||||
("ISBN", models.CharField(max_length=13, unique=True)),
|
("ISBN", models.CharField(max_length=13, unique=True)),
|
||||||
("publication_year", models.SmallIntegerField(blank=True, null=True)),
|
("publication_year", models.SmallIntegerField(blank=True, null=True)),
|
||||||
("purchase_date", models.DateField(blank=True, null=True)),
|
("purchase_date", models.DateField(blank=True, null=True)),
|
||||||
# ("notes", ckeditor_uploader.fields.RichTextUploadingField(blank=True)),
|
("notes", ckeditor_uploader.fields.RichTextUploadingField(blank=True)),
|
||||||
("notes", models.TextField(blank=True)),
|
|
||||||
("creation_time", models.DateTimeField(auto_now_add=True)),
|
("creation_time", models.DateTimeField(auto_now_add=True)),
|
||||||
("updated_time", models.DateTimeField(auto_now=True)),
|
("updated_time", models.DateTimeField(auto_now=True)),
|
||||||
("authors", models.ManyToManyField(to="bookshelf.author")),
|
("authors", models.ManyToManyField(to="bookshelf.author")),
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.2 on 2024-03-02 14:31
|
|
||||||
|
|
||||||
import tinymce.models
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("bookshelf", "0012_alter_book_notes"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="book",
|
|
||||||
name="description",
|
|
||||||
field=tinymce.models.HTMLField(blank=True),
|
|
||||||
),
|
|
||||||
]
|
|
@@ -52,7 +52,6 @@ class Book(models.Model):
|
|||||||
)
|
)
|
||||||
number_of_pages = models.SmallIntegerField(null=True, blank=True)
|
number_of_pages = models.SmallIntegerField(null=True, blank=True)
|
||||||
publication_year = models.SmallIntegerField(null=True, blank=True)
|
publication_year = models.SmallIntegerField(null=True, blank=True)
|
||||||
description = tinymce.HTMLField(blank=True)
|
|
||||||
purchase_date = models.DateField(null=True, blank=True)
|
purchase_date = models.DateField(null=True, blank=True)
|
||||||
tags = models.ManyToManyField(
|
tags = models.ManyToManyField(
|
||||||
Tag, related_name="bookshelf", blank=True
|
Tag, related_name="bookshelf", blank=True
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# Generated by Django 4.1 on 2022-08-23 15:54
|
# Generated by Django 4.1 on 2022-08-23 15:54
|
||||||
|
|
||||||
# ckeditor removal
|
import ckeditor_uploader.fields
|
||||||
# import ckeditor_uploader.fields
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
@@ -12,9 +11,9 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
# migrations.AlterField(
|
migrations.AlterField(
|
||||||
# model_name="consist",
|
model_name="consist",
|
||||||
# name="notes",
|
name="notes",
|
||||||
# field=ckeditor_uploader.fields.RichTextUploadingField(blank=True),
|
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True),
|
||||||
# ),
|
),
|
||||||
]
|
]
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
# Generated by Django 5.0.4 on 2024-04-20 12:49
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("consist", "0010_alter_consist_notes"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="consist",
|
|
||||||
name="consist_address",
|
|
||||||
field=models.SmallIntegerField(
|
|
||||||
blank=True,
|
|
||||||
default=None,
|
|
||||||
help_text="DCC consist address if enabled",
|
|
||||||
null=True,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="consist",
|
|
||||||
name="era",
|
|
||||||
field=models.CharField(
|
|
||||||
blank=True, help_text="Era or epoch of the consist", max_length=32
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
@@ -16,17 +16,10 @@ class Consist(models.Model):
|
|||||||
identifier = models.CharField(max_length=128, unique=False)
|
identifier = models.CharField(max_length=128, unique=False)
|
||||||
tags = models.ManyToManyField(Tag, related_name="consist", blank=True)
|
tags = models.ManyToManyField(Tag, related_name="consist", blank=True)
|
||||||
consist_address = models.SmallIntegerField(
|
consist_address = models.SmallIntegerField(
|
||||||
default=None,
|
default=None, null=True, blank=True
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
help_text="DCC consist address if enabled",
|
|
||||||
)
|
)
|
||||||
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
||||||
era = models.CharField(
|
era = models.CharField(max_length=32, blank=True)
|
||||||
max_length=32,
|
|
||||||
blank=True,
|
|
||||||
help_text="Era or epoch of the consist",
|
|
||||||
)
|
|
||||||
image = models.ImageField(
|
image = models.ImageField(
|
||||||
upload_to=os.path.join("images", "consists"),
|
upload_to=os.path.join("images", "consists"),
|
||||||
storage=DeduplicatedStorage,
|
storage=DeduplicatedStorage,
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
# Generated by Django 5.0.4 on 2024-04-20 12:49
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("metadata", "0016_alter_decoderdocument_file"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="property",
|
|
||||||
name="private",
|
|
||||||
field=models.BooleanField(
|
|
||||||
default=False, help_text="Property will be only visible to logged users"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
@@ -11,10 +11,7 @@ from ram.utils import DeduplicatedStorage, get_image_preview, slugify
|
|||||||
|
|
||||||
class Property(models.Model):
|
class Property(models.Model):
|
||||||
name = models.CharField(max_length=128, unique=True)
|
name = models.CharField(max_length=128, unique=True)
|
||||||
private = models.BooleanField(
|
private = models.BooleanField(default=False)
|
||||||
default=False,
|
|
||||||
help_text="Property will be only visible to logged users",
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = "Properties"
|
verbose_name_plural = "Properties"
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
# Generated by Django 4.1 on 2022-08-23 15:54
|
# Generated by Django 4.1 on 2022-08-23 15:54
|
||||||
|
|
||||||
# ckeditor dependency removal
|
import ckeditor.fields
|
||||||
# import ckeditor.fields
|
import ckeditor_uploader.fields
|
||||||
# import ckeditor_uploader.fields
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
@@ -13,24 +12,24 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
# migrations.AlterField(
|
migrations.AlterField(
|
||||||
# model_name="flatpage",
|
model_name="flatpage",
|
||||||
# name="content",
|
name="content",
|
||||||
# field=ckeditor_uploader.fields.RichTextUploadingField(),
|
field=ckeditor_uploader.fields.RichTextUploadingField(),
|
||||||
# ),
|
),
|
||||||
# migrations.AlterField(
|
migrations.AlterField(
|
||||||
# model_name="siteconfiguration",
|
model_name="siteconfiguration",
|
||||||
# name="about",
|
name="about",
|
||||||
# field=ckeditor.fields.RichTextField(blank=True),
|
field=ckeditor.fields.RichTextField(blank=True),
|
||||||
# ),
|
),
|
||||||
# migrations.AlterField(
|
migrations.AlterField(
|
||||||
# model_name="siteconfiguration",
|
model_name="siteconfiguration",
|
||||||
# name="footer",
|
name="footer",
|
||||||
# field=ckeditor.fields.RichTextField(blank=True),
|
field=ckeditor.fields.RichTextField(blank=True),
|
||||||
# ),
|
),
|
||||||
# migrations.AlterField(
|
migrations.AlterField(
|
||||||
# model_name="siteconfiguration",
|
model_name="siteconfiguration",
|
||||||
# name="footer_extended",
|
name="footer_extended",
|
||||||
# field=ckeditor.fields.RichTextField(blank=True),
|
field=ckeditor.fields.RichTextField(blank=True),
|
||||||
# ),
|
),
|
||||||
]
|
]
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
/*!
|
/*!
|
||||||
* Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/)
|
* Bootstrap Icons v1.11.1 (https://icons.getbootstrap.com/)
|
||||||
* Copyright 2019-2024 The Bootstrap Authors
|
* Copyright 2019-2023 The Bootstrap Authors
|
||||||
* Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
|
* Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-display: block;
|
font-display: block;
|
||||||
font-family: "bootstrap-icons";
|
font-family: "bootstrap-icons";
|
||||||
src: url("./fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format("woff2"),
|
src: url("./fonts/bootstrap-icons.woff2?2820a3852bdb9a5832199cc61cec4e65") format("woff2"),
|
||||||
url("./fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff");
|
url("./fonts/bootstrap-icons.woff?2820a3852bdb9a5832199cc61cec4e65") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
.bi::before,
|
.bi::before,
|
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -16,11 +16,11 @@
|
|||||||
<link rel="icon" href="{% static "favicon.png" %}" sizes="any">
|
<link rel="icon" href="{% static "favicon.png" %}" sizes="any">
|
||||||
<link rel="icon" href="{% static "favicon.svg" %}" type="image/svg+xml">
|
<link rel="icon" href="{% static "favicon.svg" %}" type="image/svg+xml">
|
||||||
{% if site_conf.use_cdn %}
|
{% if site_conf.use_cdn %}
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
{% else %}
|
{% else %}
|
||||||
<link href="{% static "bootstrap@5.3.3/dist/css/bootstrap.min.css" %}" rel="stylesheet">
|
<link href="{% static "bootstrap@5.3.2/dist/css/bootstrap.min.css" %}" rel="stylesheet">
|
||||||
<link href="{% static "bootstrap-icons@1.11.3/font/bootstrap-icons.css" %}" rel="stylesheet">
|
<link href="{% static "bootstrap-icons@1.11.1/font/bootstrap-icons.css" %}" rel="stylesheet">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<link href="{% static "css/main.css" %}?v={{ site_conf.version }}" rel="stylesheet">
|
<link href="{% static "css/main.css" %}?v={{ site_conf.version }}" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
@@ -216,9 +216,9 @@
|
|||||||
</main>
|
</main>
|
||||||
{% include 'includes/footer.html' %}
|
{% include 'includes/footer.html' %}
|
||||||
{% if site_conf.use_cdn %}
|
{% if site_conf.use_cdn %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
{% else %}
|
{% else %}
|
||||||
<script src="{% static "bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" %}"></script>
|
<script src="{% static "bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" %}"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -54,7 +54,6 @@
|
|||||||
<div class="tab-content" id="nav-tabContent">
|
<div class="tab-content" id="nav-tabContent">
|
||||||
<div class="tab-pane show active" id="nav-summary" role="tabpanel" aria-labelledby="nav-summary-tab">
|
<div class="tab-pane show active" id="nav-summary" role="tabpanel" aria-labelledby="nav-summary-tab">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
{{ book.description | safe }}
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">Book</th>
|
<th colspan="2" scope="row">Book</th>
|
||||||
|
@@ -60,7 +60,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Item number</th>
|
<th scope="row">Item number</th>
|
||||||
<td>{{ d.item.item_number }}{%if d.item.set %} | <a class="badge text-bg-primary" href="{% url 'manufacturer' manufacturer=d.item.manufacturer search=d.item.item_number %}">SET</a>{% endif %}</td>
|
<td>{{ d.item.item_number }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
{% extends "cards.html" %}
|
|
||||||
{% block pagination %}
|
|
||||||
{% if data.has_other_pages %}
|
|
||||||
<nav aria-label="Page navigation example">
|
|
||||||
<ul class="pagination justify-content-center mt-4 mb-0">
|
|
||||||
{% if data.has_previous %}
|
|
||||||
<li class="page-item">
|
|
||||||
<a class="page-link" href="{% url 'manufacturer_pagination' manufacturer=manufacturer search=search page=data.previous_page_number %}#main-content" tabindex="-1">Previous</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<span class="page-link">Previous</span>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% for i in page_range %}
|
|
||||||
{% if data.number == i %}
|
|
||||||
<li class="page-item active">
|
|
||||||
<span class="page-link">{{ i }}</span>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
{% if i == data.paginator.ELLIPSIS %}
|
|
||||||
<li class="page-item"><span class="page-link">{{ i }}</span></li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item"><a class="page-link" href="{% url 'manufacturer_pagination' manufacturer=manufacturer search=search page=i %}#main-content">{{ i }}</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if data.has_next %}
|
|
||||||
<li class="page-item">
|
|
||||||
<a class="page-link" href="{% url 'manufacturer_pagination' manufacturer=manufacturer search=search page=data.next_page_number %}#main-content" tabindex="-1">Next</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<span class="page-link">Next</span>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
@@ -52,7 +52,6 @@
|
|||||||
{% if documents or decoder_documents %}<button class="nav-link" id="nav-documents-tab" data-bs-toggle="tab" data-bs-target="#nav-documents" type="button" role="tab" aria-controls="nav-documents" aria-selected="false">Documents</button>{% endif %}
|
{% if documents or decoder_documents %}<button class="nav-link" id="nav-documents-tab" data-bs-toggle="tab" data-bs-target="#nav-documents" type="button" role="tab" aria-controls="nav-documents" aria-selected="false">Documents</button>{% endif %}
|
||||||
{% if journal %}<button class="nav-link" id="nav-journal-tab" data-bs-toggle="tab" data-bs-target="#nav-journal" type="button" role="tab" aria-controls="nav-journal" aria-selected="false">Journal</button>{% endif %}
|
{% if journal %}<button class="nav-link" id="nav-journal-tab" data-bs-toggle="tab" data-bs-target="#nav-journal" type="button" role="tab" aria-controls="nav-journal" aria-selected="false">Journal</button>{% endif %}
|
||||||
{% if rolling_stock.notes %}<button class="nav-link" id="nav-notes-tab" data-bs-toggle="tab" data-bs-target="#nav-notes" type="button" role="tab" aria-controls="nav-notes" aria-selected="false">Notes</button>{% endif %}
|
{% if rolling_stock.notes %}<button class="nav-link" id="nav-notes-tab" data-bs-toggle="tab" data-bs-target="#nav-notes" type="button" role="tab" aria-controls="nav-notes" aria-selected="false">Notes</button>{% endif %}
|
||||||
{% if set %}<button class="nav-link" id="nav-set-tab" data-bs-toggle="tab" data-bs-target="#nav-set" type="button" role="tab" aria-controls="nav-set" aria-selected="false">Set</button>{% endif %}
|
|
||||||
{% if consists %}<button class="nav-link" id="nav-consists-tab" data-bs-toggle="tab" data-bs-target="#nav-consists" type="button" role="tab" aria-controls="nav-consists" aria-selected="false">Consists</button>{% endif %}
|
{% if consists %}<button class="nav-link" id="nav-consists-tab" data-bs-toggle="tab" data-bs-target="#nav-consists" type="button" role="tab" aria-controls="nav-consists" aria-selected="false">Consists</button>{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
<select class="form-select d-lg-none mb-2" id="tabSelector" aria-label="Tab selector">
|
<select class="form-select d-lg-none mb-2" id="tabSelector" aria-label="Tab selector">
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
{% if documents or decoder_documents %}<option value="nav-documents">Documents</option>{% endif %}
|
{% if documents or decoder_documents %}<option value="nav-documents">Documents</option>{% endif %}
|
||||||
{% if journal %}<option value="nav-journal">Journal</option>{% endif %}
|
{% if journal %}<option value="nav-journal">Journal</option>{% endif %}
|
||||||
{% if rolling_stock.notes %}<option value="nav-notes">Notes</option>{% endif %}
|
{% if rolling_stock.notes %}<option value="nav-notes">Notes</option>{% endif %}
|
||||||
{% if set %}<option value="nav-set">Set</option>{% endif %}
|
|
||||||
{% if consists %}<option value="nav-consists">Consists</option>{% endif %}
|
{% if consists %}<option value="nav-consists">Consists</option>{% endif %}
|
||||||
</select>
|
</select>
|
||||||
{% with class=rolling_stock.rolling_class company=rolling_stock.rolling_class.company %}
|
{% with class=rolling_stock.rolling_class company=rolling_stock.rolling_class.company %}
|
||||||
@@ -120,7 +118,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Item number</th>
|
<th scope="row">Item number</th>
|
||||||
<td>{{ rolling_stock.item_number }}{%if rolling_stock.set %} | <a class="badge text-bg-primary" href="{% url 'manufacturer' manufacturer=rolling_stock.manufacturer search=rolling_stock.item_number %}">SET</a>{% endif %}</td>
|
<td>{{ rolling_stock.item_number }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -151,7 +149,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="nav-model" role="tabpanel" aria-labelledby="nav-model-tab">
|
<div class="tab-pane" id="nav-model" role="tabpanel" aria-labelledby="nav-model-tab">
|
||||||
{{ rolling_stock.description | safe }}
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -173,7 +170,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Item number</th>
|
<th scope="row">Item number</th>
|
||||||
<td>{{ rolling_stock.item_number }}{%if rolling_stock.set %} | <a class="badge text-bg-primary" href="{% url 'manufacturer' manufacturer=rolling_stock.manufacturer search=rolling_stock.item_number %}">SET</a>{% endif %}</td>
|
<td>{{ rolling_stock.item_number }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Era</th>
|
<th scope="row">Era</th>
|
||||||
@@ -208,7 +205,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="nav-class" role="tabpanel" aria-labelledby="nav-class-tab">
|
<div class="tab-pane" id="nav-class" role="tabpanel" aria-labelledby="nav-class-tab">
|
||||||
{{ class.description | safe }}
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -376,13 +372,6 @@
|
|||||||
<div class="tab-pane" id="nav-notes" role="tabpanel" aria-labelledby="nav-notes-tab">
|
<div class="tab-pane" id="nav-notes" role="tabpanel" aria-labelledby="nav-notes-tab">
|
||||||
{{ rolling_stock.notes | safe }}
|
{{ rolling_stock.notes | safe }}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="nav-set" role="tabpanel" aria-labelledby="nav-set-tab">
|
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
|
|
||||||
{% for d in set %}
|
|
||||||
{% include "cards/roster.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tab-pane" id="nav-consists" role="tabpanel" aria-labelledby="nav-cosists-tab">
|
<div class="tab-pane" id="nav-consists" role="tabpanel" aria-labelledby="nav-cosists-tab">
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
|
||||||
{% for d in consists %}
|
{% for d in consists %}
|
||||||
|
@@ -4,7 +4,6 @@ from portal.views import (
|
|||||||
GetData,
|
GetData,
|
||||||
GetRoster,
|
GetRoster,
|
||||||
GetObjectsFiltered,
|
GetObjectsFiltered,
|
||||||
GetManufacturerItem,
|
|
||||||
GetFlatpage,
|
GetFlatpage,
|
||||||
GetRollingStock,
|
GetRollingStock,
|
||||||
GetConsist,
|
GetConsist,
|
||||||
@@ -21,11 +20,7 @@ from portal.views import (
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", GetData.as_view(template="home.html"), name="index"),
|
path("", GetData.as_view(template="home.html"), name="index"),
|
||||||
path("roster", GetRoster.as_view(), name="roster"),
|
path("roster", GetRoster.as_view(), name="roster"),
|
||||||
path(
|
path("roster/<int:page>", GetRoster.as_view(), name="roster_pagination"),
|
||||||
"roster/page/<int:page>",
|
|
||||||
GetRoster.as_view(),
|
|
||||||
name="roster_pagination"
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"page/<str:flatpage>",
|
"page/<str:flatpage>",
|
||||||
GetFlatpage.as_view(),
|
GetFlatpage.as_view(),
|
||||||
@@ -37,13 +32,13 @@ urlpatterns = [
|
|||||||
name="consists"
|
name="consists"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"consists/page/<int:page>",
|
"consists/<int:page>",
|
||||||
Consists.as_view(template="consists.html"),
|
Consists.as_view(template="consists.html"),
|
||||||
name="consists_pagination"
|
name="consists_pagination"
|
||||||
),
|
),
|
||||||
path("consist/<uuid:uuid>", GetConsist.as_view(), name="consist"),
|
path("consist/<uuid:uuid>", GetConsist.as_view(), name="consist"),
|
||||||
path(
|
path(
|
||||||
"consist/<uuid:uuid>/page/<int:page>",
|
"consist/<uuid:uuid>/<int:page>",
|
||||||
GetConsist.as_view(),
|
GetConsist.as_view(),
|
||||||
name="consist_pagination",
|
name="consist_pagination",
|
||||||
),
|
),
|
||||||
@@ -53,7 +48,7 @@ urlpatterns = [
|
|||||||
name="companies"
|
name="companies"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"companies/page/<int:page>",
|
"companies/<int:page>",
|
||||||
Companies.as_view(template="companies.html"),
|
Companies.as_view(template="companies.html"),
|
||||||
name="companies_pagination",
|
name="companies_pagination",
|
||||||
),
|
),
|
||||||
@@ -63,7 +58,7 @@ urlpatterns = [
|
|||||||
name="manufacturers"
|
name="manufacturers"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"manufacturers/<str:category>/page/<int:page>",
|
"manufacturers/<str:category>/<int:page>",
|
||||||
Manufacturers.as_view(template="manufacturers.html"),
|
Manufacturers.as_view(template="manufacturers.html"),
|
||||||
name="manufacturers_pagination",
|
name="manufacturers_pagination",
|
||||||
),
|
),
|
||||||
@@ -73,7 +68,7 @@ urlpatterns = [
|
|||||||
name="scales"
|
name="scales"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"scales/page/<int:page>",
|
"scales/<int:page>",
|
||||||
Scales.as_view(template="scales.html"),
|
Scales.as_view(template="scales.html"),
|
||||||
name="scales_pagination"
|
name="scales_pagination"
|
||||||
),
|
),
|
||||||
@@ -83,7 +78,7 @@ urlpatterns = [
|
|||||||
name="types"
|
name="types"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"types/page/<int:page>",
|
"types/<int:page>",
|
||||||
Types.as_view(template="types.html"),
|
Types.as_view(template="types.html"),
|
||||||
name="types_pagination"
|
name="types_pagination"
|
||||||
),
|
),
|
||||||
@@ -93,7 +88,7 @@ urlpatterns = [
|
|||||||
name="books"
|
name="books"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"bookshelf/books/page/<int:page>",
|
"bookshelf/books/<int:page>",
|
||||||
Books.as_view(template="bookshelf/books.html"),
|
Books.as_view(template="bookshelf/books.html"),
|
||||||
name="books_pagination"
|
name="books_pagination"
|
||||||
),
|
),
|
||||||
@@ -104,37 +99,17 @@ urlpatterns = [
|
|||||||
name="search",
|
name="search",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"search/<str:search>/page/<int:page>",
|
"search/<str:search>/<int:page>",
|
||||||
SearchObjects.as_view(),
|
SearchObjects.as_view(),
|
||||||
name="search_pagination",
|
name="search_pagination",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"manufacturer/<str:manufacturer>",
|
|
||||||
GetManufacturerItem.as_view(),
|
|
||||||
name="manufacturer",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"manufacturer/<str:manufacturer>/page/<int:page>",
|
|
||||||
GetManufacturerItem.as_view(),
|
|
||||||
name="manufacturer_pagination",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"manufacturer/<str:manufacturer>/<str:search>",
|
|
||||||
GetManufacturerItem.as_view(),
|
|
||||||
name="manufacturer",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"manufacturer/<str:manufacturer>/<str:search>/page/<int:page>",
|
|
||||||
GetManufacturerItem.as_view(),
|
|
||||||
name="manufacturer_pagination",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"<str:_filter>/<str:search>",
|
"<str:_filter>/<str:search>",
|
||||||
GetObjectsFiltered.as_view(),
|
GetObjectsFiltered.as_view(),
|
||||||
name="filtered",
|
name="filtered",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<str:_filter>/<str:search>/page/<int:page>",
|
"<str:_filter>/<str:search>/<int:page>",
|
||||||
GetObjectsFiltered.as_view(),
|
GetObjectsFiltered.as_view(),
|
||||||
name="filtered_pagination",
|
name="filtered_pagination",
|
||||||
),
|
),
|
||||||
|
@@ -7,7 +7,7 @@ from django.views import View
|
|||||||
from django.http import Http404, HttpResponseBadRequest
|
from django.http import Http404, HttpResponseBadRequest
|
||||||
from django.db.utils import OperationalError, ProgrammingError
|
from django.db.utils import OperationalError, ProgrammingError
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.shortcuts import render, get_object_or_404, get_list_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
|
|
||||||
@@ -21,15 +21,7 @@ from metadata.models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_items_per_page():
|
def order_by_fields():
|
||||||
try:
|
|
||||||
items_per_page = get_site_conf().items_per_page
|
|
||||||
except (OperationalError, ProgrammingError):
|
|
||||||
items_per_page = 6
|
|
||||||
return items_per_page
|
|
||||||
|
|
||||||
|
|
||||||
def get_order_by_field():
|
|
||||||
try:
|
try:
|
||||||
order_by = get_site_conf().items_ordering
|
order_by = get_site_conf().items_ordering
|
||||||
except (OperationalError, ProgrammingError):
|
except (OperationalError, ProgrammingError):
|
||||||
@@ -54,22 +46,19 @@ class GetData(View):
|
|||||||
title = "Home"
|
title = "Home"
|
||||||
template = "roster.html"
|
template = "roster.html"
|
||||||
item_type = "rolling_stock"
|
item_type = "rolling_stock"
|
||||||
filter = Q() # empty filter by default
|
queryset = RollingStock.objects.order_by(*order_by_fields())
|
||||||
|
|
||||||
def get_data(self):
|
|
||||||
return RollingStock.objects.order_by(
|
|
||||||
*get_order_by_field()
|
|
||||||
).filter(self.filter)
|
|
||||||
|
|
||||||
def get(self, request, page=1):
|
def get(self, request, page=1):
|
||||||
|
site_conf = get_site_conf()
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for item in self.get_data():
|
for item in self.queryset:
|
||||||
data.append({
|
data.append({
|
||||||
"type": self.item_type,
|
"type": self.item_type,
|
||||||
"item": item
|
"item": item
|
||||||
})
|
})
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, site_conf.items_per_page)
|
||||||
data = paginator.get_page(page)
|
data = paginator.get_page(page)
|
||||||
page_range = paginator.get_elided_page_range(
|
page_range = paginator.get_elided_page_range(
|
||||||
data.number, on_each_side=2, on_ends=1
|
data.number, on_each_side=2, on_ends=1
|
||||||
@@ -91,13 +80,12 @@ class GetData(View):
|
|||||||
class GetRoster(GetData):
|
class GetRoster(GetData):
|
||||||
title = "Roster"
|
title = "Roster"
|
||||||
item_type = "rolling_stock"
|
item_type = "rolling_stock"
|
||||||
|
queryset = RollingStock.objects.order_by(*order_by_fields())
|
||||||
def get_data(self):
|
|
||||||
return RollingStock.objects.order_by(*get_order_by_field())
|
|
||||||
|
|
||||||
|
|
||||||
class SearchObjects(View):
|
class SearchObjects(View):
|
||||||
def run_search(self, request, search, _filter, page=1):
|
def run_search(self, request, search, _filter, page=1):
|
||||||
|
site_conf = get_site_conf()
|
||||||
if _filter is None:
|
if _filter is None:
|
||||||
query = reduce(
|
query = reduce(
|
||||||
operator.or_,
|
operator.or_,
|
||||||
@@ -142,7 +130,7 @@ class SearchObjects(View):
|
|||||||
rolling_stock = (
|
rolling_stock = (
|
||||||
RollingStock.objects.filter(query)
|
RollingStock.objects.filter(query)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by(*get_order_by_field())
|
.order_by(*order_by_fields())
|
||||||
)
|
)
|
||||||
for item in rolling_stock:
|
for item in rolling_stock:
|
||||||
data.append({
|
data.append({
|
||||||
@@ -174,7 +162,7 @@ class SearchObjects(View):
|
|||||||
"item": item
|
"item": item
|
||||||
})
|
})
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, site_conf.items_per_page)
|
||||||
data = paginator.get_page(page)
|
data = paginator.get_page(page)
|
||||||
page_range = paginator.get_elided_page_range(
|
page_range = paginator.get_elided_page_range(
|
||||||
data.number, on_each_side=2, on_ends=1
|
data.number, on_each_side=2, on_ends=1
|
||||||
@@ -227,58 +215,10 @@ class SearchObjects(View):
|
|||||||
return self.get(request, search, page)
|
return self.get(request, search, page)
|
||||||
|
|
||||||
|
|
||||||
class GetManufacturerItem(View):
|
|
||||||
def get(self, request, manufacturer, search="all", page=1):
|
|
||||||
if search != "all":
|
|
||||||
rolling_stock = get_list_or_404(
|
|
||||||
RollingStock.objects.order_by(*get_order_by_field()),
|
|
||||||
Q(
|
|
||||||
Q(manufacturer__name__iexact=manufacturer)
|
|
||||||
& Q(item_number__exact=search)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
title = "{0}: {1}".format(
|
|
||||||
rolling_stock[0].manufacturer,
|
|
||||||
search
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
rolling_stock = get_list_or_404(
|
|
||||||
RollingStock.objects.order_by(*get_order_by_field()),
|
|
||||||
Q(rolling_class__manufacturer__slug__iexact=manufacturer)
|
|
||||||
| Q(manufacturer__slug__iexact=manufacturer)
|
|
||||||
)
|
|
||||||
title = "Manufacturer: {0}".format(
|
|
||||||
get_object_or_404(Manufacturer, slug__iexact=manufacturer)
|
|
||||||
)
|
|
||||||
|
|
||||||
data = []
|
|
||||||
for item in rolling_stock:
|
|
||||||
data.append({
|
|
||||||
"type": "rolling_stock",
|
|
||||||
"item": item
|
|
||||||
})
|
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
|
||||||
data = paginator.get_page(page)
|
|
||||||
page_range = paginator.get_elided_page_range(
|
|
||||||
data.number, on_each_side=2, on_ends=1
|
|
||||||
)
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"manufacturer.html",
|
|
||||||
{
|
|
||||||
"title": title,
|
|
||||||
"manufacturer": manufacturer,
|
|
||||||
"search": search,
|
|
||||||
"data": data,
|
|
||||||
"matches": paginator.count,
|
|
||||||
"page_range": page_range,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GetObjectsFiltered(View):
|
class GetObjectsFiltered(View):
|
||||||
def run_filter(self, request, search, _filter, page=1):
|
def run_filter(self, request, search, _filter, page=1):
|
||||||
|
site_conf = get_site_conf()
|
||||||
|
|
||||||
if _filter == "type":
|
if _filter == "type":
|
||||||
title = get_object_or_404(RollingStockType, slug__iexact=search)
|
title = get_object_or_404(RollingStockType, slug__iexact=search)
|
||||||
query = Q(rolling_class__type__slug__iexact=search)
|
query = Q(rolling_class__type__slug__iexact=search)
|
||||||
@@ -286,6 +226,12 @@ class GetObjectsFiltered(View):
|
|||||||
title = get_object_or_404(Company, slug__iexact=search)
|
title = get_object_or_404(Company, slug__iexact=search)
|
||||||
query = Q(rolling_class__company__slug__iexact=search)
|
query = Q(rolling_class__company__slug__iexact=search)
|
||||||
query_2nd = Q(company__slug__iexact=search)
|
query_2nd = Q(company__slug__iexact=search)
|
||||||
|
elif _filter == "manufacturer":
|
||||||
|
title = get_object_or_404(Manufacturer, slug__iexact=search)
|
||||||
|
query = Q(
|
||||||
|
Q(rolling_class__manufacturer__slug__iexact=search)
|
||||||
|
| Q(manufacturer__slug__iexact=search)
|
||||||
|
)
|
||||||
elif _filter == "scale":
|
elif _filter == "scale":
|
||||||
title = get_object_or_404(Scale, slug__iexact=search)
|
title = get_object_or_404(Scale, slug__iexact=search)
|
||||||
query = Q(scale__slug__iexact=search)
|
query = Q(scale__slug__iexact=search)
|
||||||
@@ -302,7 +248,7 @@ class GetObjectsFiltered(View):
|
|||||||
rolling_stock = (
|
rolling_stock = (
|
||||||
RollingStock.objects.filter(query)
|
RollingStock.objects.filter(query)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by(*get_order_by_field())
|
.order_by(*order_by_fields())
|
||||||
)
|
)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
@@ -335,7 +281,7 @@ class GetObjectsFiltered(View):
|
|||||||
except NameError:
|
except NameError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, site_conf.items_per_page)
|
||||||
data = paginator.get_page(page)
|
data = paginator.get_page(page)
|
||||||
page_range = paginator.get_elided_page_range(
|
page_range = paginator.get_elided_page_range(
|
||||||
data.number, on_each_side=2, on_ends=1
|
data.number, on_each_side=2, on_ends=1
|
||||||
@@ -401,16 +347,6 @@ class GetRollingStock(View):
|
|||||||
consist_item__rolling_stock=rolling_stock
|
consist_item__rolling_stock=rolling_stock
|
||||||
)] # A dict with "item" is required by the consists card
|
)] # A dict with "item" is required by the consists card
|
||||||
|
|
||||||
set = [{
|
|
||||||
"type": "set",
|
|
||||||
"item": s
|
|
||||||
} for s in RollingStock.objects.filter(
|
|
||||||
Q(
|
|
||||||
Q(item_number__exact=rolling_stock.item_number)
|
|
||||||
& Q(set=True)
|
|
||||||
)
|
|
||||||
).order_by(*get_order_by_field())]
|
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"rollingstock.html",
|
"rollingstock.html",
|
||||||
@@ -422,7 +358,6 @@ class GetRollingStock(View):
|
|||||||
"decoder_documents": decoder_documents,
|
"decoder_documents": decoder_documents,
|
||||||
"documents": documents,
|
"documents": documents,
|
||||||
"journal": journal,
|
"journal": journal,
|
||||||
"set": set,
|
|
||||||
"consists": consists,
|
"consists": consists,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -431,13 +366,12 @@ class GetRollingStock(View):
|
|||||||
class Consists(GetData):
|
class Consists(GetData):
|
||||||
title = "Consists"
|
title = "Consists"
|
||||||
item_type = "consist"
|
item_type = "consist"
|
||||||
|
queryset = Consist.objects.all()
|
||||||
def get_data(self):
|
|
||||||
return Consist.objects.all()
|
|
||||||
|
|
||||||
|
|
||||||
class GetConsist(View):
|
class GetConsist(View):
|
||||||
def get(self, request, uuid, page=1):
|
def get(self, request, uuid, page=1):
|
||||||
|
site_conf = get_site_conf()
|
||||||
try:
|
try:
|
||||||
consist = Consist.objects.get(uuid=uuid)
|
consist = Consist.objects.get(uuid=uuid)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
@@ -447,7 +381,7 @@ class GetConsist(View):
|
|||||||
"item": RollingStock.objects.get(uuid=r.rolling_stock_id)
|
"item": RollingStock.objects.get(uuid=r.rolling_stock_id)
|
||||||
} for r in consist.consist_item.all()]
|
} for r in consist.consist_item.all()]
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, site_conf.items_per_page)
|
||||||
data = paginator.get_page(page)
|
data = paginator.get_page(page)
|
||||||
page_range = paginator.get_elided_page_range(
|
page_range = paginator.get_elided_page_range(
|
||||||
data.number, on_each_side=2, on_ends=1
|
data.number, on_each_side=2, on_ends=1
|
||||||
@@ -468,25 +402,20 @@ class GetConsist(View):
|
|||||||
class Manufacturers(GetData):
|
class Manufacturers(GetData):
|
||||||
title = "Manufacturers"
|
title = "Manufacturers"
|
||||||
item_type = "manufacturer"
|
item_type = "manufacturer"
|
||||||
|
queryset = None # Set via method get
|
||||||
def get_data(self):
|
|
||||||
return Manufacturer.objects.filter(self.filter)
|
|
||||||
|
|
||||||
# overload get method to filter by category
|
# overload get method to filter by category
|
||||||
def get(self, request, category, page=1):
|
def get(self, request, category, page=1):
|
||||||
if category not in ("real", "model"):
|
if category not in ("real", "model"):
|
||||||
raise Http404
|
raise Http404
|
||||||
self.filter = Q(category=category)
|
self.queryset = Manufacturer.objects.filter(category=category)
|
||||||
|
|
||||||
return super().get(request, page)
|
return super().get(request, page)
|
||||||
|
|
||||||
|
|
||||||
class Companies(GetData):
|
class Companies(GetData):
|
||||||
title = "Companies"
|
title = "Companies"
|
||||||
item_type = "company"
|
item_type = "company"
|
||||||
|
queryset = Company.objects.all()
|
||||||
def get_data(self):
|
|
||||||
return Company.objects.all()
|
|
||||||
|
|
||||||
|
|
||||||
class Scales(GetData):
|
class Scales(GetData):
|
||||||
@@ -494,24 +423,17 @@ class Scales(GetData):
|
|||||||
item_type = "scale"
|
item_type = "scale"
|
||||||
queryset = Scale.objects.all()
|
queryset = Scale.objects.all()
|
||||||
|
|
||||||
def get_data(self):
|
|
||||||
return Scale.objects.all()
|
|
||||||
|
|
||||||
|
|
||||||
class Types(GetData):
|
class Types(GetData):
|
||||||
title = "Types"
|
title = "Types"
|
||||||
item_type = "rolling_stock_type"
|
item_type = "rolling_stock_type"
|
||||||
|
queryset = RollingStockType.objects.all()
|
||||||
def get_data(self):
|
|
||||||
return RollingStockType.objects.all()
|
|
||||||
|
|
||||||
|
|
||||||
class Books(GetData):
|
class Books(GetData):
|
||||||
title = "Books"
|
title = "Books"
|
||||||
item_type = "book"
|
item_type = "book"
|
||||||
|
queryset = Book.objects.all()
|
||||||
def get_data(self):
|
|
||||||
return Book.objects.all()
|
|
||||||
|
|
||||||
|
|
||||||
class GetBook(View):
|
class GetBook(View):
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from ram.utils import git_suffix
|
from ram.utils import git_suffix
|
||||||
|
|
||||||
__version__ = "0.12.2"
|
__version__ = "0.10.0"
|
||||||
__version__ += git_suffix(__file__)
|
__version__ += git_suffix(__file__)
|
||||||
|
@@ -13,7 +13,6 @@ Including another URLconf
|
|||||||
1. Import the include() function: from django.urls import include, path
|
1. Import the include() function: from django.urls import include, path
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
@@ -25,8 +24,12 @@ from ram.views import UploadImage
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", lambda r: redirect("portal/")),
|
path("", lambda r: redirect("portal/")),
|
||||||
path("tinymce/", include("tinymce.urls")),
|
path('tinymce/', include('tinymce.urls')),
|
||||||
path("tinymce/upload_image", UploadImage.as_view(), name="upload_image"),
|
path(
|
||||||
|
"tinymce/upload_image",
|
||||||
|
UploadImage.as_view(),
|
||||||
|
name="upload_image"
|
||||||
|
),
|
||||||
path("portal/", include("portal.urls")),
|
path("portal/", include("portal.urls")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path("api/v1/consist/", include("consist.urls")),
|
path("api/v1/consist/", include("consist.urls")),
|
||||||
|
@@ -8,29 +8,29 @@ from PIL import Image, UnidentifiedImageError
|
|||||||
from django.views import View
|
from django.views import View
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import (
|
from django.http import (
|
||||||
HttpResponseBadRequest,
|
JsonResponse, HttpResponseForbidden, HttpResponse
|
||||||
HttpResponseForbidden,
|
|
||||||
JsonResponse,
|
|
||||||
)
|
)
|
||||||
from django.utils.text import slugify as slugify
|
from django.utils.text import slugify as slugify
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name="dispatch")
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class UploadImage(View):
|
class UploadImage(View):
|
||||||
def post(self, request):
|
def post(self, request, application=None, model=None):
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
raise HttpResponseForbidden()
|
raise HttpResponseForbidden()
|
||||||
|
|
||||||
file_obj = request.FILES["file"]
|
file_obj = request.FILES['file']
|
||||||
file_name, file_extension = os.path.splitext(file_obj.name)
|
file_name, file_extension = os.path.splitext(file_obj.name)
|
||||||
file_name = slugify(file_name) + file_extension
|
file_name = slugify(file_name) + file_extension
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Image.open(file_obj)
|
Image.open(file_obj)
|
||||||
except UnidentifiedImageError:
|
except UnidentifiedImageError:
|
||||||
return HttpResponseBadRequest()
|
response = HttpResponse("Invalid extension") # FIXME
|
||||||
|
response.status_code = 400
|
||||||
|
return response
|
||||||
|
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
container = (
|
container = (
|
||||||
@@ -40,23 +40,20 @@ class UploadImage(View):
|
|||||||
today.strftime("%d"),
|
today.strftime("%d"),
|
||||||
)
|
)
|
||||||
|
|
||||||
dir_path = os.path.join(settings.MEDIA_ROOT, *(p for p in container))
|
file_path = os.path.join(
|
||||||
file_path = os.path.normpath(os.path.join(dir_path, file_name))
|
settings.MEDIA_ROOT,
|
||||||
# even if we apply slugify to the file name, add more hardening
|
*(p for p in container)
|
||||||
# to avoid any path transversal risk
|
)
|
||||||
if not file_path.startswith(str(settings.MEDIA_ROOT)):
|
Path(file_path).mkdir(parents=True, exist_ok=True)
|
||||||
return HttpResponseBadRequest()
|
with open(os.path.join(file_path, file_name), 'wb+') as f:
|
||||||
|
|
||||||
Path(dir_path).mkdir(parents=True, exist_ok=True)
|
|
||||||
with open(file_path, "wb+") as f:
|
|
||||||
for chunk in file_obj.chunks():
|
for chunk in file_obj.chunks():
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse({
|
||||||
{
|
'message': 'Image uploaded successfully',
|
||||||
"message": "Image uploaded successfully",
|
'location': posixpath.join(
|
||||||
"location": posixpath.join(
|
settings.MEDIA_URL,
|
||||||
settings.MEDIA_URL, *(p for p in container), file_name
|
*(p for p in container),
|
||||||
),
|
file_name
|
||||||
}
|
)
|
||||||
)
|
})
|
||||||
|
@@ -141,9 +141,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"scale",
|
"scale",
|
||||||
"manufacturer",
|
"manufacturer",
|
||||||
"item_number",
|
"item_number",
|
||||||
"set",
|
|
||||||
"era",
|
"era",
|
||||||
"description",
|
|
||||||
"production_year",
|
"production_year",
|
||||||
"purchase_date",
|
"purchase_date",
|
||||||
"notes",
|
"notes",
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# Generated by Django 4.1 on 2022-08-23 15:54
|
# Generated by Django 4.1 on 2022-08-23 15:54
|
||||||
|
|
||||||
# ckeditor removal
|
import ckeditor_uploader.fields
|
||||||
# import ckeditor_uploader.fields
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
@@ -12,9 +11,9 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
# migrations.AlterField(
|
migrations.AlterField(
|
||||||
# model_name="rollingstock",
|
model_name="rollingstock",
|
||||||
# name="notes",
|
name="notes",
|
||||||
# field=ckeditor_uploader.fields.RichTextUploadingField(blank=True),
|
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True),
|
||||||
# ),
|
),
|
||||||
]
|
]
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# Generated by Django 4.1 on 2022-08-27 12:43
|
# Generated by Django 4.1 on 2022-08-27 12:43
|
||||||
|
|
||||||
# ckeditor removal
|
import ckeditor_uploader.fields
|
||||||
# import ckeditor_uploader.fields
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
||||||
@@ -26,8 +25,7 @@ class Migration(migrations.Migration):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
("date", models.DateField()),
|
("date", models.DateField()),
|
||||||
# ("log", ckeditor_uploader.fields.RichTextUploadingField()),
|
("log", ckeditor_uploader.fields.RichTextUploadingField()),
|
||||||
("log", models.TextField()),
|
|
||||||
("private", models.BooleanField(default=False)),
|
("private", models.BooleanField(default=False)),
|
||||||
("creation_time", models.DateTimeField(auto_now_add=True)),
|
("creation_time", models.DateTimeField(auto_now_add=True)),
|
||||||
("updated_time", models.DateTimeField(auto_now=True)),
|
("updated_time", models.DateTimeField(auto_now=True)),
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.2 on 2024-03-02 13:30
|
|
||||||
|
|
||||||
import tinymce.models
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("roster", "0022_alter_rollingstock_notes_and_more"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="rollingclass",
|
|
||||||
name="description",
|
|
||||||
field=tinymce.models.HTMLField(blank=True),
|
|
||||||
),
|
|
||||||
]
|
|
@@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.2 on 2024-03-02 14:30
|
|
||||||
|
|
||||||
import tinymce.models
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("roster", "0023_alter_rollingclass_description"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="rollingstock",
|
|
||||||
name="description",
|
|
||||||
field=tinymce.models.HTMLField(blank=True),
|
|
||||||
),
|
|
||||||
]
|
|
@@ -1,40 +0,0 @@
|
|||||||
# Generated by Django 5.0.4 on 2024-04-20 12:55
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("roster", "0024_rollingstock_description"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="rollingstock",
|
|
||||||
name="set",
|
|
||||||
field=models.BooleanField(default=False, help_text="Part of a set"),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="rollingstock",
|
|
||||||
name="era",
|
|
||||||
field=models.CharField(
|
|
||||||
blank=True, help_text="Era or epoch of the model", max_length=32
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="rollingstock",
|
|
||||||
name="item_number",
|
|
||||||
field=models.CharField(
|
|
||||||
blank=True, help_text="Catalog item number or code", max_length=32
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="rollingstockjournal",
|
|
||||||
name="private",
|
|
||||||
field=models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Journal log will be visible only to logged users",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
@@ -25,7 +25,7 @@ class RollingClass(models.Model):
|
|||||||
identifier = models.CharField(max_length=128, unique=False)
|
identifier = models.CharField(max_length=128, unique=False)
|
||||||
type = models.ForeignKey(RollingStockType, on_delete=models.CASCADE)
|
type = models.ForeignKey(RollingStockType, on_delete=models.CASCADE)
|
||||||
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
||||||
description = tinymce.HTMLField(blank=True)
|
description = models.CharField(max_length=256, blank=True)
|
||||||
manufacturer = models.ForeignKey(
|
manufacturer = models.ForeignKey(
|
||||||
Manufacturer,
|
Manufacturer,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
@@ -74,15 +74,7 @@ class RollingStock(models.Model):
|
|||||||
limit_choices_to={"category": "model"},
|
limit_choices_to={"category": "model"},
|
||||||
)
|
)
|
||||||
scale = models.ForeignKey(Scale, on_delete=models.CASCADE)
|
scale = models.ForeignKey(Scale, on_delete=models.CASCADE)
|
||||||
item_number = models.CharField(
|
item_number = models.CharField(max_length=32, blank=True)
|
||||||
max_length=32,
|
|
||||||
blank=True,
|
|
||||||
help_text="Catalog item number or code",
|
|
||||||
)
|
|
||||||
set = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Part of a set",
|
|
||||||
)
|
|
||||||
decoder_interface = models.PositiveSmallIntegerField(
|
decoder_interface = models.PositiveSmallIntegerField(
|
||||||
choices=settings.DECODER_INTERFACES, null=True, blank=True
|
choices=settings.DECODER_INTERFACES, null=True, blank=True
|
||||||
)
|
)
|
||||||
@@ -90,18 +82,13 @@ class RollingStock(models.Model):
|
|||||||
Decoder, on_delete=models.CASCADE, null=True, blank=True
|
Decoder, on_delete=models.CASCADE, null=True, blank=True
|
||||||
)
|
)
|
||||||
address = models.SmallIntegerField(default=None, null=True, blank=True)
|
address = models.SmallIntegerField(default=None, null=True, blank=True)
|
||||||
era = models.CharField(
|
era = models.CharField(max_length=32, blank=True)
|
||||||
max_length=32,
|
|
||||||
blank=True,
|
|
||||||
help_text="Era or epoch of the model",
|
|
||||||
)
|
|
||||||
production_year = models.SmallIntegerField(null=True, blank=True)
|
production_year = models.SmallIntegerField(null=True, blank=True)
|
||||||
purchase_date = models.DateField(null=True, blank=True)
|
purchase_date = models.DateField(null=True, blank=True)
|
||||||
description = tinymce.HTMLField(blank=True)
|
notes = tinymce.HTMLField(blank=True)
|
||||||
tags = models.ManyToManyField(
|
tags = models.ManyToManyField(
|
||||||
Tag, related_name="rolling_stock", blank=True
|
Tag, related_name="rolling_stock", blank=True
|
||||||
)
|
)
|
||||||
notes = tinymce.HTMLField(blank=True)
|
|
||||||
creation_time = models.DateTimeField(auto_now_add=True)
|
creation_time = models.DateTimeField(auto_now_add=True)
|
||||||
updated_time = models.DateTimeField(auto_now=True)
|
updated_time = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
@@ -189,10 +176,7 @@ class RollingStockJournal(models.Model):
|
|||||||
)
|
)
|
||||||
date = models.DateField()
|
date = models.DateField()
|
||||||
log = tinymce.HTMLField()
|
log = tinymce.HTMLField()
|
||||||
private = models.BooleanField(
|
private = models.BooleanField(default=False)
|
||||||
default=False,
|
|
||||||
help_text="Journal log will be visible only to logged users",
|
|
||||||
)
|
|
||||||
creation_time = models.DateTimeField(auto_now_add=True)
|
creation_time = models.DateTimeField(auto_now_add=True)
|
||||||
updated_time = models.DateTimeField(auto_now=True)
|
updated_time = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user