Commit 16ebd261 authored by Mouadh Kaabachi's avatar Mouadh Kaabachi Committed by GitHub

Merge pull request #5 from abilian/xmlwitch

Xmlwitch
parents 9f3a6365 dd49e8ce
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
import HTMLParser
import xmlwitch
import os import os
from datetime import datetime from datetime import datetime
from os.path import expanduser from os.path import expanduser
from lxml import etree
from spyne import AnyXml, Application, ServiceBase, rpc, Fault from spyne import AnyXml, Application, ServiceBase, rpc, Fault
from spyne.const.http import HTTP_200 from spyne.const.http import HTTP_200
from spyne.error import InvalidCredentialsError from spyne.error import InvalidCredentialsError
...@@ -158,11 +160,13 @@ class XmlaProviderService(ServiceBase): ...@@ -158,11 +160,13 @@ class XmlaProviderService(ServiceBase):
if request.Command.Statement == '': if request.Command.Statement == '':
# check if command contains a query # check if command contains a query
return etree.fromstring("""
<return> xml = xmlwitch.Builder()
<root xmlns="urn:schemas-microsoft-com:xml-analysis:empty"/> with xml['return']:
</return> xml.root(xmlns="urn:schemas-microsoft-com:xml-analysis:empty")
""")
return str(xml)
else: else:
XmlaProviderService.discover_tools.change_catalogue( XmlaProviderService.discover_tools.change_catalogue(
request.Properties.PropertyList.Catalog) request.Properties.PropertyList.Catalog)
...@@ -171,54 +175,45 @@ class XmlaProviderService(ServiceBase): ...@@ -171,54 +175,45 @@ class XmlaProviderService(ServiceBase):
df = executer.execute_mdx() df = executer.execute_mdx()
xmla_tools = XmlaExecuteTools(executer) xmla_tools = XmlaExecuteTools(executer)
return etree.fromstring(""" xml = xmlwitch.Builder()
<return> with xml['return']:
<root xmlns="urn:schemas-microsoft-com:xml-analysis:mddataset" with xml.root(
xmlns:xsd="http://www.w3.org/2001/XMLSchema" execute_xsd,
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns="urn:schemas-microsoft-com:xml-analysis:mddataset",
{0} **{
<OlapInfo> 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
<CubeInfo> 'xmlns:xsi':
<Cube> 'http://www.w3.org/2001/XMLSchema-instance'
<CubeName>Sales</CubeName> }):
<LastDataUpdate with xml.OlapInfo(xmla_tools.generate_cell_info()):
xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">{7}</LastDataUpdate> with xml.CubeInfo:
<LastSchemaUpdate with xml.Cube:
xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">{7}</LastSchemaUpdate> xml.CubeName('Sales')
</Cube> xml.LastDataUpdate(
</CubeInfo> datetime.now().strftime(
<AxesInfo> '%Y-%m-%dT%H:%M:%S'),
{1} xmlns="http://schemas.microsoft.com/analysisservices/2003/engine"
{2} )
</AxesInfo> xml.LastSchemaUpdate(
{3} datetime.now().strftime(
</OlapInfo> '%Y-%m-%dT%H:%M:%S'),
<Axes> xmlns="http://schemas.microsoft.com/analysisservices/2003/engine"
{4} )
{5}
</Axes> xml.AxesInfo(
<CellData>
{6}
</CellData>
</root>
</return>
""".format(execute_xsd,
xmla_tools.generate_axes_info(df), xmla_tools.generate_axes_info(df),
xmla_tools.generate_axes_info_slicer(df), xmla_tools.generate_axes_info_slicer(df))
xmla_tools.generate_cell_info(),
xml.Axes(
xmla_tools.generate_xs0(df), xmla_tools.generate_xs0(df),
xmla_tools.generate_slicer_axis(df), xmla_tools.generate_slicer_axis(df))
xmla_tools.generate_cell_data(df),
datetime.now().strftime('%Y-%m-%dT%H:%M:%S')).replace( xml.CellData(xmla_tools.generate_cell_data(df))
'&', '&amp;'))
html_parser = HTMLParser.HTMLParser()
# Problem: xml = html_parser.unescape(str(xml)).replace('&', '&amp;')
# An XML parser returns the error “xmlParseEntityRef: noname”
# return xml
# Cause:
# There is a stray ‘&’ (ampersand character) somewhere in the XML text eg. some text & some more text
# Solution
# .replace('&', '&amp;')
application = Application( application = Application(
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -4,6 +4,7 @@ import itertools ...@@ -4,6 +4,7 @@ import itertools
from collections import OrderedDict from collections import OrderedDict
import numpy as np import numpy as np
import xmlwitch
class XmlaExecuteTools(): class XmlaExecuteTools():
...@@ -88,7 +89,8 @@ class XmlaExecuteTools(): ...@@ -88,7 +89,8 @@ class XmlaExecuteTools():
:param splited_df: :param splited_df:
:return: :return:
""" """
axis0 = ""
xml = xmlwitch.Builder()
# only measure selected # only measure selected
if mdx_execution_result['columns_desc'][mdx_query_axis].keys() == [ if mdx_execution_result['columns_desc'][mdx_query_axis].keys() == [
self.executer.facts self.executer.facts
...@@ -128,28 +130,28 @@ class XmlaExecuteTools(): ...@@ -128,28 +130,28 @@ class XmlaExecuteTools():
] ]
first_att = 3 first_att = 3
if tuples:
with xml.Axis(name=axis):
with xml.Tuples:
for tupls in itertools.chain(*tuples): for tupls in itertools.chain(*tuples):
axis0 += "<Tuple>\n" with xml.Tuple:
if tupls[0][1] in self.executer.measures and len( if tupls[0][1] in self.executer.measures and len(
self.executer.selected_measures) > 1: self.executer.selected_measures) > 1:
with xml.Member(Hierarchy="[Measures]"):
axis0 += """ xml.UName(
<Member Hierarchy="[Measures]"> '[Measures].[{0}]'.format(tupls[0][1]))
<UName>[Measures].[{0}]</UName> xml.Caption('{0}'.format(tupls[0][1]))
<Caption>{0}</Caption> xml.LName('[Measures]')
<LName>[Measures]</LName> xml.LNum('0')
<LNum>0</LNum> xml.DisplayInfo('0')
<DisplayInfo>0</DisplayInfo> xml.HIERARCHY_UNIQUE_NAME('[Measures]')
<HIERARCHY_UNIQUE_NAME>[Measures]</HIERARCHY_UNIQUE_NAME>
</Member>
""".format(tupls[0][1])
if tupls[0][-1] in self.executer.measures: if tupls[0][-1] in self.executer.measures:
axis0 += "</Tuple>\n"
continue continue
for tupl in tupls: for tupl in tupls:
tuple_without_minus_1 = self.get_tuple_without_nan(tupl) tuple_without_minus_1 = self.get_tuple_without_nan(
tupl)
# french caracteres # french caracteres
# TODO encode dataframe # TODO encode dataframe
...@@ -159,47 +161,51 @@ class XmlaExecuteTools(): ...@@ -159,47 +161,51 @@ class XmlaExecuteTools():
for x in tuple_without_minus_1 for x in tuple_without_minus_1
] ]
axis0 += """ # todo ugly !!
<Member Hierarchy="[{0}].[{0}]"> with xml.Member(Hierarchy="[{0}].[{0}]".format(
<UName>[{0}].[{0}].[{1}].{2}</UName> tuple_without_minus_1[0])):
<Caption>{3}</Caption> xml.UName('[{0}].[{0}].[{1}].{2}'.format(
<LName>[{0}].[{0}].[{1}]</LName> tuple_without_minus_1[0], splited_df[
<LNum>{4}</LNum> tuple_without_minus_1[0]].columns[
<DisplayInfo>131076</DisplayInfo>""".format( len(tuple_without_minus_1) -
tuple_without_minus_1[0], splited_df[tuple_without_minus_1[ first_att], '.'.join([
0]].columns[len(tuple_without_minus_1) - first_att],
'.'.join([
'[' + str(i) + ']' '[' + str(i) + ']'
for i in tuple_without_minus_1[first_att - 1:] for i in
]), tuple_without_minus_1[-1], tuple_without_minus_1[
len(tuple_without_minus_1) - first_att) first_att - 1:]
# PARENT_UNIQUE_NAME must be before HIERARCHY_UNIQUE_NAME ])))
if len(tuple_without_minus_1[first_att - 1:]) > 1: xml.Caption('{0}'.format(
axis0 += """ tuple_without_minus_1[-1]))
<PARENT_UNIQUE_NAME>[{0}].[{0}].[{1}].{2}</PARENT_UNIQUE_NAME>""".format( xml.LName('[{0}].[{0}].[{1}]'.format(
tuple_without_minus_1[0], splited_df[
tuple_without_minus_1[0]].columns[
len(tuple_without_minus_1) -
first_att]))
xml.LNum('{0}'.format(
len(tuple_without_minus_1) -
first_att))
xml.DisplayInfo('131076')
# PARENT_UNIQUE_NAME must be before HIERARCHY_UNIQUE_NAME (todo change it in xsd)
if len(tuple_without_minus_1[first_att -
1:]) > 1:
xml.PARENT_UNIQUE_NAME(
'[{0}].[{0}].[{1}].{2}'.format(
tuple_without_minus_1[0], tuple_without_minus_1[0],
splited_df[tuple_without_minus_1[0]].columns[0], splited_df[
'.'.join([ tuple_without_minus_1[0]]
.columns[0], '.'.join([
'[' + str(i) + ']' '[' + str(i) + ']'
for i in tuple_without_minus_1[first_att - 1:-1] for i in
])) tuple_without_minus_1[
axis0 += """ first_att - 1:-1]
<HIERARCHY_UNIQUE_NAME>[{0}].[{0}]</HIERARCHY_UNIQUE_NAME> ])))
</Member>
""".format(tuple_without_minus_1[0])
axis0 += "</Tuple>\n"
if axis0: xml.HIERARCHY_UNIQUE_NAME(
axis0 = """ '[{0}].[{0}]'.format(
<Axis name="{0}"> tuple_without_minus_1[0]))
<Tuples>
{1}
</Tuples>
</Axis>
""".format(axis, axis0)
return axis0 return str(xml)
def generate_xs0(self, mdx_execution_result): def generate_xs0(self, mdx_execution_result):
""" """
...@@ -307,7 +313,6 @@ class XmlaExecuteTools(): ...@@ -307,7 +313,6 @@ class XmlaExecuteTools():
:param mdx_execution_result: mdx_execute() result :param mdx_execution_result: mdx_execute() result
:return: CellData as string :return: CellData as string
""" """
columns_loop = []
if ((len(mdx_execution_result['columns_desc']['columns'].keys()) == 0) if ((len(mdx_execution_result['columns_desc']['columns'].keys()) == 0)
^ ^
...@@ -331,18 +336,16 @@ class XmlaExecuteTools(): ...@@ -331,18 +336,16 @@ class XmlaExecuteTools():
index=False) index=False)
]) ])
cell_data = "" xml = xmlwitch.Builder()
index = 0 index = 0
for value in columns_loop: for value in columns_loop:
if np.isnan(value): if np.isnan(value):
value = '' value = ''
cell_data += """ with xml.Cell(CellOrdinal=str(index)):
<Cell CellOrdinal="{0}"> xml.Value(str(value), **{'xsi:type': 'xsi:long'})
<Value xsi:type="xsi:long">{1}</Value>
</Cell>
""".format(index, value)
index += 1 index += 1
return cell_data return str(xml)
def generate_axes_info_slicer(self, mdx_execution_result): def generate_axes_info_slicer(self, mdx_execution_result):
""" """
...@@ -377,43 +380,51 @@ class XmlaExecuteTools(): ...@@ -377,43 +380,51 @@ class XmlaExecuteTools():
ignore_fact=True) ignore_fact=True)
all_dimensions_names.append('Measures') all_dimensions_names.append('Measures')
hierarchy_info_slicer = "" xml = xmlwitch.Builder()
slicer_list = list( slicer_list = list(
set(all_dimensions_names) - set( set(all_dimensions_names) - set(
table_name table_name
for table_name in mdx_execution_result['columns_desc']['all'])) for table_name in mdx_execution_result['columns_desc']['all']))
# we have to write measures after dimensions ! # we have to write measures after dimensions ! (todo change xsd)
if 'Measures' in slicer_list: if 'Measures' in slicer_list:
slicer_list.insert( slicer_list.insert(
len(slicer_list), len(slicer_list),
slicer_list.pop(slicer_list.index('Measures'))) slicer_list.pop(slicer_list.index('Measures')))
if slicer_list:
with xml.AxisInfo(name='SlicerAxis'):
for dim_diff in slicer_list: for dim_diff in slicer_list:
to_write = "[{0}].[{0}]".format(dim_diff) to_write = "[{0}].[{0}]".format(dim_diff)
if dim_diff == 'Measures': if dim_diff == 'Measures':
# if measures > 1 we don't have to write measure # if measures > 1 we don't have to write measure
if self.executer.facts in mdx_execution_result['columns_desc'][ if self.executer.facts in mdx_execution_result[
'all'] and len(mdx_execution_result['columns_desc'][ 'columns_desc']['all'] and len(
mdx_execution_result['columns_desc'][
'all'][self.executer.facts]) > 1: 'all'][self.executer.facts]) > 1:
continue continue
else: else:
to_write = "[Measures]" to_write = "[Measures]"
hierarchy_info_slicer += """
<HierarchyInfo name="{0}">
<UName name="{0}.[MEMBER_UNIQUE_NAME]" type="xs:string"/>
<Caption name="{0}.[MEMBER_CAPTION]" type="xs:string"/>
<LName name="{0}.[LEVEL_UNIQUE_NAME]" type="xs:string"/>
<LNum name="{0}.[LEVEL_NUMBER]" type="xs:int"/>
<DisplayInfo name="{0}.[DISPLAY_INFO]" type="xs:unsignedInt"/>
</HierarchyInfo>
""".format(to_write)
if hierarchy_info_slicer:
hierarchy_info_slicer = "<AxisInfo name='SlicerAxis'>\n" + hierarchy_info_slicer + "\n</AxisInfo>\n"
return hierarchy_info_slicer with xml.HierarchyInfo(name=to_write):
xml.UName(
name="{0}.[MEMBER_UNIQUE_NAME]".format(to_write),
**{'type': 'xs:string'})
xml.Caption(
name="{0}.[MEMBER_CAPTION]".format(to_write),
**{'type': 'xs:string'})
xml.LName(
name="{0}.[LEVEL_UNIQUE_NAME]".format(to_write),
**{'type': 'xs:string'})
xml.LNum(
name="{0}.[LEVEL_NUMBER]".format(to_write),
**{'type': 'xs:int'})
xml.DisplayInfo(
name="{0}.[DISPLAY_INFO]".format(to_write),
**{'type': 'xs:unsignedInt'})
return str(xml)
def generate_one_axis_info(self, def generate_one_axis_info(self,
mdx_execution_result, mdx_execution_result,
...@@ -452,45 +463,72 @@ class XmlaExecuteTools(): ...@@ -452,45 +463,72 @@ class XmlaExecuteTools():
:param Axis: Axis0 or Axis1 (Axis0 by default) :param Axis: Axis0 or Axis1 (Axis0 by default)
:return: :return:
""" """
hierarchy_info = ""
# measure must be written at the top
if self.executer.facts in mdx_execution_result['columns_desc'][
mdx_query_axis].keys() and len(mdx_execution_result[
'columns_desc'][mdx_query_axis][self.executer.facts]) > 1:
hierarchy_info += """
<HierarchyInfo name="{0}">
<UName name="{0}.[MEMBER_UNIQUE_NAME]" type="xs:string"/>
<Caption name="{0}.[MEMBER_CAPTION]" type="xs:string"/>
<LName name="{0}.[LEVEL_UNIQUE_NAME]" type="xs:string"/>
<LNum name="{0}.[LEVEL_NUMBER]" type="xs:int"/>
<DisplayInfo name="{0}.[DISPLAY_INFO]" type="xs:unsignedInt"/>
<PARENT_UNIQUE_NAME name="{0}.[PARENT_UNIQUE_NAME]" type="xs:string"/>
<HIERARCHY_UNIQUE_NAME name="{0}.[HIERARCHY_UNIQUE_NAME]" type="xs:string"/>
</HierarchyInfo>
""".format('[Measures]')
for table_name in mdx_execution_result['columns_desc'][mdx_query_axis]: axis_tables = mdx_execution_result['columns_desc'][mdx_query_axis]
xml = xmlwitch.Builder()
# measure must be written at the top
if axis_tables:
with xml.AxisInfo(name=Axis):
# many measures , then write this on the top
if self.executer.facts in axis_tables.keys() and len(
axis_tables[self.executer.facts]) > 1:
with xml.HierarchyInfo(name='[Measures]'):
xml.UName(
name="[Measures].[MEMBER_UNIQUE_NAME]",
**{'type': 'xs:string'})
xml.Caption(
name="[Measures].[MEMBER_CAPTION]",
**{'type': 'xs:string'})
xml.LName(
name="[Measures].[LEVEL_UNIQUE_NAME]",
**{'type': 'xs:string'})
xml.LNum(
name="[Measures].[LEVEL_NUMBER]",
**{'type': 'xs:int'})
xml.DisplayInfo(
name="[Measures].[DISPLAY_INFO]",
**{'type': 'xs:unsignedInt'})
xml.PARENT_UNIQUE_NAME(
name="[Measures].[PARENT_UNIQUE_NAME]",
**{'type': 'xs:string'})
xml.HIERARCHY_UNIQUE_NAME(
name="[Measures].[HIERARCHY_UNIQUE_NAME]",
**{'type': 'xs:string'})
for table_name in axis_tables:
if table_name != self.executer.facts: if table_name != self.executer.facts:
hierarchy_info += """ with xml.HierarchyInfo(
<HierarchyInfo name="[{0}].[{0}]"> name='[{0}].[{0}]'.format(table_name)):
<UName name="[{0}].[{0}].[MEMBER_UNIQUE_NAME]" type="xs:string"/> xml.UName(
<Caption name="[{0}].[{0}].[MEMBER_CAPTION]" type="xs:string"/> name="[{0}].[{0}].[MEMBER_UNIQUE_NAME]".format(
<LName name="[{0}].[{0}].[LEVEL_UNIQUE_NAME]" type="xs:string"/> table_name),
<LNum name="[{0}].[{0}].[LEVEL_NUMBER]" type="xs:int"/> **{'type': 'xs:string'})
<DisplayInfo name="[{0}].[{0}].[DISPLAY_INFO]" type="xs:unsignedInt"/> xml.Caption(
<PARENT_UNIQUE_NAME name="[{0}].[{0}].[PARENT_UNIQUE_NAME]" type="xs:string"/> name="[{0}].[{0}].[MEMBER_CAPTION]".format(
<HIERARCHY_UNIQUE_NAME name="[{0}].[{0}].[HIERARCHY_UNIQUE_NAME]" type="xs:string"/> table_name),
</HierarchyInfo> **{'type': 'xs:string'})
""".format(table_name) xml.LName(
name="[{0}].[{0}].[LEVEL_UNIQUE_NAME]".format(
if hierarchy_info: table_name),
hierarchy_info = """ **{'type': 'xs:string'})
<AxisInfo name='{0}'> xml.LNum(
{1} name="[{0}].[{0}].[LEVEL_NUMBER]".format(
</AxisInfo> table_name),
""".format(Axis, hierarchy_info) **{'type': 'xs:int'})
xml.DisplayInfo(
return hierarchy_info name="[{0}].[{0}].[DISPLAY_INFO]".format(
table_name),
**{'type': 'xs:unsignedInt'})
xml.PARENT_UNIQUE_NAME(
name="[{0}].[{0}].[PARENT_UNIQUE_NAME]".format(
table_name),
**{'type': 'xs:string'})
xml.HIERARCHY_UNIQUE_NAME(
name="[{0}].[{0}].[HIERARCHY_UNIQUE_NAME]".
format(table_name),
**{'type': 'xs:string'})
return str(xml)
def generate_axes_info(self, mdx_execution_result): def generate_axes_info(self, mdx_execution_result):
""" """
...@@ -514,15 +552,16 @@ class XmlaExecuteTools(): ...@@ -514,15 +552,16 @@ class XmlaExecuteTools():
@staticmethod @staticmethod
def generate_cell_info(): def generate_cell_info():
return """ xml = xmlwitch.Builder()
<CellInfo> with xml.CellInfo:
<Value name="VALUE"/> xml.Value(name="VALUE")
<FormatString name="FORMAT_STRING" type="xs:string"/> xml.FormatString(name="FORMAT_STRING", **{'type': 'xs:string'})
<Language name="LANGUAGE" type="xs:unsignedInt"/> xml.Language(name="LANGUAGE", **{'type': 'xs:unsignedInt'})
<BackColor name="BACK_COLOR" type="xs:unsignedInt"/> xml.BackColor(name="BACK_COLOR", **{'type': 'xs:unsignedInt'})
<ForeColor name="FORE_COLOR" type="xs:unsignedInt"/> xml.ForeColor(name="FORE_COLOR", **{'type': 'xs:unsignedInt'})
<FontFlags name="FONT_FLAGS" type="xs:int"/> xml.FontFlags(name="FONT_FLAGS", **{'type': 'xs:int'})
</CellInfo>"""
return str(xml)
def generate_slicer_axis(self, mdx_execution_result): def generate_slicer_axis(self, mdx_execution_result):
""" """
...@@ -553,57 +592,51 @@ class XmlaExecuteTools(): ...@@ -553,57 +592,51 @@ class XmlaExecuteTools():
:param mdx_execution_result: mdx_execute() result :param mdx_execution_result: mdx_execute() result
:return: SlicerAxis as string :return: SlicerAxis as string
""" """
tuple = ""
# not used dimensions # not used dimensions
for dim_diff in list( unused_dimensions = list(
set(self.executer.get_all_tables_names(ignore_fact=True)) - set(self.executer.get_all_tables_names(ignore_fact=True)) - set(
set(table_name table_name
for table_name in mdx_execution_result['columns_desc'][ for table_name in mdx_execution_result['columns_desc']['all']))
'all'])): xml = xmlwitch.Builder()
if unused_dimensions:
with xml.Axis(name="SlicerAxis"):
with xml.Tuples:
with xml.Tuple:
for dim_diff in unused_dimensions:
# TODO encode dataframe # TODO encode dataframe
# french caracteres # french caracteres
if type(self.executer.tables_loaded[dim_diff].iloc[0][ if type(self.executer.tables_loaded[dim_diff].iloc[
0]) == unicode: 0][0]) == unicode:
column_attribut = self.executer.tables_loaded[dim_diff].iloc[ column_attribut = self.executer.tables_loaded[
0][0].encode('utf-8', 'replace') dim_diff].iloc[0][0].encode('utf-8',
'replace')
else: else:
column_attribut = self.executer.tables_loaded[dim_diff].iloc[ column_attribut = self.executer.tables_loaded[
0][0] dim_diff].iloc[0][0]
tuple += """ with xml.Member(
<Member Hierarchy="[{0}].[{0}]"> Hierarchy="[{0}].[{0}]".format(dim_diff)):
<UName>[{0}].[{0}].[{1}].[{2}]</UName> xml.UName('[{0}].[{0}].[{1}].[{2}]'.format(
<Caption>{2}</Caption> dim_diff, self.executer.tables_loaded[
<LName>[{0}].[{0}].[{1}]</LName> dim_diff].columns[0], column_attribut))
<LNum>0</LNum> xml.Caption(str(column_attribut))
<DisplayInfo>2</DisplayInfo> xml.LName('[{0}].[{0}].[{1}]'.format(
</Member> dim_diff, self.executer.tables_loaded[
""".format(dim_diff, dim_diff].columns[0]))
self.executer.tables_loaded[dim_diff].columns[0], xml.LNum('0')
column_attribut) xml.DisplayInfo('2')
# if we have zero on one only measures used # if we have zero on one only measures used
if len(self.executer.selected_measures) <= 1: if len(self.executer.selected_measures) <= 1:
tuple += """ with xml.Member(
<Member Hierarchy="[Measures]"> Hierarchy="[Measures]".format(dim_diff)):
<UName>[Measures].[{0}]</UName> xml.UName('[Measures].[{0}]'.format(
<Caption>{0}</Caption> self.executer.measures[0]))
<LName>[Measures]</LName> xml.Caption(
<LNum>0</LNum> '{0}'.format(self.executer.measures[0]))
<DisplayInfo>0</DisplayInfo> xml.LName('[Measures]')
</Member> xml.LNum('0')
""".format(self.executer.measures[0]) xml.DisplayInfo('0')
if tuple: return str(xml)
tuple = """
<Axis name="SlicerAxis">
<Tuples>
<Tuple>
{0}
</Tuple>
</Tuples>
</Axis>
""".format(tuple)
return tuple
...@@ -5,6 +5,7 @@ spyne<3 ...@@ -5,6 +5,7 @@ spyne<3
treelib<2 treelib<2
SQLAlchemy SQLAlchemy
psycopg2 psycopg2
xmlwitch
# Test # Test
werkzeug werkzeug
......
...@@ -15,5 +15,6 @@ pytz==2017.2 # via pandas, spyne ...@@ -15,5 +15,6 @@ pytz==2017.2 # via pandas, spyne
six==1.10.0 # via python-dateutil six==1.10.0 # via python-dateutil
spyne==2.12.14 spyne==2.12.14
sqlalchemy==1.1.11 sqlalchemy==1.1.11
treelib==1.3.5 treelib==1.3.7
werkzeug==0.12.2 werkzeug==0.12.2
xmlwitch==0.3
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment