Made all unit tests pass

This commit is contained in:
Griatch 2019-01-09 22:08:48 +01:00
parent 844b04adbb
commit aa48593a40
10 changed files with 148 additions and 110 deletions

View file

@ -258,12 +258,12 @@ def prototype_from_object(obj):
aliases = obj.aliases.get(return_list=True) aliases = obj.aliases.get(return_list=True)
if aliases: if aliases:
prot['aliases'] = aliases prot['aliases'] = aliases
tags = [(tag.db_key, tag.db_category, tag.db_data) tags = sorted([(tag.db_key, tag.db_category, tag.db_data)
for tag in obj.tags.all(return_objs=True)] for tag in obj.tags.all(return_objs=True)])
if tags: if tags:
prot['tags'] = tags prot['tags'] = tags
attrs = [(attr.key, attr.value, attr.category, ';'.join(attr.locks.all())) attrs = sorted([(attr.key, attr.value, attr.category, ';'.join(attr.locks.all()))
for attr in obj.attributes.all()] for attr in obj.attributes.all()])
if attrs: if attrs:
prot['attrs'] = attrs prot['attrs'] = attrs

View file

@ -122,9 +122,9 @@ class TestUtils(EvenniaTest):
self.assertEqual(obj_prototype, self.assertEqual(obj_prototype,
{'aliases': ['foo'], {'aliases': ['foo'],
'attrs': [('oldtest', 'to_keep', None, ''), 'attrs': [('desc', 'changed desc', None, ''),
('test', 'testval', None, ''), ('oldtest', 'to_keep', None, ''),
('desc', 'changed desc', None, '')], ('test', 'testval', None, '')],
'key': 'Obj', 'key': 'Obj',
'home': '#1', 'home': '#1',
'location': '#1', 'location': '#1',
@ -213,9 +213,9 @@ class TestUtils(EvenniaTest):
self.assertEqual(count, 1) self.assertEqual(count, 1)
new_prot = spawner.prototype_from_object(self.obj1) new_prot = spawner.prototype_from_object(self.obj1)
self.assertEqual({'attrs': [('oldtest', 'to_keep', None, ''), self.assertEqual({'attrs': [('fooattr', 'fooattrval', None, ''),
('fooattr', 'fooattrval', None, ''),
('new', 'new_val', None, ''), ('new', 'new_val', None, ''),
('oldtest', 'to_keep', None, ''),
('test', 'testval_changed', None, '')], ('test', 'testval_changed', None, '')],
'home': Something, 'home': Something,
'key': 'Obj', 'key': 'Obj',

View file

@ -1401,7 +1401,7 @@ def create_superuser():
""" """
print( print(
"\nCreate a superuser below. The superuser is Account #1, the 'owner' " "\nCreate a superuser below. The superuser is Account #1, the 'owner' "
"account of the server.\n") "account of the server. Email is optional and can be empty.\n")
django.core.management.call_command("createsuperuser", interactive=True) django.core.management.call_command("createsuperuser", interactive=True)

View file

@ -114,20 +114,20 @@ class TestTelnet(TwistedTestCase):
self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'], {0: DEFAULT_WIDTH}) self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'], {0: DEFAULT_WIDTH})
self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'], {0: DEFAULT_HEIGHT}) self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'], {0: DEFAULT_HEIGHT})
self.proto.dataReceived(IAC + WILL + NAWS) self.proto.dataReceived(IAC + WILL + NAWS)
self.proto.dataReceived([IAC, SB, NAWS, '', 'x', '', 'd', IAC, SE]) self.proto.dataReceived(b"".join([IAC, SB, NAWS, b'', b'x', b'', b'd', IAC, SE]))
self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'][0], 120) self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'][0], 78)
self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'][0], 100) self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'][0], 45)
self.assertEqual(self.proto.handshakes, 6) self.assertEqual(self.proto.handshakes, 6)
# test ttype # test ttype
self.assertTrue(self.proto.protocol_flags["FORCEDENDLINE"]) self.assertTrue(self.proto.protocol_flags["FORCEDENDLINE"])
self.assertFalse(self.proto.protocol_flags["TTYPE"]) self.assertFalse(self.proto.protocol_flags["TTYPE"])
self.assertTrue(self.proto.protocol_flags["ANSI"]) self.assertTrue(self.proto.protocol_flags["ANSI"])
self.proto.dataReceived(IAC + WILL + TTYPE) self.proto.dataReceived(IAC + WILL + TTYPE)
self.proto.dataReceived([IAC, SB, TTYPE, IS, "MUDLET", IAC, SE]) self.proto.dataReceived(b"".join([IAC, SB, TTYPE, IS, b"MUDLET", IAC, SE]))
self.assertTrue(self.proto.protocol_flags["XTERM256"]) self.assertTrue(self.proto.protocol_flags["XTERM256"])
self.assertEqual(self.proto.protocol_flags["CLIENTNAME"], "MUDLET") self.assertEqual(self.proto.protocol_flags["CLIENTNAME"], "MUDLET")
self.proto.dataReceived([IAC, SB, TTYPE, IS, "XTERM", IAC, SE]) self.proto.dataReceived(b"".join([IAC, SB, TTYPE, IS, b"XTERM", IAC, SE]))
self.proto.dataReceived([IAC, SB, TTYPE, IS, "MTTS 137", IAC, SE]) self.proto.dataReceived(b"".join([IAC, SB, TTYPE, IS, b"MTTS 137", IAC, SE]))
self.assertEqual(self.proto.handshakes, 5) self.assertEqual(self.proto.handshakes, 5)
# test mccp # test mccp
self.proto.dataReceived(IAC + DONT + MCCP) self.proto.dataReceived(IAC + DONT + MCCP)
@ -138,7 +138,7 @@ class TestTelnet(TwistedTestCase):
self.assertEqual(self.proto.handshakes, 3) self.assertEqual(self.proto.handshakes, 3)
# test oob # test oob
self.proto.dataReceived(IAC + DO + MSDP) self.proto.dataReceived(IAC + DO + MSDP)
self.proto.dataReceived([IAC, SB, MSDP, MSDP_VAR, "LIST", MSDP_VAL, "COMMANDS", IAC, SE]) self.proto.dataReceived(b"".join([IAC, SB, MSDP, MSDP_VAR, b"LIST", MSDP_VAL, b"COMMANDS", IAC, SE]))
self.assertTrue(self.proto.protocol_flags['OOB']) self.assertTrue(self.proto.protocol_flags['OOB'])
self.assertEqual(self.proto.handshakes, 2) self.assertEqual(self.proto.handshakes, 2)
# test mxp # test mxp

