Add new class based dashboard widget. This new widget supports subclassing and is template based. All exising widgets have been converted. ACL filtering was added to the widget results.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2018-08-23 03:30:06 -04:00
parent 26b31da443
commit f11eef7445
10 changed files with 188 additions and 110 deletions

View File

@@ -97,6 +97,9 @@
ActionError exception. ActionError exception.
- Add favorite documents per user. Adds new setting option - Add favorite documents per user. Adds new setting option
DOCUMENTS_FAVORITE_COUNT. DOCUMENTS_FAVORITE_COUNT.
- Add new class based dashboard widget. This new widget supports
subclassing and is template based. All exising widgets have been
converted. ACL filtering was added to the widget results.
3.0.3 (2018-08-17) 3.0.3 (2018-08-17)
================== ==================

View File

@@ -67,11 +67,6 @@
</div> </div>
</div> </div>
{% get_dashboard 'main' as dashboard %} {% render_dashboard 'main' %}
<div class="row">
{% for widget in dashboard.get_widgets %}
{% include 'appearance/dashboard_widget.html' %}
{% endfor %}
</div>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,5 @@
<div class="row">
{% for widget in widgets %}
{{ widget }}
{% endfor %}
</div>

View File

@@ -5,28 +5,24 @@
<div class="panel-heading"> <div class="panel-heading">
<div class="row"> <div class="row">
<div class="col-xs-2"> <div class="col-xs-2">
{% if widget.icon %} {% if icon %}
<i class="dashboard-widget-icon {{ widget.icon }}"></i> <i class="dashboard-widget-icon {{ icon }}"></i>
{% elif widget.icon_class %} {% elif icon_class %}
<div class="dashboard-widget-icon"> <div class="dashboard-widget-icon">
{{ widget.icon_class.render }} {{ icon_class.render }}
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="col-xs-10 text-right"> <div class="col-xs-10 text-right">
<strong>{{ widget.label }}</strong> <strong>{{ label }}</strong>
<div class="huge"> <div class="huge">
{% if widget.func %} {{ count }}
{{ widget.func }}
{% else %}
{{ widget.queryset.count }}
{% endif %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% if widget.link %} {% if link %}
<a href="{{ widget.link }}"> <a href="{{ link }}">
<div class="panel-footer"> <div class="panel-footer">
<span class="pull-left">{% trans 'View details' %}</span> <span class="pull-left">{% trans 'View details' %}</span>
<span class="pull-right"><i class="fa fa-external-link-alt"></i></span> <span class="pull-right"><i class="fa fa-external-link-alt"></i></span>
@@ -36,3 +32,4 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -14,7 +14,7 @@ from common.dashboards import dashboard_main
from events import ModelEventType from events import ModelEventType
from mayan.celery import app from mayan.celery import app
from .dashboard_widgets import widget_checkouts from .dashboard_widgets import DashboardWidgetTotalCheckouts
from .events import ( from .events import (
event_document_auto_check_in, event_document_check_in, event_document_auto_check_in, event_document_check_in,
event_document_check_out, event_document_forceful_check_in event_document_check_out, event_document_forceful_check_in
@@ -117,7 +117,9 @@ class CheckoutsApp(MayanAppConfig):
} }
) )
dashboard_main.add_widget(order=-1, widget=widget_checkouts) dashboard_main.add_widget(
widget=DashboardWidgetTotalCheckouts, order=-1
)
menu_facet.bind_links(links=(link_checkout_info,), sources=(Document,)) menu_facet.bind_links(links=(link_checkout_info,), sources=(Document,))
menu_main.bind_links(links=(link_checkout_list,), position=98) menu_main.bind_links(links=(link_checkout_list,), position=98)

View File

@@ -4,21 +4,33 @@ from django.apps import apps
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.classes import DashboardWidget from common.classes import DashboardWidgetNumeric
from documents.permissions import permission_document_view
from .icons import icon_dashboard_checkouts from .icons import icon_dashboard_checkouts
from .permissions import permission_document_checkout_detail_view
def checkedout_documents_queryset(): class DashboardWidgetTotalCheckouts(DashboardWidgetNumeric):
icon_class = icon_dashboard_checkouts
label = _('Checkedout documents')
link = reverse_lazy('checkouts:checkout_list')
def render(self, request):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
)
DocumentCheckout = apps.get_model( DocumentCheckout = apps.get_model(
app_label='checkouts', model_name='DocumentCheckout' app_label='checkouts', model_name='DocumentCheckout'
) )
return DocumentCheckout.objects.all() queryset = AccessControlList.objects.filter_by_access(
permission=permission_document_checkout_detail_view,
user=request.user,
widget_checkouts = DashboardWidget( queryset=DocumentCheckout.objects.checked_out_documents()
icon_class=icon_dashboard_checkouts,
label=_('Checkedout documents'),
link=reverse_lazy('checkouts:checkout_list'),
queryset=checkedout_documents_queryset
) )
queryset = AccessControlList.objects.filter_by_access(
permission=permission_document_view, user=request.user,
queryset=queryset
)
self.count = queryset.count()
return super(DashboardWidgetTotalCheckouts, self).render(request)

