from __future__ import unicode_literals import hashlib from django.apps import apps from django.db import models from django.template import loader from django.template.response import TemplateResponse from django.urls import reverse from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.translation import ugettext from .settings import setting_home_view @python_2_unicode_compatible class Collection(object): _registry = [] @classmethod def get_all(cls): return sorted(cls._registry, key=lambda entry: entry._order) def __init__(self, label, icon=None, icon_class=None, link=None, queryset=None, model=None, order=None): self._label = label self._icon = icon self._icon_class = icon_class self._link = link self._queryset = queryset self._model = model self._order = order or 99 self.__class__._registry.append(self) def __str__(self): return force_text(self.label) def resolve(self): self.children = self._get_children() self.icon = self._icon self.label = self._label self.url = None if self._link: self.icon = getattr(self._link, 'icon', self._icon) self.icon_class = getattr(self._link, 'icon_class', self._icon_class) self.url = reverse(viewname=self._link.view, args=self._link.args) return '' def _get_children(self): if self._queryset: return self._queryset else: if self._model: return self._model.objects.all() class Dashboard(object): _registry = {} @classmethod def get(cls, name): return cls._registry[name] def __init__(self, name, label): self.name = name self.label = label self.widgets = {} self.removed_widgets = [] self.__class__._registry[name] = self def add_widget(self, widget, order=0): self.widgets[widget] = {'widget': widget, 'order': order} def get_widgets(self): """ Returns a list of widgets sorted by their 'order'. If two or more widgets have the same 'order', sort by label. """ return map( lambda x: x['widget'], filter( lambda x: x['widget'] not in self.removed_widgets, sorted( self.widgets.values(), key=lambda x: (x['order'], x['widget'].label) ) ) ) def remove_widget(self, widget): self.removed_widgets.append(widget) def render(self, request): rendered_widgets = [widget().render(request=request) for widget in self.get_widgets()] return loader.render_to_string( template_name='dashboard/dashboard.html', context={ 'widgets': rendered_widgets } ) class BaseDashboardWidget(object): _registry = {} context = {} template_name = None @classmethod def get(cls, name): return cls._registry[name] @classmethod def get_all(cls): return cls._registry.items() @classmethod def register(cls, klass): cls._registry[klass.name] = klass def get_context(self): return self.context def render(self, request): if self.template_name: return loader.render_to_string( template_name=self.template_name, context=self.get_context(), ) class DashboardWidgetNumeric(BaseDashboardWidget): count = 0 icon_class = None label = None link = None template_name = 'dashboard/numeric_widget.html' def get_context(self): return { 'count': self.count, 'icon_class': self.icon_class, 'label': self.label, 'link': self.link, } @python_2_unicode_compatible class ErrorLogNamespace(object): def __init__(self, name, label=None): self.name = name self.label = label or name def __str__(self): return force_text(self.label) def create(self, obj, result): obj.error_logs.create(namespace=self.name, result=result) def all(self): ErrorLogEntry = apps.get_model( app_label='common', model_name='ErrorLogEntry' ) return ErrorLogEntry.objects.filter(namespace=self.name) class MissingItem(object): _registry = [] @classmethod def get_all(cls): return cls._registry def __init__(self, label, condition, description, view): self.label = label self.condition = condition self.description = description self.view = view self.__class__._registry.append(self) @python_2_unicode_compatible class ModelAttribute(object): _registry = {} @classmethod def get_for(cls, model): try: return cls._registry[model] except KeyError: # We were passed a model instance, try again using the model of # the instance # If we are already in the model class, exit with an error if model.__class__ == models.base.ModelBase: raise return cls.get_for(model=type(model)) @classmethod def get_choices_for(cls, model): return [ (attribute.name, attribute) for attribute in cls.get_for(model) ] @classmethod def get_help_text_for(cls, model, show_name=False): result = [] for count, attribute in enumerate(cls.get_for(model=model), 1): result.append( '{}) {}'.format( count, force_text(attribute.get_display(show_name=show_name)) ) ) return ' '.join( [ugettext('Available attributes: \n'), '\n'.join(result)] ) def __init__(self, model, name, label=None, description=None): self.model = model self.label = label self.name = name self.description = description self._registry.setdefault(model, []) self._registry[model].append(self) def __str__(self): return self.get_display() def get_display(self, show_name=False): if self.description: return '{} - {}'.format( self.name if show_name else self.label, self.description ) else: return force_text(self.name if show_name else self.label) class ModelField(ModelAttribute): """Subclass to handle model database fields""" _registry = {} @classmethod def get_help_text_for(cls, model, show_name=False): result = [] for count, model_field in enumerate(cls.get_for(model=model), 1): result.append( '{}) {} - {}'.format( count, model_field.name if show_name else model_field.label, model_field.description ) ) return ' '.join( [ugettext('Available fields: \n'), '\n'.join(result)] ) def __init__(self, *args, **kwargs): super(ModelField, self).__init__(*args, **kwargs) self._final_model_verbose_name = None if not self.label: self.label = self.get_field_attribute( attribute='verbose_name' ) if self.label != self._final_model_verbose_name: self.label = '{} {}'.format( self._final_model_verbose_name, self.label ) if not self.description: self.description = self.get_field_attribute( attribute='help_text' ) def get_field_attribute(self, attribute, model=None, field_name=None): if not model: model = self.model if not field_name: field_name = self.name parts = field_name.split('__') if len(parts) > 1: return self.get_field_attribute( model=model._meta.get_field(parts[0]).related_model, field_name='__'.join(parts[1:]), attribute=attribute ) else: self._final_model_verbose_name = model._meta.verbose_name return getattr( model._meta.get_field(field_name=field_name), attribute ) class ModelProperty(object): _registry = [] @classmethod def get_for(cls, model): result = [] for klass in cls._registry: result.extend(klass.get_for(model=model)) return result @classmethod def get_choices_for(cls, model): result = [] for klass in cls._registry: result.extend(klass.get_choices_for(model=model)) return result @classmethod def get_help_text_for(cls, model, show_name=False): result = [] for klass in cls._registry: result.append( klass.get_help_text_for(model=model, show_name=show_name) ) return '\n'.join(result) @classmethod def register(cls, klass): cls._registry.append(klass) class Package(object): _registry = [] @classmethod def get_all(cls): return cls._registry def __init__(self, label, license_text): self.label = label self.license_text = license_text self.__class__._registry.append(self) class PropertyHelper(object): """ Makes adding fields using __class__.add_to_class easier. Each subclass must implement the `constructor` and the `get_result` method. """ @staticmethod @property def constructor(source_object): return PropertyHelper(source_object) def __init__(self, instance): self.instance = instance def __getattr__(self, name): return self.get_result(name=name) def get_result(self, name): """ The method that produces the actual result. Must be implemented by each subclass. """ raise NotImplementedError class Template(object): _registry = {} @classmethod def all(cls, rendered=False, request=None): if not rendered: return cls._registry.values() else: result = [] for template in cls._registry.values(): result.append(template.render(request=request)) return result @classmethod def get(cls, name): return cls._registry[name] def __init__(self, name, template_name): self.name = name self.template_name = template_name self.__class__._registry[name] = self def get_absolute_url(self): return reverse( kwargs={'name': self.name}, viewname='rest_api:template-detail' ) def render(self, request): context = { 'home_view': setting_home_view.value, } result = TemplateResponse( request=request, template=self.template_name, context=context, ).render() # Calculate the hash of the bytes version but return the unicode # version self.html = result.rendered_content.replace('\n', '') self.hex_hash = hashlib.sha256(result.content).hexdigest() return self ModelProperty.register(ModelAttribute) ModelProperty.register(ModelField)