diff --git a/doc/data_analytics_example/data_analytics/__init__.py b/doc/data_analytics_example/data_analytics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/doc/data_analytics_example/data_analytics/data_analytics_button.png b/doc/data_analytics_example/data_analytics/data_analytics_button.png new file mode 100644 index 0000000..11e26a0 Binary files /dev/null and b/doc/data_analytics_example/data_analytics/data_analytics_button.png differ diff --git a/doc/data_analytics_example/data_analytics/pyxll_extension.py b/doc/data_analytics_example/data_analytics/pyxll_extension.py new file mode 100644 index 0000000..7193c4a --- /dev/null +++ b/doc/data_analytics_example/data_analytics/pyxll_extension.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +def load(submit_ribbon_tab=None, **kw): + """ Loads the PyXLL modules to be exposed to Excel. """ + + # Try to submit our ribbon piece to PyXLL + + if submit_ribbon_tab: + import os + import pkgutil + tab_template = pkgutil.get_data('data_analytics', 'ribbon_tab.xml') + root_path = os.path.join(os.path.dirname(__file__), '') + tab = tab_template.format(ROOT=root_path) + submit_ribbon_tab('data_analytics', tab) diff --git a/doc/data_analytics_example/data_analytics/ribbon_tab.xml b/doc/data_analytics_example/data_analytics/ribbon_tab.xml new file mode 100644 index 0000000..7e1cb0e --- /dev/null +++ b/doc/data_analytics_example/data_analytics/ribbon_tab.xml @@ -0,0 +1,9 @@ + + + + +''' + + +class RibbonSynthesizer(object): + + @classmethod + def from_file(cls, filename): + if os.path.exists(filename): + with open(filename, 'rb') as f: + default_ribbon = f.read() + else: + default_ribbon = None + return cls(default_ribbon=default_ribbon) + + def __init__(self, default_ribbon=None): + self.ribbon = default_ribbon or EMPTY_RIBBON + self._elements_to_insert = OrderedDict() + + def to_bytes(self, names=None): + if names is None: + names = sorted(self._elements_to_insert.keys()) + ribbon = self.parse(self.ribbon) + tabs = self.get_tabs(ribbon) + for name in names: + try: + tab = self._elements_to_insert[name] + except KeyError: + logger.info("skip {}".format(name)) + # This isn't an error. It probably just means that the + # requested extension didn't submit a fragment. + continue + self.upsert_by_attribute(tabs, tab) + return self.element_as_bytes(ribbon) + + @staticmethod + def parse(buf): + return etree.fromstring(buf) + + @staticmethod + def element_as_bytes(element): + return etree.tostring(element, pretty_print=True) + + @staticmethod + def get_tabs(root): + return root.find('.//{*}ribbon/{*}tabs') + + def submit_ribbon_tab(self, extension_name, tab_buffer): + root_elem = self.parse(tab_buffer) + if root_elem.tag != 'tab': + msg = ("Ignoring fragment {}:\n{}\n" + "Ribbons must be elements like this:\n{}") + logger.warning( + msg.format(extension_name, tab_buffer, SAMPLE_RIBBON_FRAGMENT)) + return + else: + logger.info("Adding ribbon fragment: {}".format(extension_name)) + self._elements_to_insert[extension_name] = root_elem + + @staticmethod + def upsert_by_attribute(parent, element, attr='id'): + """Add or append `element` to parent depending on `attr`. + + If `parent` contains a child such that: + (element.tag, element.get(attr)) == (child.tag, child.get(attr)) + then we add the contents of element to child. + Otherwise, we add element as a sibling of child. + + This allows us to extend existing elements by matching on attr. + """ + + tag = element.tag + attr_val = element.get(attr) + query = '{tag}[@{attr}="{attr_val}"]'.format(**locals()) + matches = parent.findall("{*}" + query) + if matches: + match = matches[0] + match.extend(element.getchildren()) + else: + parent.append(element) + return parent diff --git a/setup.py b/setup.py index bf4050a..d3e3439 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,6 @@ version='1.0', author='PyXLL Ltd, Enthought Inc.', packages=find_packages(), - # Provides a namespace for extension points to contribute to. This - # functionnality is required by the pyxll_addons.extension_loader module - provides=['pyxll.modules'] -) + # Provides a namespace for extension points to contribute to. This + # functionality is required by the pyxll_addons.extension_loader module + provides=['pyxll.extensions'])