Source code for projectroles.app_settings

"""Projectroles app settings API"""

import json
import logging

from typing import Any, Optional, Union

from django.conf import settings
from django.contrib.auth import get_user_model

from djangoplugins.point import Plugin

from projectroles.models import AppSetting, Project, SODARUser, SODAR_CONSTANTS
from projectroles.plugins import PluginAppSettingDef, PluginAPI
from projectroles.utils import get_display_name


logger = logging.getLogger(__name__)
plugin_api = PluginAPI()
User = get_user_model()


# SODAR constants
PROJECT_TYPE_PROJECT = SODAR_CONSTANTS['PROJECT_TYPE_PROJECT']
PROJECT_TYPE_CATEGORY = SODAR_CONSTANTS['PROJECT_TYPE_CATEGORY']
SITE_MODE_SOURCE = SODAR_CONSTANTS['SITE_MODE_SOURCE']
SITE_MODE_TARGET = SODAR_CONSTANTS['SITE_MODE_TARGET']
APP_SETTING_SCOPE_PROJECT = SODAR_CONSTANTS['APP_SETTING_SCOPE_PROJECT']
APP_SETTING_SCOPE_USER = SODAR_CONSTANTS['APP_SETTING_SCOPE_USER']
APP_SETTING_SCOPE_PROJECT_USER = SODAR_CONSTANTS[
    'APP_SETTING_SCOPE_PROJECT_USER'
]
APP_SETTING_SCOPE_SITE = SODAR_CONSTANTS['APP_SETTING_SCOPE_SITE']
APP_SETTING_TYPE_BOOLEAN = SODAR_CONSTANTS['APP_SETTING_TYPE_BOOLEAN']
APP_SETTING_TYPE_INTEGER = SODAR_CONSTANTS['APP_SETTING_TYPE_INTEGER']
APP_SETTING_TYPE_JSON = SODAR_CONSTANTS['APP_SETTING_TYPE_JSON']
APP_SETTING_TYPE_STRING = SODAR_CONSTANTS['APP_SETTING_TYPE_STRING']

# Local constants
APP_NAME = 'projectroles'
APP_SETTING_GLOBAL_DEFAULT = False
APP_SETTING_DEFAULT_VALUES = {
    APP_SETTING_TYPE_BOOLEAN: False,
    APP_SETTING_TYPE_INTEGER: 0,
    APP_SETTING_TYPE_JSON: {},
    APP_SETTING_TYPE_STRING: '',
}
APP_SETTING_SCOPE_ARGS = {
    APP_SETTING_SCOPE_PROJECT: {'project': True, 'user': False},
    APP_SETTING_SCOPE_USER: {'project': False, 'user': True},
    APP_SETTING_SCOPE_PROJECT_USER: {'project': True, 'user': True},
    APP_SETTING_SCOPE_SITE: {'project': False, 'user': False},
}
PROJECT_LIST_PAGE_OPTIONS = [10, 25, 50, 100, (-1, 'No pagination')]
DELETE_SCOPE_ERR_MSG = 'Argument "{arg}" must be set for {scope} scope setting'
GLOBAL_PROJECT_ERR_MSG = (
    'Overriding global settings for remote projects not allowed'
)
GLOBAL_USER_ERR_MSG = (
    'Overriding global user settings on target site not allowed'
)


