Skip to content

Commit

Permalink
refactor clean_app_settings() (#1545)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikkonie committed Jan 16, 2025
1 parent 1f17dee commit 9464070
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 206 deletions.
246 changes: 103 additions & 143 deletions projectroles/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def add_error(self, field, error):
super().add_error(field, error) # Call the error method in Django forms


class SODARAppSettingsFormMixin:
class SODARAppSettingFormMixin:
"""Helpers for app settings handling in forms"""

def set_app_setting_field(self, plugin_name, s_field, s_def):
Expand All @@ -114,13 +114,11 @@ def set_app_setting_field(self, plugin_name, s_field, s_def):
:param s_def: PluginAppSettingDef object
"""
scope = s_def.scope
scope_kwargs = {}
scope_kw = {}
if scope == APP_SETTING_SCOPE_PROJECT:
scope_kwargs = {
'project': self.instance if self.instance.pk else None
}
scope_kw = {'project': self.instance if self.instance.pk else None}
elif scope == APP_SETTING_SCOPE_USER:
scope_kwargs = {'user': self.user} # NOTE: Requires self.user
scope_kw = {'user': self.user} # NOTE: Requires self.user
s_widget_attrs = s_def.widget_attrs
if scope == APP_SETTING_SCOPE_PROJECT:
s_project_types = s_def.project_types or [PROJECT_TYPE_PROJECT]
Expand All @@ -137,7 +135,7 @@ def set_app_setting_field(self, plugin_name, s_field, s_def):

# Option
if s_def.options and callable(s_def.options):
values = s_def.options(**scope_kwargs)
values = s_def.options(**scope_kw)
self.fields[s_field] = forms.ChoiceField(
choices=[
(
Expand Down Expand Up @@ -189,7 +187,7 @@ def set_app_setting_field(self, plugin_name, s_field, s_def):
value = app_settings.get(
plugin_name=plugin_name,
setting_name=s_def.name,
**scope_kwargs,
**scope_kw,
)
if s_def.type == APP_SETTING_TYPE_JSON:
value = json.dumps(value)
Expand All @@ -211,6 +209,79 @@ def get_app_setting_label(self, plugin, label):
label,
)

@classmethod
def clean_app_settings(
cls,
cleaned_data,
app_plugins,
scope,
user_modifiable,
project=None,
user=None,
):
"""Validate and clean app settings form fields"""
scope_kw = {}
if scope == APP_SETTING_SCOPE_PROJECT:
scope_kw['project'] = project
elif scope == APP_SETTING_SCOPE_USER:
scope_kw['user'] = user
errors = []
for plugin in app_plugins + [None]:
if plugin:
p_name = plugin.name
def_kwarg = {'plugin': plugin}
else:
p_name = 'projectroles'
def_kwarg = {'plugin_name': p_name}
s_defs = app_settings.get_definitions(
scope=scope, user_modifiable=user_modifiable, **def_kwarg
)
p_settings = {}

for s_def in s_defs.values():
s_field = '.'.join(['settings', p_name, s_def.name])
p_settings[s_def.name] = cleaned_data.get(s_field)

if s_def.type == APP_SETTING_TYPE_JSON:
if not p_settings[s_def.name]:
cleaned_data[s_field] = '{}'
try:
cleaned_data[s_field] = json.loads(
cleaned_data.get(s_field)
)
except json.JSONDecodeError as err:
errors.append((s_field, 'Invalid JSON\n' + str(err)))
elif s_def.type == APP_SETTING_TYPE_INTEGER:
# Convert integers from select fields
cleaned_data[s_field] = int(cleaned_data[s_field])

try:
app_settings.validate(
setting_type=s_def.type,
setting_value=cleaned_data.get(s_field),
setting_options=s_def.options,
**scope_kw,
)
except Exception as ex:
errors.append((s_field, 'Invalid value: {}'.format(ex)))

# Custom plugin validation for app settings
if plugin and hasattr(plugin, 'validate_form_app_settings'):
try:
p_errors = plugin.validate_form_app_settings(
p_settings, **scope_kw
)
if p_errors:
for field, error in p_errors.items():
f_name = '.'.join(['settings', p_name, field])
errors.append((f_name, error))
except Exception as ex:
err_msg = SETTING_CUSTOM_VALIDATE_MSG.format(
plugin=p_name, exception=ex
)
errors.append((None, err_msg))
return cleaned_data, errors


class SODARForm(SODARFormMixin, forms.Form):
"""Override of Django base form with SODAR Core specific helpers."""
Expand Down Expand Up @@ -363,7 +434,7 @@ def __init__(
# Project form -----------------------------------------------------------------


class ProjectForm(SODARAppSettingsFormMixin, SODARModelForm):
class ProjectForm(SODARAppSettingFormMixin, SODARModelForm):
"""Form for Project creation/updating"""

# Set up owner field
Expand Down Expand Up @@ -484,7 +555,7 @@ def _init_remote_sites(self):
required=False,
)

# TODO: Refactor into generic helper in SODARAppSettingsFormMixin
# TODO: Refactor into generic helper in SODARAppSettingFormMixin
def _set_app_setting_notes(self, s_field, s_def, plugin):
"""
Internal helper for setting app setting label notes.
Expand All @@ -509,29 +580,28 @@ def _set_app_setting_notes(self, s_field, s_def, plugin):
plugin, self.fields[s_field].label
)

