def filegraph_dot(self, node2rule=(lambda node: node), node2style=(lambda node: 'rounded'), node2label=(lambda node: node)):
    graph = defaultdict(set)
    for job in self.jobs:
        graph[job.rule].update((dep.rule for dep in self.dependencies[job]))
    ids = {node: i for (i, node) in enumerate(graph)}

    def hsv_to_htmlhexrgb(h, s, v):
        'Convert hsv colors to hex-encoded rgb colors usable by html.'
        import colorsys
        (hex_r, hex_g, hex_b) = (round((255 * x)) for x in colorsys.hsv_to_rgb(h, s, v))
        return '#{hex_r:0>2X}{hex_g:0>2X}{hex_b:0>2X}'.format(hex_r=hex_r, hex_g=hex_g, hex_b=hex_b)
    huefactor = (2 / (3 * len(self.rules)))
    rulecolor = {rule: hsv_to_htmlhexrgb((i * huefactor), 0.6, 0.85) for (i, rule) in enumerate(self.rules)}

    def resolve_input_functions(input_files):
        'Iterate over all input files and replace input functions\n            with a fixed string.\n            '
        files = []
        for f in input_files:
            if callable(f):
                files.append('<input function>')
            else:
                files.append(repr(f).strip("'"))
        return files

    def html_node(node_id, node, color):
        'Assemble a html style node for graphviz'
        input_files = resolve_input_functions(node._input)
        output_files = [repr(f).strip("'") for f in node._output]
        input_header = ('<b><font point-size="14">&#8618; input</font></b>' if input_files else '')
        output_header = ('<b><font point-size="14">output &rarr;</font></b>' if output_files else '')
        html_node = ['{node_id} [ shape=none, margin=0, label=<<table border="2" color="{color}" cellspacing="3" cellborder="0">'.format(node_id=node_id, color=color), '<tr><td>', '<b><font point-size="18">{node.name}</font></b>'.format(node=node), '</td></tr>', '<hr/>', '<tr><td align="left"> {input_header} </td></tr>'.format(input_header=input_header)]
        for filename in sorted(input_files):
            in_file = html.escape(filename)
            html_node.extend(['<tr>', '<td align="left"><font face="monospace">{in_file}</font></td>'.format(in_file=in_file), '</tr>'])
        html_node.append('<hr/>')
        html_node.append('<tr><td align="right"> {output_header} </td> </tr>'.format(output_header=output_header))
        for filename in sorted(output_files):
            out_file = html.escape(filename)
            html_node.extend(['<tr>', '<td align="left"><font face="monospace">{out_file}</font></td></tr>'.format(out_file=out_file)])
        html_node.append('</table>>]')
        return '\n'.join(html_node)
    nodes = [html_node(ids[node], node, rulecolor[node2rule(node)]) for node in graph]
    edge_markup = '\t{} -> {}'.format
    edges = [edge_markup(ids[dep], ids[node], ids[dep], ids[node]) for (node, deps) in graph.items() for dep in deps]
    return textwrap.dedent('            digraph snakemake_dag {{\n                graph[bgcolor=white, margin=0];\n                node[shape=box, style=rounded, fontname=sans,                 fontsize=10, penwidth=2];\n                edge[penwidth=2, color=grey];\n            {items}\n            }}            ').format(items='\n'.join((nodes + edges)))
def filegraph_dot(self, node2rule=(lambda node: node), node2style=(lambda node: 'rounded'), node2label=(lambda node: node)):
    graph = defaultdict(set)
    for job in self.jobs:
        graph[job.rule].update((dep.rule for dep in self.dependencies[job]))
    ids = {node: i for (i, node) in enumerate(graph)}

    def hsv_to_htmlhexrgb(h, s, v):
        'Convert hsv colors to hex-encoded rgb colors usable by html.'
        import colorsys
        (hex_r, hex_g, hex_b) = (round((255 * x)) for x in colorsys.hsv_to_rgb(h, s, v))
        return '#{hex_r:0>2X}{hex_g:0>2X}{hex_b:0>2X}'.format(hex_r=hex_r, hex_g=hex_g, hex_b=hex_b)
    huefactor = (2 / (3 * len(self.rules)))
    rulecolor = {rule: hsv_to_htmlhexrgb((i * huefactor), 0.6, 0.85) for (i, rule) in enumerate(self.rules)}

    def resolve_input_functions(input_files):
        'Iterate over all input files and replace input functions\n            with a fixed string.\n            '
        files = []
        for f in input_files:
            if callable(f):
                files.append('<input function>')
            else:
                files.append(repr(f).strip("'"))
        return files

    def html_node(node_id, node, color):
        'Assemble a html style node for graphviz'
        input_files = resolve_input_functions(node._input)
        output_files = [repr(f).strip("'") for f in node._output]
        input_header = ('<b><font point-size="14">&#8618; input</font></b>' if input_files else )
        output_header = ('<b><font point-size="14">output &rarr;</font></b>' if output_files else )
        html_node = ['{node_id} [ shape=none, margin=0, label=<<table border="2" color="{color}" cellspacing="3" cellborder="0">'.format(node_id=node_id, color=color), '<tr><td>', '<b><font point-size="18">{node.name}</font></b>'.format(node=node), '</td></tr>', '<hr/>', '<tr><td align="left"> {input_header} </td></tr>'.format(input_header=input_header)]
        for filename in sorted(input_files):
            in_file = html.escape(filename)
            html_node.extend(['<tr>', '<td align="left"><font face="monospace">{in_file}</font></td>'.format(in_file=in_file), '</tr>'])
        html_node.append('<hr/>')
        html_node.append('<tr><td align="right"> {output_header} </td> </tr>'.format(output_header=output_header))
        for filename in sorted(output_files):
            out_file = html.escape(filename)
            html_node.extend(['<tr>', '<td align="left"><font face="monospace">{out_file}</font></td></tr>'.format(out_file=out_file)])
        html_node.append('</table>>]')
        return '\n'.join(html_node)
    nodes = [html_node(ids[node], node, rulecolor[node2rule(node)]) for node in graph]
    edge_markup = '\t{} -> {}'.format
    edges = [edge_markup(ids[dep], ids[node], ids[dep], ids[node]) for (node, deps) in graph.items() for dep in deps]
    return textwrap.dedent('            digraph snakemake_dag {{\n                graph[bgcolor=white, margin=0];\n                node[shape=box, style=rounded, fontname=sans,                 fontsize=10, penwidth=2];\n                edge[penwidth=2, color=grey];\n            {items}\n            }}            ').format(items='\n'.join((nodes + edges)))