View file

@ -10,6 +10,25 @@ from evennia.utils.test_resources import EvenniaTest
# ------------------------------------------------------------ # ------------------------------------------------------------
class TestAttributes(EvenniaTest):
def test_attrhandler(self):
key = 'testattr'
value = 'test attr value '
self.obj1.attributes.add(key, value)
self.assertEqual(self.obj1.attributes.get(key), value)
self.obj1.db.testattr = value
self.assertEqual(self.obj1.db.testattr, value)
def test_weird_text_save(self):
"test 'weird' text type (different in py2 vs py3)"
from django.utils.safestring import SafeText
key = 'test attr 2'
value = SafeText('test attr value 2')
self.obj1.attributes.add(key, value)
self.assertEqual(self.obj1.attributes.get(key), value)
class TestTypedObjectManager(EvenniaTest): class TestTypedObjectManager(EvenniaTest):
def _manager(self, methodname, *args, **kwargs): def _manager(self, methodname, *args, **kwargs):
return list(getattr(self.obj1.__class__.objects, methodname)(*args, **kwargs)) return list(getattr(self.obj1.__class__.objects, methodname)(*args, **kwargs))

View file

@ -29,6 +29,7 @@ except ImportError:
from pickle import dumps, loads from pickle import dumps, loads
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import SafeString, SafeBytes
from evennia.utils.utils import to_str, uses_database, is_iter from evennia.utils.utils import to_str, uses_database, is_iter
from evennia.utils import logger from evennia.utils import logger
@ -521,7 +522,7 @@ def to_pickle(data):
def process_item(item): def process_item(item):
"""Recursive processor and identification of data""" """Recursive processor and identification of data"""
dtype = type(item) dtype = type(item)
if dtype in (str, int, float, bool): if dtype in (str, int, float, bool, bytes, SafeString, SafeBytes):
return item return item
elif dtype == tuple: elif dtype == tuple:
return tuple(process_item(val) for val in item) return tuple(process_item(val) for val in item)
@ -573,7 +574,7 @@ def from_pickle(data, db_obj=None):
def process_item(item): def process_item(item):
"""Recursive processor and identification of data""" """Recursive processor and identification of data"""
dtype = type(item) dtype = type(item)
if dtype in (str, int, float, bool): if dtype in (str, int, float, bool, bytes, SafeString, SafeBytes):
return item return item
elif _IS_PACKED_DBOBJ(item): elif _IS_PACKED_DBOBJ(item):
# this must be checked before tuple # this must be checked before tuple
@ -602,7 +603,7 @@ def from_pickle(data, db_obj=None):
def process_tree(item, parent): def process_tree(item, parent):
"""Recursive processor, building a parent-tree from iterable data""" """Recursive processor, building a parent-tree from iterable data"""
dtype = type(item) dtype = type(item)
if dtype in (str, int, float, bool): if dtype in (str, int, float, bool, bytes, SafeString, SafeBytes):
return item return item
elif _IS_PACKED_DBOBJ(item): elif _IS_PACKED_DBOBJ(item):
# this must be checked before tuple # this must be checked before tuple

