smok-serwis/bunia

View on GitHub
bunia/runner/html.py

Summary

Maintainability
A
2 hrs
Test Coverage
# coding=UTF-8
import warnings

from bunia.runner.base import Runner
from bunia.api import ValuelessArgument
from bunia.output import ConsoleOutput, BytesOutput
import io
import six


class HTMLOutputBuilder(object):
    """
    Used to build output HTML.

    You can subclass it and pass to HTMLRunner for customization
    """

    def __init__(self):
        self.out = io.StringIO()

    def start(self):
        """Start of rendering"""

    def stop(self):
        """Stop of rendering"""

    def default_output(self, output):
        """Default (no-name) output is being displayed"""
        self.out.write(six.text_type(''.join(_output_to_html(output))))
        self.out.write(six.text_type('<br>'))

    def named_output(self, output):
        """Named output is being displayed"""
        self.out.write(six.text_type('<h3>'))
        self.out.write(six.text_type(output.name))
        self.out.write(six.text_type('</h3>'))
        self.out.write(six.text_type(''.join(_output_to_html(output))))
        self.out.write(six.text_type('<br>'))

    def to_html(self):
        q = self.out.getvalue()
        self.out.close()
        return q


class HTMLRunner(Runner):
    """
    A HTML-based runner. Arguments are a dictionary received by POST, output is utf8 HTML.

    :ivar files: (tp.List[BytesOutput]) - list of files generated by this runner
    """

    def __init__(self, output_builder=HTMLOutputBuilder):
        self.outputs = []
        self.builder = output_builder()
        self.files = []

    def new_console(self, name=None, sink=None):
        if sink is not None:
            warnings.warn('sink is deprecated, use NullOutput instead', DeprecationWarning)
        con = ConsoleOutput(name)
        self.outputs.append(con)
        return con

    def new_file(self, filename=None, mimetype=None):
        file = BytesOutput(filename)
        file.mimetype = mimetype
        self.files.append(file)

    def get_html(self):
        """Return HTML generated with output_builder"""
        self.builder.start()

        for output in self.outputs:
            if output.name is None:
                default = output
                break
        else:
            default = None

        if default is not None:
            self.builder.default_output(default)

        for output in self.outputs:
            if output is not default:
                self.builder.named_output(output)

        self.builder.stop()

        return self.builder.to_html()

    def run(self, cmd, http_dict):
        """
        Run a command.

        :param cmd: Command instance to run
        :param http_dict: dictionary of variables
        :raise ValueError: argument was invalid or internal failure
        """
        cmd.run(self, **arguments_from_post(cmd, http_dict))


def _output_to_html(output):
    a = []
    if output.name is not None:
        a.append(six.text_type('<h1>%s</h1>' % (output.name, )))

    a.append(six.text_type('<p>'))
    a.append(output.to('html'))
    a.append(six.text_type('</p>'))
    return a

def arguments_from_post(command, post):
    """
    Convert a dictionary of HTTP variables (eg. POST) into dictionary of
    arguments passable to a command.
    :param command: command instance
    :param post: variable dictionary
    :return: kwargs for command
    :raise ValueError: argument validation failed
    """
    out = {}

    for argument in command.ARGUMENTS:
        try:
            v = post[argument.name]

            if isinstance(argument, ValuelessArgument):
                # if we're here, that means that checkbox was clicked
                v = argument.default

        except KeyError:
            if isinstance(argument, ValuelessArgument):
                v = None
            else:
                if argument.required:
                    raise ValueError('Argument %s is required' % (argument.name, ))
                else:
                    v = argument.default

        try:
            out[argument.name] = argument.clean(v)
        except ValueError as e:
            raise ValueError('Argument %s invalid: %s' % (argument.name, e.message))

    return out