wbcompliance 1.54.11__tar.gz → 1.59.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/PKG-INFO +1 -1
  2. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/risk_management/checks.py +8 -0
  3. wbcompliance-1.59.2/wbcompliance/migrations/0022_riskincident_precheck.py +39 -0
  4. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/compliance_task.py +5 -5
  5. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/enums.py +1 -1
  6. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/checks.py +92 -6
  7. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/incidents.py +23 -7
  8. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/mixins.py +2 -2
  9. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/rules.py +9 -70
  10. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/risk_management/checks.py +1 -1
  11. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/disable_signals.py +11 -10
  12. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/risk_management/models/test_incidents.py +9 -7
  13. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/test_views.py +1 -1
  14. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/compliance_form.py +3 -3
  15. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/compliance_task.py +14 -14
  16. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/risk_management/checks.py +14 -3
  17. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/risk_management/mixins.py +1 -1
  18. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/.gitignore +0 -0
  19. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/pyproject.toml +0 -0
  20. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/__init__.py +0 -0
  21. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/__init__.py +0 -0
  22. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/compliance_form.py +0 -0
  23. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/compliance_task.py +0 -0
  24. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/compliance_type.py +0 -0
  25. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/risk_management/__init__.py +0 -0
  26. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/risk_management/checks.py +0 -0
  27. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/risk_management/incidents.py +0 -0
  28. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/risk_management/rules.py +0 -0
  29. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/admin/utils.py +0 -0
  30. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/apps.py +0 -0
  31. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/__init__.py +0 -0
  32. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/compliance.py +0 -0
  33. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/risk_management/__init__.py +0 -0
  34. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/risk_management/backends.py +0 -0
  35. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/risk_management/checks.py +0 -0
  36. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/risk_management/incidents.py +0 -0
  37. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/factories/risk_management/rules.py +0 -0
  38. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/__init__.py +0 -0
  39. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/compliances.py +0 -0
  40. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/risk_management/__init__.py +0 -0
  41. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/risk_management/incidents.py +0 -0
  42. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/risk_management/rules.py +0 -0
  43. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/risk_management/tables.py +0 -0
  44. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/filters/risk_management/utils.py +0 -0
  45. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/fixtures/compliance.yaml +0 -0
  46. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/locale/de/LC_MESSAGES/django.mo +0 -0
  47. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/locale/de/LC_MESSAGES/django.po +0 -0
  48. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/locale/en/LC_MESSAGES/django.mo +0 -0
  49. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/locale/en/LC_MESSAGES/django.po +0 -0
  50. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/locale/fr/LC_MESSAGES/django.mo +0 -0
  51. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/locale/fr/LC_MESSAGES/django.po +0 -0
  52. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/management/__init__.py +0 -0
  53. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0001_initial_squashed_squashed_0010_alter_checkedobjectincidentrelationship_resolved_by_and_more.py +0 -0
  54. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0011_alter_riskrule_parameters.py +0 -0
  55. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0012_alter_compliancetype_options.py +0 -0
  56. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0013_alter_riskrule_unique_together.py +0 -0
  57. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0014_alter_reviewcompliancetask_year.py +0 -0
  58. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0015_auto_20240103_0957.py +0 -0
  59. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0016_checkedobjectincidentrelationship_report_details_and_more.py +0 -0
  60. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0017_alter_rulebackend_incident_report_template.py +0 -0
  61. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0018_alter_rulecheckedobjectrelationship_unique_together.py +0 -0
  62. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0019_rulegroup_riskrule_activation_date_and_more.py +0 -0
  63. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0020_rename_trigger_content_type_riskcheck_checked_object_content_type_and_more.py +0 -0
  64. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/0021_riskcheck_passive_check.py +0 -0
  65. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/migrations/__init__.py +0 -0
  66. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/__init__.py +0 -0
  67. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/compliance_form.py +0 -0
  68. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/compliance_type.py +0 -0
  69. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/__init__.py +0 -0
  70. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/backend.py +0 -0
  71. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/models/risk_management/dispatch.py +0 -0
  72. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/permissions.py +0 -0
  73. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/__init__.py +0 -0
  74. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/compliance_form.py +0 -0
  75. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/compliance_task.py +0 -0
  76. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/compliance_type.py +0 -0
  77. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/risk_management/__init__.py +0 -0
  78. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/risk_management/incidents.py +0 -0
  79. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/serializers/risk_management/rules.py +0 -0
  80. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tasks.py +0 -0
  81. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/templates/compliance/compliance_form.html +0 -0
  82. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/templates/compliance/review_compliance_task_report.html +0 -0
  83. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/templates/risk_management/incident_notification.html +0 -0
  84. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/templates/risk_management/incident_report.html +0 -0
  85. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/__init__.py +0 -0
  86. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/conftest.py +0 -0
  87. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/mixins.py +0 -0
  88. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/risk_management/__init__.py +0 -0
  89. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/risk_management/models/__init__.py +0 -0
  90. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/risk_management/models/test_backends.py +0 -0
  91. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/risk_management/models/test_checks.py +0 -0
  92. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/risk_management/models/test_rules.py +0 -0
  93. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/signals.py +0 -0
  94. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/test_filters.py +0 -0
  95. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/test_models.py +0 -0
  96. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/test_serializers.py +0 -0
  97. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/tests/tests.py +0 -0
  98. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/urls.py +0 -0
  99. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/__init__.py +0 -0
  100. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/__init__.py +0 -0
  101. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/risk_managment/__init__.py +0 -0
  102. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/risk_managment/checks.py +0 -0
  103. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/risk_managment/incidents.py +0 -0
  104. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/buttons/risk_managment/rules.py +0 -0
  105. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/compliance_form.py +0 -0
  106. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/compliance_task.py +0 -0
  107. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/compliance_type.py +0 -0
  108. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/__init__.py +0 -0
  109. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/compliance_form.py +0 -0
  110. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/compliance_task.py +0 -0
  111. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/compliance_type.py +0 -0
  112. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/risk_managment/__init__.py +0 -0
  113. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/risk_managment/checks.py +0 -0
  114. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/risk_managment/incidents.py +0 -0
  115. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/risk_managment/rules.py +0 -0
  116. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/display/risk_managment/tables.py +0 -0
  117. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/__init__.py +0 -0
  118. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/compliance_form.py +0 -0
  119. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/compliance_task.py +0 -0
  120. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/compliance_type.py +0 -0
  121. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/risk_managment/__init__.py +0 -0
  122. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/risk_managment/checks.py +0 -0
  123. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/risk_managment/incidents.py +0 -0
  124. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/risk_managment/rules.py +0 -0
  125. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/endpoints/risk_managment/tables.py +0 -0
  126. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/menu/__init__.py +0 -0
  127. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/menu/compliance_form.py +0 -0
  128. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/menu/compliance_task.py +0 -0
  129. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/menu/compliance_type.py +0 -0
  130. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/menu/risk_management.py +0 -0
  131. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/risk_management/__init__.py +0 -0
  132. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/risk_management/incidents.py +0 -0
  133. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/risk_management/rules.py +0 -0
  134. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/risk_management/tables.py +0 -0
  135. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/__init__.py +0 -0
  136. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/compliance_form.py +0 -0
  137. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/compliance_task.py +0 -0
  138. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/compliance_type.py +0 -0
  139. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/risk_managment/__init__.py +0 -0
  140. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/risk_managment/checks.py +0 -0
  141. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/risk_managment/incidents.py +0 -0
  142. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/risk_managment/rules.py +0 -0
  143. {wbcompliance-1.54.11 → wbcompliance-1.59.2}/wbcompliance/viewsets/titles/risk_managment/tables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbcompliance
