Dodat je update content button, nakon sto se manuelno selektuju rizici
This commit is contained in:
@@ -95,12 +95,12 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||||||
search_fields = ['organization__name', 'organization__email']
|
search_fields = ['organization__name', 'organization__email']
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
'created_at', 'modified_at', 'regen_note_action',
|
'created_at', 'modified_at', 'regen_note_action',
|
||||||
'regen_document_action', 'regen_top_risks_action', 'regen_controls_action', 'regen_keyfindings_action', 'regen_recommendations_action',
|
'regen_document_action', 'regen_top_risks_action','update_risk_content_action', 'regen_controls_action', 'regen_keyfindings_action', 'regen_recommendations_action',
|
||||||
)
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Organization & Risks', {
|
('Organization & Risks', {
|
||||||
'fields': ('organization', 'regen_note_action', 'regen_document_action', 'organization_risks', 'risk_explanations', 'regen_top_risks_action')
|
'fields': ('organization', 'regen_note_action', 'regen_document_action', 'organization_risks','update_risk_content_action', 'risk_explanations', 'regen_top_risks_action')
|
||||||
}),
|
}),
|
||||||
('Key Findings', {
|
('Key Findings', {
|
||||||
'fields': ('key_findings', 'regen_keyfindings_action')
|
'fields': ('key_findings', 'regen_keyfindings_action')
|
||||||
@@ -157,6 +157,16 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
regen_document_action.short_description = ''
|
regen_document_action.short_description = ''
|
||||||
|
|
||||||
|
def update_risk_content_action(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<div class="ai-callout ai-callout-info">'
|
||||||
|
'🔁 <strong>Update existing content:</strong> synchronize risks, controls, and explanations without regenerating everything from scratch.'
|
||||||
|
'</div>'
|
||||||
|
'<br/>'
|
||||||
|
'<button type="submit" name="_update_risk_content" class="button">🌀 Update Risk Content</button>'
|
||||||
|
)
|
||||||
|
update_risk_content_action.short_description = ''
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
super().save_model(request, obj, form, change)
|
super().save_model(request, obj, form, change)
|
||||||
org_risks = form.cleaned_data.get('organization_risks')
|
org_risks = form.cleaned_data.get('organization_risks')
|
||||||
@@ -298,7 +308,7 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
|
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
|
||||||
if request.method == 'POST' and any(k in request.POST for k in ("_regen_controls", "_regen_key_findings", "_regen_recommendations", "_regen_top_risks", "_regen_document")):
|
if request.method == 'POST' and any(k in request.POST for k in ("_regen_controls", "_regen_key_findings", "_regen_recommendations", "_regen_top_risks", "_regen_document", "_update_risk_content")):
|
||||||
obj = self.get_object(request, object_id)
|
obj = self.get_object(request, object_id)
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return super().changeform_view(request, object_id, form_url, extra_context)
|
return super().changeform_view(request, object_id, form_url, extra_context)
|
||||||
@@ -312,6 +322,13 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||||||
self.message_user(request, "Top risks regenerated and risk segment updated.")
|
self.message_user(request, "Top risks regenerated and risk segment updated.")
|
||||||
else:
|
else:
|
||||||
self.message_user(request, "Top risks could not be generated.", level=messages.WARNING)
|
self.message_user(request, "Top risks could not be generated.", level=messages.WARNING)
|
||||||
|
elif "_update_risk_content" in request.POST:
|
||||||
|
try:
|
||||||
|
self._update_risk_content(obj)
|
||||||
|
self.message_user(request, "Risk content successfully synchronized (kept existing explanations and controls).")
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("Update risk content failed")
|
||||||
|
self.message_user(request, f"Risk content update failed: {e}", level=messages.ERROR)
|
||||||
elif "_regen_controls" in request.POST:
|
elif "_regen_controls" in request.POST:
|
||||||
self._regen_controls(obj)
|
self._regen_controls(obj)
|
||||||
self.message_user(request, "Risks and controls regenerated successfully.")
|
self.message_user(request, "Risks and controls regenerated successfully.")
|
||||||
@@ -403,6 +420,40 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to refresh segments from current mappings for document %s", getattr(obj, 'pk', None))
|
logger.exception("Failed to refresh segments from current mappings for document %s", getattr(obj, 'pk', None))
|
||||||
|
|
||||||
|
def _update_risk_content(self, obj):
|
||||||
|
"""
|
||||||
|
Synchronizes the document risks and explanations with the organization's current risks.
|
||||||
|
Keeps existing explanations for unchanged risks, adds blanks for new ones,
|
||||||
|
and removes explanations + controls for deleted risks.
|
||||||
|
Does NOT auto-generate new controls for added risks.
|
||||||
|
"""
|
||||||
|
org = obj.organization
|
||||||
|
if not org:
|
||||||
|
raise ValueError("Organization not set for this document.")
|
||||||
|
|
||||||
|
existing_explanations = obj.risk_explanations or {}
|
||||||
|
existing_risk_ids = set(map(int, existing_explanations.keys()))
|
||||||
|
actual_risk_ids = set(org.risks.values_list('pk', flat=True))
|
||||||
|
removed_risks = existing_risk_ids - actual_risk_ids
|
||||||
|
added_risks = actual_risk_ids - existing_risk_ids
|
||||||
|
new_explanations = {}
|
||||||
|
|
||||||
|
for risk_id in actual_risk_ids & existing_risk_ids:
|
||||||
|
new_explanations[str(risk_id)] = existing_explanations.get(str(risk_id), "")
|
||||||
|
|
||||||
|
for risk_id in added_risks:
|
||||||
|
new_explanations[str(risk_id)] = ""
|
||||||
|
|
||||||
|
obj.risk_explanations = new_explanations
|
||||||
|
obj.save(update_fields=["risk_explanations", "modified_at"])
|
||||||
|
|
||||||
|
if removed_risks:
|
||||||
|
control = DocumentRiskControl.objects.filter(document=obj, risk_id__in=removed_risks)
|
||||||
|
for c in control:
|
||||||
|
c.risk = None
|
||||||
|
|
||||||
|
self._refresh_segments_from_current_mappings(obj)
|
||||||
|
|
||||||
class DocumentTemplateAdmin(admin.ModelAdmin):
|
class DocumentTemplateAdmin(admin.ModelAdmin):
|
||||||
list_display = ['name', 'created_at', 'updated_at', 'preview_button']
|
list_display = ['name', 'created_at', 'updated_at', 'preview_button']
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
.form-row.field-regen_keyfindings_action label,
|
.form-row.field-regen_keyfindings_action label,
|
||||||
.form-row.field-regen_document_action label,
|
.form-row.field-regen_document_action label,
|
||||||
.form-row.field-regen_note_action label,
|
.form-row.field-regen_note_action label,
|
||||||
|
.form-row.field-update_risk_content_action label,
|
||||||
.form-row.field-regen_recommendations_action label { display: none; }
|
.form-row.field-regen_recommendations_action label { display: none; }
|
||||||
.form-row.field-regen_top_risks_action label { display: none; }
|
.form-row.field-regen_top_risks_action label { display: none; }
|
||||||
.form-row.field-regen_controls_action { display: none !important; }
|
.form-row.field-regen_controls_action { display: none !important; }
|
||||||
|
|||||||
Reference in New Issue
Block a user