Instead of passing strings as arguments to backends, all settings must
be formatted according to YAML specifications. This is to remove the
need to add separate YAML parsing to each backend argument in each
app that needs it. Argument passing to backends is not fully
uniform.
Users need to update their config files.
Example:
DOCUMENTS_STORAGE_BACKEND_ARGUMENTS: '{location: /home/rosarior/development/mayan-edms/mayan/media/document_storage}'
must be changed to:
DOCUMENTS_STORAGE_BACKEND_ARGUMENTS:
location: /home/rosarior/development/mayan-edms/mayan/media/document_storage
Example 2:
CONVERTER_GRAPHICS_BACKEND_CONFIG: ' { libreoffice_path: /usr/bin/libreoffice, pdftoppm_dpi:
300, pdftoppm_format: jpeg, pdftoppm_path: /usr/bin/pdftoppm, pdfinfo_path:
/usr/bin/pdfinfo, pillow_format: JPEG } '
must be changed to:
CONVERTER_GRAPHICS_BACKEND_CONFIG:
libreoffice_path: /usr/bin/libreoffice
pdftoppm_dpi: 300
pdftoppm_format: jpeg
pdftoppm_path: /usr/bin/pdftoppm
pdfinfo_path: /usr/bin/pdfinfo
pillow_format: JPEG
Example 3:
OCR_BACKEND_ARGUMENTS: ''
must be changed to:
OCR_BACKEND_ARGUMENTS: {}
Settings that need to be updated are:
- COMMON_SHARED_STORAGE_ARGUMENTS
- CONVERTER_GRAPHICS_BACKEND_CONFIG
- DOCUMENTS_CACHE_STORAGE_BACKEND_ARGUMENTS
- DOCUMENTS_STORAGE_BACKEND_ARGUMENTS
- OCR_BACKEND_ARGUMENTS
- SIGNATURES_STORAGE_BACKEND_ARGUMENTS
- SOURCES_STAGING_FILE_CACHE_STORAGE_BACKEND_ARGUMENTS
The following error will appear in the console if a setting is not yet
updated to this new format::
TypeError: type object argument after ** must be a mapping, not str
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
194 lines
5.8 KiB
Python
194 lines
5.8 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import errno
|
|
from importlib import import_module
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
import yaml
|
|
|
|
from django.apps import apps
|
|
from django.conf import settings
|
|
from django.utils.functional import Promise
|
|
from django.utils.encoding import force_text, python_2_unicode_compatible
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Namespace(object):
|
|
_registry = {}
|
|
|
|
@staticmethod
|
|
def initialize():
|
|
for app in apps.get_app_configs():
|
|
try:
|
|
import_module('{}.settings'.format(app.name))
|
|
except ImportError as exception:
|
|
if force_text(exception) not in ('No module named settings', 'No module named \'{}.settings\''.format(app.name)):
|
|
logger.error(
|
|
'Error importing %s settings.py file; %s', app.name,
|
|
exception
|
|
)
|
|
|
|
@classmethod
|
|
def get_all(cls):
|
|
return sorted(cls._registry.values(), key=lambda x: x.label)
|
|
|
|
@classmethod
|
|
def get(cls, name):
|
|
return cls._registry[name]
|
|
|
|
@classmethod
|
|
def invalidate_cache_all(cls):
|
|
for namespace in cls.get_all():
|
|
namespace.invalidate_cache()
|
|
|
|
def __init__(self, name, label):
|
|
if name in self.__class__._registry:
|
|
raise Exception(
|
|
'Namespace names must be unique; "%s" already exists.' % name
|
|
)
|
|
self.name = name
|
|
self.label = label
|
|
self.__class__._registry[name] = self
|
|
self._settings = []
|
|
|
|
def __str__(self):
|
|
return force_text(self.label)
|
|
|
|
def add_setting(self, **kwargs):
|
|
return Setting(namespace=self, **kwargs)
|
|
|
|
def invalidate_cache(self):
|
|
for setting in self._settings:
|
|
setting.invalidate_cache()
|
|
|
|
@property
|
|
def settings(self):
|
|
return sorted(self._settings, key=lambda x: x.global_name)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class Setting(object):
|
|
_registry = {}
|
|
|
|
@staticmethod
|
|
def deserialize_value(value):
|
|
return yaml.safe_load(value)
|
|
|
|
@staticmethod
|
|
def serialize_value(value):
|
|
if isinstance(value, Promise):
|
|
value = force_text(value)
|
|
|
|
result = yaml.safe_dump(value, allow_unicode=True)
|
|
# safe_dump returns bytestrings
|
|
# Disregard the last 3 dots that mark the end of the YAML document
|
|
if force_text(result).endswith('...\n'):
|
|
result = result[:-4]
|
|
|
|
return result
|
|
|
|
@classmethod
|
|
def dump_data(cls, filter_term=None, namespace=None):
|
|
dictionary = {}
|
|
|
|
for setting in cls.get_all():
|
|
if (namespace and setting.namespace.name == namespace) or not namespace:
|
|
if (filter_term and filter_term.lower() in setting.global_name.lower()) or not filter_term:
|
|
if isinstance(setting.value, Promise):
|
|
dictionary[setting.global_name] = force_text(setting.value)
|
|
else:
|
|
dictionary[setting.global_name] = setting.value
|
|
|
|
return yaml.safe_dump(dictionary, default_flow_style=False)
|
|
|
|
@classmethod
|
|
def get(cls, global_name):
|
|
return cls._registry[global_name]
|
|
|
|
@classmethod
|
|
def get_all(cls):
|
|
return sorted(cls._registry.values(), key=lambda x: x.global_name)
|
|
|
|
@classmethod
|
|
def save_configuration(cls, path=settings.CONFIGURATION_FILEPATH):
|
|
try:
|
|
with open(path, 'w') as file_object:
|
|
file_object.write(cls.dump_data())
|
|
except IOError as exception:
|
|
if exception.errno == errno.ENOENT:
|
|
logger.warning(
|
|
'The path to the configuration file doesn\'t '
|
|
'exist. It is not possible to save the backup file.'
|
|
)
|
|
|
|
@classmethod
|
|
def save_last_known_good(cls):
|
|
# Don't write over the last good configuration if we are trying
|
|
# to restore the last good configuration
|
|
if 'revertsettings' not in sys.argv:
|
|
cls.save_configuration(
|
|
path=settings.CONFIGURATION_LAST_GOOD_FILEPATH
|
|
)
|
|
|
|
def __init__(self, namespace, global_name, default, help_text=None, is_path=False):
|
|
self.global_name = global_name
|
|
self.default = default
|
|
self.help_text = help_text
|
|
self.loaded = False
|
|
self.namespace = namespace
|
|
self.environment_variable = False
|
|
namespace._settings.append(self)
|
|
self.__class__._registry[global_name] = self
|
|
|
|
def __str__(self):
|
|
return force_text(self.global_name)
|
|
|
|
def cache_value(self):
|
|
environment_value = os.environ.get('MAYAN_{}'.format(self.global_name))
|
|
if environment_value:
|
|
self.environment_variable = True
|
|
try:
|
|
self.raw_value = environment_value
|
|
except yaml.YAMLError as exception:
|
|
raise type(exception)(
|
|
'Error interpreting environment variable: {} with '
|
|
'value: {}; {}'.format(
|
|
self.global_name, environment_value, exception
|
|
)
|
|
)
|
|
else:
|
|
self.raw_value = getattr(settings, self.global_name, self.default)
|
|
self.yaml = Setting.serialize_value(self.raw_value)
|
|
self.loaded = True
|
|
|
|
def invalidate_cache(self):
|
|
self.loaded = False
|
|
|
|
@property
|
|
def serialized_value(self):
|
|
"""
|
|
YAML serialize value of the setting.
|
|
Used for UI display.
|
|
"""
|
|
if not self.loaded:
|
|
self.cache_value()
|
|
|
|
return self.yaml
|
|
|
|
@property
|
|
def value(self):
|
|
if not self.loaded:
|
|
self.cache_value()
|
|
|
|
return self.raw_value
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
# value is in YAML format
|
|
self.yaml = value
|
|
self.raw_value = Setting.deserialize_value(value)
|