diff --git a/docs/templating.rst b/docs/templating.rst index 3e619fd..6b244d2 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -20,7 +20,7 @@ The python object above can be expressed in a YAML notation and read into **Example:** >>> my_template = """ ... - Column: - ... Title: + ... - Title: ... data: ['This is a', 'sample title'] ... formats: ... align: 'left' @@ -41,7 +41,7 @@ using jinja, we can insert data into our template. **Example:** >>> my_template = """ ... - Column: - ... Title: + ... - Title: ... data: ['This is a', {{title}}] ... formats: ... align: 'left' @@ -70,10 +70,10 @@ variable, jinja will grab the string representation of the object. **This will not work:** >>> my_template = """ ... - Column: - ... Title: + ... - Title: ... data: ['This is a', 'sample title'] - ... DataFrame: - ... data: {{data}} + ... - DataFrame: + ... data: {{data}} ... """ >>> xlc.load_yaml(my_template, data=data) @@ -90,10 +90,10 @@ We don't want the string representation of our `DataFrame`, we want the actual **This is correct:** >>> my_template = """ ... - Column: - ... Title: + ... - Title: ... data: ['This is a', 'sample title'] - ... DataFrame: - ... data: {% eval %}data{% endeval %} + ... - DataFrame: + ... data: {% eval %}data{% endeval %} ... """ >>> xlc.load_yaml(my_template, data=data) @@ -109,10 +109,10 @@ pass other variables as context to your `eval` directive. **Example:** >>> my_template = """ ... - Column: - ... Title: + ... - Title: ... data: ['This is a', 'sample title'] - ... DataFrame: - ... data: {% eval %}data.groupby('{{group}}').sum(){% endeval %} + ... - DataFrame: + ... data: {% eval %}data.groupby('{{group}}').sum(){% endeval %} ... """ >>> xlc.load_yaml(my_template, data=data, group='country') @@ -129,14 +129,14 @@ revenue and expense by product. **Example:** >>> my_template = """ ... - Column: - ... Title: + ... - Title: ... data: ['Summary of Revenue and Expense', 'By Product and Country'] ... {% for country in data['country'].unique() %} - ... Series: + ... - Series: ... data: 'Revenue and Expense for {{country}}' ... formats: ... bold: True - ... DataFrame: + ... - DataFrame: ... data: {% eval %}data.groupby({{country}})[['Revenue', 'Expense']].sum(){% endeval %} ... formats: ... align: 'center' diff --git a/setup.py b/setup.py index 0510d7e..c280721 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ descr = "A declarative API for composing spreadsheets from python" name = 'xlcompose' url = 'https://github.com/jbogaardt/xlcompose' -version='0.2.0' # Put this in __init__.py +version='0.2.1' # Put this in __init__.py data_path = '' setup( diff --git a/xlcompose/__init__.py b/xlcompose/__init__.py index f15e2f8..7611160 100644 --- a/xlcompose/__init__.py +++ b/xlcompose/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.2.0' +__version__ = '0.2.1' from xlcompose.core import ( Tabs, Sheet, Row, Column, DataFrame, Series, CSpacer, RSpacer, Title, Image, VSpacer, HSpacer) diff --git a/xlcompose/templates.py b/xlcompose/templates.py index 440b4bf..c9df55a 100644 --- a/xlcompose/templates.py +++ b/xlcompose/templates.py @@ -5,7 +5,7 @@ import ast import os import xlcompose.core as core -from jinja2 import nodes, Template, TemplateSyntaxError, FileSystemLoader, Environment +from jinja2 import nodes, Template, TemplateSyntaxError, FileSystemLoader, Environment, BaseLoader from jinja2.ext import Extension from jinja2.nodes import Const @@ -28,7 +28,7 @@ def _eval(self, eval_str, caller): return '__eval__' + caller() + '__eval__' -def kwarg_parse(formula): +def _kwarg_parse(formula): names = pd.Series(list(set([node.id for node in ast.walk(ast.parse(formula)) if isinstance(node, ast.Name)]))) names = names[~names.isin(['str', 'list', 'dict', 'int', 'float'])] @@ -41,20 +41,23 @@ def kwarg_parse(formula): formula = formula.replace(item[1],item[2]) return formula -def make_xlc(template, **kwargs): +def _make_xlc(template, **kwargs): """ Recursively generate xlcompose object""" if type(template) is list: - tabs = [make_xlc(element, **kwargs) for element in template] - return core.Tabs(*[(item.name, item) for item in tabs]) + tabs = [_make_xlc(element, **kwargs) for element in template] + try: + return core.Tabs(*[(item.name, item) for item in tabs]) + except: + return core.Tabs(*[('Sheet1', item) for item in tabs]) key = list(template.keys())[0] if key in ['Row', 'Column']: - return getattr(core, key)(*[make_xlc(element, **kwargs) + return getattr(core, key)(*[_make_xlc(element, **kwargs) for element in template[key]]) if key in ['Sheet']: sheet_kw = {k: v for k, v in template[key].items() if k not in ['name', 'layout']} return core.Sheet(template[key]['name'], - make_xlc(template[key]['layout'], **kwargs), + _make_xlc(template[key]['layout'], **kwargs), **sheet_kw) if key in ['DataFrame', 'Title', 'CSpacer', 'RSpacer', 'HSpacer', 'Series', 'Image']: @@ -62,18 +65,23 @@ def make_xlc(template, **kwargs): if type(v) is str: if v[:8] == '__eval__': v_adj = v.replace('__eval__', '') - template[key][k] = eval(kwarg_parse(v_adj)) + template[key][k] = eval(_kwarg_parse(v_adj)) return getattr(core, key)(**template[key]) def load(template, env, kwargs): if env: env.add_extension(EvalExtension) else: - path = os.path.dirname(os.path.abspath(template)) - template = os.path.split(os.path.abspath(template))[-1] - env = Environment(loader=FileSystemLoader(path)) - env.add_extension(EvalExtension) - template = env.get_template(template).render(kwargs) + try: + path = os.path.dirname(os.path.abspath(template)) + template = os.path.split(os.path.abspath(template))[-1] + env = Environment(loader=FileSystemLoader(path)) + env.add_extension(EvalExtension) + template = env.get_template(template).render(kwargs) + except: + env = Environment(loader=BaseLoader()) + env.add_extension(EvalExtension) + template = env.from_string(template).render(kwargs) replace = [item.strip() for item in re.findall('[ :]{{.+}}', template)] for item in replace: template = template.replace(item, '\'' + item + '\'') @@ -84,10 +92,10 @@ def load_yaml(template, env=None, **kwargs): """ Loads a YAML template specifying the structure of the XLCompose Object. """ template = load(template, env, kwargs) - return make_xlc(yaml.load(template, Loader=yaml.SafeLoader), **kwargs) + return _make_xlc(yaml.load(template, Loader=yaml.SafeLoader), **kwargs) def load_json(template, env=None, **kwargs): """ Loads a JSON template specifying the structure of the XLCompose Object. """ template = load(template, env, kwargs) - return make_xlc(json.loads(template), **kwargs) + return _make_xlc(json.loads(template), **kwargs)