[CORD-1539] Static choices from xproto to GUI

Change-Id: I7ad877884c7803bb64c086c860b73c48951f5231
diff --git a/lib/xos-genx/tests/translator_test.py b/lib/xos-genx/tests/translator_test.py
index 40e1b50..e82af6d 100644
--- a/lib/xos-genx/tests/translator_test.py
+++ b/lib/xos-genx/tests/translator_test.py
@@ -153,6 +153,81 @@
         self.assertEqual(len(yaml_ir['items']), 1)
         self.assertIn('name', output)
         self.assertNotIn('secret', output)
+
+    def test_static_options(self):
+        xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+    required string name = 1 [ null = "False", blank="False"];
+    required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+}
+"""
+
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = 'modeldefs.xtarget'
+        output = XOSGenerator.generate(args)
+        self.assertIn("options:", output)
+        self.assertIn(" {'id': 'container_vm', 'label': 'Container In VM'}", output)
+
+    def test_not_static_options(self):
+        xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+    required string name = 1 [ null = "False", blank="False"];
+}
+"""
+
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = 'modeldefs.xtarget'
+        output = XOSGenerator.generate(args)
+        self.assertNotIn("options:", output)
+
+    def test_default_value_in_modeldef(self):
+        xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+    required string name = 1 [ null = "False", blank="False", default = "bar"];
+    required string falsetrue = 1 [ null = "False", blank="False", default = False];
+    required string truefalse = 1 [ null = "False", blank="False", default = True];
+    required string some = 1 [ null = "False", blank="False", default = None];
+    required string zero = 1 [ null = "False", blank="False", default = 0];
+}
+"""
+
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = 'modeldefs.xtarget'
+        output = XOSGenerator.generate(args)
+        self.assertIn('default: "bar"', output)
+        self.assertIn('default: "false"', output)
+        self.assertIn('default: "true"', output)
+        self.assertIn('default: "null"', output)
+        self.assertIn('default: "0"', output)
+
+    def test_not_default_value_in_modeldef(self):
+        xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+    required string name = 1 [ null = "False", blank="False"];
+}
+"""
+
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = 'modeldefs.xtarget'
+        output = XOSGenerator.generate(args)
+        self.assertNotIn('default:', output)
+
 if __name__ == '__main__':
     unittest.main()
 
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/__init__.py b/lib/xos-genx/xosgenx/jinja2_extensions/__init__.py
index ea30100..ad03907 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/__init__.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/__init__.py
@@ -1,3 +1,4 @@
 from .django import *
 from .base import *
 from .fol2 import *
+from .gui import *
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/base.py b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
index c15bf32..3cb9886 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/base.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
@@ -16,6 +16,8 @@
 def unquote(s):
     if (s.startswith('"') and s.endswith('"')):
         return s[1:-1]
+    else:
+        return s
 
 def xproto_singularize(field):
     try:
@@ -151,44 +153,6 @@
             links.extend(model_links)
     return links
 
-
-def xproto_validators(f):
-    # To be cleaned up when we formalize validation in xproto
-    validators = []
-
-    # bound-based validators
-    bound_validators = [('max_length','maxlength'), ('min', 'min'), ('max', 'max')]
-
-    for v0, v1 in bound_validators:
-        try:
-            validators.append({'name':v1, 'int_value':int(f['options'][v0])})
-        except KeyError:
-            pass
-
-    # validators based on content_type
-    content_type_validators = ['ip', 'url', 'email']
-
-    for v in content_type_validators:
-        #if f['name']=='ip': pdb.set_trace()
-        try:
-            val = unquote(f['options']['content_type'])==v
-            if not val:
-                raise KeyError
-
-            validators.append({'name':v, 'bool_value': True})
-        except KeyError:
-            pass
-
-    # required validator
-    try:
-        required = f['options']['blank']=='False' and f['options']['null']=='False'
-        if required:
-            validators.append({'name':'required', 'bool_value':required})
-    except KeyError:
-        pass
-
-    return validators
-
 def xproto_string_type(xptags):
     try:
         max_length = eval(xptags['max_length'])
@@ -200,25 +164,6 @@
     else:
         return 'text'
 
