def render_diagram(root_task, out_base, max_param_len=20, horizontal=False, colored=False):
    import re
    import codecs
    import subprocess
    from ozelot import config
    from ozelot.etl.tasks import get_task_name, get_task_param_string
    lines = [u"digraph G {"]
    if horizontal:
        lines.append(u"rankdir=LR;")
    def get_id(task):
        s = get_task_name(task) + "_" + get_task_param_string(task)
        return re.sub(r'\W+', '', re.sub(' ', '_', s))
    existing_nodes = set()
    existing_edges = set()
    def _build(task, parent_id=None):
        tid = get_id(task)
        if tid not in existing_nodes:
            params = task.to_str_params()
            param_list = ""
            for k, v in params.items():
                if len(v) > max_param_len:
                    v = v[:max_param_len] + "..."
                param_list += "<TR><TD ALIGN=\"LEFT\">" \
                              "<FONT POINT-SIZE=\"10\">{:s}</FONT>" \
                              "</TD><TD ALIGN=\"LEFT\">" \
                              "<FONT POINT-SIZE=\"10\">{:s}</FONT>" \
                              "</TD></TR>".format(k, v)
            label = "<TABLE BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"1\">" \
                    "<TR><TD COLSPAN=\"2\" ALIGN=\"CENTER\">" \
                    "<FONT POINT-SIZE=\"12\">{:s}</FONT>" \
                    "</TD></TR>" \
                    "".format(get_task_name(task)) + param_list + "</TABLE>"
            style = getattr(task, 'diagram_style', [])
            if colored:
                color = ', color="{:s}"'.format("green" if task.complete() else "red")
            else:
                color = ''
            lines.append(u"{name:s} [label=< {label:s} >, shape=\"rect\" {color:s}, style=\"{style:s}\"];\n"
                         u"".format(name=tid,
                                    label=label,
                                    color=color,
                                    style=','.join(style)))
            existing_nodes.add(tid)
            for req in task.requires():
                _build(req, parent_id=tid)
        if parent_id is not None and (tid, parent_id) not in existing_edges:
            lines.append(u"{source:s} -> {target:s};\n".format(source=tid, target=parent_id))
    _build(root_task)
    lines.append(u"}")
    with codecs.open(out_base + '.dot', 'w', encoding='utf-8') as f:
        f.write(u"\n".join(lines))
    if not hasattr(config, 'DOT_EXECUTABLE'):
        raise RuntimeError("Please configure the 'DOT_EXECUTABLE' variable in your 'project_config.py'")
    if not os.path.exists(config.DOT_EXECUTABLE):
        raise IOError("Could not find file pointed to by 'DOT_EXECUTABLE': " + str(config.DOT_EXECUTABLE))
    subprocess.check_call([
        config.DOT_EXECUTABLE,
        '-T', 'png',
        '-o', out_base + '.png',
              out_base + '.dot'
    ])