View file

@ -10,13 +10,10 @@ class TestEvForm(TestCase):
def test_form(self): def test_form(self):
self.maxDiff = None self.maxDiff = None
form1 = evform._test() form1 = evform._test()
print("len(form1): {}".format(len(form1)))
form2 = evform._test() form2 = evform._test()
print("len(form2): {}".format(len(form2)))
self.assertEqual(form1, form2) self.assertEqual(form1, form2)
# self.assertEqual(form, "") # self.assertEqual(form1, "")
# '.------------------------------------------------.\n' # '.------------------------------------------------.\n'
# '| |\n' # '| |\n'
# '| Name: \x1b[0m\x1b[1m\x1b[32mTom\x1b[1m\x1b[32m \x1b' # '| Name: \x1b[0m\x1b[1m\x1b[32mTom\x1b[1m\x1b[32m \x1b'

View file

@ -140,10 +140,12 @@ class CharacterForm(ObjectForm):
} }
# Fields pertaining to configurable attributes on the Character object. # Fields pertaining to configurable attributes on the Character object.
desc = forms.CharField(label='Description', max_length=2048, required=False, desc = forms.CharField(
label='Description', max_length=2048, required=False,
widget=forms.Textarea(attrs={'rows': 3}), widget=forms.Textarea(attrs={'rows': 3}),
help_text="A brief description of your character.") help_text="A brief description of your character.")
class CharacterUpdateForm(CharacterForm): class CharacterUpdateForm(CharacterForm):
""" """
This is a Django form for updating Evennia Character objects. This is a Django form for updating Evennia Character objects.

View file