# Define App Settings for projectroles app
PROJECTROLES_APP_SETTINGS = [
    PluginAppSettingDef(
        name='ip_restrict',
        scope=APP_SETTING_SCOPE_PROJECT,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        label='IP restrict',
        description='Restrict project access to the IP allow list',
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='ip_allow_list',
        scope=APP_SETTING_SCOPE_PROJECT,
        type=APP_SETTING_TYPE_STRING,
        label='IP allow list',
        description='Comma-separated list of allowed IPs for project access',
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='project_star',
        scope=APP_SETTING_SCOPE_PROJECT_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        global_edit=False,
        project_types=[PROJECT_TYPE_PROJECT, PROJECT_TYPE_CATEGORY],
    ),
    PluginAppSettingDef(
        name='notify_alert_project',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=True,
        label='Receive alerts for {} updates'.format(
            get_display_name(PROJECT_TYPE_PROJECT)
        ),
        description=(
            'Receive UI alerts for {} or {} creation, updating, moving, '
            'archiving and deletion.'.format(
                get_display_name(PROJECT_TYPE_CATEGORY),
                get_display_name(PROJECT_TYPE_PROJECT),
            )
        ),
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='notify_email_project',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=True,
        label='Receive email for {} updates'.format(
            get_display_name(PROJECT_TYPE_PROJECT)
        ),
        description=(
            'Receive email notifications for {} or {} creation, updating, '
            'moving, archiving and deletion.'.format(
                get_display_name(PROJECT_TYPE_CATEGORY),
                get_display_name(PROJECT_TYPE_PROJECT),
            )
        ),
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='notify_alert_role',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=True,
        label='Receive alerts for {} membership updates'.format(
            get_display_name(PROJECT_TYPE_PROJECT)
        ),
        description=(
            'Receive UI alerts for {} or {} membership updates and invitation '
            'activity.'.format(
                get_display_name(PROJECT_TYPE_CATEGORY),
                get_display_name(PROJECT_TYPE_PROJECT),
            )
        ),
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='notify_email_role',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=True,
        label='Receive email for {} membership updates'.format(
            get_display_name(PROJECT_TYPE_PROJECT)
        ),
        description=(
            'Receive email notifications for {} or {} membership updates and '
            'invitation activity.'.format(
                get_display_name(PROJECT_TYPE_CATEGORY),
                get_display_name(PROJECT_TYPE_PROJECT),
            )
        ),
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='site_read_only',
        scope=APP_SETTING_SCOPE_SITE,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        label='Site read-only mode',
        description='Set site in read-only mode. Data altering operations will '
        'be prohibited. Mode must be explicitly unset to allow data '
        'modification.',
        user_modifiable=True,
        global_edit=False,
    ),
    PluginAppSettingDef(
        name='project_list_highlight',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=True,
        label='Project list title highlight',
        description='Highlight project title in paths displayed in the project '
        'list.',
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='project_list_home_starred',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        label='Home view project list starred display',
        description='Initial starred mode to display in home view project list',
        user_modifiable=False,  # Editable only in project list
        global_edit=False,
    ),
    PluginAppSettingDef(
        name='dropdown_user_name_display',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        label='Display user name in user dropdown',
        description='Display your user name in the user dropdown link',
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='project_list_pagination',
        scope=APP_SETTING_SCOPE_USER,
        type=APP_SETTING_TYPE_INTEGER,
        default=10,
        label='Project list page size',
        description='Amount of projects per page in the project list.',
        options=PROJECT_LIST_PAGE_OPTIONS,
        user_modifiable=True,
        global_edit=True,
    ),
    PluginAppSettingDef(
        name='project_access_block',
        scope=APP_SETTING_SCOPE_PROJECT,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        label='Block project access',
        description='Temporarily block all user access to project.',
        user_modifiable=False,  # Only allow modification by superuser
        global_edit=False,
        project_types=[PROJECT_TYPE_PROJECT],
    ),
    PluginAppSettingDef(
        name='category_public_stats',
        scope=APP_SETTING_SCOPE_PROJECT,
        type=APP_SETTING_TYPE_BOOLEAN,
        default=False,
        label='Display public category statistics',
        description='Enable all users to view {} readme and statistics. '
        'Limited to top level {}.'.format(
            get_display_name(PROJECT_TYPE_CATEGORY),
            get_display_name(PROJECT_TYPE_CATEGORY, plural=True),
        ),
        user_modifiable=True,  # Only allow modification by superuser
        global_edit=False,
        project_types=[PROJECT_TYPE_CATEGORY],
    ),
]


