Files
django-ram/ram/portal/views.py
Daniele Viganò c73efb01e4 Introduce private docs and flatpages preview (#26)
* Add support for private documents
* Fix migrations after merge
* Rebase fixtures
* Filter private decoder docs
* Enable preview of unpublished pages
2023-10-07 22:38:20 +02:00

461 lines
14 KiB
Python

import base64
import operator
from functools import reduce
from urllib.parse import unquote
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.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator
from portal.utils import get_site_conf
from portal.models import Flatpage
from roster.models import RollingStock
from consist.models import Consist
from bookshelf.models import Book
from metadata.models import (
Company, Manufacturer, Scale, DecoderDocument, RollingStockType, Tag
)
def order_by_fields():
try:
order_by = get_site_conf().items_ordering
except (OperationalError, ProgrammingError):
order_by = "type"
fields = [
"rolling_class__type",
"rolling_class__company",
"rolling_class__identifier",
"road_number_int",
]
if order_by == "type":
return (fields[0], fields[1], fields[2], fields[3])
elif order_by == "company":
return (fields[1], fields[0], fields[2], fields[3])
elif order_by == "identifier":
return (fields[2], fields[0], fields[1], fields[3])
class GetData(View):
title = "Home"
template = "roster.html"
item_type = "rolling_stock"
queryset = RollingStock.objects.order_by(*order_by_fields())
def get(self, request, page=1):
site_conf = get_site_conf()
data = []
for item in self.queryset:
data.append({
"type": self.item_type,
"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,
self.template,
{
"title": self.title,
"type": self.item_type,
"data": data,
"matches": paginator.count,
"page_range": page_range,
},
)
class GetRoster(GetData):
title = "Rolling stock"
item_type = "rolling_stock"
queryset = RollingStock.objects.order_by(*order_by_fields())
class SearchRoster(View):
def run_search(self, request, search, _filter, page=1):
site_conf = get_site_conf()
if _filter is None:
query = reduce(
operator.or_,
(
Q(
Q(rolling_class__identifier__icontains=s)
| Q(rolling_class__description__icontains=s)
| Q(rolling_class__type__type__icontains=s)
| Q(road_number__icontains=s)
| Q(item_number=s)
| Q(rolling_class__company__name__icontains=s)
| Q(rolling_class__company__country__icontains=s)
| Q(manufacturer__name__icontains=s)
| Q(scale__scale__icontains=s)
| Q(tags__name__icontains=s)
)
for s in search.split()
),
)
elif _filter == "type":
query = Q(
Q(rolling_class__type__type__icontains=search)
| Q(rolling_class__type__category__icontains=search)
)
elif _filter == "company":
query = Q(
Q(rolling_class__company__name__icontains=search)
| Q(rolling_class__company__extended_name__icontains=search)
)
elif _filter == "manufacturer":
query = Q(
Q(manufacturer__name__icontains=search)
| Q(rolling_class__manufacturer__name__icontains=search)
)
elif _filter == "scale":
query = Q(scale__scale__icontains=search)
else:
raise Http404
# FIXME duplicated code!
data = []
rolling_stock = (
RollingStock.objects.filter(query)
.distinct()
.order_by(*order_by_fields())
)
for item in rolling_stock:
data.append({
"type": "rolling_stock",
"item": item
})
if _filter is None:
consists = (
Consist.objects.filter(
Q(
Q(identifier__icontains=search)
| Q(company__name__icontains=search)
)
)
.distinct()
)
for item in consists:
data.append({
"type": "consist",
"item": item
})
books = (
Book.objects.filter(title__icontains=search)
.distinct()
)
for item in books:
data.append({
"type": "book",
"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 data, paginator.count, page_range
def split_search(self, search):
search = search.strip().split(":")
if not search:
raise Http404
elif len(search) == 1: # no filter
_filter = None
search = search[0].strip()
elif len(search) == 2: # filter: search
_filter = search[0].strip().lower()
search = search[1].strip()
else:
return HttpResponseBadRequest
return _filter, search
def get(self, request, search, page=1):
try:
encoded_search = search
search = base64.b64decode(search.encode()).decode()
except Exception:
encoded_search = base64.b64encode(
search.encode()).decode()
_filter, keyword = self.split_search(search)
data, matches, page_range = self.run_search(
request, keyword, _filter, page
)
return render(
request,
"search.html",
{
"title": "Search: \"{}\"".format(search),
"search": search,
"encoded_search": encoded_search,
"data": data,
"matches": matches,
"page_range": page_range,
},
)
def post(self, request, page=1):
search = request.POST.get("search")
return self.get(request, search, page)
class GetRosterFiltered(View):
def run_filter(self, request, search, _filter, page=1):
site_conf = get_site_conf()
if _filter == "type":
title = RollingStockType.objects.get(slug__iexact=search)
query = Q(rolling_class__type__slug__iexact=search)
elif _filter == "company":
title = get_object_or_404(Company, slug__iexact=search)
query = Q(rolling_class__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)
elif _filter == "tag":
title = get_object_or_404(Tag, slug__iexact=search)
query = Q(tags__slug__iexact=search)
else:
raise Http404
rolling_stock = (
RollingStock.objects.filter(query)
.distinct()
.order_by(*order_by_fields())
)
data = []
for item in rolling_stock:
data.append({
"type": "rolling_stock",
"item": item
})
if _filter == "tag":
consists = (
Consist.objects.filter(query)
.distinct()
)
for item in consists:
data.append({
"type": "consist",
"item": item
})
books = (
Book.objects.filter(query)
.distinct()
)
for item in books:
data.append({
"type": "book",
"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 data, title, paginator.count, page_range
def get(self, request, search, _filter, page=1):
data, title, matches, page_range = self.run_filter(
request, unquote(search), _filter, page
)
return render(
request,
"filter.html",
{
"title": "{0}: {1}".format(
_filter.capitalize(), title),
"search": search,
"filter": _filter,
"data": data,
"matches": matches,
"page_range": page_range,
},
)
class GetRollingStock(View):
def get(self, request, uuid):
try:
rolling_stock = RollingStock.objects.get(uuid=uuid)
except ObjectDoesNotExist:
raise Http404
# FIXME there's likely a better and more efficient way of doing this
# but keeping KISS for now
decoder_documents = []
if request.user.is_authenticated:
class_properties = rolling_stock.rolling_class.property.all()
properties = rolling_stock.property.all()
documents = rolling_stock.document.all()
journal = rolling_stock.journal.all()
if rolling_stock.decoder:
decoder_documents = rolling_stock.decoder.document.all()
else:
class_properties = rolling_stock.rolling_class.property.filter(
property__private=False
)
properties = rolling_stock.property.filter(
property__private=False
)
documents = rolling_stock.document.filter(private=False)
journal = rolling_stock.journal.filter(private=False)
if rolling_stock.decoder:
decoder_documents = rolling_stock.decoder.document.filter(
private=False
)
return render(
request,
"rollingstock.html",
{
"title": rolling_stock,
"rolling_stock": rolling_stock,
"class_properties": class_properties,
"properties": properties,
"decoder_documents": decoder_documents,
"documents": documents,
"journal": journal,
},
)
class Consists(GetData):
def __init__(self):
self.title = "Consists"
self.item_type = "consist"
self.queryset = Consist.objects.all()
class GetConsist(View):
def get(self, request, uuid, page=1):
site_conf = get_site_conf()
try:
consist = Consist.objects.get(uuid=uuid)
except ObjectDoesNotExist:
raise Http404
data = [{
"type": "rolling_stock",
"item": RollingStock.objects.get(uuid=r.rolling_stock_id)
} for r in consist.consist_item.all()]
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,
"consist.html",
{
"title": consist,
"consist": consist,
"data": data,
"page_range": page_range,
},
)
class Manufacturers(GetData):
title = "Manufacturers"
item_type = "manufacturer"
queryset = None # Set via method get
# overload get method to filter by category
def get(self, request, category, page=1):
if category not in ("real", "model"):
raise Http404
self.queryset = Manufacturer.objects.filter(category=category)
return super().get(request, page)
class Companies(GetData):
title = "Companies"
item_type = "company"
queryset = Company.objects.all()
class Scales(GetData):
title = "Scales"
item_type = "scale"
queryset = Scale.objects.all()
class Types(GetData):
title = "Types"
item_type = "rolling_stock_type"
queryset = RollingStockType.objects.all()
class Books(GetData):
title = "Books"
item_type = "book"
queryset = Book.objects.all()
class GetBook(View):
def get(self, request, uuid):
try:
book = Book.objects.get(uuid=uuid)
except ObjectDoesNotExist:
raise Http404
book_properties = (
book.property.all()
if request.user.is_authenticated
else book.property.filter(property__private=False)
)
return render(
request,
"bookshelf/book.html",
{
"title": book,
"book_properties": book_properties,
"book": book,
},
)
class GetFlatpage(View):
def get(self, request, flatpage):
try:
flatpage = Flatpage.objects.get(path=flatpage)
except ObjectDoesNotExist:
raise Http404
if not request.user.is_authenticated:
flatpage = flatpage.filter(published=False)
return render(
request,
"flatpages/flatpage.html",
{"title": flatpage.name, "flatpage": flatpage},
)