@ -1,24 +1,22 @@
"""
This file contains the generic, assorted views that don't fall under one of the other applications.
Views are django's way of processing e.g. html templates on the fly.
""" """
This file contains the generic, assorted views that don't fall under one of
the other applications. Views are django's way of processing e.g. html
templates on the fly.
"""
from collections import OrderedDict from collections import OrderedDict
from django.contrib.admin.sites import site from django.contrib.admin.sites import site
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db.models.functions import Lower from django.db.models.functions import Lower
from django.http import HttpResponseBadRequest, HttpResponseRedirect, Http404 from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse, reverse_lazy from django.urls import reverse_lazy
from django.views.generic import View, TemplateView, ListView, DetailView, FormView from django.views.generic import TemplateView, ListView, DetailView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic.edit import CreateView, UpdateView, DeleteView
@ -26,15 +24,15 @@ from evennia import SESSION_HANDLER
from evennia.help.models import HelpEntry from evennia.help.models import HelpEntry
from evennia.objects.models import ObjectDB from evennia.objects.models import ObjectDB
from evennia.accounts.models import AccountDB from evennia.accounts.models import AccountDB
from evennia.utils import class_from_module, logger from evennia.utils import class_from_module
from evennia.utils.logger import tail_log_file from evennia.utils.logger import tail_log_file
from evennia.web.website.forms import * from evennia.web.website import forms as website_forms
from django.contrib.auth import login
from django.utils.text import slugify from django.utils.text import slugify
_BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS _BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
def _gamestats(): def _gamestats():
# Some misc. configurable stuff. # Some misc. configurable stuff.
# TODO: Move this to either SQL or settings.py based configuration. # TODO: Move this to either SQL or settings.py based configuration.
@ -49,8 +47,10 @@ def _gamestats():
# nsess = len(AccountDB.objects.get_connected_accounts()) or "no one" # nsess = len(AccountDB.objects.get_connected_accounts()) or "no one"
nobjs = ObjectDB.objects.all().count() nobjs = ObjectDB.objects.all().count()
nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude(db_typeclass_path=_BASE_CHAR_TYPECLASS).count() nrooms = ObjectDB.objects.filter(
nexits = ObjectDB.objects.filter(db_location__isnull=False, db_destination__isnull=False).count() db_location__isnull=True).exclude(db_typeclass_path=_BASE_CHAR_TYPECLASS).count()
nexits = ObjectDB.objects.filter(
db_location__isnull=False, db_destination__isnull=False).count()
nchars = ObjectDB.objects.filter(db_typeclass_path=_BASE_CHAR_TYPECLASS).count() nchars = ObjectDB.objects.filter(db_typeclass_path=_BASE_CHAR_TYPECLASS).count()
nothers = nobjs - nrooms - nchars - nexits nothers = nobjs - nrooms - nchars - nexits
@ -99,6 +99,7 @@ def admin_wrapper(request):
""" """
return staff_member_required(site.index)(request) return staff_member_required(site.index)(request)
# #
# Class-based views # Class-based views
# #
@ -237,6 +238,7 @@ class EvenniaDeleteView(DeleteView, TypeclassMixin):
# Makes sure the page has a sensible title. # Makes sure the page has a sensible title.
return 'Delete %s' % self.typeclass._meta.verbose_name.title() return 'Delete %s' % self.typeclass._meta.verbose_name.title()
# #
# Object views # Object views
# #
@ -336,7 +338,8 @@ class ObjectDetailView(EvenniaDetailView):
# Check if this object was requested in a valid manner # Check if this object was requested in a valid manner
if slugify(obj.name) != self.kwargs.get(self.slug_url_kwarg): if slugify(obj.name) != self.kwargs.get(self.slug_url_kwarg):
raise HttpResponseBadRequest(u"No %(verbose_name)s found matching the query" % raise HttpResponseBadRequest(
u"No %(verbose_name)s found matching the query" %
{'verbose_name': queryset.model._meta.verbose_name}) {'verbose_name': queryset.model._meta.verbose_name})
# Check if the requestor account has permissions to access object # Check if the requestor account has permissions to access object
@ -430,7 +433,8 @@ class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
object detail page so the user can see their changes reflected. object detail page so the user can see their changes reflected.
""" """
if self.success_url: return self.success_url if self.success_url:
return self.success_url
return self.object.web_get_detail_url() return self.object.web_get_detail_url()
def get_initial(self): def get_initial(self):
@ -475,13 +479,14 @@ class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
# Update the object attributes # Update the object attributes
for key, value in data.items(): for key, value in data.items():
setattr(self.object.db, key, value) self.object.attributes.add(key, value)
messages.success(self.request, "Successfully updated '%s' for %s." % (key, self.object)) messages.success(self.request, "Successfully updated '%s' for %s." % (key, self.object))
# Do not return super().form_valid; we don't want to update the model # Do not return super().form_valid; we don't want to update the model
# instance, just its attributes. # instance, just its attributes.
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
# #
# Account views # Account views
# #
@ -496,7 +501,7 @@ class AccountMixin(TypeclassMixin):
""" """
# -- Django constructs -- # -- Django constructs --
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS) model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
form_class = AccountForm form_class = website_forms.AccountForm
class AccountCreateView(AccountMixin, EvenniaCreateView): class AccountCreateView(AccountMixin, EvenniaCreateView):
@ -537,11 +542,13 @@ class AccountCreateView(AccountMixin, EvenniaCreateView):
return self.form_invalid(form) return self.form_invalid(form)
# Inform user of success # Inform user of success
messages.success(self.request, "Your account '%s' was successfully created! You may log in using it now." % account.name) messages.success(self.request, "Your account '%s' was successfully created! "
"You may log in using it now." % account.name)
# Redirect the user to the login page # Redirect the user to the login page
return HttpResponseRedirect(self.success_url) return HttpResponseRedirect(self.success_url)
# #
# Character views # Character views
# #
@ -556,7 +563,7 @@ class CharacterMixin(TypeclassMixin):
""" """
# -- Django constructs -- # -- Django constructs --
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS) model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
form_class = CharacterForm form_class = website_forms.CharacterForm
success_url = reverse_lazy('character-manage') success_url = reverse_lazy('character-manage')
def get_queryset(self): def get_queryset(self):
@ -608,7 +615,8 @@ class CharacterListView(LoginRequiredMixin, CharacterMixin, ListView):
# Return a queryset consisting of characters the user is allowed to # Return a queryset consisting of characters the user is allowed to
# see. # see.
ids = [obj.id for obj in self.typeclass.objects.all() if obj.access(account, self.access_type)] ids = [obj.id for obj in self.typeclass.objects.all()
if obj.access(account, self.access_type)]
return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key')) return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key'))
@ -637,7 +645,7 @@ class CharacterPuppetView(LoginRequiredMixin, CharacterMixin, RedirectView, Obje
char = self.get_object() char = self.get_object()
# Get the page the user came from # Get the page the user came from
next = self.request.GET.get('next', self.success_url) next_page = self.request.GET.get('next', self.success_url)
if char: if char:
# If the account owns the char, store the ID of the char in the # If the account owns the char, store the ID of the char in the
@ -650,7 +658,7 @@ class CharacterPuppetView(LoginRequiredMixin, CharacterMixin, RedirectView, Obje
self.request.session['puppet'] = None self.request.session['puppet'] = None
messages.error(self.request, "You cannot become '%s'." % char) messages.error(self.request, "You cannot become '%s'." % char)
return next return next_page
class CharacterManageView(LoginRequiredMixin, CharacterMixin, ListView): class CharacterManageView(LoginRequiredMixin, CharacterMixin, ListView):
@ -674,7 +682,7 @@ class CharacterUpdateView(CharacterMixin, ObjectUpdateView):
""" """
# -- Django constructs -- # -- Django constructs --
form_class = CharacterUpdateForm form_class = website_forms.CharacterUpdateForm
template_name = 'website/character_form.html' template_name = 'website/character_form.html'
@ -705,7 +713,8 @@ class CharacterDetailView(CharacterMixin, ObjectDetailView):
# Return a queryset consisting of characters the user is allowed to # Return a queryset consisting of characters the user is allowed to
# see. # see.
ids = [obj.id for obj in self.typeclass.objects.all() if obj.access(account, self.access_type)] ids = [obj.id for obj in self.typeclass.objects.all()
if obj.access(account, self.access_type)]
return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key')) return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key'))
@ -746,7 +755,6 @@ class CharacterCreateView(CharacterMixin, ObjectCreateView):
self.attributes = {k: form.cleaned_data[k] for k in form.cleaned_data.keys()} self.attributes = {k: form.cleaned_data[k] for k in form.cleaned_data.keys()}
charname = self.attributes.pop('db_key') charname = self.attributes.pop('db_key')
description = self.attributes.pop('desc') description = self.attributes.pop('desc')
# Create a character # Create a character
character, errors = self.typeclass.create(charname, account, description=description) character, errors = self.typeclass.create(charname, account, description=description)
@ -768,6 +776,7 @@ class CharacterCreateView(CharacterMixin, ObjectCreateView):
messages.error(self.request, "Your character could not be created.") messages.error(self.request, "Your character could not be created.")
return self.form_invalid(form) return self.form_invalid(form)
# #
# Channel views # Channel views
# #
@ -881,12 +890,14 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
context = super(ChannelDetailView, self).get_context_data(**kwargs) context = super(ChannelDetailView, self).get_context_data(**kwargs)
# Get the filename this Channel is recording to # Get the filename this Channel is recording to
filename = self.object.attributes.get("log_file", default="channel_%s.log" % self.object.key) filename = self.object.attributes.get(
"log_file", default="channel_%s.log" % self.object.key)
# Split log entries so we can filter by time # Split log entries so we can filter by time
bucket = [] bucket = []
for log in (x.strip() for x in tail_log_file(filename, 0, self.max_num_lines)): for log in (x.strip() for x in tail_log_file(filename, 0, self.max_num_lines)):
if not log: continue if not log:
continue
time, msg = log.split(' [-] ') time, msg = log.split(' [-] ')
time_key = time.split(':')[0] time_key = time.split(':')[0]
@ -904,7 +915,6 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
return context return context
def get_object(self, queryset=None): def get_object(self, queryset=None):
""" """
Override of Django hook that retrieves an object by slugified channel Override of Django hook that retrieves an object by slugified channel
@ -924,7 +934,8 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
# Check if this object was requested in a valid manner # Check if this object was requested in a valid manner
if not obj: if not obj:
raise HttpResponseBadRequest(u"No %(verbose_name)s found matching the query" % raise HttpResponseBadRequest(
u"No %(verbose_name)s found matching the query" %
{'verbose_name': queryset.model._meta.verbose_name}) {'verbose_name': queryset.model._meta.verbose_name})
return obj return obj
@ -976,6 +987,7 @@ class HelpMixin(TypeclassMixin):
return filtered return filtered
class HelpListView(HelpMixin, ListView): class HelpListView(HelpMixin, ListView):
""" """
Returns a list of help entries that can be viewed by a user, authenticated Returns a list of help entries that can be viewed by a user, authenticated
@ -989,6 +1001,7 @@ class HelpListView(HelpMixin, ListView):
# -- Evennia constructs -- # -- Evennia constructs --
page_title = "Help Index" page_title = "Help Index"
class HelpDetailView(HelpMixin, EvenniaDetailView): class HelpDetailView(HelpMixin, EvenniaDetailView):
""" """
Returns the detail page for a given help entry. Returns the detail page for a given help entry.
@ -1012,7 +1025,8 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
obj = self.get_object() obj = self.get_object()
# Get queryset and filter out non-related categories # Get queryset and filter out non-related categories
queryset = self.get_queryset().filter(db_help_category=obj.db_help_category).order_by(Lower('db_key')) queryset = self.get_queryset().filter(
db_help_category=obj.db_help_category).order_by(Lower('db_key'))
context['topic_list'] = queryset context['topic_list'] = queryset
# Find the index position of the given obj in the queryset # Find the index position of the given obj in the queryset
@ -1025,12 +1039,14 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
try: try:
assert i+1 <= len(objs) and objs[i+1] is not obj assert i+1 <= len(objs) and objs[i+1] is not obj
context['topic_next'] = objs[i+1] context['topic_next'] = objs[i+1]
except: context['topic_next'] = None except:
context['topic_next'] = None
try: try:
assert i-1 >= 0 and objs[i-1] is not obj assert i-1 >= 0 and objs[i-1] is not obj
context['topic_previous'] = objs[i-1] context['topic_previous'] = objs[i-1]
except: context['topic_previous'] = None except:
context['topic_previous'] = None
# Format the help entry using HTML instead of newlines # Format the help entry using HTML instead of newlines
text = obj.db_entrytext text = obj.db_entrytext
@ -1057,11 +1073,14 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
# Find the object in the queryset # Find the object in the queryset
category = slugify(self.kwargs.get('category', '')) category = slugify(self.kwargs.get('category', ''))
topic = slugify(self.kwargs.get('topic', '')) topic = slugify(self.kwargs.get('topic', ''))
obj = next((x for x in queryset if slugify(x.db_help_category)==category and slugify(x.db_key)==topic), None) obj = next((x for x in queryset
if slugify(x.db_help_category) == category and
slugify(x.db_key) == topic), None)
# Check if this object was requested in a valid manner # Check if this object was requested in a valid manner
if not obj: if not obj:
raise HttpResponseBadRequest(u"No %(verbose_name)s found matching the query" % raise HttpResponseBadRequest(
u"No %(verbose_name)s found matching the query" %
{'verbose_name': queryset.model._meta.verbose_name}) {'verbose_name': queryset.model._meta.verbose_name})
return obj return obj