3
- Version: 1.54.11
3
+ Version: 1.59.2
4
4
  Summary: A workbench module for managing compliance.
5
5
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
6
6
  Requires-Dist: wbcore
@@ -12,6 +12,14 @@ class RiskCheckFilterSet(wb_filters.FilterSet):
12
12
  distinct=True,
13
13
  hidden=True,
14
14
  )
15
+ activators = wb_filters.MultipleChoiceContentTypeFilter(
16
+ label="Activators",
17
+ field_name="activators",
18
+ object_id_label="activator_id",
19
+ content_type_label="activator_content_type",
20
+ distinct=True,
21
+ hidden=True,
22
+ )
15
23
  passive_check = wb_filters.BooleanFilter(initial=True, required=True, label="Passive")
16
24
 
17
25
  class Meta:
@@ -0,0 +1,39 @@
1
+ # Generated by Django 5.0.12 on 2025-11-28 09:18
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+ from django.db.models import Exists, OuterRef
6
+
7
+
8
+ def handle_data(apps, schema_editor):
9
+ CheckedObjectIncidentRelationship = apps.get_model('wbcompliance', 'CheckedObjectIncidentRelationship')
10
+ RiskIncident = apps.get_model('wbcompliance', 'RiskIncident')
11
+ CheckedObjectIncidentRelationship.objects.filter(incident__isnull=True).delete()
12
+ RiskIncident.objects.filter(checked_object_relationships__isnull=True).delete()
13
+ RiskIncident.objects.annotate(
14
+ is_precheck=Exists(CheckedObjectIncidentRelationship.objects.filter(incident=OuterRef("id"), rule_check__activator_id__isnull=False))
15
+ ).filter(is_precheck=True).update(precheck=True)
16
+
17
+ class Migration(migrations.Migration):
18
+
19
+ dependencies = [
20
+ ('wbcompliance', '0021_riskcheck_passive_check'),
21
+ ]
22
+
23
+ operations = [
24
+ migrations.AddField(
25
+ model_name='riskincident',
26
+ name='precheck',
27
+ field=models.BooleanField(default=False, help_text='If true, this incident was created during a precheck evaluation', verbose_name='Precheck Incident'),
28
+ ),
29
+ migrations.RunSQL(sql="SET CONSTRAINTS ALL IMMEDIATE;"),
30
+ migrations.RunPython(handle_data),
31
+ migrations.RunSQL(sql="SET CONSTRAINTS ALL DEFERRED;"),
32
+ migrations.AlterField(
33
+ model_name='checkedobjectincidentrelationship',
34
+ name='incident',
35
+ field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE,
36
+ related_name='checked_object_relationships', to='wbcompliance.riskincident'),
37
+ preserve_default=False,
38
+ ),
39
+ ]
@@ -75,7 +75,7 @@ class ComplianceTask(WBModel):
75
75
  @classmethod