# TODO: Refactor into generic helper in SODARAppSettingsFormMixin
# TODO: Refactor into generic helper in SODARAppSettingFormMixin
def _init_app_settings(self):
"""
Initialize app settings fields in the form.
"""
# Set up setting query kwargs
self.p_kwargs = {}
# Show unmodifiable settings to superusers
if not self.current_user.is_superuser:
self.p_kwargs['user_modifiable'] = True
user_mod = False if self.current_user.is_superuser else True
self.app_plugins = sorted(get_active_plugins(), key=lambda x: x.name)
for plugin in self.app_plugins + [None]: # Projectroles has no plugin
if plugin:
plugin_name = plugin.name
s_defs = app_settings.get_definitions(
APP_SETTING_SCOPE_PROJECT, plugin=plugin, **self.p_kwargs
APP_SETTING_SCOPE_PROJECT,
plugin=plugin,
user_modifiable=user_mod,
)
else:
plugin_name = APP_NAME
s_defs = app_settings.get_definitions(
APP_SETTING_SCOPE_PROJECT,
plugin_name=plugin_name,
**self.p_kwargs,
user_modifiable=user_mod,
)
for s_def in s_defs.values():
s_field = 'settings.{}.{}'.format(plugin_name, s_def.name)
Expand All @@ -540,72 +610,6 @@ def _init_app_settings(self):
# Set label notes
self._set_app_setting_notes(s_field, s_def, plugin)

# TODO: Refactor into generic helper in SODARAppSettingsFormMixin
@classmethod
def _validate_app_settings(
self,
cleaned_data,
app_plugins,
app_settings,
p_kwargs,
instance,
):
"""Validate and clean app_settings form fields"""
errors = []
for plugin in app_plugins + [None]:
if plugin:
p_name = plugin.name
def_kwarg = {'plugin': plugin}
else:
p_name = 'projectroles'
def_kwarg = {'plugin_name': p_name}
s_defs = app_settings.get_definitions(
APP_SETTING_SCOPE_PROJECT, **{**p_kwargs, **def_kwarg}
)
p_settings = {}

for s_def in s_defs.values():
s_field = '.'.join(['settings', p_name, s_def.name])
p_settings[s_def.name] = cleaned_data.get(s_field)

if s_def.type == APP_SETTING_TYPE_JSON:
if not p_settings[s_def.name]:
cleaned_data[s_field] = '{}'
try:
cleaned_data[s_field] = json.loads(
cleaned_data.get(s_field)
)
except json.JSONDecodeError as err:
errors.append((s_field, 'Invalid JSON\n' + str(err)))
elif s_def.type == APP_SETTING_TYPE_INTEGER:
# Convert integers from select fields
cleaned_data[s_field] = int(cleaned_data[s_field])

if not app_settings.validate(
setting_type=s_def.type,
setting_value=cleaned_data.get(s_field),
setting_options=s_def.options,
project=instance,
):
errors.append((s_field, 'Invalid value'))