View File

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
from django.apps import apps from django.apps import apps
from django.db import models from django.db import models
from django.template import loader
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext from django.utils.translation import ugettext
@@ -83,24 +84,57 @@ class Dashboard(object):
def remove_widget(self, widget): def remove_widget(self, widget):
self.removed_widgets.append(widget) self.removed_widgets.append(widget)
def render(self, request):
rendered_widgets = [widget().render(request=request) for widget in self.get_widgets()]
class DashboardWidget(object): return loader.render_to_string(
_registry = [] 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 @classmethod
def get_all(cls): def get_all(cls):
return cls._registry return cls._registry.items()
def __init__(self, label, func=None, icon=None, icon_class=None, link=None, queryset=None, statistic_slug=None): @classmethod
self.label = label def register(cls, klass):
self.icon = icon cls._registry[klass.name] = klass
self.icon_class = icon_class
self.link = link
self.queryset = queryset
self.func = func
self.statistic_slug = statistic_slug
self.__class__._registry.append(self) 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 @python_2_unicode_compatible

View File

@@ -45,12 +45,6 @@ def check_sqlite():
def get_collections(): def get_collections():
return Collection.get_all() return Collection.get_all()
@register.simple_tag
def get_dashboard(name):
return Dashboard.get(name=name)
@register.filter @register.filter
def get_encoded_parameter(item, parameters_dict): def get_encoded_parameter(item, parameters_dict):
result = {} result = {}
@@ -74,6 +68,11 @@ def project_information(attribute_name):
return getattr(mayan, attribute_name) return getattr(mayan, attribute_name)
@register.simple_tag(takes_context=True)
def render_dashboard(context, name):
return Dashboard.get(name=name).render(request=context.request)
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def render_subtemplate(context, template_name, template_context): def render_subtemplate(context, template_name, template_context):
""" """

View File

@@ -35,9 +35,9 @@ from navigation import SourceColumn
from rest_api.fields import DynamicSerializerField from rest_api.fields import DynamicSerializerField
from .dashboard_widgets import ( from .dashboard_widgets import (
widget_document_types, widget_documents_in_trash, DashboardWidgetDocumentsInTrash, DashboardWidgetDocumentsNewThisMonth,
widget_new_documents_this_month, widget_pages_per_month, DashboardWidgetDocumentsPagesNewThisMonth, DashboardWidgetDocumentsTotal,
widget_total_documents DashboardWidgetDocumentsTypesTotal,
) )
from .events import ( from .events import (
event_document_create, event_document_download, event_document_create, event_document_download,
@@ -195,7 +195,7 @@ class DocumentsApp(MayanAppConfig):
model=Document, related='document_type', model=Document, related='document_type',
) )
ModelPermission.register_inheritance( ModelPermission.register_inheritance(
model=DocumentPage, related='document', model=DocumentPage, related='document_version__document',
) )
ModelPermission.register_inheritance( ModelPermission.register_inheritance(
model=DocumentPageResult, related='document_version__document', model=DocumentPageResult, related='document_version__document',
@@ -389,11 +389,21 @@ class DocumentsApp(MayanAppConfig):
} }
) )
dashboard_main.add_widget(widget=widget_document_types) dashboard_main.add_widget(
dashboard_main.add_widget(widget=widget_documents_in_trash) widget=DashboardWidgetDocumentsTotal, order=0
dashboard_main.add_widget(widget=widget_new_documents_this_month) )
dashboard_main.add_widget(widget=widget_pages_per_month) dashboard_main.add_widget(
dashboard_main.add_widget(widget=widget_total_documents) widget=DashboardWidgetDocumentsInTrash, order=1
)
dashboard_main.add_widget(
widget=DashboardWidgetDocumentsTypesTotal, order=2
)
dashboard_main.add_widget(
widget=DashboardWidgetDocumentsNewThisMonth, order=3
)
dashboard_main.add_widget(
widget=DashboardWidgetDocumentsPagesNewThisMonth, order=4
)
menu_documents.bind_links( menu_documents.bind_links(
links=( links=(

View File

@@ -4,78 +4,99 @@ from django.apps import apps
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.classes import DashboardWidget from common.classes import DashboardWidgetNumeric
from .icons import ( from .icons import (
icon_dashboard_documents_in_trash, icon_dashboard_document_types, icon_dashboard_documents_in_trash, icon_dashboard_document_types,
icon_dashboard_pages_per_month, icon_dashboard_new_documents_this_month, icon_dashboard_pages_per_month, icon_dashboard_new_documents_this_month,
icon_dashboard_total_document icon_dashboard_total_document
) )
from .permissions import (
permission_document_view, permission_document_type_view
)
from .statistics import ( from .statistics import (
new_document_pages_this_month, new_documents_this_month, new_document_pages_this_month, new_documents_this_month,
) )
def get_total_documents_queryset(): class DashboardWidgetDocumentsTotal(DashboardWidgetNumeric):
icon_class = icon_dashboard_total_document
label = _('Total documents')
link = reverse_lazy('documents:document_list')
def render(self, request):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
)
Document = apps.get_model( Document = apps.get_model(
app_label='documents', model_name='Document' app_label='documents', model_name='Document'
) )
return Document.objects.all() self.count = AccessControlList.objects.filter_by_access(
permission=permission_document_view, user=request.user,
queryset=Document.objects.filter(is_stub=False)
).count()
return super(DashboardWidgetDocumentsTotal, self).render(request)
def get_document_types_queryset(): class DashboardWidgetDocumentsInTrash(DashboardWidgetNumeric):
DocumentType = apps.get_model( icon_class = icon_dashboard_documents_in_trash
app_label='documents', model_name='DocumentType' label = _('Documents in trash')
link = reverse_lazy('documents:document_list_deleted')
def render(self, request):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
) )
return DocumentType.objects.all()
def get_deleted_documents_queryset():
DeletedDocument = apps.get_model( DeletedDocument = apps.get_model(
app_label='documents', model_name='DeletedDocument' app_label='documents', model_name='DeletedDocument'
) )
return DeletedDocument.objects.all() self.count = AccessControlList.objects.filter_by_access(
permission=permission_document_view, user=request.user,
queryset=DeletedDocument.objects.all()
).count()
return super(DashboardWidgetDocumentsInTrash, self).render(request)
widget_pages_per_month = DashboardWidget( class DashboardWidgetDocumentsTypesTotal(DashboardWidgetNumeric):
func=new_document_pages_this_month, icon_class = icon_dashboard_document_types
icon_class=icon_dashboard_pages_per_month, label = _('Document types')
label=_('New pages this month'), link = reverse_lazy('documents:document_type_list')
link=reverse_lazy(
'statistics:statistic_detail', def render(self, request):
args=('new-document-pages-per-month',) AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
) )
DocumentType = apps.get_model(
app_label='documents', model_name='DocumentType'
) )
self.count = AccessControlList.objects.filter_by_access(
permission=permission_document_type_view, user=request.user,
queryset=DocumentType.objects.all()
).count()
return super(DashboardWidgetDocumentsTypesTotal, self).render(request)
widget_new_documents_this_month = DashboardWidget(
func=new_documents_this_month, class DashboardWidgetDocumentsNewThisMonth(DashboardWidgetNumeric):
icon_class=icon_dashboard_new_documents_this_month, icon_class = icon_dashboard_new_documents_this_month
label=_('New documents this month'), label = _('New documents this month')
link=reverse_lazy( link=reverse_lazy(
'statistics:statistic_detail', 'statistics:statistic_detail',
args=('new-documents-per-month',) args=('new-documents-per-month',)
) )
def render(self, request):
self.count = new_documents_this_month(user=request.user)
return super(DashboardWidgetDocumentsNewThisMonth, self).render(request)
class DashboardWidgetDocumentsPagesNewThisMonth(DashboardWidgetNumeric):
icon_class = icon_dashboard_pages_per_month
label = _('New pages this month')
link=reverse_lazy(
'statistics:statistic_detail',
args=('new-document-pages-per-month',)
) )
widget_total_documents = DashboardWidget( def render(self, request):
icon_class=icon_dashboard_total_document, self.count = new_document_pages_this_month(user=request.user)
queryset=get_total_documents_queryset, return super(DashboardWidgetDocumentsPagesNewThisMonth, self).render(request)
label=_('Total documents'),
link=reverse_lazy('documents:document_list')
)
widget_document_types = DashboardWidget(
icon_class=icon_dashboard_document_types,
queryset=get_document_types_queryset,
label=_('Document types'),
link=reverse_lazy('documents:document_type_list')
)
widget_documents_in_trash = DashboardWidget(
icon_class=icon_dashboard_documents_in_trash,
queryset=get_deleted_documents_queryset,
label=_('Documents in trash'),
link=reverse_lazy('documents:document_list_deleted')
)