# -*- coding: utf-8 -*-
# pylint: disable = too-many-lines
"""SQLAlchemy-powered model definitions for dashboards."""
from json import dumps
from sqlalchemy import Column, ForeignKey, String, Integer, Boolean, Enum
from sqlalchemy import PickleType, Text
from sqlalchemy.orm import relationship
from lxml import etree
import colander
from chrysalio.lib.i18n import record_format_i18n, translate_field
from chrysalio.lib.i18n import schema_i18n_labels, defaults_i18n_labels
from chrysalio.lib.i18n import view_i18n_labels, edit_i18n_labels
from chrysalio.lib.utils import make_id, deltatime_label
from chrysalio.lib.xml import i18n_xml_text, db2xml_i18n_labels
from chrysalio.helpers.literal import Literal
from chrysalio.models import ID_LEN, LABEL_LEN, DESCRIPTION_LEN
from chrysalio.models import DBDeclarativeClass
from chrysalio.models.dbbase import DBBaseClass
from chrysalio.models.dbuser import DBUser
from chrysalio.models.dbgroup import DBGroup
from ..relaxng import RELAXNG_CIODASHBOARD
from ..lib.utils import get_ciowarehouse, route_file_view
from ..lib.i18n import _
from . import XPATH_LEN, REGEX_LEN, PATH_LEN
DASHBOARD_ACCESSES = {
'free': _('free'), 'restricted': _('restricted'), 'closed': _('closed')}
REFRESH_PERIOD = 3600
REFRESH_PERIOD_HINT = _('in seconds, default: 3600 (1 hour)')
# =============================================================================
[docs]
class DBDashboard(DBDeclarativeClass, DBBaseClass):
"""SQLAlchemy-powered dashboard class."""
suffix = 'ciodsh'
attachments_dir = 'Dashboards'
_settings_tabs = (
_('Information'), _('Gauges'), _('Authorized users'),
_('Authorized groups'))
__tablename__ = 'dsh_dashboards'
__table_args__ = {'mysql_engine': 'InnoDB'}
dashboard_id = Column(String(ID_LEN), primary_key=True)
i18n_label = Column(Text, nullable=False)
i18n_description = Column(PickleType(1))
attachments_key = Column(String(ID_LEN + 20))
picture = Column(String(ID_LEN + 4))
access = Column(
Enum(*DASHBOARD_ACCESSES.keys(), name='dsh_access_enum'),
nullable=False)
refresh_period = Column(Integer, default=REFRESH_PERIOD)
warehouse = Column(String(ID_LEN), nullable=False)
cioset = Column(String(PATH_LEN), nullable=False)
xpath = Column(String(XPATH_LEN))
gauges = relationship('DBDashboardGauge', cascade='all, delete')
users = relationship('DBDashboardUser', cascade='all, delete')
groups = relationship('DBDashboardGroup', cascade='all, delete')
# -------------------------------------------------------------------------
[docs]
@classmethod
def xml2db(
cls, dbsession, dashboard_elt, error_if_exists=True, kwargs=None):
"""Load a dashboard from a XML element.
:type dbsession: sqlalchemy.orm.session.Session
:param dbsession:
SQLAlchemy session.
:type dashboard_elt: lxml.etree.Element
:param dashboard_elt:
Dashboard XML element.
:param bool error_if_exists: (default=True)
It returns an error if user dashboard already exists.
:param dict kwargs: (optional)
Dictionary of keyword arguments.
:rtype: :class:`pyramid.i18n.TranslationString` or ``None``
:return:
Error message or ``None``.
"""
# Check if already exists
dashboard_id = make_id(dashboard_elt.get('id'), 'token', ID_LEN)
dbdashboard = dbsession.query(cls).filter_by(
dashboard_id=dashboard_id).first()
if dbdashboard is not None:
if error_if_exists:
return _(
'Dashboard "${d}" already exists.', {'d': dashboard_id})
return None
# Create dashboard
record = cls.record_from_xml(dashboard_id, dashboard_elt)
error = cls.record_format(record)
if error:
return error
dbdashboard = cls(**record)
dbsession.add(dbdashboard)
# Add Gauges
namespace = RELAXNG_CIODASHBOARD['namespace']
for item_id, elt in enumerate(dashboard_elt.xpath(
'ns0:gauges/ns0:gauge', namespaces={'ns0': namespace})):
dbsession.add(DBDashboardGauge(
dashboard_id=dashboard_id, gauge_id=item_id,
i18n_label=dumps(i18n_xml_text(
elt, 'ns0:label', {'ns0': namespace})),
metafield=elt.findtext('{{{0}}}metafield'.format(namespace)),
count=elt.findtext('{{{0}}}count'.format(namespace))))
# Add users
refs = kwargs.get('users', {})
done = set()
for elt in dashboard_elt.xpath(
'ns0:users/ns0:user', namespaces={'ns0': namespace}):
item_id = refs.get(elt.text)
if item_id is not None and item_id not in done:
done.add(item_id)
dbsession.add(DBDashboardUser(
dashboard_id=dashboard_id, user_id=item_id,
favorite=elt.get('favorite') == 'true' or None))
# Add groups
refs = kwargs.get('groups', ())
done = set()
for elt in dashboard_elt.xpath(
'ns0:groups/ns0:group', namespaces={'ns0': namespace}):
item_id = elt.text
if item_id and item_id in refs and item_id not in done:
done.add(item_id)
dbsession.add(DBDashboardGroup(
dashboard_id=dashboard_id, group_id=item_id))
return None
# -------------------------------------------------------------------------
[docs]
@classmethod
def record_from_xml(cls, dashboard_id, dashboard_elt):
"""Convert a dashboard XML element into a dictionary.
:param str dashboard_id:
Dashboard ID.
:type dashboard_elt: lxml.etree.Element
:param dashboard_elt:
Dashboard XML element.
:rtype: dict
"""
namespace = RELAXNG_CIODASHBOARD['namespace']
attachments_elt = dashboard_elt.find(
'{{{0}}}attachments'.format(namespace))
cioset_elt = dashboard_elt.find('{{{0}}}cioset'.format(namespace))
return {
'dashboard_id': dashboard_id,
'i18n_label': dumps(i18n_xml_text(
dashboard_elt, 'ns0:label', {'ns0': namespace})),
'i18n_description': i18n_xml_text(
dashboard_elt, 'ns0:description', {'ns0': namespace}),
'attachments_key':
attachments_elt is not None and attachments_elt.get(
'key') or None,
'picture':
attachments_elt is not None and attachments_elt.findtext(
'{{{0}}}picture'.format(namespace)) or None,
'access': dashboard_elt.findtext(
'{{{0}}}access'.format(namespace)),
'refresh_period': int(dashboard_elt.findtext(
'{{{0}}}refresh-period'.format(namespace)) or REFRESH_PERIOD),
'warehouse': dashboard_elt.findtext(
'{{{0}}}warehouse'.format(namespace)),
'cioset': cioset_elt is not None and cioset_elt.text.strip(),
'xpath': cioset_elt is not None and cioset_elt.get(
'xpath') or None}
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
[docs]
def db2xml(self, dbsession):
"""Serialize a dashboard to a XML representation.
:type dbsession: sqlalchemy.orm.session.Session
:param dbsession:
SQLAlchemy session.
:rtype: lxml.etree.Element
"""
# pylint: disable = too-many-branches
dashboard_elt = etree.Element('dashboard')
dashboard_elt.set('id', self.dashboard_id)
# Labels and descriptions
db2xml_i18n_labels(self, dashboard_elt, 6)
# Attachment
if self.attachments_key:
elt = etree.SubElement(
dashboard_elt, 'attachments', key=self.attachments_key)
if self.picture:
etree.SubElement(elt, 'picture').text = self.picture
# File access
etree.SubElement(dashboard_elt, 'access').text = self.access
# Refresh period
if self.refresh_period != REFRESH_PERIOD:
etree.SubElement(dashboard_elt, 'refresh-period').text = str(
self.refresh_period)
# Cioset
etree.SubElement(dashboard_elt, 'warehouse').text = self.warehouse
elt = etree.SubElement(dashboard_elt, 'cioset')
elt.text = self.cioset
if self.xpath:
elt.set('xpath', self.xpath)
# Gauges
if self.gauges:
gauges_elt = etree.SubElement(dashboard_elt, 'gauges')
for dbgauge in self.gauges:
subelt = etree.SubElement(gauges_elt, 'gauge')
db2xml_i18n_labels(dbgauge, subelt, 8)
etree.SubElement(subelt, 'metafield').text = dbgauge.metafield
etree.SubElement(subelt, 'count').text = dbgauge.count
# Users and groups
if self.users:
users = {}
users_elt = etree.SubElement(dashboard_elt, 'users')
for dbuser in self.users:
if dbuser.user_id not in users:
users[dbuser.user_id] = dbsession.query(
DBUser.login).filter_by(
user_id=dbuser.user_id).first()[0]
subelt = etree.SubElement(users_elt, 'user')
subelt.text = users[dbuser.user_id]
if dbuser.favorite:
subelt.set('favorite', 'true')
if self.groups:
groups_elt = etree.SubElement(dashboard_elt, 'groups')
for dbgroup in self.groups:
subelt = etree.SubElement(groups_elt, 'group')
subelt.text = dbgroup.group_id
return dashboard_elt
# -------------------------------------------------------------------------
[docs]
def tab4view(self, request, tab_index, form, user_filter, user_paging):
"""Generate the tab content of a dashboard.
:type request: pyramid.request.Request
:param request:
Current request.
:param int index:
Index of the tab.
:type form: .lib.form.Form
:param form:
Current form object.
:type user_filter: chrysalio.lib.filter.Filter
:param user_filter:
Filter for users.
:type user_paging: chrysalio.lib.paging.Paging
:param user_paging:
Paging for dashboard users.
:rtype: chrysalio.helpers.literal.Literal
"""
if tab_index == 0:
return self._tab4view_information(request, form)
if tab_index == 1:
return self._tab4view_gauges(request, form)
if tab_index == 2:
return self._tab4view_users(
request, form, user_filter, user_paging)
if tab_index == 3:
return self._tab4view_groups(request)
return ''
# -------------------------------------------------------------------------
def _tab4view_information(self, request, form):
"""Generate the information tab.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: .lib.form.Form
:param form:
Current form object.
:rtype: chrysalio.helpers.literal.Literal
"""
translate = request.localizer.translate
warehouse = get_ciowarehouse(request).warehouse(
request, self.warehouse)
html = form.grid_item(
translate(_('Identifier:')), self.dashboard_id, clear=True)
html += view_i18n_labels(request, form, self)
html += form.grid_item(
translate(_('Access:')),
translate(DASHBOARD_ACCESSES.get(self.access)), clear=True)
if self.refresh_period != REFRESH_PERIOD:
html += form.grid_item(
translate(_('Refreshment every:')),
deltatime_label(self.refresh_period), clear=True)
html += form.grid_item(
translate(_('Warehouse:')),
warehouse.label(request) if warehouse else self.warehouse,
title=self.warehouse, clear=True)
if warehouse is not None:
html += Literal(
'<div class="cioFormItem"><label><strong>{0}</strong></label>'
'<div><a href="{1}">{2}</a></div>'
'<div class="cioClear"></div></div>'.format(
translate(_('Cioset file:')),
route_file_view(request, warehouse.uid, self.cioset),
self.cioset))
else:
html += form.grid_item(
translate(_('Cioset file:')), self.cioset, clear=True)
if self.xpath:
html += form.grid_item(
translate(_('XPath:')), self.xpath, clear=True)
return html
# -------------------------------------------------------------------------
def _tab4view_gauges(self, request, form):
"""Generate the information tab.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: .lib.form.Form
:param form:
Current form object.
:rtype: chrysalio.helpers.literal.Literal
"""
translate = request.localizer.translate
if not self.gauges:
return translate(_('No gauge is available.'))
html = ''
fields = request.registry['metafields'] \
if 'ciowarehouse' in request.registry['modules'] else \
request.registry['fields']
for num, dbgauge in enumerate(self.gauges):
metafield = fields.get(dbgauge.metafield)
html += Literal('<section>\n<h3>{0}</h3>\n<div>\n'.format(
translate(_('Gauge n° ${n}', {'n': num + 1}))))
html += view_i18n_labels(
request, form, dbgauge, with_description=False)
html += form.grid_item(
translate(_('Metadata field:')),
translate_field(request, metafield['label'])
if metafield else dbgauge.metafield,
title=dbgauge.metafield, clear=True)
html += form.grid_item(
translate(_('Count:')), dbgauge.count, clear=True)
html += Literal('</div>\n</section>\n')
return html
# -------------------------------------------------------------------------
def _tab4view_users(self, request, form, user_filter, user_paging):
"""Generate the users tab.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: .lib.form.Form
:param form:
Current form object.
:type user_filter: chrysalio.lib.filter.Filter
:param user_filter:
Filter for users.
:type user_paging: chrysalio.lib.paging.Paging
:param user_paging:
Paging for dashboard users.
"""
translate = request.localizer.translate
if self.access == 'free':
return translate(_('Access to this dashboard is not restricted.'))
if self.access == 'closed':
return translate(_('This dashboard is closed.'))
if not self.users:
return translate(_('No user is authorized.'))
html = DBUser.paging_filter(request, form, user_filter, user_paging)
html += self._user_thead(request, user_paging)
users = {k.user_id: k.favorite for k in self.users}
for dbuser in user_paging:
html += \
'<tr><th><a href="{user_view}">{login}</a></th>'\
'<td class="cioOptional">{fname}</td><td>{lname}</td>'\
'<td class="cioBoolean">{favorite}</td></tr>\n'.format(
user_view=request.route_path(
'user_view', user_id=dbuser.user_id),
login=dbuser.login,
fname=dbuser.first_name or '',
lname=dbuser.last_name,
favorite='✔' if users[dbuser.user_id] else '')
html += '</tbody>\n</table>\n'
return Literal(html)
# -------------------------------------------------------------------------
def _tab4view_groups(self, request):
"""Generate the users tab.
:type request: pyramid.request.Request
:param request:
Current request.
"""
translate = request.localizer.translate
if self.access == 'free':
return translate(_('Access to this dashboard is not restricted.'))
if self.access == 'closed':
return translate(_('This dashboard is closed.'))
if not self.groups:
return translate(_('No group is authorized.'))
dbgroups = request.dbsession.query(DBGroup).filter(
DBGroup.group_id.in_([k.group_id for k in self.groups])).order_by(
'group_id')
html = '<table>\n<thead>\n'\
'<tr><th>{label}</th><th>{description}</th></tr>\n'\
'</thead>\n<tbody>\n'.format(
label=translate(_('Label')),
description=translate(_('Description')))
for dbgroup in dbgroups:
html += \
'<tr><th><a href="{group_view}">{label}</a></th>'\
'<td>{description}</td></tr>\n'.format(
group_view=request.route_path(
'group_view', group_id=dbgroup.group_id),
label=dbgroup.label(request),
description=dbgroup.description(request))
html += '</tbody>\n</table>\n'
return Literal(html)
# -------------------------------------------------------------------------
[docs]
@classmethod
def settings_schema(cls, request, defaults, groups, dbdashboard=None):
"""Return a Colander schema to edit a dashboard.
:type request: pyramid.request.Request
:param request:
Current request.
:param dict defaults:
Default values for the form set by the user paging object.
:param dict groups:
A dictionary such as ``{group_id: (label, description),...}``.
:type dbdashboard: DBDashboard
:param dbdashboard: (optional)
Current user dashboard SqlAlchemy object.
:rtype: tuple
:return:
A tuple such as ``(schema, defaults)``.
"""
# Schema
schema = colander.SchemaNode(colander.Mapping())
if dbdashboard is None:
schema.add(colander.SchemaNode(
colander.String(), name='dashboard_id',
validator=colander.Length(min=2, max=ID_LEN)))
schema_i18n_labels(request, schema, LABEL_LEN, DESCRIPTION_LEN)
schema.add(colander.SchemaNode(
colander.String(), name='access',
validator=colander.OneOf(DASHBOARD_ACCESSES.keys())))
schema.add(colander.SchemaNode(
colander.Integer(), name='refresh_period',
validator=colander.Range(min=0), missing=REFRESH_PERIOD))
schema.add(colander.SchemaNode(colander.String(), name='warehouse'))
schema.add(colander.SchemaNode(colander.String(), name='cioset'))
schema.add(colander.SchemaNode(
colander.String(), name='xpath', missing=None))
# Gauges
fields = request.registry['metafields'] \
if 'ciowarehouse' in request.registry['modules'] else \
request.registry['fields']
if dbdashboard is not None:
for num, dbgauge in enumerate(dbdashboard.gauges):
prefix = 'gge:{0}:'.format(num)
schema_i18n_labels(
request, schema, LABEL_LEN, prefix=prefix, required=False)
schema.add(colander.SchemaNode(
colander.String(), name=f'{prefix}metafield',
validator=colander.OneOf(fields.keys()), missing=None))
schema.add(colander.SchemaNode(
colander.String(), name='{0}count'.format(prefix),
missing=None))
defaults.update(defaults_i18n_labels(
dbgauge, with_description=False, prefix=prefix))
defaults['{0}metafield'.format(prefix)] = dbgauge.metafield
defaults['{0}count'.format(prefix)] = dbgauge.count
prefix = 'gge:x:'
schema_i18n_labels(
request, schema, LABEL_LEN, prefix=prefix, required=False)
schema.add(colander.SchemaNode(
colander.String(), name='{0}metafield'.format(prefix),
validator=colander.OneOf(fields.keys()), missing=None))
schema.add(colander.SchemaNode(
colander.String(), name='{0}count'.format(prefix), missing=None))
# Groups
for group_id in groups:
schema.add(colander.SchemaNode(
colander.Boolean(), name='grp:{0}'.format(group_id),
missing=False))
# Defaults
if dbdashboard is None:
defaults.update({
'access': 'free', 'refresh_period': REFRESH_PERIOD})
else:
defaults.update(defaults_i18n_labels(dbdashboard))
for dbitem in dbdashboard.users:
defaults['usr:{0}'.format(dbitem.user_id)] = True
defaults['fav:{0}'.format(dbitem.user_id)] = \
dbitem.favorite
for dbitem in dbdashboard.groups:
defaults['grp:{0}'.format(dbitem.group_id)] = True
return schema, defaults
# -------------------------------------------------------------------------
[docs]
@classmethod
def tab4edit(cls, request, tab_index, form, user_filter, user_paging,
groups, dbdashboard=None):
"""Generate the tab content of user dashboard for edition.
:type request: pyramid.request.Request
:param request:
Current request.
:param int tab_index:
Index of the tab.
:type form: chrysalio.lib.form.Form
:param form:
Current form object.
:type user_filter: chrysalio.lib.filter.Filter
:param user_filter:
Filter for users.
:type user_paging: chrysalio.lib.paging.Paging
:param user_paging:
Paging for all users.
:param dict groups:
A dictionary such as ``{group_id: (label, description),...}``.
:type dbdashboard: DBDashboard
:param dbdashboard: (optional)
Current user dashboard SqlAlchemy object.
:rtype: chrysalio.helpers.literal.Literal
"""
# pylint: disable = too-many-arguments
if tab_index == 0:
return cls._tab4edit_information(request, form, dbdashboard)
if tab_index == 1:
return cls._tab4edit_gauges(request, form, dbdashboard)
if tab_index == 2:
return cls._tab4edit_users(
request, form, user_filter, user_paging, dbdashboard)
if tab_index == 3:
return cls._tab4edit_groups(request, form, groups, dbdashboard)
return ''
# -------------------------------------------------------------------------
@classmethod
def _tab4edit_information(cls, request, form, dbdashboard):
"""Generate the information tab for edition.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: .lib.form.Form
:param form:
Current form object.
:type dbdashboard: DBDashboard
:param dbdashboard:
Current user dashboard SqlAlchemy object.
:rtype: chrysalio.helpers.literal.Literal
"""
translate = request.localizer.translate
if dbdashboard is None:
html = form.grid_text(
'dashboard_id', translate(_('Identifier:')), required=True,
maxlength=ID_LEN, clear=True)
else:
html = form.grid_item(
translate(_('Identifier:')), dbdashboard.dashboard_id,
clear=True)
html += edit_i18n_labels(request, form, LABEL_LEN, DESCRIPTION_LEN)
html += form.grid_select(
'access', translate(_('Access:')),
DASHBOARD_ACCESSES.items(), required=True, clear=True)
html += form.grid_text(
'refresh_period', translate(_('Refreshment every:')), maxlength=10,
hint=translate(REFRESH_PERIOD_HINT), clear=True)
html += form.grid_text(
'warehouse', translate(_('Warehouse:')), maxlength=ID_LEN,
required=True, clear=True)
html += form.grid_text(
'cioset', translate(_('Cioset file:')), maxlength=PATH_LEN,
required=True, clear=True)
html += form.grid_text(
'xpath', translate(_('XPath:')), maxlength=REGEX_LEN, clear=True)
return html
# -------------------------------------------------------------------------
@classmethod
def _tab4edit_gauges(cls, request, form, dbdashboard):
"""Generate the information tab for edition.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: .lib.form.Form
:param form:
Current form object.
:type dbdashboard: DBDashboard
:param dbdashboard:
Current user dashboard SqlAlchemy object.
:rtype: chrysalio.helpers.literal.Literal
"""
translate = request.localizer.translate
html = ''
fields = request.registry['metafields'] \
if 'ciowarehouse' in request.registry['modules'] else \
request.registry['fields']
metafields = [
(k, translate_field(request, fields[k]['label']))
for k in fields]
if dbdashboard is not None:
for num in range(len(dbdashboard.gauges)):
prefix = f'gge:{num}:'
html += Literal('<section>\n<h3>{0}</h3>\n<div>\n'.format(
translate(_('Gauge n° ${n}', {'n': num + 1}))))
html += edit_i18n_labels(
request, form, LABEL_LEN, prefix=prefix)
html += form.grid_select(
f'{prefix}metafield',
translate(_('Metadata field:')), metafields, required=True,
clear=True)
html += form.grid_text(
f'{prefix}count', translate(_('Count:')),
maxlength=REGEX_LEN, required=True, clear=True)
html += Literal('</div>\n</section>\n')
prefix = 'gge:x:'
html += Literal('<section>\n<h3>{0}</h3>\n<div>\n'.format(
translate(_('New Gauge'))))
html += edit_i18n_labels(request, form, LABEL_LEN, prefix=prefix)
html += form.grid_select(
f'{prefix}metafield',
translate(_('Metadata field:')), metafields, required=True,
clear=True)
html += form.grid_text(
f'{prefix}count', translate(_('Count:')),
maxlength=REGEX_LEN, required=True, clear=True)
html += Literal('</div>\n</section>\n')
return html
# -------------------------------------------------------------------------
@classmethod
def _tab4edit_users(
cls, request, form, user_filter, user_paging, dbdashboard):
"""Generate the user tab for edition.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: chrysalio.lib.form.Form
:param form:
Current form object.
:type user_filter: chrysalio.lib.filter.Filter
:param user_filter:
Filter for users.
:type user_paging: chrysalio.lib.paging.Paging
:param user_paging:
Paging for all users.
:type dbdashboard: DBDashboard
:param dbdashboard:
Current user dashboard SqlAlchemy object.
:rtype: chrysalio.helpers.literal.Literal
"""
translate = request.localizer.translate
if dbdashboard and dbdashboard.access != 'restricted':
return translate(_('Access to this dashboard is not restricted.'))
html = DBUser.paging_filter(request, form, user_filter, user_paging)
html += cls._user_thead(request, user_paging).replace(
'<tr>', '<tr><th class="cioCheckbox" id="check_all"></th>')
for dbuser in user_paging:
html += \
'<tr><td class="cioCheckbox cioSelect">{check}{visible}</td>'\
'<td>{login}</td>'\
'<td class="cioOptional">{fname}</td><td>{lname}</td>'\
'<td class="cioBoolean">{favorite}</td></tr>\n'.format(
check=form.custom_checkbox(
'usr:{0}'.format(dbuser.user_id)),
visible=form.hidden(
'set:{0}'.format(dbuser.user_id), '1'),
login=dbuser.login,
fname=dbuser.first_name or '',
lname=dbuser.last_name,
favorite=form.custom_checkbox(
'fav:{0}'.format(dbuser.user_id)))
html += '</tbody>\n</table>\n'
return Literal(html)
# -------------------------------------------------------------------------
@classmethod
def _tab4edit_groups(cls, request, form, groups, dbdashboard):
"""Generate the group tab for edition.
:type request: pyramid.request.Request
:param request:
Current request.
:type form: .lib.form.Form
:param form:
Current form object.
:param dict groups:
A dictionary such as ``{group_id: (label, description),...}``.
:type dbdashboard: DBDashboard
:param dbdashboard:
Current user dashboard SqlAlchemy object.
:rtype: chrysalio.helpers.literal.Literal
"""
translate = request.localizer.translate
if dbdashboard and dbdashboard.access != 'restricted':
return translate(_('Access to this dashboard is not restricted.'))
html = '<table>\n<thead>\n'\
'<tr><th></th><th>{label}</th>'\
'<th>{description}</th></tr>\n'\
'</thead>\n<tbody>\n'.format(
label=translate(_('Label')),
description=translate(_('Description')))
for group_id in groups:
html += \
'<tr><td>{selected}</td>'\
'<th><label for="{id}">{label}</label></th>'\
'<td>{description}</td></tr>\n'.format(
selected=form.custom_checkbox('grp:{0}'.format(group_id)),
id='grp{0}'.format(group_id),
label=groups[group_id][0],
description=groups[group_id][1])
html += '</tbody>\n</table>\n'
return Literal(html)
# -------------------------------------------------------------------------
@classmethod
def _user_thead(cls, request, user_paging):
"""Table header for dashboard users.
:type request: pyramid.request.Request
:param request:
Current request.
:type user_paging: chrysalio.lib.paging.Paging
:param user_paging:
Paging for users.
:rtype: str
"""
translate = request.localizer.translate
return \
'<table class="cioPagingList">\n<thead><tr>'\
'<th>{login}</th>'\
'<th class="cioOptional">{fname}</th><th>{lname}</th>'\
'<th class="cioBoolean">{favorite}</th>'\
'</tr></thead>\n<tbody>\n'.format(
login=user_paging.sortable_column(
translate(_('Login')), 'login'),
fname=user_paging.sortable_column(
translate(_('First name')), 'first_name'),
lname=user_paging.sortable_column(
translate(_('Last name')), 'last_name'),
favorite=translate(_('Favorite')))
# =============================================================================
[docs]
class DBDashboardGauge(DBDeclarativeClass):
"""Class to link dashboards with their authorized gauges (many-to-many)."""
# pylint: disable = too-few-public-methods
__tablename__ = 'dsh_dashboards_gauges'
__table_args__ = {'mysql_engine': 'InnoDB'}
__mapper_args__ = {'confirm_deleted_rows': False}
dashboard_id = Column(
String(ID_LEN),
ForeignKey('dsh_dashboards.dashboard_id', ondelete='CASCADE'),
primary_key=True)
gauge_id = Column(Integer, primary_key=True)
i18n_label = Column(Text, nullable=False)
metafield = Column(String(ID_LEN), nullable=False)
count = Column(String(REGEX_LEN), nullable=False)
# =============================================================================
[docs]
class DBDashboardUser(DBDeclarativeClass):
"""Class to link dashboards with their authorized users (many-to-many)."""
# pylint: disable = too-few-public-methods
__tablename__ = 'dsh_dashboards_users'
__table_args__ = {'mysql_engine': 'InnoDB'}
__mapper_args__ = {'confirm_deleted_rows': False}
dashboard_id = Column(
String(ID_LEN),
ForeignKey('dsh_dashboards.dashboard_id', ondelete='CASCADE'),
primary_key=True)
user_id = Column(
Integer, ForeignKey('users.user_id', ondelete='CASCADE'),
primary_key=True)
favorite = Column(Boolean(name='favorite'), default=False)
# =============================================================================
[docs]
class DBDashboardGroup(DBDeclarativeClass):
"""Class to link dashboards with their authorized groups (many-to-many)."""
# pylint: disable = too-few-public-methods
__tablename__ = 'dsh_dashboards_groups'
__table_args__ = {'mysql_engine': 'InnoDB'}
__mapper_args__ = {'confirm_deleted_rows': False}
dashboard_id = Column(
String(ID_LEN),
ForeignKey('dsh_dashboards.dashboard_id', ondelete='CASCADE'),
primary_key=True)
group_id = Column(
String(ID_LEN), ForeignKey('groups.group_id', ondelete='CASCADE'),
primary_key=True)