9696from dje .views import manage_tab_permissions_view
9797from dje .views import object_compare_view
9898from dje .views import object_copy_view
99+ from policy .rules import RULE_REGISTRY
99100
100101EXTERNAL_SOURCE_LOOKUP = "external_references__external_source_id"
101102
@@ -1046,12 +1047,11 @@ def render(self, name, value, attrs=None, renderer=None):
10461047
10471048
10481049class DataspaceConfigurationForm (forms .ModelForm ):
1049- """
1050- Configure Dataspace settings.
1050+ """Configure Dataspace integration settings, with sensitive values hidden in the UI."""
10511051
1052- This form includes fields for various API keys, with sensitive values
1053- hidden in the UI using the HiddenValueWidget.
1054- """
1052+ class Meta :
1053+ model = DataspaceConfiguration
1054+ exclude = [ "policy_rules_config" ]
10551055
10561056 hidden_value_fields = [
10571057 "scancodeio_api_key" ,
@@ -1078,6 +1078,73 @@ def clean(self):
10781078 del self .cleaned_data [field_name ]
10791079
10801080
1081+ class PolicyRulesConfigurationForm (forms .ModelForm ):
1082+ """Form for configuring policy rule overrides stored in policy_rules_config."""
1083+
1084+ class Meta :
1085+ model = DataspaceConfiguration
1086+ fields = []
1087+
1088+ def __init__ (self , * args , ** kwargs ):
1089+ super ().__init__ (* args , ** kwargs )
1090+ self .add_policy_rule_config_fields ()
1091+
1092+ def add_policy_rule_config_fields (self ):
1093+ """Inject per-rule form fields with initial values from the saved policy_rules_config."""
1094+ config = getattr (self .instance , "policy_rules_config" , {}) or {}
1095+ for rule_type , handler in RULE_REGISTRY .items ():
1096+ rule_config = config .get (rule_type , {})
1097+ self .fields [f"rule_{ rule_type } _disabled" ] = forms .BooleanField (
1098+ label = "Disable this rule" ,
1099+ required = False ,
1100+ initial = not rule_config .get ("is_active" , True ),
1101+ )
1102+ self .fields [f"rule_{ rule_type } _threshold" ] = forms .IntegerField (
1103+ label = "Threshold" ,
1104+ required = False ,
1105+ min_value = 0 ,
1106+ initial = rule_config .get ("threshold" ),
1107+ widget = forms .NumberInput (
1108+ attrs = {"placeholder" : f"Default: { handler .default_threshold } " }
1109+ ),
1110+ help_text = "Minimum violations to trigger the rule. Leave blank to use the default." ,
1111+ )
1112+ for param_name , param_desc in handler .parameters_schema .items ():
1113+ self .fields [f"rule_{ rule_type } _param_{ param_name } " ] = forms .FloatField (
1114+ label = param_name .replace ("_" , " " ).title (),
1115+ required = False ,
1116+ initial = (rule_config .get ("parameters" ) or {}).get (param_name ),
1117+ help_text = param_desc ,
1118+ )
1119+
1120+ def build_policy_rules_config (self ):
1121+ """Serialize the per-rule form fields back into the policy_rules_config dict."""
1122+ policy_rules_config = {}
1123+ for rule_type , handler in RULE_REGISTRY .items ():
1124+ rule_config = {}
1125+ if self .cleaned_data .get (f"rule_{ rule_type } _disabled" ):
1126+ rule_config ["is_active" ] = False
1127+ threshold = self .cleaned_data .get (f"rule_{ rule_type } _threshold" )
1128+ if threshold is not None :
1129+ rule_config ["threshold" ] = threshold
1130+ parameters = {}
1131+ for param_name in handler .parameters_schema :
1132+ param_value = self .cleaned_data .get (f"rule_{ rule_type } _param_{ param_name } " )
1133+ if param_value is not None :
1134+ parameters [param_name ] = param_value
1135+ if parameters :
1136+ rule_config ["parameters" ] = parameters
1137+ if rule_config :
1138+ policy_rules_config [rule_type ] = rule_config
1139+ return policy_rules_config
1140+
1141+ def save (self , commit = True ):
1142+ self .instance .policy_rules_config = self .build_policy_rules_config ()
1143+ if commit :
1144+ self .instance .save (update_fields = ["policy_rules_config" ])
1145+ return self .instance
1146+
1147+
10811148class DataspaceConfigurationInline (DataspacedFKMixin , admin .StackedInline ):
10821149 model = DataspaceConfiguration
10831150 form = DataspaceConfigurationForm
@@ -1141,17 +1208,14 @@ class DataspaceConfigurationInline(DataspacedFKMixin, admin.StackedInline):
11411208 ),
11421209 ]
11431210 # Do not include the Dataspace related FKs on addition as the Dataspace does not exist yet
1144- policy_rules_fieldset = (
1145- "Policy Rules" ,
1146- {"fields" : ("policy_rules_config" ,)},
1147- )
1148- fieldsets = [("" , {"fields" : ("homepage_layout" ,)})] + add_fieldsets + [policy_rules_fieldset ]
1211+ fieldsets = [("" , {"fields" : ("homepage_layout" ,)})] + add_fieldsets
1212+ inline_classes = ("grp-collapse grp-open" ,)
11491213 can_delete = False
11501214
11511215 def get_fieldsets (self , request , obj = None ):
11521216 if not obj :
11531217 return self .add_fieldsets
1154- return super (). get_fieldsets ( request , obj )
1218+ return [( "" , { "fields" : ( "homepage_layout" ,)})] + self . add_fieldsets
11551219
11561220 def get_readonly_fields (self , request , obj = None ):
11571221 """Only a user from the current Dataspace can edit Dataspace related FKs."""
@@ -1164,6 +1228,38 @@ def get_readonly_fields(self, request, obj=None):
11641228 return readonly_fields
11651229
11661230
1231+ class PolicyRulesConfigurationInline (DataspacedFKMixin , admin .StackedInline ):
1232+ model = DataspaceConfiguration
1233+ form = PolicyRulesConfigurationForm
1234+ verbose_name_plural = _ ("Policy Rules Configuration" )
1235+ verbose_name = _ ("Policy Rules Configuration" )
1236+ classes = ("grp-collapse grp-open" ,)
1237+ inline_classes = ("grp-collapse grp-open" ,)
1238+ can_delete = False
1239+
1240+ def get_fieldsets (self , request , obj = None ):
1241+ if not obj :
1242+ return []
1243+ rule_fieldsets = []
1244+ for rule_type , handler in RULE_REGISTRY .items ():
1245+ fields = [f"rule_{ rule_type } _disabled" , f"rule_{ rule_type } _threshold" ]
1246+ for param_name in handler .parameters_schema :
1247+ fields .append (f"rule_{ rule_type } _param_{ param_name } " )
1248+ rule_fieldsets .append ((
1249+ handler .label ,
1250+ {
1251+ "fields" : fields ,
1252+ "description" : handler .description ,
1253+ "classes" : ("grp-collapse grp-open" ,),
1254+ },
1255+ ))
1256+ return rule_fieldsets
1257+
1258+ def get_formset (self , request , obj = None , ** kwargs ):
1259+ kwargs ["fields" ] = []
1260+ return super ().get_formset (request , obj , ** kwargs )
1261+
1262+
11671263@admin .register (Dataspace , site = dejacode_site )
11681264class DataspaceAdmin (
11691265 ReferenceOnlyPermissions ,
@@ -1243,7 +1339,7 @@ class DataspaceAdmin(
12431339 ),
12441340 )
12451341 search_fields = ("name" ,)
1246- inlines = [DataspaceConfigurationInline ]
1342+ inlines = [DataspaceConfigurationInline , PolicyRulesConfigurationInline ]
12471343 form = DataspaceAdminForm
12481344 change_form_template = "admin/dje/dataspace/change_form.html"
12491345 change_list_template = "admin/change_list_extended.html"
0 commit comments