-def xproto_type_to_ui_type(f):
-    try:
-        content_type = f['options']['content_type']
-        content_type = eval(content_type)
-    except:
-        content_type = None
-        pass
-
-    if content_type == 'date':
-        return 'date'
-    elif f['type'] == 'bool':
-        return 'boolean'
-    elif f['type'] == 'string':
-        return xproto_string_type(f['options'])
-    elif f['type'] in ['int','uint32','int32'] or 'link' in f:
-        return 'number'
-    elif f['type'] in ['double','float']:
-        return 'string'
-
 def xproto_tuplify(nested_list_or_set):
     if not isinstance(nested_list_or_set, list) and not isinstance(nested_list_or_set, set):
         return nested_list_or_set
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/gui.py b/lib/xos-genx/xosgenx/jinja2_extensions/gui.py
new file mode 100644
index 0000000..bd23fdb
--- /dev/null
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/gui.py
@@ -0,0 +1,90 @@
+from base import xproto_string_type, unquote
+
+def xproto_type_to_ui_type(f):
+    try:
+        content_type = f['options']['content_type']
+        content_type = eval(content_type)
+    except:
+        content_type = None
+        pass
+
+    if 'choices' in f['options']:
+        return 'select';
+    elif content_type == 'date':
+        return 'date'
+    elif f['type'] == 'bool':
+        return 'boolean'
+    elif f['type'] == 'string':
+        return xproto_string_type(f['options'])
+    elif f['type'] in ['int','uint32','int32'] or 'link' in f:
+        return 'number'
+    elif f['type'] in ['double','float']:
+        return 'string'
+
+def xproto_options_choices_to_dict(choices):
+    list = []
+
+    for c in eval(choices):
+        list.append({'id': c[0], 'label': c[1]})
+    if len(list) > 0:
+        return list
+    else:
+        return None
+
+def xproto_validators(f):
+    # To be cleaned up when we formalize validation in xproto
+    validators = []
+
+    # bound-based validators
+    bound_validators = [('max_length','maxlength'), ('min', 'min'), ('max', 'max')]
+
+    for v0, v1 in bound_validators:
+        try:
+            validators.append({'name':v1, 'int_value':int(f['options'][v0])})
+        except KeyError:
+            pass
+
+    # validators based on content_type
+    content_type_validators = ['ip', 'url', 'email']
+
+    for v in content_type_validators:
+        #if f['name']=='ip': pdb.set_trace()
+        try:
+            val = unquote(f['options']['content_type'])==v
+            if not val:
+                raise KeyError
+
+            validators.append({'name':v, 'bool_value': True})
+        except KeyError:
+            pass
+
+    # required validator
+    try:
+        required = f['options']['blank']=='False' and f['options']['null']=='False'
+        if required:
+            validators.append({'name':'required', 'bool_value':required})
+    except KeyError:
+        pass
+
+    return validators
+
+def is_number(s):
+    try:
+        float(s)
+        return True
+    except ValueError:
+        return False
+
+def xproto_default_to_gui(default):
+    val = "null"
+    if is_number(default):
+        val = str(default)
+    elif eval(default) == True:
+        val = 'true'
+    elif eval(default) == False:
+        val = 'false'
+    elif eval(default) == None:
+        val = 'null'
+    else:
+        val = str(default)
+    return val
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/modeldefs.xtarget b/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
index 8c4771f..366f2ee 100644
--- a/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
+++ b/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
@@ -13,6 +13,15 @@
     name: {{ f.name }}_id
     relation: {model: {{ f.options.model }}, type: {{ f.options.link_type }}}
     {% endif %}
+  {%- if f.options.default %}
+    default: "{{ xproto_unquote(xproto_default_to_gui(f.options.default)) }}"
+  {%- endif %}
+  {%- if f.options.choices %}
+    options:
+    {% for o in xproto_options_choices_to_dict(xproto_unquote(f.options.choices)) %}
+    - {{ o }}
+    {% endfor %}
+  {%- endif %}
     type: {{ xproto_type_to_ui_type(f) }}
     {% set validators = xproto_validators(f) -%}
     {% if validators -%}
@@ -25,7 +34,7 @@
     {% endif %}
   {% endif -%}
   {% endfor %}
-  name: {{ m.name }}  
+  name: {{ m.name }}
   {%- set goodlinks = xproto_links_to_modeldef_relations( xproto_base_links(m, proto.message_table) + m.links ) %}
   {% if goodlinks %}
   relations:
diff --git a/xos/coreapi/protos/modeldefs.proto b/xos/coreapi/protos/modeldefs.proto
index 3cd5fa8..d0d95fc 100644
--- a/xos/coreapi/protos/modeldefs.proto
+++ b/xos/coreapi/protos/modeldefs.proto
@@ -17,6 +17,11 @@
     };
 };
 
+message FieldOption {
+    string id = 1;
+    string label = 2;
+}
+
 message FieldRelation {
     string model = 1;
     string type = 2;
@@ -28,6 +33,8 @@
     string type = 3;
     FieldRelation relation = 4;
     repeated FieldValidator validators = 5;
+    repeated FieldOption options = 6;
+    string default = 7;
 };
 
 message ModelDef {