-# Copyright (C) 2014 Ipsilon project Contributors, for licensee see COPYING
+# Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
from ipsilon.util.log import Log
+import os
+import json
+import base64
+import imghdr
+import hashlib
+import cherrypy
+
+
+def name_from_image(image):
+ if image is None:
+ return None
+
+ fext = imghdr.what(None, base64.b64decode(image))
+ m = hashlib.sha1()
+ m.update(base64.b64decode(image))
+
+ return '%s.%s' % (m.hexdigest(), fext)
+
+
+def url_from_image(image):
+ if image is None:
+ return None
+
+ return '%s/cache/%s' % (
+ cherrypy.config.get('base.mount', ""),
+ name_from_image(image)
+ )
class Config(Log):
class Option(Log):
- def __init__(self, name, description):
+ def __init__(self, name, description, readonly=False):
self.name = name
self.description = description
self._default_value = None
self._assigned_value = None
+ self._readonly = readonly
def __repr__(self):
- return "%s: %s {%s}, value = %s [def: %s]" % (self.__class__,
- self.name,
- self.description,
- self._assigned_value,
- self._default_value)
+ return "%s: %s {%s}, value = %s [def: %s] readonly=%s" % (
+ self.__class__,
+ self.name,
+ self.description,
+ self._assigned_value,
+ self._default_value,
+ self._readonly)
def __str__(self):
return '%s=%s' % (self.name, self.get_value())
return None
def _str_import_value(self, value):
- if type(value) is not str:
+ if not isinstance(value, str):
raise ValueError('Value must be string')
self._assigned_value = value
+ def is_readonly(self):
+ return self._readonly
+
class String(Option):
- def __init__(self, name, description, default_value=None):
- super(String, self).__init__(name, description)
+ def __init__(self, name, description, default_value=None, readonly=False):
+ super(String, self).__init__(name, description, readonly=readonly)
self._default_value = str(default_value)
def set_value(self, value):
self._str_import_value(value)
+class Image(Option):
+ """
+ An image has two components: the binary blob of the image itself and
+ the SHA1 sum of the image.
+
+ We only need the image blob when writing to the cache file or
+ updating the database.
+
+ For the purposes of the UI we only need the filename which is
+ the SHA1 sum of file type the blob + file type.
+ """
+
+ def __init__(self, name, description, default_value=None, readonly=False):
+ super(Image, self).__init__(name, description, readonly=readonly)
+ self._image = None
+
+ if default_value:
+ self._image = default_value
+
+ self._assigned_value = url_from_image(self._image)
+ self.__write_cache_file()
+
+ def set_value(self, value):
+ if value is None:
+ return None
+
+ if os.path.exists(self.__filename()):
+ try:
+ os.remove(self.__filename())
+ except IOError as e:
+ self.error('Error removing %s: %s' % (self.__filename(), e))
+
+ self._image = base64.b64encode(value)
+ self._assigned_value = url_from_image(value)
+
+ def export_value(self):
+ if self._image is None:
+ return None
+
+ self.__write_cache_file()
+ return base64.b64decode(self._image)
+
+ def import_value(self, value):
+ if value is None:
+ return None
+
+ if os.path.exists(self.__filename()):
+ try:
+ os.remove(self.__filename())
+ except IOError as e:
+ self.error('Error removing %s: %s' % (self.__filename(), e))
+ self._image = base64.b64encode(value)
+ self._assigned_value = url_from_image(self._image)
+ self.__write_cache_file()
+
+ def __filename(self):
+ if self._image is None:
+ return None
+
+ cdir = cherrypy.config.get('cache_dir', '/var/cache/ipsilon')
+
+ return '%s/%s' % (cdir, name_from_image(self._image))
+
+ def __write_cache_file(self):
+ if self._image is None:
+ return None
+
+ if not os.path.exists(self.__filename()):
+ with open(self.__filename(), 'w') as imagefile:
+ imagefile.write(base64.b64decode(self._image))
+
+
class Template(Option):
- def __init__(self, name, description, default_template=None):
- super(Template, self).__init__(name, description)
+ def __init__(self, name, description, default_template=None,
+ readonly=False):
+ super(Template, self).__init__(name, description, readonly=readonly)
self._default_value = str(default_template)
def set_value(self, value):
class List(Option):
- def __init__(self, name, description, default_list=None):
- super(List, self).__init__(name, description)
+ def __init__(self, name, description, default_list=None, readonly=False):
+ super(List, self).__init__(name, description, readonly=readonly)
if default_list:
- self._default_value = list(default_list)
+ self._default_value = default_list
else:
self._default_value = []
return None
def import_value(self, value):
- if type(value) is not str:
+ if not isinstance(value, str):
raise ValueError('Value (type: %s) must be string' % type(value))
self._assigned_value = [x.strip() for x in value.split(',')]
+class ComplexList(List):
+
+ def _check_value(self, value):
+ if value is None:
+ return
+ if not isinstance(value, list):
+ raise ValueError('The value type must be a list, not "%s"' %
+ type(value))
+
+ def set_value(self, value):
+ self._check_value(value)
+ self._assigned_value = value
+
+ def export_value(self):
+ if self._assigned_value:
+ return json.dumps(self._assigned_value)
+ return None
+
+ def import_value(self, value):
+ if not isinstance(value, str):
+ raise ValueError('The value type must be a string, not "%s"' %
+ type(value))
+ jsonval = json.loads(value)
+ self.set_value(jsonval)
+
+
+class MappingList(ComplexList):
+
+ def _check_value(self, value):
+ if value is None:
+ return
+ if not isinstance(value, list):
+ raise ValueError('The value type must be a list, not "%s"' %
+ type(value))
+ for v in value:
+ if not isinstance(v, list):
+ raise ValueError('Each element must be a list, not "%s"' %
+ type(v))
+ if len(v) != 2:
+ raise ValueError('Each element must contain 2 values,'
+ ' not %d' % len(v))
+
+ def import_value(self, value):
+ if not isinstance(value, str):
+ raise ValueError('Value (type: %s) must be string' % type(value))
+ jsonval = json.loads(value)
+ self.set_value(jsonval)
+
+
class Choice(Option):
- def __init__(self, name, description, allowed=None, default=None):
- super(Choice, self).__init__(name, description)
+ def __init__(self, name, description, allowed=None, default=None,
+ readonly=False):
+ super(Choice, self).__init__(name, description, readonly=readonly)
if allowed:
self._allowed_values = list(allowed)
else:
return '%s=%s' % (self.name, self.get_value())
def set_value(self, value):
- if type(value) is not list:
+ if not isinstance(value, list):
value = [value]
self._assigned_value = list()
for val in value:
self._assigned_value = None
def unset_value(self, value):
- if type(value) is str:
+ if isinstance(value, str):
value = [value]
unset = list()
for val in value:
class Pick(Option):
- def __init__(self, name, description, allowed, default_value):
- super(Pick, self).__init__(name, description)
+ def __init__(self, name, description, allowed, default_value,
+ readonly=False):
+ super(Pick, self).__init__(name, description, readonly=readonly)
self._allowed_values = list(allowed)
if default_value not in self._allowed_values:
raise ValueError('The default value is not in the allowed list')
class Condition(Pick):
- def __init__(self, name, description, default_value=False):
+ def __init__(self, name, description, default_value=False,
+ readonly=False):
+ # The db stores 1/0. Convert the passed-in value if
+ # necessary
+ if default_value in [u'1', 'True', True]:
+ default_value = True
+ else:
+ default_value = False
super(Condition, self).__init__(name, description,
- [True, False], default_value)
+ [True, False], default_value,
+ readonly=readonly)
def import_value(self, value):
- self._assigned_value = value == 'True'
+ self._assigned_value = value
+
+
+class ConfigHelper(Log):
+
+ def __init__(self):
+ self._config = None
+
+ def new_config(self, name, *config_args):
+ self._config = Config(name, *config_args)
+
+ def get_config_obj(self):
+ if self._config is None:
+ raise AttributeError('Config not initialized')
+ return self._config
+
+ def import_config(self, config):
+ if not self._config:
+ raise AttributeError('Config not initialized, cannot import')
+
+ for key, value in config.iteritems():
+ if key in self._config:
+ self._config[key].import_value(str(value))
+
+ def export_config(self):
+ config = dict()
+ for name, option in self._config.iteritems():
+ config[name] = option.export_value()
+ return config
+
+ def get_config_value(self, name):
+ if not self._config:
+ raise AttributeError('Config not initialized')
+ return self._config[name].get_value()
+
+ def set_config_value(self, name, value):
+ if not self._config:
+ raise AttributeError('Config not initialized')
+ return self._config[name].set_value(value)