add index HTML interface
authorTero Marttila <terom@fixme.fi>
Tue, 05 May 2009 21:49:56 +0300
changeset 16 9234f5ae765b
parent 15 707ddd7a7912
child 17 301725d72e29
add index HTML interface
index.py
--- a/index.py	Tue May 05 19:39:44 2009 +0300
+++ b/index.py	Tue May 05 21:49:56 2009 +0300
@@ -126,7 +126,12 @@
     """
 
     data = []
-
+    
+    # if no chars given, don't insert any
+    if not chars :
+        chars = []
+    
+    # randomize char order across lines?
     if random_chars :
         chars = randomize(chars)
     
@@ -135,11 +140,16 @@
             continue
 
         # pick position to place char
-        pos = random.randint(1, len(line) - 1)
-
+        if len(line) >= 2 :
+            pos = random.randint(1, len(line) - 1)
+        else :
+            pos = random.randint(0, 1)
+        
+        # default color
         if not color :
             color = "#000000"
-
+        
+        # randomize text in some way?
         if random_text :
             line = ''.join(randomize(line))
 
@@ -251,21 +261,92 @@
 
     return data
 
-def arg_bool (val) :
-    if val.lower() in ('true', 't', '1', 'yes', 'y') :
-        return True
-    elif val.lower() in ('false', 'f', '0', 'no', 'n') :
-        return False
-    else :
-        raise ValueError(val)
+class OptionType (object) :
+    def parse (self, val) :
+        """
+            Unicode value -> object
+        """
 
-def arg_color (val) :
-    if val.beginswith('#') :
-        int(val[1:], 16)
+        abstract
 
-        return val
-    else :
-        raise ValueError(val)
+    def build (self, val) :
+        """
+            object -> unicode value
+        """
+
+        return unicode(val)
+    
+    def input (self, val) :
+        """
+            HTML input item
+        """
+
+        abstract
+
+class StringOption (OptionType) :
+    def parse (self, val) :
+        return unicode(val)
+    
+    def build (self, val) :
+        if val is None :
+            return ""
+
+        else :
+            return val
+
+    def input (self, opt, val) :
+        return """<input type="text" name="%(name)s" id="%(name)s" value="%(value)s" />""" % dict(
+            name        = opt.name,
+            value       = self.build(val),
+        )
+    
+    def select (self, opt, val) :
+        return """<select name="%(name)s">
+%(options)s
+</select>""" % dict(
+            name        = opt.name,
+            options     = '\n'.join(
+                "\t<option %(selected)s>%(value)s</option>" % dict(
+                    value       = self.build(optval),
+                    selected    = 'selected="selected"' if val == optval else "",
+                ) for optval in opt.range
+            ),
+        )
+
+class BoolOption (OptionType) :
+    def parse (self, val) :
+        if val.lower() in ('true', 't', '1', 'yes', 'y') :
+            return True
+
+        elif val.lower() in ('false', 'f', '0', 'no', 'n') :
+            return False
+
+        else :
+            raise ValueError(val)
+    
+    def input (self, opt, val) :
+        return """<input type="checkbox" name="%(name)s" id="%(name)s" %(checked)s/>""" % dict(
+            name        = opt.name,
+            checked     = 'checked="checked"' if val else '',
+        )
+
+class IntOption (StringOption) :
+    def parse (self, val) :
+        return int(val)
+
+class FloatOption (StringOption) :
+    def parse (self, val) :
+        return float(val)
+
+class ColorOption (StringOption) :
+    def _parse (self, val) :
+        if val.startswith('#') :
+            int(val[1:], 16)
+
+            return val
+        else :
+            raise ValueError(val)
+
 
 class Option (object) :
     def __init__ (self, name, is_list, type, default, range) :
@@ -275,43 +356,94 @@
         self.default = default
         self.range = range
 
-    def parse (self, args) :
+    def parse (self, args, force_bool=False) :
         if self.is_list :
             if self.name in args :
-                return args.getlist(self.name, self.type)
+                l = args.getlist(self.name, self.type.parse)
+                
+                # special-case to handle a single param with a newline-separtated list
+                if len(l) == 1 :
+                    if not l[0] :
+                        return None
+
+                    else :
+                        return l[0].split('\r\n')
+
+                else :
+                    return l
+
             else :
                 return self.default
+
         else :
-            if self.type == arg_bool and not self.default and self.name in args :
+            if isinstance(self.type, BoolOption) and force_bool :
+                return self.name in args
+
+            elif isinstance(self.type, BoolOption) and not self.default and self.name in args :
                 return True
 
             else :
-                return args.get(self.name, self.default, self.type)
+                return args.get(self.name, self.default, self.type.parse)
+    
+    def build_list (self, value) :
+        if self.is_list and value :
+            return [self.type.build(val) for val in value]
+
+        else :
+            return [self.type.build(value)]
+
+    def _build_input (self, value) :
+        if self.is_list :
+            return """\
+<textarea name="%(name)s" cols="30">\
+%(data)s\
+</textarea>""" % dict(
+                name        = self.name,
+                data        = '\n'.join(self.type.build(val) for val in value) if value else '',
+            )
+
+        elif self.range :
+            return self.type.select(self, value)
+
+        else :
+            return self.type.input(self, value)
+
+    def build_form (self, opts) :
+        value = opts[self.name]
+
+        return """\
+<div class="param"><label for="%(name)s">%(title)s</label>%(input)s</div>\
+        """ % dict(
+            name        = self.name,
+            title       = self.name.title().replace('-', ' '),
+            input       = self._build_input(value)
+        )
 
 class Options (object) :
     def __init__ (self, *options) :
-        self.options = options
+        self.options = list(options)
+        self.options_by_name = dict((opt.name, opt) for opt in options)
 
-    def parse (self, args) :
-        return dict((opt.name, opt.parse(args)) for opt in self.options)
+    def parse (self, args, **kwargs) :
+        return dict((opt.name, opt.parse(args, **kwargs)) for opt in self.options)
 
 OPTIONS = Options(
-    Option('lang',          False,  str,        Defaults.text_lang,     TEXT_BY_LANG.keys()),
-    Option('text',          True,   unicode,    None,                   None),
-    Option('random-text',   False,  arg_bool,   False,                  None),
-    Option('random-text-char', False, arg_bool, False,                  None),
-    Option('chars',         True,   unicode,    Defaults.chars,         None),
-    Option('random-chars',  False,  arg_bool,   True,                   None),
-    Option('colors',        True,   arg_color,  Defaults.colors,        None),
-    Option('font',          False,  str,        Defaults.font_name,     FONTS.keys()),
-    Option('font-size',     False,  int,        Defaults.font_size,     None),
-    Option('bg-color',      False,  arg_color,  Defaults.bg_color,      None),
-    Option('line-spacing',  False,  int,        Defaults.line_spacing,  None),
-    Option('sharpness',     False,  float,      Defaults.sharpness,     None),
-    Option('image-format',  False,  str,        Defaults.img_format,    IMAGE_FORMATS.keys()),
-    Option('seed',          False,  int,        None,                   None),
-    Option('img_width',     False,  int,        None,                   None),
-    Option('img_height',    False,  int,        None,                   None),
+    Option('lang',          False,  StringOption(), Defaults.text_lang,     TEXT_BY_LANG.keys()),
+    Option('text',          True,   StringOption(), None,                   None),
+    Option('random-text',   False,  BoolOption(),   False,                  None),
+    Option('random-text-char',False,BoolOption(),   False,                  None),
+    Option('chars',         True,   StringOption(), Defaults.chars,         None),
+    Option('random-chars',  False,  BoolOption(),   True,                   None),
+    Option('colors',        True,   ColorOption(),  Defaults.colors,        None),
+    Option('font',          False,  StringOption(), Defaults.font_name,     FONTS.keys()),
+    Option('font-size',     False,  IntOption(),    Defaults.font_size,     None),
+    Option('bg-color',      False,  ColorOption(),  Defaults.bg_color,      None),
+    Option('line-spacing',  False,  IntOption(),    Defaults.line_spacing,  None),
+    Option('sharpness',     False,  FloatOption(),  Defaults.sharpness,     None),
+    Option('image-format',  False,  StringOption(), Defaults.img_format,    IMAGE_FORMATS.keys()),
+    Option('seed',          False,  IntOption(),    None,                   None),
+    Option('img_width',     False,  IntOption(),    None,                   None),
+    Option('img_height',    False,  IntOption(),    None,                   None),
 )
 
 def handle_generic (req, img_size=None) :
@@ -374,8 +506,81 @@
 def handle_tile (req) :
     return handle_generic(req, img_size=TILE_SIZE)
 
+def handle_index (options, req) :
+    # parse options, force booleans if any form data was submitted, as checkboxes work that way
+    opts = options.parse(req.values, force_bool=bool(req.form))
+
+    # build query string of req things
+    qargs = [
+        (opt.name, opt.build_list(val)) for opt, val in (
+            (options.options_by_name[opt_name], val) for opt_name, val in opts.iteritems()
+        ) if val != opt.default
+    ]
+
+    img_url = req.url_root + "logo" + ("?%s" % werkzeug.url_encode(qargs) if qargs else '')
+
+    return werkzeug.Response("""\
+<html>
+    <head>
+        <title>Aaltologotin</title>
+        <style type="text/css">
+div#logo {
+    text-align: center;
+    padding: 100px;
+}
+
+img {
+    border: none;
+}
+
+div#info {
+    font-size: small;
+    padding-left: 25px;
+    margin-top: 50px;
+    color: grey;
+}
+
+label {
+    display: block;
+    float: left;
+
+    width: 150px;
+}
+
+div.param {
+    padding: 3px;
+}
+        </style>
+    </head>
+    <body>
+        <div id='logo'>
+            <a href="%(img_url)s"><img src="%(img_url)s" alt="Aaltologo" /></a>
+        </div>
+        <div id='info'>
+            <h1>Aaltologotin</h1>
+            <p>Aaltologotin pulauttaa sulle uuden, sattumanvaraisesti valitun aalto-logon!</p>
+            
+            <form action="." method="POST">
+                <fieldset>
+                    <legend>Aalto-parameterit</legend>
+%(form_fields)s
+                    <input type="submit" value="Logota!" />
+                </fieldset>
+            </form>
+        </div>
+    </body>
+</html>""" % dict(
+        img_url     = img_url,
+        form_fields = "\n".join(
+            "\t%s" % opt.build_form(opts) for opt in options.options
+        ),
+    ), mimetype='text/html')
+
 def handle_request (req) :
-    if req.path == '/' or req.path.startswith('/logo.') :
+    if req.path == '/' :
+        return handle_index(OPTIONS, req)
+    
+    elif req.path.startswith('/logo') :
         return handle_logo(req)
     
     elif req.path == '/tile' :