[docs] class AppSettingAPI: @classmethod def _validate_project_and_user( cls, scope: str, project: Project, user: User ): """ Ensure project and user parameters are set according to scope. :param scope: Scope of Setting (USER, PROJECT, PROJECT_USER, SITE) :param project: Project object :param user: User object :raise: ValueError if none or both objects exist """ if not APP_SETTING_SCOPE_ARGS[scope] == { 'project': project is not None, 'user': user is not None, }: raise ValueError( 'Project and/or user invalid for setting with {} scope ' '(project={}, user={})'.format( scope, project.get_log_title() if project else None, user.username if user else None, ) ) @classmethod def _validate_value_in_options( cls, setting_value: str, setting_options: Union[list, callable], project: Optional[Project] = None, user: Optional[User] = None, ): """ Ensure setting_value is present in setting_options. :param setting_value: String :param setting_options: List of options :param project: Project object :param user: User object :raise: ValueError if type is not recognized """ if callable(setting_options): valid_options = [ val[0] if isinstance(val, tuple) else val for val in setting_options(project, user) ] if setting_value not in valid_options: raise ValueError( 'Choice "{}" not found in options ({})'.format( setting_value, ', '.join(map(str, valid_options)), ) ) elif setting_options: opts = [ o[0] if isinstance(o, tuple) else o for o in setting_options ] if setting_value not in opts: raise ValueError( 'Choice "{}" not found in options ({})'.format( setting_value, ', '.join(map(str, setting_options)) ) ) @classmethod def _get_app_plugin(cls, plugin_name: str) -> Plugin: """ Return app plugin by name. :param plugin_name: Name of the app plugin (string) :return: App plugin object :raise: ValueError if plugin is not found with the name """ plugin = plugin_api.get_app_plugin(plugin_name) if not plugin: raise ValueError(f'Plugin not found with name "{plugin_name}"') return plugin @classmethod def _get_defs( cls, plugin: Any = None, plugin_name: Optional[str] = None, ) -> dict: """ Get app setting definitions for a plugin. :param plugin: Plugin object or None :param plugin_name: Name of the app plugin (string or None) :return: Dict :raise: ValueError if args are not valid or plugin is not found """ if not plugin and not plugin_name: raise ValueError('Plugin object and name both unset') if plugin_name == APP_NAME: return cls.get_projectroles_defs() if not plugin: plugin = cls._get_app_plugin(plugin_name) return {s.name: s for s in plugin.app_settings} @classmethod def _get_json_value(cls, value: Union[str, dict]) -> dict: """ Return JSON value as dict regardless of input type :param value: Original value (string or dict) :raise: json.decoder.JSONDecodeError if string value is not valid JSON :raise: ValueError if value type is not recognized or if value is not valid JSON :return: dict """ if not value: return {} try: if isinstance(value, str): return json.loads(value) json.dumps(value) # Ensure this is valid return value except Exception: raise ValueError(f'Value is not valid JSON: {value}') @classmethod def _log_debug( cls, action: str, plugin_name: str, setting_name: str, value: Optional[str], project: Optional[Project] = None, user: Optional[User] = None, ): """ Helper method for logging setting changes in set() and delete() methods. :param action: Action string (string) :param plugin_name: App plugin name (string) :param setting_name: Setting name (string) :param value: Setting value (string) :param project: Project object or None :param user: User object or None """ args = [] if project: args.append(f'project={project.sodar_uuid}') if user: args.append(f'user={user.username}') log_args = ' ({})'.format('; '.join(args)) if args else '' log_value = f' = {value}' if action.lower() != 'delete' else '' logger.debug( f'{action.capitalize()} app setting: {plugin_name}.{setting_name}' f'{log_value}{log_args}' )
[docs] @classmethod def get_default( cls, plugin_name: str, setting_name: str, project: Optional[Project] = None, user: Optional[User] = None, post_safe: bool = False, ) -> Any: """ Get default setting value from an app plugin. :param plugin_name: App plugin name (string, equals "name" in plugin) :param setting_name: Setting name (string) :param project: Project object (optional) :param user: User object (optional) :param post_safe: Whether a POST safe value should be returned (bool) :return: Setting value :raise: ValueError if app plugin is not found :raise: ValueError if nothing is found with setting_name """ if plugin_name == APP_NAME: s_defs = cls.get_projectroles_defs() else: s_defs = cls._get_defs(plugin_name=plugin_name) if setting_name not in s_defs: raise ValueError( f'Setting "{setting_name}" not found in app plugin ' f'"{plugin_name}"' ) s_def = s_defs[setting_name] if callable(s_def.default): try: return s_def.default(project, user) except Exception: logger.error( f'Error in callable setting "{setting_name}" for plugin ' f'"{plugin_name}"' ) return APP_SETTING_DEFAULT_VALUES[s_def.type] elif s_def.type == APP_SETTING_TYPE_JSON: if s_def.default is None: return {} if post_safe: return json.dumps(s_def.default) return s_def.default
[docs] @classmethod def get( cls, plugin_name: str, setting_name: str, project: Optional[Project] = None, user: Optional[User] = None, post_safe: bool = False, validate: bool = True, ) -> Any: """ Return app setting value for a project or a user. If not set, return default. :param plugin_name: App plugin name (string, equals "name" in plugin) :param setting_name: Setting name (string) :param project: Project object (optional) :param user: User object (optional) :param post_safe: Whether a POST safe value should be returned (bool) :param validate: Validate project and user args (bool, default=True) :return: Value :raise: ValueError if nothing is found with setting_name :raise: ValueError if neither project nor user are set """ if validate: s_def = cls.get_definition( name=setting_name, plugin_name=plugin_name ) cls._validate_project_and_user(s_def.scope, project, user) if not user or user.is_authenticated: try: val = AppSetting.objects.get_setting_value( plugin_name, setting_name, project=project, user=user ) except AppSetting.DoesNotExist: val = cls.get_default( plugin_name, setting_name, project=project, user=user, post_safe=post_safe, ) else: # Anonymous user val = cls.get_default( plugin_name, setting_name, project=project, user=user, post_safe=post_safe, ) # Handle post_safe for dict values (JSON) if post_safe and isinstance(val, (dict, list)): return json.dumps(val) return val
[docs] @classmethod def get_all_by_scope( cls, scope: str, project: Optional[Project] = None, user: Optional[User] = None, post_safe: bool = False, ) -> dict: """ Return all setting values by scope. If a value is not set, return the default. :param scope: String :param project: Project object (optional) :param user: User object (optional) :param post_safe: Whether POST safe values should be returned (bool) :return: Dict :raise: ValueError if scope or project and user args are invalid """ PluginAppSettingDef.validate_scope(scope) cls._validate_project_and_user(scope, project, user) ret = {} all_defs = cls.get_all_defs() for plugin_name, s_defs in all_defs.items(): for s_def in [d for d in s_defs.values() if d.scope == scope]: ret[f'settings.{plugin_name}.{s_def.name}'] = cls.get( plugin_name, s_def.name, project, user, post_safe ) return ret
[docs] @classmethod def get_defaults( cls, scope: str, project: Optional[Project] = None, user: Optional[User] = None, post_safe: bool = False, ) -> dict: """ Get all default settings for a scope. :param scope: Setting scope (PROJECT, USER or PROJECT_USER) :param project: Project object (optional) :param user: User object (optional) :param post_safe: Whether POST safe values should be returned (bool) :return: Dict """ PluginAppSettingDef.validate_scope(scope) ret = {} app_plugins = plugin_api.get_active_plugins() for plugin in app_plugins: p_defs = cls.get_definitions(scope, plugin=plugin) for s_key in p_defs: ret[f'settings.{plugin.name}.{s_key}'] = cls.get_default( plugin.name, s_key, project=project, user=user, post_safe=post_safe, ) p_defs = cls.get_definitions(scope, plugin_name=APP_NAME) for s_key in p_defs: ret[f'settings.{APP_NAME}.{s_key}'] = cls.get_default( APP_NAME, s_key, project=project, user=user, post_safe=post_safe, ) return ret
[docs] @classmethod def set( cls, plugin_name: str, setting_name: str, value: Any, project: Optional[Project] = None, user: Optional[User] = None, validate: bool = True, ) -> bool: """ Set value of an existing project or user settings. Creates the object if not found. :param plugin_name: App plugin name (string, equals "name" in plugin) :param setting_name: Setting name (string) :param value: Value to be set :param project: Project object (optional) :param user: User object (optional) :param validate: Validate value (bool, default=True) :return: True if changed, False if not changed :raise: ValueError if validating and value is not accepted for setting type :raise: ValueError if neither project nor user are set :raise: ValueError if setting name is not found in plugin specification """ s_def = cls.get_definition(name=setting_name, plugin_name=plugin_name) cls._validate_project_and_user(s_def.scope, project, user) # Check project type if project and project.type not in s_def.project_types: raise ValueError( f'Project type {project.type} not allowed for setting ' f'{setting_name}' ) # Prevent updating global setting on target site if s_def.global_edit: if project and project.is_remote(): raise ValueError(GLOBAL_PROJECT_ERR_MSG) if ( user and not project and settings.PROJECTROLES_SITE_MODE == SITE_MODE_TARGET ): raise ValueError(GLOBAL_USER_ERR_MSG) # Validate v = ( cls._get_json_value(value) if s_def.type == APP_SETTING_TYPE_JSON else value ) if validate: cls.validate( s_def.type, v, s_def.options, project=project, user=user, ) try: # Update existing setting q_kwargs = {'name': setting_name, 'project': project, 'user': user} if not plugin_name == APP_NAME: q_kwargs['app_plugin__name'] = plugin_name else: q_kwargs['app_plugin'] = None setting = AppSetting.objects.get(**q_kwargs) if cls.compare_value(setting, value): return False if setting.type == APP_SETTING_TYPE_JSON: setting.value_json = v else: setting.value = v setting.save() cls._log_debug( 'set', plugin_name, setting_name, value, project, user ) return True except AppSetting.DoesNotExist: # Create new s_type = s_def.type if plugin_name == APP_NAME: app_plugin_model = None else: app_plugin = cls._get_app_plugin(plugin_name) app_plugin_model = app_plugin.get_model() s_vals = { 'app_plugin': app_plugin_model, 'project': project, 'user': user, 'name': setting_name, 'type': s_type, } if s_type == APP_SETTING_TYPE_JSON: s_vals['value_json'] = cls._get_json_value(value) else: s_vals['value'] = value AppSetting.objects.create(**s_vals) cls._log_debug( 'create', plugin_name, setting_name, value, project, user ) return True
[docs] @classmethod def is_set( cls, plugin_name: str, setting_name: str, project: Optional[Project] = None, user: Optional[User] = None, ) -> bool: """ Return True if the setting has been set, instead of retrieving the default value from the definition. NOTE: Also returns True if the current set value equals the default. :param plugin_name: App plugin name (string, equals "name" in plugin) :param setting_name: Setting name (string) :param project: Project object (optional) :param user: User object (optional) :return: Boolean """ s_def = cls.get_definition(name=setting_name, plugin_name=plugin_name) cls._validate_project_and_user(s_def.scope, project, user) q_kwargs = {'name': setting_name, 'project': project, 'user': user} if not plugin_name == APP_NAME: q_kwargs['app_plugin__name'] = plugin_name else: q_kwargs['app_plugin'] = None return AppSetting.objects.filter(**q_kwargs).exists()
[docs] @classmethod def delete( cls, plugin_name: str, setting_name: str, project: Optional[Project] = None, user: Optional[User] = None, ): """ Delete one or more app setting objects. In case of a PROJECT_USER setting, can be used to delete all settings related to project. :param plugin_name: App plugin name (string, equals "name" in plugin) :param setting_name: Setting name (string) :param project: Project object to delete setting from (optional) :param user: User object to delete setting from (optional) :raise: ValueError with invalid project/user args """ s_def = cls.get_definition(setting_name, plugin_name=plugin_name) if s_def.scope != APP_SETTING_SCOPE_PROJECT_USER: cls._validate_project_and_user(s_def.scope, project, user) elif not project: raise ValueError( 'Project must be set for {} scope settings'.format( APP_SETTING_SCOPE_PROJECT_USER ) ) q_kwargs = {'name': setting_name} if project: q_kwargs['project'] = project if user: q_kwargs['user'] = user cls._log_debug('delete', plugin_name, setting_name, None, project, user) app_settings = AppSetting.objects.filter(**q_kwargs) sc = app_settings.count() app_settings.delete() logger.debug( 'Deleted {} app setting{}'.format(sc, 's' if sc != 1 else '') )
[docs] @classmethod def delete_by_scope( cls, scope: str, project: Optional[Project] = None, user: Optional[User] = None, ): """ Delete all app settings within a given scope for a project and/or user. :param scope: Setting scope (string) :param project: Project object to delete setting from :param user: User object to delete setting from :raise: ValueError if scope, project or user are incorrect """ PluginAppSettingDef.validate_scope(scope) cls._validate_project_and_user(scope, project, user) for plugin_name, app_settings in cls.get_all_defs().items(): for s_name, s_def in app_settings.items(): if s_def.scope == scope: cls.delete(plugin_name, s_name, project=project, user=user)
[docs] @classmethod def validate( cls, setting_type: str, setting_value: Any, setting_options: Union[callable, None, list], project: Optional[Project] = None, user: Optional[User] = None, ) -> bool: """ Validate setting value according to its type. :param setting_type: Setting type (string) :param setting_value: Setting value :param setting_options: Setting options (can be None) :param project: Project object (optional) :param user: User object (optional) :raise: ValueError if setting_type or setting_value is invalid """ PluginAppSettingDef.validate_type(setting_type) cls._validate_value_in_options( setting_value, setting_options, project=project, user=user ) # Test callable value if callable(setting_value): setting_value(project, user) else: # Else validate normal value PluginAppSettingDef.validate_value(setting_type, setting_value) return True
[docs] @classmethod def get_definition( cls, name: str, plugin: Optional[Plugin] = None, plugin_name: Optional[str] = None, ) -> PluginAppSettingDef: """ Return definition for a single app setting, either based on an app name or the plugin object. :param name: Setting name (string) :param plugin: Plugin object or None :param plugin_name: Name of the app plugin (string or None) :return: PluginAppSettingDef :raise: ValueError if neither plugin_name nor plugin are set, or if setting is not found in plugin """ defs = cls._get_defs(plugin, plugin_name) if name not in defs: raise ValueError( f'App setting not found in plugin ' f'"{plugin_name or plugin.name}" with name "{name}"' ) return defs[name]
[docs] @classmethod def get_definitions( cls, scope: Optional[str] = None, plugin: Optional[Plugin] = None, plugin_name: Optional[str] = None, user_modifiable: bool = False, ) -> dict: """ Return app setting definitions from a plugin, optionally limited by scope. :param scope: String or None :param plugin: Plugin object or None :param plugin_name: App plugin name (string, equals "name" in plugin) :param user_modifiable: Only return non-superuser modifiable settings if True (boolean) :return: Dict :raise: ValueError if scope is invalid or if neither plugin_name nor plugin are set """ if scope: PluginAppSettingDef.validate_scope(scope) defs = cls._get_defs(plugin, plugin_name) return { k: v for k, v in defs.items() if (not scope or v.scope == scope) and (not user_modifiable or v.user_modifiable) }
[docs] @classmethod def get_projectroles_defs(cls) -> dict: """ Return projectroles settings definitions. If it exists, get value from settings.PROJECTROLES_APP_SETTINGS_TEST for testing modifications. :return: Dict """ try: app_settings = ( settings.PROJECTROLES_APP_SETTINGS_TEST or PROJECTROLES_APP_SETTINGS ) except AttributeError: app_settings = PROJECTROLES_APP_SETTINGS return {s.name: s for s in app_settings}
[docs] @classmethod def get_all_defs(cls) -> dict: """ Return app setting definitions for projectroles and all active app plugins in a dictionary with the app name as key. :return: Dict """ ret = {APP_NAME: cls.get_projectroles_defs()} plugins = ( [] + plugin_api.get_active_plugins('project_app') + plugin_api.get_active_plugins('site_app') ) for p in plugins: ret[p.name] = cls._get_defs(p) return ret
[docs] @classmethod def compare_value(cls, obj: AppSetting, input_value: Any) -> bool: """ Compare input value to value in an AppSetting object. Return True if values match, False if there is a mismatch. :param obj: AppSetting object :param input_value: Input value (string, int, bool or dict) :return: Bool """ if obj.type == APP_SETTING_TYPE_JSON: return ( not obj.value_json and not input_value ) or obj.value_json == cls._get_json_value(input_value) elif obj.type == APP_SETTING_TYPE_BOOLEAN: if isinstance(input_value, str): input_value = bool(int(input_value)) return bool(int(obj.value)) == input_value return obj.value == str(input_value)
def get_example_setting_default( project: Optional[Project] = None, user: Optional[SODARUser] = None ) -> str: """ Example method for callable default value retrieval for app settings. :param project: Project object :param user: SODARUser object :return: String with project and user info or 'No project' """ response = 'N/A' if project and user: response = f'{project.title}:{user.username}' elif project: response = str(project.sodar_uuid) elif user: response = str(user.sodar_uuid) return response def get_example_setting_options( project: Optional[Project] = None, user: Optional[SODARUser] = None ) -> list[tuple]: """ Example method for callable option list retrieval for app settings. :param project: Project object :param user: SODARUser object :return: List of tuples for ChoiceField """ ret = [('N/A', 'No project or user for callable'), 'Example string option'] if project and user: ret.append( ( str(project.sodar_uuid), f'Project UUID {project.sodar_uuid} by {user.username}', ) ) elif project: ret.append( ( str(project.sodar_uuid), f'Project UUID: {project.sodar_uuid}', ) ) elif user: ret.append((str(user.sodar_uuid), f'User UUID: {user.sodar_uuid}')) return ret