Coverage for src/backend/InvenTree/plugin/base/integration/SettingsMixin.py: 51%

80 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""Plugin mixin class for SettingsMixin.""" 

2 

3from typing import TYPE_CHECKING, Any, Optional 

4 

5from django.db.utils import OperationalError, ProgrammingError 

6 

7import structlog 

8 

9from plugin import PluginMixinEnum 

10 

11logger = structlog.get_logger('inventree') 

12 

13# import only for typechecking, otherwise this throws a model is unready error 

14if TYPE_CHECKING: 14 ↛ 15line 14 didn't jump to line 15 because the condition on line 14 was never true

15 from django.contrib.auth.models import User 

16 

17 from common.models import SettingsKeyType 

18else: 

19 

20 class User: 

21 """Dummy class, so that python throws no error.""" 

22 

23 class SettingsKeyType: 

24 """Dummy class, so that python throws no error.""" 

25 

26 

27class SettingsMixin: 

28 """Mixin that enables global settings for the plugin.""" 

29 

30 SETTINGS: dict[str, SettingsKeyType] = {} 

31 USER_SETTINGS: dict[str, SettingsKeyType] = {} 

32 

33 class MixinMeta: 

34 """Meta for mixin.""" 

35 

36 MIXIN_NAME = 'Settings' 

37 

38 def __init__(self): 

39 """Register mixin.""" 

40 super().__init__() 

41 self.add_mixin(PluginMixinEnum.SETTINGS, 'has_settings', __class__) 

42 self.settings = getattr(self, 'SETTINGS', {}) 

43 self.user_settings = getattr(self, 'USER_SETTINGS', {}) 

44 

45 @classmethod 

46 def _activate_mixin(cls, registry, plugins, *args, **kwargs): 

47 """Activate plugin settings. 

48 

49 Add all defined settings form the plugins to a unified dict in the registry. 

50 This dict is referenced by the PluginSettings for settings definitions. 

51 """ 

52 logger.debug('Activating plugin settings') 

53 

54 registry.mixins_settings = {} 

55 registry.mixins_user_settings = {} 

56 

57 for slug, plugin in plugins: 

58 if plugin.mixin_enabled(PluginMixinEnum.SETTINGS): 

59 plugin_setting = plugin.settings or {} 

60 registry.mixins_settings[slug] = plugin_setting 

61 

62 plugin_user_setting = plugin.user_settings or {} 

63 registry.mixins_user_settings[slug] = plugin_user_setting 

64 

65 @classmethod 

66 def _deactivate_mixin(cls, registry, **kwargs): 

67 """Deactivate all plugin settings.""" 

68 logger.debug('Deactivating plugin settings') 

69 # clear settings cache 

70 registry.mixins_settings = {} 

71 

72 @property 

73 def has_settings(self): 

74 """Does this plugin use custom global settings.""" 

75 return bool(self.settings) or bool(self.user_settings) 

76 

77 def get_setting( 

78 self, key: str, cache: bool = False, backup_value: Any = None 

79 ) -> Any: 

80 """Return the 'value' of the setting associated with this plugin. 

81 

82 Arguments: 

83 key: The 'name' of the setting value to be retrieved 

84 cache: Whether to use cached value (default = False) 

85 backup_value: A backup value to return if the setting is not found 

86 """ 

87 from plugin.models import PluginSetting 

88 

89 return PluginSetting.get_setting( 

90 key, plugin=self.plugin_config(), cache=cache, backup_value=backup_value 

91 ) 

92 

93 def set_setting( 

94 self, key: str, value: Any, user: Optional[User] = None, **kwargs 

95 ) -> None: 

96 """Set plugin setting value by key. 

97 

98 Arguments: 

99 key: The 'name' of the setting value to be set 

100 value: The value to be set for the setting 

101 user: The user who is making the change (optional) 

102 """ 

103 from plugin.models import PluginSetting 

104 from plugin.registry import registry 

105 

106 try: 

107 plugin = registry.get_plugin_config(self.plugin_slug(), self.plugin_name()) 

108 except (OperationalError, ProgrammingError): # pragma: no cover 

109 plugin = None 

110 

111 if not plugin: # pragma: no cover 

112 # Cannot find associated plugin model, return 

113 logger.error("Plugin configuration not found for plugin '%s'", self.slug) 

114 return 

115 

116 PluginSetting.set_setting(key, value, plugin=plugin) 

117 

118 def get_user_setting( 

119 self, key: str, user: User, cache: bool = False, backup_value: Any = None 

120 ) -> Any: 

121 """Return the 'value' of the user setting associated with this plugin. 

122 

123 Arguments: 

124 key: The 'name' of the user setting value to be retrieved 

125 user: The user for which the setting is to be retrieved 

126 cache: Whether to use cached value (default = False) 

127 backup_value: A backup value to return if the setting is not found 

128 """ 

129 from plugin.models import PluginUserSetting 

130 

131 return PluginUserSetting.get_setting( 

132 key, 

133 plugin=self.plugin_config(), 

134 user=user, 

135 cache=cache, 

136 backup_value=backup_value, 

137 settings=self.user_settings, 

138 ) 

139 

140 def set_user_setting(self, key: str, value: Any, user: User) -> None: 

141 """Set user setting value by key. 

142 

143 Arguments: 

144 key: The 'name' of the user setting value to be set 

145 value: The value to be set for the user setting 

146 user: The user for which the setting is to be set 

147 """ 

148 from plugin.models import PluginUserSetting 

149 from plugin.registry import registry 

150 

151 try: 

152 plugin = registry.get_plugin_config(self.plugin_slug(), self.plugin_name()) 

153 except (OperationalError, ProgrammingError): 

154 plugin = None 

155 

156 if not plugin: # pragma: no cover 

157 # Cannot find associated plugin model, return 

158 logger.error("Plugin configuration not found for plugin '%s'", self.slug) 

159 return 

160 

161 PluginUserSetting.set_setting(key, value, user, user=user, plugin=plugin) 

162 

163 def check_settings(self): 

164 """Check if all required settings for this machine are defined. 

165 

166 Warning: This method cannot be used in the __init__ function of the plugin 

167 

168 Returns: 

169 is_valid: Are all required settings defined 

170 missing_settings: List of all settings that are missing (empty if is_valid is 'True') 

171 """ 

172 from plugin.models import PluginSetting 

173 

174 return PluginSetting.check_all_settings( 

175 settings_definition=self.settings, plugin=self.plugin_config() 

176 ) 

177 

178 def get_settings_dict(self) -> dict: 

179 """Return a dictionary of all settings for this plugin. 

180 

181 - For each setting, return <key>: <value> pair. 

182 - If the setting is not defined, return the default value (if defined). 

183 

184 Returns: 

185 dict: Dictionary of all settings for this plugin 

186 """ 

187 from plugin.models import PluginSetting 

188 

189 keys = self.settings.keys() 

190 

191 settings = PluginSetting.objects.filter( 

192 plugin=self.plugin_config(), key__in=keys 

193 ) 

194 

195 settings_dict = {} 

196 

197 for setting in settings: 

198 settings_dict[setting.key] = setting.value 

199 

200 # Add any missing settings 

201 for key in keys: 

202 if key not in settings_dict: 

203 settings_dict[key] = self.settings[key].get('default') 

204 

205 return settings_dict