# Custom plugin validation for app settings
if plugin and hasattr(plugin, 'validate_form_app_settings'):
try:
p_errors = plugin.validate_form_app_settings(
p_settings, project=instance
)
if p_errors:
for field, error in p_errors.items():
f_name = '.'.join(['settings', p_name, field])
errors.append((f_name, error))
except Exception as ex:
err_msg = SETTING_CUSTOM_VALIDATE_MSG.format(
plugin=p_name, exception=ex
)
errors.append((None, err_msg))
return cleaned_data, errors

def __init__(self, project=None, current_user=None, *args, **kwargs):
"""Override for form initialization"""
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -797,13 +801,13 @@ def clean(self):
'Public guest access is not allowed for categories',
)

# Verify remote site fields
cleaned_data, errors = self._validate_app_settings(
# Clean and validate app settings
cleaned_data, errors = self.clean_app_settings(
self.cleaned_data,
self.app_plugins,
app_settings,
self.p_kwargs,
self.instance,
APP_SETTING_SCOPE_PROJECT,
False if self.current_user.is_superuser else True,
project=self.instance,
)
for key, value in cleaned_data.items():
self.cleaned_data[key] = value
Expand Down Expand Up @@ -1197,13 +1201,13 @@ def save(self, *args, **kwargs):
# Site app settings form -------------------------------------------------------


class SiteAppSettingsForm(SODARAppSettingsFormMixin, SODARForm):
class SiteAppSettingsForm(SODARAppSettingFormMixin, SODARForm):
"""Form for managing site app settings"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Add settings fields
# TODO: Refactor into generic helper in SODARAppSettingsFormMixin
# TODO: Refactor into generic helper in SODARAppSettingFormMixin
self.project_plugins = get_active_plugins(plugin_type='project_app')
self.site_plugins = get_active_plugins(plugin_type='site_app')
self.app_plugins = self.project_plugins + self.site_plugins
Expand All @@ -1229,59 +1233,15 @@ def __init__(self, *args, **kwargs):
)

def clean(self):
# TODO: Refactor into generic helper in SODARAppSettingsFormMixin
for plugin in self.app_plugins + [None]:
p_kwargs = {'user_modifiable': True}
if plugin:
p_name = plugin.name
p_kwargs['plugin'] = plugin
else:
p_name = 'projectroles'
p_kwargs['plugin_name'] = p_name
s_defs = app_settings.get_definitions(
APP_SETTING_SCOPE_SITE, **p_kwargs
)
p_settings = {}

for s_def in s_defs.values():
s_field = '.'.join(['settings', p_name, s_def.name])
p_settings[s_def.name] = self.cleaned_data.get(s_field)

if s_def.type == APP_SETTING_TYPE_JSON:
if not self.cleaned_data.get(s_field):
self.cleaned_data[s_field] = '{}'
try:
self.cleaned_data[s_field] = json.loads(
self.cleaned_data.get(s_field)
)
except json.JSONDecodeError as err:
self.add_error(s_field, 'Invalid JSON\n' + str(err))
elif s_def.type == APP_SETTING_TYPE_INTEGER:
# Convert integers from select fields
self.cleaned_data[s_field] = int(self.cleaned_data[s_field])

if not app_settings.validate(
setting_type=s_def.type,
setting_value=self.cleaned_data.get(s_field),
setting_options=s_def.options,
):
self.add_error(s_field, 'Invalid value')

# Custom plugin validation for app settings
if plugin and hasattr(plugin, 'validate_form_app_settings'):
try:
p_errors = plugin.validate_form_app_settings(p_settings)
if p_errors:
for field, error in p_errors.items():
f_name = '.'.join(['settings', p_name, field])
self.add_error(f_name, error)
except Exception as ex:
self.add_error(
None,
SETTING_CUSTOM_VALIDATE_MSG.format(
plugin=p_name, exception=ex
),
)
cleaned_data, errors = self.clean_app_settings(
self.cleaned_data,
self.app_plugins,
APP_SETTING_SCOPE_SITE,
False,
)
self.cleaned_data = cleaned_data
for field, error in errors:
self.add_error(field, error)
return self.cleaned_data


Expand Down
Loading

0 comments on commit 9464070

Please sign in to comment.