76
76
  def get_color_map(cls) -> list:
77
77
  colors = [WBColor.GREEN_LIGHT.value, WBColor.YELLOW_LIGHT.value, WBColor.RED_LIGHT.value]
78
- return [choice for choice in zip(cls, colors)]
78
+ return [choice for choice in zip(cls, colors, strict=False)]
79
79
 
80
80
  title = models.CharField(max_length=255, verbose_name=_("Title"))
81
81
  description = models.TextField(default="", blank=True, verbose_name=_("Description"))
@@ -171,7 +171,7 @@ class ComplianceTaskInstance(models.Model):
171
171
  WBColor.BLUE_LIGHT.value,
172
172
  WBColor.RED_LIGHT.value,
173
173
  ]
174
- return [choice for choice in zip(cls, colors)]
174
+ return [choice for choice in zip(cls, colors, strict=False)]
175
175
 
176
176
  task = models.ForeignKey(
177
177
  on_delete=models.CASCADE,
@@ -235,7 +235,7 @@ class ComplianceAction(models.Model):
235
235
  @classmethod
236
236
  def get_color_map(cls) -> list:
237
237
  colors = [WBColor.GREY.value, WBColor.YELLOW_LIGHT.value, WBColor.GREEN_LIGHT.value]
238
- return [choice for choice in zip(cls, colors)]
238
+ return [choice for choice in zip(cls, colors, strict=False)]
239
239
 
240
240
  title = models.CharField(max_length=255, verbose_name=_("Title"))
241
241
  description = models.TextField(
@@ -310,7 +310,7 @@ class ComplianceEvent(models.Model):
310
310
  @classmethod
311
311
  def get_color_map(cls) -> list:
312
312
  colors = [WBColor.GREY.value, WBColor.BLUE_LIGHT.value]
313
- return [choice for choice in zip(cls, colors)]
313
+ return [choice for choice in zip(cls, colors, strict=False)]
314
314
 
315
315
  type_event = models.CharField(
316
316
  max_length=32,
@@ -414,7 +414,7 @@ class ReviewComplianceTask(ComplianceDocumentMixin, ComplexToStringMixin, WBMode
414
414
  WBColor.YELLOW_LIGHT.value,
415
415
  WBColor.GREEN_LIGHT.value,
416
416
  ]
417
- return [choice for choice in zip(cls, colors)]
417
+ return [choice for choice in zip(cls, colors, strict=False)]
418
418
 
419
419
  class Meta:
420
420
  verbose_name = "Review Compliance Task"
@@ -10,4 +10,4 @@ class IncidentSeverity(models.TextChoices):
10
10
  @classmethod
11
11
  def get_color_map(cls) -> list:
12
12
  colors = [WBColor.GREEN_LIGHT.value, WBColor.YELLOW_LIGHT.value, WBColor.RED_LIGHT.value]
13
- return [choice for choice in zip(cls, colors)]
13
+ return [choice for choice in zip(cls, colors, strict=False)]
@@ -1,6 +1,7 @@
1
1
  from contextlib import suppress
2
+ from datetime import date, timedelta
2
3
  from types import DynamicClassAttribute
3
- from typing import Self
4
+ from typing import Any, Self
4
5
 
5
6
  from celery import shared_task
6
7
  from django.contrib.contenttypes.fields import GenericForeignKey
@@ -8,12 +9,14 @@ from django.contrib.contenttypes.models import ContentType
8
9
  from django.db import models
9
10
  from django.template import Context, Template
10
11
  from django.utils.translation import gettext_lazy as _
12
+ from pandas._libs.tslibs.offsets import BDay
13
+ from psycopg.types.range import DateRange
11
14
  from wbcore.contrib.color.enums import WBColor
12
15
  from wbcore.contrib.icons import WBIcon
13
16
  from wbcore.models import WBModel
14
17
  from wbcore.utils.models import ComplexToStringMixin
15
18
 
16
- from .incidents import CheckedObjectIncidentRelationship
19
+ from .incidents import CheckedObjectIncidentRelationship, RiskIncident, RiskIncidentType
17
20
 
18
21
 
19
22
  class RiskCheckManager(models.Manager):
@@ -99,9 +102,16 @@ class RiskCheck(ComplexToStringMixin, WBModel):
99
102
  @property
100
103
  def needs_incident(self) -> bool:
101
104
  # if the there is a similar checked object relationship, we needs to create a general incident if the check evaluates to a breach
102
- return self.rule.checked_object_relationships.filter(
103
- checked_object_content_type=self.checked_object_content_type, checked_object_id=self.checked_object_id
104
- ).exists()
105
+ return (
106
+ self.rule.checked_object_relationships.filter(
107
+ checked_object_content_type=self.checked_object_content_type, checked_object_id=self.checked_object_id
108
+ ).exists()
109
+ or self.precheck
110
+ )
111
+
112
+ @property
113
+ def precheck(self) -> bool:
114
+ return self.activator_id is not None
105
115
 
106
116
  @property
107
117
  def previous_check(self) -> Self | None:
@@ -127,6 +137,20 @@ class RiskCheck(ComplexToStringMixin, WBModel):
127
137
  self.checked_object_repr = str(self.checked_object)
128
138
  super().save(*args, **kwargs)
129
139
 
140
+ def delete(self, *args, **kwargs):
141
+ incidents = RiskIncident.all_objects.filter(
142
+ id__in=list(
143
+ CheckedObjectIncidentRelationship.objects.filter(rule_check_id=self.id).values_list(
144
+ "incident", flat=True
145
+ )
146
+ )
147
+ )
148
+ super().delete(*args, **kwargs)
149
+ # ensure that incident without subincidents are deleted automatically
150
+ for incident in incidents:
151
+ if not incident.checked_object_relationships.exists():
152
+ incident.delete()
153
+
130
154
  def compute_str(self) -> str:
131
155
  return _("{} - {}").format(
132
156
  self.checked_object,
@@ -164,7 +188,7 @@ class RiskCheck(ComplexToStringMixin, WBModel):
164
188
  # If the check is passive, we aggregated incident per breached object and return it for further processing
165
189
  if self.needs_incident:
166
190
  report = report_template.render(Context({"report_details": incident_result.report_details}))
167
- incident, created = self.rule.get_or_create_incident(
191
+ incident, created = self.get_or_create_incident(
168
192
  self.evaluation_date,
169
193
  incident_result.severity,
170
194
  incident_result.breached_object,
@@ -192,6 +216,68 @@ class RiskCheck(ComplexToStringMixin, WBModel):
192
216
  self.save()
193
217
  return incidents
194
218
 
219
+ def get_or_create_incident(
220
+ self,
221
+ evaluation_date: date,
222
+ incident_severity: "RiskIncidentType",
223
+ breached_object: Any,
224
+ breached_object_repr: str,
225
+ ) -> tuple[RiskIncident, bool]:
226
+ """
227
+ Utility function to get or create incident base on the given breached object
228
+
229
+ Args:
230
+ evaluation_date: The incident date
231
+ incident_severity: The incident severity
232
+ breached_object: The breached object (i.e. the object that triggers the incident)
233
+ breached_object_repr: Its string representation
234
+
235
+ Returns:
236
+ A tuple (incident, is_created)
237
+ """
238
+
239
+ # Consider that if an incident happens one business day in the future or past, it is continue
240
+ date_range = DateRange( # type: ignore
241
+ (evaluation_date - BDay(1)).date(), (evaluation_date + BDay(1)).date(), "[]"
242
+ )
243
+ incidents = RiskIncident.objects.filter(rule=self.rule, date_range__overlap=date_range, precheck=self.precheck)
244
+ if breached_object:
245
+ # If a breached_object is provided, we lookup over it
246
+ incidents = incidents.filter(
247
+ breached_content_type=ContentType.objects.get_for_model(breached_object),
248
+ breached_object_id=breached_object.id,
249
+ )
250
+ else:
251
+ # Otherwise, we lookup over its string representation. Might need to change as this is not very robust
252
+ incidents = incidents.filter(
253
+ breached_object_repr=breached_object_repr,
254
+ )
255
+ created = False
256
+ if incident := incidents.first():
257
+ incident.date_range = DateRange( # type: ignore
258
+ lower=incident.date_range.lower, upper=(evaluation_date + timedelta(days=1))
259
+ )
260
+ incident.severity = incident_severity
261
+ else:
262
+ incident = RiskIncident(
263
+ rule=self.rule,
264
+ date_range=DateRange(lower=evaluation_date, upper=(evaluation_date + timedelta(days=1))), # type: ignore
265
+ breached_content_type=ContentType.objects.get_for_model(breached_object) if breached_object else None,
266
+ breached_object_id=breached_object.id if breached_object else None,
267
+ breached_object_repr=breached_object_repr,
268
+ severity=incident_severity,
269
+ status=RiskIncident.Status.OPEN,
270
+ )
271
+ created = True
272
+
273
+ if self.rule.automatically_close_incident:
274
+ incident.status = RiskIncident.Status.CLOSED
275
+ elif self.precheck:
276
+ incident.status = RiskIncident.Status.IGNORED
277
+ incident.precheck = self.precheck
278
+ incident.save()
279
+ return incident, created
280
+
195
281
  class Meta:
196
282
  verbose_name = "Risk Check"
197
283
  verbose_name_plural = "Risk Checks"
@@ -29,7 +29,6 @@ from wbcore.metadata.configs.buttons import ActionButton, ButtonDefaultColor
29
29
  from wbcore.metadata.configs.display.instance_display.shortcuts import (
30
30
  create_simple_display,
31
31
  )
32
- from wbcore.models import WBModel
33
32
  from wbcore.utils.models import ComplexToStringMixin
34
33
 
35
34
  User: BaseUser = get_user_model()
@@ -109,13 +108,11 @@ class RiskIncidentMixin(models.Model):
109
108
  abstract = True
110
109
 
111
110
 
112
- class CheckedObjectIncidentRelationship(ComplexToStringMixin, RiskIncidentMixin, WBModel):
111
+ class CheckedObjectIncidentRelationship(ComplexToStringMixin, RiskIncidentMixin):
113
112
  incident = models.ForeignKey(
114
113
  to="wbcompliance.RiskIncident",
115
114
  related_name="checked_object_relationships",
116
- null=True,
117
- blank=True,
118
- on_delete=models.SET_NULL,
115
+ on_delete=models.CASCADE,
119
116
  )
120
117
 
121
118
  rule_check = models.ForeignKey(
@@ -229,6 +226,9 @@ class CheckedObjectIncidentRelationship(ComplexToStringMixin, RiskIncidentMixin,
229
226
  models.Index(fields=["incident", "rule_check"]),
230
227
  ]
231
228
 
229
+ def __str__(self) -> str:
230
+ return super().__str__()
231
+
232
232
  def compute_str(self) -> str:
233
233
  return _("{} {} Sub Incident for checked_object {}").format(self.status, self.severity, self.rule_check)
234
234
 
@@ -246,8 +246,12 @@ class CheckedObjectIncidentRelationship(ComplexToStringMixin, RiskIncidentMixin,
246
246
 
247
247
 
248
248
  class RiskIncidentDefaultManager(models.Manager):
249
+ def __init__(self, only_passive_checks: bool = True):
250
+ self.only_passive_checks = only_passive_checks
251
+ super().__init__()
252
+
249
253
  def get_queryset(self):
250
- return (
254
+ qs = (
251
255
  super()
252
256
  .get_queryset()
253
257
  .annotate(
@@ -257,9 +261,12 @@ class RiskIncidentDefaultManager(models.Manager):
257
261
  ignore_until=TruncDate("ignore_until_time"),
258
262
  )
259
263
  )
264
+ if self.only_passive_checks:
265
+ qs = qs.filter(precheck=False)
266
+ return qs
260
267
 
261
268
 
262
- class RiskIncident(ComplexToStringMixin, RiskIncidentMixin, WBModel):
269
+ class RiskIncident(ComplexToStringMixin, RiskIncidentMixin):
263
270
  """
264
271
  Instance defining the incident that has happened during a check initiated by a certain rule
265
272
  """
@@ -295,6 +302,11 @@ class RiskIncident(ComplexToStringMixin, RiskIncidentMixin, WBModel):
295
302
  ignore_duration = models.DurationField(
296
303
  blank=True, null=True, help_text=_("If set, will ignore the forthcoming incidents for the specified duration")
297
304
  )
305
+ precheck = models.BooleanField(
306
+ default=False,
307
+ verbose_name=_("Precheck Incident"),
308
+ help_text="If true, this incident was created during a precheck evaluation",
309
+ )
298
310
 
299
311
  def get_ignore_until_date(self) -> date | None:
300
312
  if self.last_ignored_date and self.ignore_duration:
@@ -365,6 +377,7 @@ class RiskIncident(ComplexToStringMixin, RiskIncidentMixin, WBModel):
365
377
  return dict()
366
378
 
367
379
  objects = RiskIncidentDefaultManager()
380
+ all_objects = RiskIncidentDefaultManager(only_passive_checks=False)
368
381
 
369
382
  class Meta:
370
383
  verbose_name = "Risk Incident"
@@ -382,6 +395,9 @@ class RiskIncident(ComplexToStringMixin, RiskIncidentMixin, WBModel):
382
395
  )
383
396
  ]
384
397
 
398
+ def __str__(self) -> str:
399
+ return super().__str__()
400
+
385
401
  def save(self, *args, **kwargs):
386
402
  if not self.breached_object_repr:
387
403
  self.breached_object_repr = str(self.breached_content_object)
@@ -97,8 +97,8 @@ class RiskCheckMixin(models.Model):
97
97
  )[0]
98
98
  if asynchronously:
99
99
  transaction.on_commit(
100
- lambda: evaluate_as_task.delay(
101
- check.id, *dto, override_incident=True, ignore_informational_threshold=True
100
+ lambda check_id=check.id: evaluate_as_task.delay(
101
+ check_id, *dto, override_incident=True, ignore_informational_threshold=True
102
102
  )
103
103
  )
104
104
  else:
@@ -22,7 +22,6 @@ from django.template.loader import get_template
22
22
  from django.utils.functional import cached_property
23
23
  from django.utils.translation import gettext_lazy as _
24
24
  from pandas.tseries.offsets import BDay
25
- from psycopg.types.range import DateRange
26
25
  from rest_framework.reverse import reverse
27
26
  from wbcore.contrib.directory.models import Person
28
27
  from wbcore.contrib.guardian.models.mixins import PermissionObjectModelMixin
@@ -33,7 +32,7 @@ from wbcore.utils.rrules import convert_rrulestr_to_dict
33
32
 
34
33
  from .backend import AbstractRuleBackend
35
34
  from .checks import RiskCheck
36
- from .incidents import CheckedObjectIncidentRelationship, RiskIncident, RiskIncidentType
35
+ from .incidents import CheckedObjectIncidentRelationship, RiskIncident
37
36
 
38
37
  User: BaseUser = get_user_model()
39
38
 
@@ -47,6 +46,9 @@ class RuleGroup(models.Model):
47
46
  self.name = self.key.title()
48
47
  super().save(*args, **kwargs)
49
48
 
49
+ def __str__(self) -> str:
50
+ return self.name
51
+
50
52
  @classmethod
51
53
  def get_representation_endpoint(cls) -> str:
52
54
  return "wbcompliance:rulegrouprepresentation-list"
@@ -60,7 +62,7 @@ class RuleGroup(models.Model):
60
62
  return "{{name}}"
61
63
 
62
64
 
63
- class RuleCheckedObjectRelationship(ComplexToStringMixin, models.Model):
65
+ class RuleCheckedObjectRelationship(ComplexToStringMixin):
64
66
  rule = models.ForeignKey(
65
67
  to="wbcompliance.RiskRule", related_name="checked_object_relationships", on_delete=models.CASCADE
66
68
  )
@@ -261,7 +263,7 @@ class RuleBackend(models.Model):
261
263
  return "{{name}}"
262
264
 
263
265
 
264
- class RuleThreshold(ComplexToStringMixin, models.Model):
266
+ class RuleThreshold(ComplexToStringMixin):
265
267
  """
266
268
  Represent the list of threshold and its associated severity link to a certain rule
267
269
  """
@@ -447,7 +449,7 @@ class RiskRule(PermissionObjectModelMixin, WBModel):
447
449
  def checked_object_representation(self) -> str:
448
450
  try:
449
451
  backend_class = self.rule_backend.backend_class
450
- checked_object_repr = getattr(backend_class, "OBJECT_FIELD_NAME").title()
452
+ checked_object_repr = backend_class.OBJECT_FIELD_NAME.title()
451
453
  except AttributeError:
452
454
  checked_object_repr = "Object"
453
455
  return checked_object_repr
@@ -508,69 +510,6 @@ class RiskRule(PermissionObjectModelMixin, WBModel):
508
510
 
509
511
  return permissions
510
512
 
511
- def get_or_create_incident(
512
- self,
513
- evaluation_date: date,
514
- incident_severity: "RiskIncidentType",
515
- breached_object: Any,
516
- breached_object_repr: str,
517
- ) -> tuple[RiskIncident, bool]:
518
- """
519
- Utility function to get or create incident base on the given breached object
520
-
521
- Args:
522
- rule: The rule this incident happens
523
- evaluation_date: The incident date
524
- incident_severity: The incident severity
525
- breached_object: The breached object (i.e. the object that triggers the incident)
526
- breached_object_repr: Its string representation
527
-
528
- Returns:
529
- A tuple (incident, is_created)
530
- """
531
-
532
- # Consider that if an incident happens one business day in the future or past, it is continue
533
- date_range = DateRange( # type: ignore
534
- (evaluation_date - BDay(1)).date(), (evaluation_date + BDay(1)).date(), "[]"
535
- )
536
- incidents = RiskIncident.objects.filter(rule=self, date_range__overlap=date_range)
537
- if breached_object:
538
- # If a breached_object is provided, we lookup over it
539
- incidents = incidents.filter(
540
- breached_content_type=ContentType.objects.get_for_model(breached_object),
541
- breached_object_id=breached_object.id,
542
- )
543
- else:
544
- # Otherwise, we lookup over its string representation. Might need to change as this is not very robust
545
- incidents = incidents.filter(
546
- breached_object_repr=breached_object_repr,
547
- )
548
- if incident := incidents.first():
549
- incident.date_range = DateRange(
550
- lower=incident.date_range.lower, upper=(evaluation_date + timedelta(days=1))
551
- ) # type: ignore
552
- if self.automatically_close_incident:
553
- incident.status = RiskIncident.Status.CLOSED
554
- incident.save()
555
- return incident, False
556
- else:
557
- return (
558
- RiskIncident.objects.create(
559
- rule=self,
560
- date_range=DateRange(lower=evaluation_date, upper=(evaluation_date + timedelta(days=1))), # type: ignore
561
- breached_content_type=ContentType.objects.get_for_model(breached_object)
562
- if breached_object
563
- else None,
564
- breached_object_id=breached_object.id if breached_object else None,
565
- breached_object_repr=breached_object_repr,
566
- severity=incident_severity,
567
- status=RiskIncident.Status.CLOSED
568
- if self.automatically_close_incident
569
- else RiskIncident.Status.OPEN,
570
- ),
571
- True,
572
- )
573
-
574
513
  def notify(self, evaluation_date: date):
575
514
  """
576
515
  Create the notification for this rule relationship and all implied persons
@@ -659,7 +598,7 @@ class RiskRule(PermissionObjectModelMixin, WBModel):
659
598
  def pre_save_risk_threshold(sender, instance, **kwargs):
660
599
  with suppress(RuleThreshold.DoesNotExist):
661
600
  pre_save_instance = RuleThreshold.objects.get(id=instance.id)
662
- RiskIncident.objects.filter(rule=instance.rule, severity=pre_save_instance.severity).update(
601
+ RiskIncident.all_objects.filter(rule=instance.rule, severity=pre_save_instance.severity).update(
663
602
  severity=instance.severity
664
603
  )
665
604
  CheckedObjectIncidentRelationship.objects.filter(
@@ -669,7 +608,7 @@ def pre_save_risk_threshold(sender, instance, **kwargs):
669
608
 
670
609
  @receiver(pre_delete, sender="wbcompliance.RuleThreshold")
671
610
  def pre_delete_risk_threshold(sender, instance, **kwargs):
672
- RiskIncident.objects.filter(rule=instance.rule, severity=instance.severity).delete()
611
+ RiskIncident.all_objects.filter(rule=instance.rule, severity=instance.severity).delete()
673
612
  CheckedObjectIncidentRelationship.objects.filter(incident__rule=instance.rule, severity=instance.severity).delete()
674
613
 
675
614
 
@@ -17,7 +17,7 @@ class RiskCheckModelSerializer(wb_serializers.ModelSerializer):
17
17
  _rule = RiskRuleRepresentationSerializer(source="rule")
18
18
  _checked_object_content_type = ContentTypeRepresentationSerializer(source="checked_object_content_type")
19
19
 
20
- incident_reports = wb_serializers.TextField()
20
+ incident_reports = wb_serializers.TextField(read_only=True)
21
21
  status_icon = IconSelectField(read_only=True)
22
22
 
23
23
  @wb_serializers.register_resource()
@@ -1,6 +1,15 @@
1
1
  from collections import defaultdict
2
2
 
3
- from django.db.models.signals import *
3
+ from django.db.models.signals import (
4
+ post_delete,
5
+ post_init,
6
+ post_migrate,
7
+ post_save,
8
+ pre_delete,
9
+ pre_init,
10
+ pre_migrate,
11
+ pre_save,
12
+ )
4
13
 
5
14
 
6
15
  class DisableSignals(object):
@@ -50,7 +59,7 @@ class DisableSignals(object):
50
59
  # self.disconnect(signal)
51
60
 
52
61
 
53
- class temp_disconnect_signal:
62
+ class TempDisconnectSignal:
54
63
  """Temporarily disconnect a model from a signal"""
55
64
 
56
65
  def __init__(self, signal, receiver, sender, dispatch_uid=None):
@@ -72,11 +81,3 @@ class temp_disconnect_signal:
72
81
  sender=self.sender,
73
82
  dispatch_uid=self.dispatch_uid,
74
83
  )
75
-
76
-
77
- class DisableSignalsNotification(temp_disconnect_signal):
78
- def __init__(self, dispatch_uid=None):
79
- self.signal = post_save
80
- self.receiver = post_create_notification
81
- self.sender = Notification
82
- self.dispatch_uid = dispatch_uid
@@ -85,7 +85,7 @@ class TestRiskIncidentFactory:
85
85
  (fake.date_object(), False),
86
86
  ],
87
87
  )
88
- def test_get_or_create_incident(self, risk_rule, risk_incident_type, evaluation_date, is_breached_object):
88
+ def test_get_or_create_incident(self, risk_check, risk_incident_type, evaluation_date, is_breached_object):
89
89
  if is_breached_object:
90
90
  breached_object = get_anonymous_user()
91
91
  breached_object_repr = str(get_anonymous_user())
@@ -94,11 +94,11 @@ class TestRiskIncidentFactory:
94
94
  breached_object_repr = fake.text(max_nb_chars=125)
95
95
 
96
96
  assert not RiskIncident.objects.exists()
97
- risk_rule.get_or_create_incident(evaluation_date, risk_incident_type, breached_object, breached_object_repr)
97
+ risk_check.get_or_create_incident(evaluation_date, risk_incident_type, breached_object, breached_object_repr)
98
98
  if breached_object:
99
99
  assert (
100
100
  RiskIncident.objects.filter(
101
- rule=risk_rule,
101
+ rule=risk_check.rule,
102
102
  date_range__contains=evaluation_date,
103
103
  breached_content_type=ContentType.objects.get_for_model(breached_object),
104
104
  breached_object_id=breached_object.id,
@@ -108,19 +108,21 @@ class TestRiskIncidentFactory:
108
108
  else:
109
109
  assert (
110
110
  RiskIncident.objects.filter(
111
- rule=risk_rule, date_range__contains=evaluation_date, breached_object_repr=breached_object_repr
111
+ rule=risk_check.rule,
112
+ date_range__contains=evaluation_date,
113
+ breached_object_repr=breached_object_repr,
112
114
  ).count()
113
115
  == 1
114
116
  )
115
117
  assert RiskIncident.objects.count() == 1
116
118
  # Check if we reevaluate this incident one business day later, the incident is consider the same
117
- risk_rule.get_or_create_incident(
119
+ risk_check.get_or_create_incident(
118
120
  (evaluation_date + BDay(1)).date(), risk_incident_type, breached_object, breached_object_repr
119
121
  )
120
122
  assert RiskIncident.objects.count() == 1
121
123
 
122
124
  # Check if we reevaluate this incident three business day later, the incident is consider the discontinue, and then a new incident is created
123
- risk_rule.get_or_create_incident(
125
+ risk_check.get_or_create_incident(
124
126
  (evaluation_date + BDay(3)).date(), risk_incident_type, breached_object, breached_object_repr
125
127
  )
126
128
 
@@ -135,7 +137,7 @@ class TestRiskIncidentFactory:
135
137
  risk_incident.save()
136
138
  assert risk_incident.status == RiskIncident.Status.RESOLVED
137
139
  new_evaluation_date = subincident.rule_check.evaluation_date + timedelta(days=1)
138
- incident, created = risk_incident.rule.get_or_create_incident(
140
+ incident, created = subincident.rule_check.get_or_create_incident(
139
141
  new_evaluation_date,
140
142
  subincident.severity,
141
143
  risk_incident.breached_content_object,
@@ -342,7 +342,7 @@ class TestSpecificViewsets(UserTestMixin):
342
342
  assert response.data.get("results")
343
343
 
344
344
  @pytest.mark.parametrize("mvs", [ComplianceTaskMatrixPandasViewSet])
345
- def test_PandasAPIView(self, mvs, compliance_task_factory, compliance_task_instance_factory):
345
+ def test_pandasapiview(self, mvs, compliance_task_factory, compliance_task_instance_factory):
346
346
  request = APIRequestFactory().get("")
347
347
  request.user = get_or_create_superuser()
348
348
 
@@ -20,9 +20,9 @@ class AbstractComplianceDocumentButtonConfig:
20
20
  return bt.ActionButton(
21
21
  method=RequestType.PATCH,
22
22
  key="regenerate_document",
23
- action_label=_("Regenerate Document"),
24
- title=_("Regenerate Document"),
25
- label=_("Regenerate Document"),
23
+ action_label=_("PDF Document is being generated"),
24
+ title=_("Generate PDF Document"),
25
+ label=_("Generate PDF Document"),
26
26
  icon=WBIcon.DOCUMENT.icon,
27
27
  serializer=ComplianceDocumentSerializer,
28
28
  instance_display=create_simple_display([["send_email"]]),