2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 21:15:41 +00:00

Merge branch 'inventree:master' into webhooks

This commit is contained in:
Matthias Mair
2022-01-08 21:42:18 +01:00
committed by GitHub
47 changed files with 1095 additions and 446 deletions

View File

@ -61,7 +61,7 @@ class BaseInvenTreeSetting(models.Model):
single values (e.g. one-off settings values).
"""
GLOBAL_SETTINGS = {}
SETTINGS = {}
class Meta:
abstract = True
@ -73,7 +73,7 @@ class BaseInvenTreeSetting(models.Model):
self.key = str(self.key).upper()
self.clean()
self.clean(**kwargs)
self.validate_unique()
super().save()
@ -90,6 +90,7 @@ class BaseInvenTreeSetting(models.Model):
results = cls.objects.all()
# Optionally filter by user
if user is not None:
results = results.filter(user=user)
@ -101,13 +102,13 @@ class BaseInvenTreeSetting(models.Model):
settings[setting.key.upper()] = setting.value
# Specify any "default" values which are not in the database
for key in cls.GLOBAL_SETTINGS.keys():
for key in cls.SETTINGS.keys():
if key.upper() not in settings:
settings[key.upper()] = cls.get_setting_default(key)
if exclude_hidden:
hidden = cls.GLOBAL_SETTINGS[key].get('hidden', False)
hidden = cls.SETTINGS[key].get('hidden', False)
if hidden:
# Remove hidden items
@ -131,98 +132,92 @@ class BaseInvenTreeSetting(models.Model):
return settings
@classmethod
def get_setting_name(cls, key):
def get_setting_definition(cls, key, **kwargs):
"""
Return the 'definition' of a particular settings value, as a dict object.
- The 'settings' dict can be passed as a kwarg
- If not passed, look for cls.SETTINGS
- Returns an empty dict if the key is not found
"""
settings = kwargs.get('settings', cls.SETTINGS)
key = str(key).strip().upper()
if settings is not None and key in settings:
return settings[key]
else:
return {}
@classmethod
def get_setting_name(cls, key, **kwargs):
"""
Return the name of a particular setting.
If it does not exist, return an empty string.
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('name', '')
else:
return ''
setting = cls.get_setting_definition(key, **kwargs)
return setting.get('name', '')
@classmethod
def get_setting_description(cls, key):
def get_setting_description(cls, key, **kwargs):
"""
Return the description for a particular setting.
If it does not exist, return an empty string.
"""
key = str(key).strip().upper()
setting = cls.get_setting_definition(key, **kwargs)
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('description', '')
else:
return ''
return setting.get('description', '')
@classmethod
def get_setting_units(cls, key):
def get_setting_units(cls, key, **kwargs):
"""
Return the units for a particular setting.
If it does not exist, return an empty string.
"""
key = str(key).strip().upper()
setting = cls.get_setting_definition(key, **kwargs)
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('units', '')
else:
return ''
return setting.get('units', '')
@classmethod
def get_setting_validator(cls, key):
def get_setting_validator(cls, key, **kwargs):
"""
Return the validator for a particular setting.
If it does not exist, return None
"""
key = str(key).strip().upper()
setting = cls.get_setting_definition(key, **kwargs)
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('validator', None)
else:
return None
return setting.get('validator', None)
@classmethod
def get_setting_default(cls, key):
def get_setting_default(cls, key, **kwargs):
"""
Return the default value for a particular setting.
If it does not exist, return an empty string
"""
key = str(key).strip().upper()
setting = cls.get_setting_definition(key, **kwargs)
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('default', '')
else:
return ''
return setting.get('default', '')
@classmethod
def get_setting_choices(cls, key):
def get_setting_choices(cls, key, **kwargs):
"""
Return the validator choices available for a particular setting.
"""
key = str(key).strip().upper()
setting = cls.get_setting_definition(key, **kwargs)
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
choices = setting.get('choices', None)
else:
choices = None
choices = setting.get('choices', None)
if callable(choices):
# Evaluate the function (we expect it will return a list of tuples...)
@ -245,17 +240,40 @@ class BaseInvenTreeSetting(models.Model):
key = str(key).strip().upper()
settings = cls.objects.all()
# Filter by user
user = kwargs.get('user', None)
if user is not None:
settings = settings.filter(user=user)
try:
setting = cls.objects.filter(**cls.get_filters(key, **kwargs)).first()
setting = settings.filter(**cls.get_filters(key, **kwargs)).first()
except (ValueError, cls.DoesNotExist):
setting = None
except (IntegrityError, OperationalError):
setting = None
plugin = kwargs.pop('plugin', None)
if plugin:
from plugin import InvenTreePlugin
if issubclass(plugin.__class__, InvenTreePlugin):
plugin = plugin.plugin_config()
kwargs['plugin'] = plugin
# Setting does not exist! (Try to create it)
if not setting:
setting = cls(key=key, value=cls.get_setting_default(key), **kwargs)
# Attempt to create a new settings object
setting = cls(
key=key,
value=cls.get_setting_default(key, **kwargs),
**kwargs
)
try:
# Wrap this statement in "atomic", so it can be rolled back if it fails
@ -267,21 +285,6 @@ class BaseInvenTreeSetting(models.Model):
return setting
@classmethod
def get_setting_pk(cls, key):
"""
Return the primary-key value for a given setting.
If the setting does not exist, return None
"""
setting = cls.get_setting_object(cls)
if setting:
return setting.pk
else:
return None
@classmethod
def get_setting(cls, key, backup_value=None, **kwargs):
"""
@ -291,18 +294,19 @@ class BaseInvenTreeSetting(models.Model):
# If no backup value is specified, atttempt to retrieve a "default" value
if backup_value is None:
backup_value = cls.get_setting_default(key)
backup_value = cls.get_setting_default(key, **kwargs)
setting = cls.get_setting_object(key, **kwargs)
if setting:
value = setting.value
# If the particular setting is defined as a boolean, cast the value to a boolean
if setting.is_bool():
# Cast to boolean if necessary
if setting.is_bool(**kwargs):
value = InvenTree.helpers.str2bool(value)
if setting.is_int():
# Cast to integer if necessary
if setting.is_int(**kwargs):
try:
value = int(value)
except (ValueError, TypeError):
@ -365,7 +369,7 @@ class BaseInvenTreeSetting(models.Model):
def units(self):
return self.__class__.get_setting_units(self.key)
def clean(self):
def clean(self, **kwargs):
"""
If a validator (or multiple validators) are defined for a particular setting key,
run them against the 'value' field.
@ -373,25 +377,16 @@ class BaseInvenTreeSetting(models.Model):
super().clean()
validator = self.__class__.get_setting_validator(self.key)
validator = self.__class__.get_setting_validator(self.key, **kwargs)
if self.is_bool():
self.value = InvenTree.helpers.str2bool(self.value)
if self.is_int():
try:
self.value = int(self.value)
except (ValueError):
raise ValidationError(_('Must be an integer value'))
if validator is not None:
self.run_validator(validator)
options = self.valid_options()
if options and self.value not in options:
raise ValidationError(_("Chosen value is not a valid option"))
if validator is not None:
self.run_validator(validator)
def run_validator(self, validator):
"""
Run a validator against the 'value' field for this InvenTreeSetting object.
@ -403,7 +398,7 @@ class BaseInvenTreeSetting(models.Model):
value = self.value
# Boolean validator
if self.is_bool():
if validator is bool:
# Value must "look like" a boolean value
if InvenTree.helpers.is_bool(value):
# Coerce into either "True" or "False"
@ -414,7 +409,7 @@ class BaseInvenTreeSetting(models.Model):
})
# Integer validator
if self.is_int():
if validator is int:
try:
# Coerce into an integer value
@ -467,12 +462,12 @@ class BaseInvenTreeSetting(models.Model):
return [opt[0] for opt in choices]
def is_bool(self):
def is_bool(self, **kwargs):
"""
Check if this setting is required to be a boolean value
"""
validator = self.__class__.get_setting_validator(self.key)
validator = self.__class__.get_setting_validator(self.key, **kwargs)
return self.__class__.validator_is_bool(validator)
@ -485,15 +480,15 @@ class BaseInvenTreeSetting(models.Model):
return InvenTree.helpers.str2bool(self.value)
def setting_type(self):
def setting_type(self, **kwargs):
"""
Return the field type identifier for this setting object
"""
if self.is_bool():
if self.is_bool(**kwargs):
return 'boolean'
elif self.is_int():
elif self.is_int(**kwargs):
return 'integer'
else:
@ -512,12 +507,12 @@ class BaseInvenTreeSetting(models.Model):
return False
def is_int(self):
def is_int(self, **kwargs):
"""
Check if the setting is required to be an integer value:
"""
validator = self.__class__.get_setting_validator(self.key)
validator = self.__class__.get_setting_validator(self.key, **kwargs)
return self.__class__.validator_is_int(validator)
@ -549,21 +544,20 @@ class BaseInvenTreeSetting(models.Model):
return value
@classmethod
def is_protected(cls, key):
def is_protected(cls, key, **kwargs):
"""
Check if the setting value is protected
"""
key = str(key).strip().upper()
setting = cls.get_setting_definition(key, **kwargs)
if key in cls.GLOBAL_SETTINGS:
return cls.GLOBAL_SETTINGS[key].get('protected', False)
else:
return False
return setting.get('protected', False)
def settings_group_options():
"""build up group tuple for settings based on gour choices"""
"""
Build up group tuple for settings based on your choices
"""
return [('', _('No group')), *[(str(a.id), str(a)) for a in Group.objects.all()]]
@ -585,7 +579,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
super().save()
if self.requires_restart():
InvenTreeSetting.set_setting('SERVER_REQUIRES_RESTART', True, None)
InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', True, None)
"""
Dict of all global settings values:
@ -603,7 +597,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
The keys must be upper-case
"""
GLOBAL_SETTINGS = {
SETTINGS = {
'SERVER_RESTART_REQUIRED': {
'name': _('Restart required'),
@ -985,13 +979,6 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'validator': bool,
'requires_restart': True,
},
'ENABLE_PLUGINS_GLOBALSETTING': {
'name': _('Enable global setting integration'),
'description': _('Enable plugins to integrate into inventree global settings'),
'default': False,
'validator': bool,
'requires_restart': True,
},
'ENABLE_PLUGINS_APP': {
'name': _('Enable app integration'),
'description': _('Enable plugins to add apps'),
@ -999,6 +986,13 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'validator': bool,
'requires_restart': True,
},
'ENABLE_PLUGINS_SCHEDULE': {
'name': _('Enable schedule integration'),
'description': _('Enable plugins to run scheduled tasks'),
'default': False,
'validator': bool,
'requires_restart': True,
}
}
class Meta:
@ -1025,7 +1019,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
Return True if this setting requires a server restart after changing
"""
options = InvenTreeSetting.GLOBAL_SETTINGS.get(self.key, None)
options = InvenTreeSetting.SETTINGS.get(self.key, None)
if options:
return options.get('requires_restart', False)
@ -1038,7 +1032,7 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
An InvenTreeSetting object with a usercontext
"""
GLOBAL_SETTINGS = {
SETTINGS = {
'HOMEPAGE_PART_STARRED': {
'name': _('Show subscribed parts'),
'description': _('Show subscribed parts on the homepage'),

View File

@ -50,9 +50,9 @@ class SettingsTest(TestCase):
- Ensure that every global setting has a description.
"""
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
for key in InvenTreeSetting.SETTINGS.keys():
setting = InvenTreeSetting.GLOBAL_SETTINGS[key]
setting = InvenTreeSetting.SETTINGS[key]
name = setting.get('name', None)
@ -65,14 +65,14 @@ class SettingsTest(TestCase):
raise ValueError(f'Missing GLOBAL_SETTING description for {key}')
if not key == key.upper():
raise ValueError(f"GLOBAL_SETTINGS key '{key}' is not uppercase")
raise ValueError(f"SETTINGS key '{key}' is not uppercase")
def test_defaults(self):
"""
Populate the settings with default values
"""
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
for key in InvenTreeSetting.SETTINGS.keys():
value = InvenTreeSetting.get_setting_default(key)