1#!/usr/bin/env python3
  2
  3"""Display information about XML Reporting widgets."""
  4
  5from collections import OrderedDict
  6
  7from django.urls import reverse
  8from django.shortcuts import render
  9from django.views.decorators.cache import cache_page  # , never_cache
 10from django.http import Http404
 11from docutils.core import publish_parts
 12from django.utils.safestring import mark_safe
 13
 14from chart.project import settings
 15from chart.reports.widget import widget_classes
 16from chart.common import traits
 17from chart.common.prettyprint import Table
 18from chart.common.decorators import memoized
 19
 20
 21def safe_rst_module_docstring(mod):
 22    """Convert the docstring of `mod` into html via a restructredtext parser."""
 23    docstring = getattr(mod, '__doc__', '')
 24    if docstring is None:
 25        docstring='no docstring provided'
 26    joined = ' '.join(docstring.split('\n')).strip()
 27    rst = publish_parts(source=joined,
 28                        writer_name='html4css1')['fragment']
 29    return mark_safe(rst)
 30
 31
 32@memoized
 33def widget_info():
 34    """Return widget infomation as a dictionary (core, proj) of lists of widgets.
 35    Each widget is a dictionary of metadata."""
 36    result = {}
 37    for widget_dir in settings.WIDGET_DIRS:
 38        # Allow dirs (like the digest dir) to be hidden from the gallery
 39        if not widget_dir.get('show-in-gallery', True):
 40            continue
 41
 42        classes = widget_classes(widget_dir['dir'])
 43        widget_infos = []
 44        for widget_name in sorted(classes.keys()):
 45            widget_class = classes[widget_name]
 46            widget_infos.append({'name': widget_class.name,
 47                           'description': safe_rst_module_docstring(widget_class),
 48                           'thumbnail': getattr(widget_class, 'thumbnail', None),
 49                           'image': getattr(widget_class, 'image', None),
 50                           'url': reverse('widgets:single', kwargs=dict(widget_name=widget_name))})
 51
 52        result[widget_dir['name']] = widget_infos
 53
 54    return result
 55
 56
 57@cache_page(86400 * 365)
 58def index(request):  # (unused arg) pylint: disable=W0613
 59    """Display list of all available built-in widgets."""
 60    return render(request,
 61                  'widgets/widgets.html',
 62                  {'widgets': widget_info()})
 63
 64
 65def html_options(options, choices, unit):
 66    """Convert a widget `options` field into a table showing available options.
 67
 68    Recursive.
 69    """
 70    if options is None:
 71        return '<p>No configuration options</p>'
 72
 73    if isinstance(options, list):
 74        t = Table(headings=('Name', 'Description', 'Quantisation', 'Default', 'Type'),
 75                  cssclass='table table-striped table-bordered')
 76        for o in options:
 77            if o.multiple:
 78                quantisation = 'multiple'
 79
 80            else:
 81                if o.optional:
 82                    quantisation = 'optional'
 83
 84                else:
 85                    quantisation = 'required'
 86
 87            t.append(('<b>' + o.name + '</b>',
 88                      o.description,
 89                      quantisation,
 90                      o.default,
 91                      html_options(o.datatype, o.choices, o.unit)))
 92
 93        return t.to_html_str()
 94
 95    else:
 96        if choices is not None:
 97            l = []
 98            for c in choices:
 99                if c.description is not None:
100                    l.append('"{name}" ({desc})'.format(name=c.name, desc=c.description))
101
102                else:
103                    l.append('"{name}"'.format(name=c.name))
104
105            return 'string, select from:<ul><li>' + '</li><li>'.join(l) + '</li></ul>'
106
107        # print 'determining choices options ', options, ' x ', choices
108        # res = traits.traitname(options)
109        res = options.value
110
111        if unit is not None:
112            res = res + ' <span title="unit">({unit})</span>'.format(unit=unit)
113
114        tooltip = None  # traits.traittooltip(options)
115        if tooltip is not None:
116            res = "<span title='{tooltip}'>{res}</span>".format(tooltip=tooltip, res=res)
117
118        return res
119
120
121def view_widget(request, widget_name):
122    """Display information about a single widget."""
123    for widget_dir in settings.WIDGET_DIRS:
124        w = widget_classes(widget_dir['dir']).get(widget_name)
125        if w is not None:
126            break
127
128    if w is None:
129        raise Http404('Widget {name} not found'.format(name=widget_name))
130
131    example = '<{name}>\n    option 1\n    option 2\n    ...\n</{name}>'.format(name=w.name)
132
133    # force the ordereddict to list converter in the Widget constructor to run
134    dummy = w()
135
136    return render(request,
137                  'widgets/widget.html',
138                  dict(name=widget_name,
139                       options=html_options(w.options, None, None),
140                       doc=safe_rst_module_docstring(w),
141                       widget=w,
142                       example=example))