Source code for evennia.web.website.views.channels

"""
Views for managing channels.

"""

from django.conf import settings
from django.db.models.functions import Lower
from django.http import HttpResponseBadRequest
from django.utils.text import slugify
from django.views.generic import ListView

from evennia.utils import class_from_module
from evennia.utils.logger import tail_log_file

from .mixins import TypeclassMixin
from .objects import ObjectDetailView


[docs]class ChannelMixin(TypeclassMixin): """ This is a "mixin", a modifier of sorts. Any view class with this in its inheritance list will be modified to work with HelpEntry objects instead of generic Objects or otherwise. """ # -- Django constructs -- model = class_from_module( settings.BASE_CHANNEL_TYPECLASS, fallback=settings.FALLBACK_CHANNEL_TYPECLASS ) # -- Evennia constructs -- page_title = "Channels" # What lock type to check for the requesting user, authenticated or not. # https://github.com/evennia/evennia/wiki/Locks#valid-access_types access_type = "listen"
[docs] def get_queryset(self): """ Django hook; here we want to return a list of only those Channels and other documentation that the current user is allowed to see. Returns: queryset (QuerySet): List of Channels available to the user. """ account = self.request.user # Get list of all Channels channels = self.typeclass.objects.all().iterator() # Now figure out which ones the current user is allowed to see bucket = [channel.id for channel in channels if channel.access(account, "listen")] # Re-query and set a sorted list filtered = self.typeclass.objects.filter(id__in=bucket).order_by(Lower("db_key")) return filtered
[docs]class ChannelListView(ChannelMixin, ListView): """ Returns a list of channels that can be viewed by a user, authenticated or not. """ # -- Django constructs -- paginate_by = 100 template_name = "website/channel_list.html" # -- Evennia constructs -- page_title = "Channel Index" max_popular = 10
[docs] def get_context_data(self, **kwargs): """ Django hook; we override it to calculate the most popular channels. Returns: context (dict): Django context object """ context = super().get_context_data(**kwargs) # Calculate which channels are most popular context["most_popular"] = sorted( list(self.get_queryset()), key=lambda channel: len(channel.subscriptions.all()), reverse=True, )[: self.max_popular] return context
[docs]class ChannelDetailView(ChannelMixin, ObjectDetailView): """ Returns the log entries for a given channel. """ # -- Django constructs -- template_name = "website/channel_detail.html" # -- Evennia constructs -- # What attributes of the object you wish to display on the page. Model-level # attributes will take precedence over identically-named db.attributes! # The order you specify here will be followed. attributes = ["name"] # How many log entries to read and display. max_num_lines = 10000
[docs] def get_context_data(self, **kwargs): """ Django hook; before we can display the channel logs, we need to recall the logfile and read its lines. Returns: context (dict): Django context object """ # Get the parent context object, necessary first step context = super().get_context_data(**kwargs) channel = self.object # Get the filename this Channel is recording to filename = channel.get_log_filename() # Split log entries so we can filter by time bucket = [] for log in (x.strip() for x in tail_log_file(filename, 0, self.max_num_lines)): if not log: continue try: time, msg = log.split(" [-] ") time_key = time.split(":")[0] except ValueError: # malformed log line. Skip. continue bucket.append({"key": time_key, "timestamp": time, "message": msg}) # Add the processed entries to the context context["object_list"] = bucket # Get a list of unique timestamps by hour and sort them context["object_filters"] = sorted(set([x["key"] for x in bucket])) return context
[docs] def get_object(self, queryset=None): """ Override of Django hook that retrieves an object by slugified channel name. Returns: channel (Channel): Channel requested in the URL. """ # Get the queryset for the help entries the user can access if not queryset: queryset = self.get_queryset() # Find the object in the queryset channel = slugify(self.kwargs.get("slug", "")) obj = next((x for x in queryset if slugify(x.db_key) == channel), None) # Check if this object was requested in a valid manner if not obj: raise HttpResponseBadRequest( "No %(verbose_name)s found matching the query" % {"verbose_name": queryset.model._meta.verbose_name} ) return obj