Files
Touchh/.venv/lib/python3.10/site-packages/jet/dashboard/modules.py
2024-12-06 10:45:08 +09:00

610 lines
19 KiB
Python

import json
from django import forms
from django.contrib.admin.models import LogEntry
from django.db.models import Q
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from jet.utils import get_app_list, LazyDateTimeEncoder, context_to_dict
import datetime
class DashboardModule(object):
"""
Base dashboard module class. All dashboard modules (widgets) should inherit it.
"""
#: Path to widget's template. There is no need to extend such templates from any base templates.
template = 'jet.dashboard/module.html'
enabled = True
#: Specify if module can be draggable or has static position.
draggable = True
#: Specify if module can be collapsed.
collapsible = True
#: Specify if module can be deleted.
deletable = True
show_title = True
#: Default widget title that will be displayed for widget in the dashboard. User can change it later
#: for every widget.
title = ''
#: Specify title url. ``None`` if title shouldn't be clickable.
title_url = None
css_classes = None
#: HTML content that will be displayed before widget content.
pre_content = None
#: HTML content that will be displayed after widget content.
post_content = None
children = None
#: A ``django.forms.Form`` class which may contain custom widget settings. Not required.
settings_form = None
#: A ``django.forms.Form`` class which may contain custom widget child settings, if it has any. Not required.
child_form = None
#: Child name that will be displayed when editing module contents. Required if ``child_form`` set.
child_name = None
#: Same as child name, but plural.
child_name_plural = None
settings = None
column = None
order = None
#: A boolean field which specify if widget should be rendered on dashboard page load or fetched
#: later via AJAX.
ajax_load = False
#: A boolean field which makes widget ui color contrast.
contrast = False
#: Optional style attributes which will be applied to widget content container.
style = False
class Media:
css = ()
js = ()
def __init__(self, title=None, model=None, context=None, **kwargs):
if title is not None:
self.title = title
self.model = model
self.context = context or {}
for key in kwargs:
if hasattr(self.__class__, key):
setattr(self, key, kwargs[key])
self.children = self.children or []
if self.model:
self.load_from_model()
def fullname(self):
return self.__module__ + "." + self.__class__.__name__
def load_settings(self, settings):
"""
Should be implemented to restore saved in database settings. Required if you have custom settings.
"""
pass
def load_children(self, children):
self.children = children
def store_children(self):
"""
Specify if children field should be saved to database.
"""
return False
def settings_dict(self):
"""
Should be implemented to save settings to database. This method should return ``dict`` which will be serialized
using ``json``. Required if you have custom settings.
"""
pass
def dump_settings(self, settings=None):
settings = settings or self.settings_dict()
if settings:
return json.dumps(settings, cls=LazyDateTimeEncoder)
else:
return ''
def dump_children(self):
if self.store_children():
return json.dumps(self.children, cls=LazyDateTimeEncoder)
else:
return ''
def load_from_model(self):
self.title = self.model.title
if self.model.settings:
try:
self.settings = json.loads(self.model.settings)
self.load_settings(self.settings)
except ValueError:
pass
if self.store_children() and self.model.children:
try:
children = json.loads(self.model.children)
self.load_children(children)
except ValueError:
pass
def init_with_context(self, context):
"""
Allows you to load data and initialize module's state.
"""
pass
def get_context_data(self):
context = context_to_dict(self.context)
context.update({
'module': self
})
return context
def render(self):
self.init_with_context(self.context)
return render_to_string(self.template, self.get_context_data())
class LinkListItemForm(forms.Form):
url = forms.CharField(label=_('URL'))
title = forms.CharField(label=_('Title'))
external = forms.BooleanField(label=_('External link'), required=False)
class LinkListSettingsForm(forms.Form):
layout = forms.ChoiceField(label=_('Layout'), choices=(('stacked', _('Stacked')), ('inline', _('Inline'))))
class LinkList(DashboardModule):
"""
List of links widget.
Usage example:
.. code-block:: python
from django.utils.translation import ugettext_lazy as _
from jet.dashboard import modules
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
class CustomIndexDashboard(Dashboard):
columns = 3
def init_with_context(self, context):
self.available_children.append(modules.LinkList)
self.children.append(modules.LinkList(
_('Support'),
children=[
{
'title': _('Django documentation'),
'url': 'http://docs.djangoproject.com/',
'external': True,
},
{
'title': _('Django "django-users" mailing list'),
'url': 'http://groups.google.com/group/django-users',
'external': True,
},
{
'title': _('Django irc channel'),
'url': 'irc://irc.freenode.net/django',
'external': True,
},
],
column=0,
order=0
))
"""
title = _('Links')
template = 'jet.dashboard/modules/link_list.html'
#: Specify widget layout.
#: Allowed values ``stacked`` and ``inline``.
layout = 'stacked'
#: Links are contained in ``children`` attribute which you can pass as constructor parameter
#: to make your own preinstalled link lists.
#:
#: ``children`` is an array of dictinaries::
#:
#: [
#: {
#: 'title': _('Django documentation'),
#: 'url': 'http://docs.djangoproject.com/',
#: 'external': True,
#: },
#: ...
#: ]
children = []
settings_form = LinkListSettingsForm
child_form = LinkListItemForm
child_name = _('Link')
child_name_plural = _('Links')
def __init__(self, title=None, children=list(), **kwargs):
children = list(map(self.parse_link, children))
kwargs.update({'children': children})
super(LinkList, self).__init__(title, **kwargs)
def settings_dict(self):
return {
'draggable': self.draggable,
'deletable': self.deletable,
'collapsible': self.collapsible,
'layout': self.layout
}
def load_settings(self, settings):
self.draggable = settings.get('draggable', self.draggable)
self.deletable = settings.get('deletable', self.deletable)
self.collapsible = settings.get('collapsible', self.collapsible)
self.layout = settings.get('layout', self.layout)
def store_children(self):
return True
def parse_link(self, link):
if isinstance(link, (tuple, list)):
link_dict = {'title': link[0], 'url': link[1]}
if len(link) >= 3:
link_dict['external'] = link[2]
return link_dict
elif isinstance(link, (dict,)):
return link
class AppList(DashboardModule):
"""
Shows applications and containing models links. For each model "created" and "change" links are displayed.
Usage example:
.. code-block:: python
from django.utils.translation import ugettext_lazy as _
from jet.dashboard import modules
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
class CustomIndexDashboard(Dashboard):
columns = 3
def init_with_context(self, context):
self.children.append(modules.AppList(
_('Applications'),
exclude=('auth.*',),
column=0,
order=0
))
"""
title = _('Applications')
template = 'jet.dashboard/modules/app_list.html'
#: Specify models which should be displayed. ``models`` is an array of string formatted as ``app_label.model``.
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
models = None
#: Specify models which should NOT be displayed. ``exclude`` is an array of string formatted as ``app_label.model``.
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
exclude = None
hide_empty = True
def settings_dict(self):
return {
'models': self.models,
'exclude': self.exclude
}
def load_settings(self, settings):
self.models = settings.get('models')
self.exclude = settings.get('exclude')
def init_with_context(self, context):
app_list = get_app_list(context)
app_to_remove = []
for app in app_list:
app_name = app.get('app_label', app.get('name', ''))
app['models'] = filter(
lambda model: self.models is None or ('%s.%s' % (app_name, model['object_name'])) in self.models or ('%s.*' % app_name) in self.models,
app['models']
)
app['models'] = filter(
lambda model: self.exclude is None or (('%s.%s' % (app_name, model['object_name'])) not in self.exclude and ('%s.*' % app_name) not in self.exclude),
app['models']
)
app['models'] = list(app['models'])
if self.hide_empty and len(list(app['models'])) == 0:
app_to_remove.append(app)
for app in app_to_remove:
app_list.remove(app)
self.children = app_list
class ModelList(DashboardModule):
"""
Shows models links. For each model "created" and "change" links are displayed.
Usage example:
.. code-block:: python
from django.utils.translation import ugettext_lazy as _
from jet.dashboard import modules
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
class CustomIndexDashboard(Dashboard):
columns = 3
def init_with_context(self, context):
self.children.append(modules.ModelList(
_('Models'),
exclude=('auth.*',),
column=0,
order=0
))
"""
title = _('Models')
template = 'jet.dashboard/modules/model_list.html'
#: Specify models which should be displayed. ``models`` is an array of string formatted as ``app_label.model``.
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
models = None
#: Specify models which should NOT be displayed. ``exclude`` is an array of string formatted as ``app_label.model``.
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
exclude = None
hide_empty = True
def settings_dict(self):
return {
'models': self.models,
'exclude': self.exclude
}
def load_settings(self, settings):
self.models = settings.get('models')
self.exclude = settings.get('exclude')
def init_with_context(self, context):
app_list = get_app_list(context)
models = []
for app in app_list:
app_name = app.get('app_label', app.get('name', ''))
app['models'] = filter(
lambda model: self.models is None or ('%s.%s' % (app_name, model['object_name'])) in self.models or ('%s.*' % app_name) in self.models,
app['models']
)
app['models'] = filter(
lambda model: self.exclude is None or (('%s.%s' % (app_name, model['object_name'])) not in self.exclude and ('%s.*' % app_name) not in self.exclude),
app['models']
)
app['models'] = list(app['models'])
models.extend(app['models'])
self.children = models
class RecentActionsSettingsForm(forms.Form):
limit = forms.IntegerField(label=_('Items limit'), min_value=1)
class RecentActions(DashboardModule):
"""
Display list of most recent admin actions with following information:
entity name, type of action, author, date
Usage example:
.. code-block:: python
from django.utils.translation import ugettext_lazy as _
from jet.dashboard import modules
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
class CustomIndexDashboard(Dashboard):
columns = 3
def init_with_context(self, context):
self.children.append(modules.RecentActions(
_('Recent Actions'),
10,
column=0,
order=0
))
"""
title = _('Recent Actions')
template = 'jet.dashboard/modules/recent_actions.html'
#: Number if entries to be shown (may be changed by each user personally).
limit = 10
#: Specify actions of which models should be displayed. ``include_list`` is an array of string
#: formatted as ``app_label.model``. Also its possible to specify all application models
#: with * sign (e.g. ``auth.*``).
include_list = None
#: Specify actions of which models should NOT be displayed. ``exclude_list`` is an array of string
#: formatted as ``app_label.model``. Also its possible to specify all application models
#: with * sign (e.g. ``auth.*``).
exclude_list = None
settings_form = RecentActionsSettingsForm
user = None
def __init__(self, title=None, limit=10, **kwargs):
kwargs.update({'limit': limit})
super(RecentActions, self).__init__(title, **kwargs)
def settings_dict(self):
return {
'limit': self.limit,
'include_list': self.include_list,
'exclude_list': self.exclude_list,
'user': self.user
}
def load_settings(self, settings):
self.limit = settings.get('limit', self.limit)
self.include_list = settings.get('include_list')
self.exclude_list = settings.get('exclude_list')
self.user = settings.get('user', None)
def init_with_context(self, context):
def get_qset(list):
qset = None
for contenttype in list:
try:
app_label, model = contenttype.split('.')
if model == '*':
current_qset = Q(
content_type__app_label=app_label
)
else:
current_qset = Q(
content_type__app_label=app_label,
content_type__model=model
)
except:
raise ValueError('Invalid contenttype: "%s"' % contenttype)
if qset is None:
qset = current_qset
else:
qset = qset | current_qset
return qset
qs = LogEntry.objects
if self.user:
qs = qs.filter(
user__pk=int(self.user)
)
if self.include_list:
qs = qs.filter(get_qset(self.include_list))
if self.exclude_list:
qs = qs.exclude(get_qset(self.exclude_list))
self.children = qs.select_related('content_type', 'user')[:int(self.limit)]
class FeedSettingsForm(forms.Form):
limit = forms.IntegerField(label=_('Items limit'), min_value=1)
feed_url = forms.URLField(label=_('Feed URL'))
class Feed(DashboardModule):
"""
Display RSS Feed entries with following information:
entry title, date and link to the full version
Usage example:
.. code-block:: python
from django.utils.translation import ugettext_lazy as _
from jet.dashboard import modules
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
class CustomIndexDashboard(Dashboard):
columns = 3
def init_with_context(self, context):
self.children.append(modules.Feed(
_('Latest Django News'),
feed_url='http://www.djangoproject.com/rss/weblog/',
limit=5,
column=0,
order=0
))
"""
title = _('RSS Feed')
template = 'jet.dashboard/modules/feed.html'
#: URL of the RSS feed (may be changed by each user personally).
feed_url = None
#: Number if entries to be shown (may be changed by each user personally).
limit = None
settings_form = FeedSettingsForm
ajax_load = True
def __init__(self, title=None, feed_url=None, limit=None, **kwargs):
kwargs.update({'feed_url': feed_url, 'limit': limit})
super(Feed, self).__init__(title, **kwargs)
def settings_dict(self):
return {
'feed_url': self.feed_url,
'limit': self.limit
}
def load_settings(self, settings):
self.feed_url = settings.get('feed_url')
self.limit = settings.get('limit')
def init_with_context(self, context):
if self.feed_url is not None:
try:
import feedparser
feed = feedparser.parse(self.feed_url)
if self.limit is not None:
entries = feed['entries'][:self.limit]
else:
entries = feed['entries']
for entry in entries:
try:
entry.date = datetime.date(*entry.published_parsed[0:3])
except:
pass
self.children.append(entry)
except ImportError:
self.children.append({
'title': _('You must install the FeedParser python module'),
'warning': True,
})
else:
self.children.append({
'title': _('You must provide a valid feed URL'),
'warning': True,
})