Implement support for sets and other improvements (#34)

* Add a boolean to define item as part of a set
* Add contextual help in admin
* Introduce support to sets and to item code lookup
Also review the url path for pagination
This commit is contained in:
2024-04-21 00:31:52 +02:00
committed by GitHub
parent b37f5420c5
commit e4ad98fa38
14 changed files with 275 additions and 28 deletions

View File

@@ -60,7 +60,7 @@
</tr>
<tr>
<th scope="row">Item number</th>
<td>{{ d.item.item_number }}</td>
<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>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,40 @@
{% 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 %}

View File

@@ -52,6 +52,7 @@
{% 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 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 %}
</nav>
<select class="form-select d-lg-none mb-2" id="tabSelector" aria-label="Tab selector">
@@ -63,6 +64,7 @@
{% if documents or decoder_documents %}<option value="nav-documents">Documents</option>{% endif %}
{% if journal %}<option value="nav-journal">Journal</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 %}
</select>
{% with class=rolling_stock.rolling_class company=rolling_stock.rolling_class.company %}
@@ -118,7 +120,7 @@
</tr>
<tr>
<th scope="row">Item number</th>
<td>{{ rolling_stock.item_number }}</td>
<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>
</tr>
</tbody>
</table>
@@ -171,7 +173,7 @@
</tr>
<tr>
<th scope="row">Item number</th>
<td>{{ rolling_stock.item_number }}</td>
<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>
</tr>
<tr>
<th scope="row">Era</th>
@@ -374,6 +376,13 @@
<div class="tab-pane" id="nav-notes" role="tabpanel" aria-labelledby="nav-notes-tab">
{{ rolling_stock.notes | safe }}
</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="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
{% for d in consists %}

View File

@@ -4,6 +4,7 @@ from portal.views import (
GetData,
GetRoster,
GetObjectsFiltered,
GetManufacturerItem,
GetFlatpage,
GetRollingStock,
GetConsist,
@@ -20,7 +21,11 @@ from portal.views import (
urlpatterns = [
path("", GetData.as_view(template="home.html"), name="index"),
path("roster", GetRoster.as_view(), name="roster"),
path("roster/<int:page>", GetRoster.as_view(), name="roster_pagination"),
path(
"roster/page/<int:page>",
GetRoster.as_view(),
name="roster_pagination"
),
path(
"page/<str:flatpage>",
GetFlatpage.as_view(),
@@ -32,13 +37,13 @@ urlpatterns = [
name="consists"
),
path(
"consists/<int:page>",
"consists/page/<int:page>",
Consists.as_view(template="consists.html"),
name="consists_pagination"
),
path("consist/<uuid:uuid>", GetConsist.as_view(), name="consist"),
path(
"consist/<uuid:uuid>/<int:page>",
"consist/<uuid:uuid>/page/<int:page>",
GetConsist.as_view(),
name="consist_pagination",
),
@@ -48,7 +53,7 @@ urlpatterns = [
name="companies"
),
path(
"companies/<int:page>",
"companies/page/<int:page>",
Companies.as_view(template="companies.html"),
name="companies_pagination",
),
@@ -58,7 +63,7 @@ urlpatterns = [
name="manufacturers"
),
path(
"manufacturers/<str:category>/<int:page>",
"manufacturers/<str:category>/page/<int:page>",
Manufacturers.as_view(template="manufacturers.html"),
name="manufacturers_pagination",
),
@@ -68,7 +73,7 @@ urlpatterns = [
name="scales"
),
path(
"scales/<int:page>",
"scales/page/<int:page>",
Scales.as_view(template="scales.html"),
name="scales_pagination"
),
@@ -78,7 +83,7 @@ urlpatterns = [
name="types"
),
path(
"types/<int:page>",
"types/page/<int:page>",
Types.as_view(template="types.html"),
name="types_pagination"
),
@@ -88,7 +93,7 @@ urlpatterns = [
name="books"
),
path(
"bookshelf/books/<int:page>",
"bookshelf/books/page/<int:page>",
Books.as_view(template="bookshelf/books.html"),
name="books_pagination"
),
@@ -99,17 +104,37 @@ urlpatterns = [
name="search",
),
path(
"search/<str:search>/<int:page>",
"search/<str:search>/page/<int:page>",
SearchObjects.as_view(),
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(
"<str:_filter>/<str:search>",
GetObjectsFiltered.as_view(),
name="filtered",
),
path(
"<str:_filter>/<str:search>/<int:page>",
"<str:_filter>/<str:search>/page/<int:page>",
GetObjectsFiltered.as_view(),
name="filtered_pagination",
),

View File

@@ -7,7 +7,7 @@ from django.views import View
from django.http import Http404, HttpResponseBadRequest
from django.db.utils import OperationalError, ProgrammingError
from django.db.models import Q
from django.shortcuts import render, get_object_or_404
from django.shortcuts import render, get_object_or_404, get_list_or_404
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator
@@ -215,6 +215,58 @@ class SearchObjects(View):
return self.get(request, search, page)
class GetManufacturerItem(View):
def get(self, request, manufacturer, search="all", page=1):
site_conf = get_site_conf()
if search != "all":
rolling_stock = get_list_or_404(
RollingStock.objects.order_by(*order_by_fields()),
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(*order_by_fields()),
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, site_conf.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):
def run_filter(self, request, search, _filter, page=1):
site_conf = get_site_conf()
@@ -226,12 +278,6 @@ class GetObjectsFiltered(View):
title = get_object_or_404(Company, slug__iexact=search)
query = Q(rolling_class__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":
title = get_object_or_404(Scale, slug__iexact=search)
query = Q(scale__slug__iexact=search)
@@ -347,6 +393,16 @@ class GetRollingStock(View):
consist_item__rolling_stock=rolling_stock
)] # 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)
)
)]
return render(
request,
"rollingstock.html",
@@ -358,6 +414,7 @@ class GetRollingStock(View):
"decoder_documents": decoder_documents,
"documents": documents,
"journal": journal,
"set": set,
"consists": consists,
},
)