# coding: UTF-8 '''@package common.api.datapanels.datapanel Модуль содержит классы и функции для работы с информационной панелью. Created on 26 мая 2015 г. @author: tugushev.rr @todo:layout для tab. @todo Определения стилей для элементов и тэгов <tr> <td> @todo элементы plugin, geomap, chart ''' from common.api.core import ShowcaseBaseElement, ShowcaseBaseNamedElement, IXMLSerializable from common.api.utils.tools import procname elementsToJson = lambda elList: map(lambda x: x.toJSONDict(), elList) isIdExists = lambda el, elList: len(filter(lambda x: x.id == el.id, elList)) > 0 def getElementsFromContainer(elementId, elementsList): """Возвращает элементы по @a elementId из контейнера элементов Showcase @a elementsList @param elementId (@c string) @param elementsList (<em>list of common.api.datapanels.datapanel.DatapanelElement</em>) @return <em>list of common.api.datapanels.datapanel.DatapanelElement</em> или *None*, если ничего не найдено @note Обычно контейнер элементов содержит элементы с уникальным ИД, поэтому результирующий список как правило будет содержать 1 элемент. Поведение, возвращающее именно список, а не сам элемент реализовано для унификации процесса поиска, т.к. по сути @a elementList может быть любым списком элементов Showcase, в том числе и с повторяющимися ИД. """ elemets = filter(lambda x: x.id() == elementId, elementsList) if elemets: return elemets return None class TabOrder(object): """Описывает порядок вкладок на информационной панели""" ## Текущая вкладка CURRENT='current' ## Первая или текущая вкладка FIRST_OR_CURRENT='firstOrCurrent' class DatapanelElementTypes: """Описывает возможные типы элементов информационной панели""" ## Неопределено UNDEFINED = None ## Грид GRID = u'grid' ## XForms XFORMS = u'xforms' ## webtext WEBTEXT = u'webtext' class ProcTypes: """Описывает типы процедур-обработчиков для элементов информационной панели""" ## Неопределено UNDEFINED = None ## Процедура сохранения данных SAVE = u'SAVE' ## Процедура скачивания DOWNLOAD = u'DOWNLOAD' ## Процедура загрузки UPLOAD = u'UPLOAD' class DatapanelElement(ShowcaseBaseElement): """Базовый класс для элементов информационной панели""" def __init__(self, elementId, elementType, procName=None): """ @param elementId (@c string) ИД элемента @param elementType (@c DatapanelElementTypes) тип элемента @param procName (<tt>string or function object</tt>) фунция-обработчик загрузки данных для элемента """ super(DatapanelElement, self).__init__(elementId) self.__procId = 0 self.__type = elementType self.__hideOnLoad = False self.__neverShowInPanel = False self.setProc(procName) self.__elementProc = [] self.__related = [] self.__refreshInterval = None def type(self): """Возвращает тип элемента @return @c DatapanelElementTypes """ return self.__type def hideOnLoad(self): """Возвращает флаг, определяющий, нужно ли скрывать элемент при загрузке информационной панели @return @c bool """ return self.__hideOnLoad def setHideOnLoad(self, value): """Устанавливает флаг, определяющий, нужно ли скрывать элемент при загрузке информационной панели @param value (@c bool) @return ссылка на себя """ self.__hideOnLoad = value return self def neverShowInPanel(self): """Возвращает флаг, определяющий, нужно ли отображать элемент на информационной панели @return @c bool """ return self.__neverShowInPanel def setNeverShowInPanel(self, value): """Устанавливает флаг, определяющий, нужно ли отображать элемент на информационной панели @param value (@c bool) @return ссылка на себя """ self.__neverShowInPanel = value return self def proc(self): """Возвращает функцию-обработчик загрузки данных в элемент @return (@c string) полное имя функции (*qualified name*) """ return self.__proc @procname def setProc(self, value): """Устанавливает функцию-обработчик загрузки данных в элемент. @param value (<tt>string or function object</tt>) фунция-обработчик загрузки данных @return ссылка на себя """ self.__proc = value return self def saveProc(self): """Возвращает функцию-обработчик сохранения данных @return (@c string) полное имя функции (*qualified name*) """ return self._getProc(ProcTypes.SAVE) def setSaveProc(self, value): """Устанавливает функцию-обработчик сохранения данных. @param value (<tt>string or function object</tt>) фунция-обработчик сохранения данных @return ссылка на себя """ self._addProc(ProcTypes.SAVE, value) return self def refreshInterval(self): """Возвращает интервал обновления элемента в секундах @return @c int or @c None, если интервал не задан """ return self.__refreshInterval def setRefreshInterval(self, numSec): """Включает обновление элемента по времени. Если @c numSec <= 0 или @c None, обновление отключается. @param numSec (@c int) интервал обновления в секундах @return ссылка на себя """ self.__refreshInterval = numSec if numSec and numSec > 0 else None def downloadProc(self): """Возвращает функцию-обработчик скачивания данных @return (@c string) полное имя функции (*qualified name*) """ return self._getProc(ProcTypes.DOWNLOAD) def setDownloadProc(self, value): """Устанавливает функцию-обработчик скачивания данных. @param value (<tt>string or function object</tt>) фунция-обработчик скачивания данных @return ссылка на себя """ self._addProc(ProcTypes.DOWNLOAD, value) return self def uploadProc(self): """Возвращает функцию-обработчик загрузки данных на сервер @return (@c string) полное имя функции (*qualified name*) """ return self._getProc(ProcTypes.UPLOAD) def setUploadProc(self, value): """Устанавливает функцию-обработчик загрузки данных на сервер. @param value (<tt>string or function object</tt>) фунция-обработчик загрузки данных на сервер @return ссылка на себя """ self._addProc(ProcTypes.UPLOAD, value) return self def addRelated(self, datapanelElement): """Добавлеят связанный элемент @param datapanelElement (@c DatapanelElement) @return ссылка на себя """ self.__related.append(datapanelElement) return self @procname def _addProc(self, procType, procName): self.__procId += 1 self.__elementProc.append( { "@id": u"%sp%i" % (self.id(), self.__procId), "@name": procName, "@type": procType }) def _getProc(self, inProcType): p = filter(lambda x: x['@type'] == inProcType, self.__elementProc) return p and p[0] or None def toJSONDict(self): if not self.__proc: raise Exception(u"Procedure @proc for element id = '%s' is not set!" % self.id()) d = super(DatapanelElement, self).toJSONDict() d['@type'] = self.type() d['@hideOnLoad'] = unicode(self.hideOnLoad()).lower() d['@neverShowInPanel'] = unicode(self.neverShowInPanel()).lower() d['@proc'] = self.proc() if self.__elementProc: d['proc'] = self.__elementProc if self.__related: d['related'] = map(lambda x: {'@id': x.id()}, self.__related) if self.refreshInterval(): d['@refreshByTimer'] = 'true' d['@refreshInterval'] = self.refreshInterval() return d class XForm(DatapanelElement): """Описывает элемент с типом XForm""" def __init__(self, elementId, templateName=None, procName=None, buildTemplate=True): """ @param elementId (@c string) ИД элемента @param templateName (@c string) имя файла шаблона @param procName (<tt>string or function object</tt>) функция-обработчик загрузки данных для элемента @param buildTemplate (@c boolean) фдаг сборки шаблона из частей """ super(XForm, self).__init__(elementId, DatapanelElementTypes.XFORMS, procName) self.setBuildTemplate(buildTemplate) self.setTemplate(templateName) def template(self): """Возвращает имя файла шаблона XForms @return (@c string) """ return self.__template @procname def setTemplate(self, value): """Устанавливает имя файла шаблона XForms @param value (<tt>string or function object</tt>) имя файла шаблона @return ссылка на себя """ self.__template = value return self def buildTemplate(self): """Возвращает флаг сборки шаблона формы из частей. @return @c boolean """ return self.__buildTemplate def setBuildTemplate(self, value): """Устанавливает флаг сборки шаблона формы из частей. @param value (@c boolean) @c True - собирать шаблон из частей. """ self.__buildTemplate = value return self def toJSONDict(self): if not self.__template: raise ValueError(u"Template for XForms element id = '%s' is not set!" % self.id()) d = super(XForm, self).toJSONDict() d["@template"] = self.template() d["@buildTemplate"] = str(self.buildTemplate()).lower() return d class Webtext(DatapanelElement): """Описывает элемент с типом Webtext""" def __init__(self, elementId, procName, transform=None): """ @param elementId (@c string) ИД элемента @param procName (<tt>string or function object</tt>) фунция-обработчик загрузки данных для элемента @param transform (@c string) имя файла XSL-преобразования """ super(Webtext, self).__init__(elementId, DatapanelElementTypes.WEBTEXT, procName) self.__transform = transform def transform(self): """Возвращает имя файла XSL-преобразования @return (@c string) """ return self.__transform def setTransform(self, value): """Устанавливает имя файла XSL-преобразования @param value (@c string) @return ссылка на себя """ self.__transform = value return self def toJSONDict(self): if not self.proc(): raise ValueError(u"Proc for Webtext element id = '%s' is not set!" % self.id) d = super(Webtext, self).toJSONDict() d["@transform"] = self.transform() return d class Tab(ShowcaseBaseNamedElement): """Описывает вкладку информационной панели. Вкладка имеет наименование, ИД и содержит в себе элементы информационной панели. """ def __init__(self, tabId, tabName=u""): """ @param tabId (@c string) ИД вкладки @param tabName (@c string) наименование вкладки """ super(Tab, self).__init__(tabId, tabName) self.__elements = [] def elements(self): """Возвращает список элементов вкладки. Эсли на вкладке нет элементов, возвращает пустой список. @return <tt>list of common.api.datapanels.datapanel.DatapanelElement</tt> """ return self.__elements def getElement(self, byId): """Ищет на вкладке элемент с @a byId. @param byId (@c string) ИД элемента @return @c common.api.datapanels.datapanel.DatapanelElement или @c None, если элемент не найден """ elements = getElementsFromContainer(byId, self.elements()) return elements and elements[0] or None def addElement(self, datapanelElement): """Добавляет элемент на вкладку @param datapanelElement (@c common.api.datapanels.datapanel.DatapanelElement) @return ссылка на себя @throw @c ValueError если элемент с таким ИД уже существует на вкладке """ if isIdExists(datapanelElement, self.__elements): raise ValueError(u"Error adding element to the tab id = '%s': element with the same id = '%s' already exists!" % (self.id, datapanelElement.id)) self.__elements.append(datapanelElement) return self def toJSONDict(self): d = super(Tab, self).toJSONDict() d['element'] = elementsToJson(self.__elements) return d class Datapanel(IXMLSerializable): """Описывает информационную панель. Информационная панель представляет собой контейнер вкладок, которые уже явялются контейнерами самих элементов """ def __init__(self): self.__tabs = [] def tabs(self): """Возвращает список вкладок. Если на информационной панели нет вкладок, возвращает пустой список. @return <tt>list of common.api.datapanels.datapanel.Tab</tt> """ return self.__tabs def getTab(self, tabId): """Ищет вкладку с @a tabId. @param tabId (@c string) ИД вкладки @return @c common.api.datapanels.datapanel.Tab или @c None, если вкладка не найдена """ tabs_ = getElementsFromContainer(tabId, self.tabs()) return tabs_ and tabs_[0] or None def addTab(self, tab): """Добавляет вкладку на информационную панель @param tab (@c common.api.datapanels.datapanel.Tab) @return ссылка на себя @throw ValueError если вкладка с таким ИД уже существует """ if isIdExists(tab, self.__tabs): raise ValueError(u"Error adding tab: tab with the id = '%s' already exists!" % tab.id) self.__tabs.append(tab) return self def getElement(self, byId): """Ищет на информационной панели элемент с @a byId. Поиск осущесвтляется на всех вкладках. @param byId (@c string) ИД элемента @return @c common.api.datapanels.datapanel.DatapanelElement или @c None, если элемент не найден """ for t in self.tabs(): element = t.getElement(byId) if element: return element return None def toJSONDict(self): d = { 'datapanel': { "tab": elementsToJson(self.__tabs) } } return d def testProc(): pass if __name__ == '__main__': from grids import PageGrid t = Tab(u't1', u"Таб 1") pg = PageGrid(u"pg1", u"test.grid.proc.name") t.addElement(pg) f1 = XForm(u"xf1", u"f1.xml", u"test.forms.form1.data") f1.setSaveProc(testProc) # (u'test.forms.form1.save') f1.addRelated(pg) t.addElement(f1) d = Datapanel() d.addTab(t) print d.toXML()