From 9d02badf1413b6b305165d0be4929169cb165ba2 Mon Sep 17 00:00:00 2001 From: Kevin Heyer Date: Thu, 11 Sep 2025 10:22:20 +0200 Subject: [PATCH] feat: Add risk matrix view and related functionality - Implemented a new view for the risk matrix, allowing users to visualize risks based on their impact and likelihood. - Added filters for category, asset, and process in the risk listing view. - Enhanced risk listing template to include new filters and improved layout. - Introduced new CSS variables for better color management in the design. - Updated existing template tags to support new functionalities, including score background class mapping. - Modified existing risk listing to display residual risk details alongside gross risk. - Added new risk matrix HTML template with tabbed interface for gross and net risk views. --- db.sqlite3 | Bin 266240 -> 266240 bytes locale/de/LC_MESSAGES/django.mo | Bin 8412 -> 11765 bytes locale/de/LC_MESSAGES/django.po | 396 +++++++++++++++++++++++------- locale/en/LC_MESSAGES/django.po | 387 ++++++++++++++++++++++------- risks/templatetags/risk_extras.py | 40 ++- risks/urls.py | 1 + risks/views.py | 63 ++++- static/css/design.css | 7 +- templates/base.html | 2 + templates/risks/list_risks.html | 227 ++++++++++------- templates/risks/risk_matrix.html | 185 ++++++++++++++ 11 files changed, 1038 insertions(+), 270 deletions(-) create mode 100644 templates/risks/risk_matrix.html diff --git a/db.sqlite3 b/db.sqlite3 index f3537cd717e17f2fd88f83f04fea1869ee3f37d5..f1189600f3eefc17d3a8b1fcd9e592d2d1310f63 100644 GIT binary patch delta 662 zcmZvYO-~b16o&7eJHxaCy{9k*5(FJTk_HHLriHds4W^=rHBpq{$~0KhNoZ)SmLiFU z1mYjSAh}Drb|oQUT=~IH-P!KYHYt7(YP6FocB3%dG}o2y>I{* z?{yC2;>y)a{f#;;APIGJ5EHZUBxzfG)qlCcdq^Q?oD2`+&bu+69rNKwc@@4Q35p1Q zz(;rsub~LV)#kaUz=~z8X$T>iM--h-rvl$NT0-y>YOu}W5|lXl0$6QX5Lx9j_B3<0 z0U<&-%||b^Gr5KbaJwC;;SIV63bL1V{l*`N7lxK(;k*Dte9IPC=dl-br7XjLk zsML{CcRg=<*jQRMv`wXTegd~3A{E*iUh*_Av0ha zp{#W)WzCFdt(()Std+D&lR}t?ywmn#!u@{jh9KsNPAg1ylekE?Xl2#VZ~ram5Oj%P zEconC2Z`FO!gsD+-qrpBRd@$wDD%QE|7mZYB%Ut5SK707L7l?|1UnqvgbgS_fx}OL z6-J1*`^w$eJ0nC{@Hmup1U>XVRb6URzNxn{qpWk&2MU?*@v`nWBrGMZw3TDesw8&o zLgu>FePnhvF8g)=V4rUG8T+imx<2R++LCb$-qT)gZ+qU>TNsd!OGr9Ma_L3+RAWfvz(Byk!NK4_V+cdBi8{Do z+?*iNoRRw{V4859)KyI!yq)}L6RV4tNCl$_kl>+pfAZw_{MP;Sx}V-S58-BC0mA2Z zZn%~!Ll$Gby*LeOX&HN-eQrKPBliy^qcnR8-1o) zHM91bRILGy6Bl3t=R%mKnP{VRMkn-#TJ%H1b&b9y)!ODlecgv_CWfGK#15UKer2@a zQgF2eALZs^SBD%rg(l{xP%-$t7VOKQK>Xz*X}_?vzbEx3xntFZ^PTvybmW zhIzx6#{s||0(D0i;YPzj4;|mwr)1->IP|z5CzPyoT9+f%<~8q*VKWkkJtx zYP3n8DAe#PQsDwRMvMyvCJ&AlFtKsYuu6OVHX-Ge0)@Z<0+ztnmJJFi#W~ICLYNLC1FS~m! z3)g{=C$!;FLP9Wv(BMJ{(9#AInzZSR$aLC}&`wL5PDx5=+C17!rwN@-+DY5rfA{R( zy^?H_?C86{J@0eA^PTUUv-^@_hnQmGclh4n75shX>)C;h)*~uR+!S z4V(TgsB+(fYUjV;nehKC&pbIxztnOSJdN~GcsiW0@weLeyP?`YV8c(tQwYz%1@Iei z3H%OJ|4t@RJPV!(FN7+8sg1u1N_DF(H$(OFMyPSuAYFADmiu5o;X9$m`2t)DzY3NA zXHfk<^3<$9XF>JnBB=b^pvJcgs@=%)y-@wS1O6(!7gCk;1XTNfV)=FWR>J=X&w&4F zy#0_Q=NcOxh3elfC_Rl^2G9_mhDX5%pz=Ko)!zfK2R;eUfiFY#=XI!g zKbl6>?kSc%P~*SaavRjR0w}#sLD|hA+k~wsB!!?RDG{N+0}O}UxymUu^69t8dSaKLD|hhcq#0I@(Wei2R{Zi z4_~qT8dN(k!@(mQ=T)frI-kkwhuh&h;76h2Uw|6VPodh=q^td7pz1jls=wzzwSNIr zzU@%@+y&(~no#+tpzP~58-EX!-u6T3;W4Ok&)W2_K-uj}5EVE-g3`bQA@M^+8wc+LGXZfyys&_kF4y#b}_7GeQ4?yYdZ!Q1H zrvDhKo&Sci(^J@_wQk=I74C-`?;3~+J2yhv-L&PMP~-g!)cSb}N-r}|<9-QB4=+R6 z*{e|f`2p0tpYirA-`k+Ze*s(uFNSo<*#*_^tx)yvx8Y}?=JiDz{vkAkkG?Q#?;@yv zErsgGl~CgxgzDeB;E~XSntvbSGMw9>+I!H(KL@3Uzk-_o*P!}w#6?-T)1ma$2bDeu zrLT8GwKoOT-d*r$xF4!tk3g-j&q2-8D^T|NT^p9uRk`D!@}CAZzvtNaOQ7_6xec#` zYJUW3e{I4Z_&%uldII*sFWdAVK(+rf8(vIijejLv1b0G~igP=Zo<9rKpFf8h*WW

9@IR%>EcYEXF-j3k>wRo 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 JT)?Ev{|6G&Y*PRL delta 3239 zcmY+_3v7*N9LMpeb=y$AsOvd1P% z7)wlTA&ZsGF0xB!u~|a0v1LX=G7>`AhAffU_jmdjd(!{;JnwVf_j&%$if;egJuV6pnaH(_0F%b1J@T({r-sw z_z>eTHj1RME9Nr3$)%!>=Q$T6eMeSKjHe89cMJ1Ai>L?R6&;ZnZL$MDQpjNEP zeZR(izY*0>t^0l_YMkAuL=K}83w=sO9iKvSFyA{bp$58xTH^cI4C+ck(j=Fy|YD=fM_W7ulSc01QO4M_;;qTf1T~u^P-o}pjxx4-a)!}{A-be6m zGPdzh9S%axY$Pgy)y_?*=eDB~-R;`?6yY`!?_r5v59@>)jsG0RgC0u}-`DoPPoQzt*8st&4&0W8XdhSRk)?W{N zLWB0E5tZ>dcf&>0K-W=A+>(QmiYcfwGn5Z?Gz*n*3F<9ciP^XfwbvhFSNs}>;dRu? zXN2foOWYH+0{u`+GZ8h@GE`!#QKxsaYqzfb0BQw~qGoyqbyj{vC3Y2+*lpBAmAzJ~ zHRfX|hDvykQ8&y)-QYuYSmxR*QKx?mY9)4}W_AEIlVhmFPonnvf@{BxO5_3NV>7Sm zH6Dr2>-}FvMIC&En&}s)j(GHqNS6!_K786Q%9-%EBL-O3*(22$LB0UOG#G$E34eqR_7)Ry81HgPH06QmkcVJ@n~WOQAUg)CK25T zm9LNxv#`e1 z$2c`+Hqny^>5vT}R9+-j5I!P{SVE}e5(C1uCm&T*dlBVNn!^9n^SGW$tbD3n)q#Xw z5SLU~kartpx z_5y!tWtHEn@cRPQRsKMZ&5R#n1M$=BKk-R6FX5q`)IK|5qCe$Wbqp_{iZV2d--8tOB$BJ9nsW9*2`WLuWG&PHU-vpcf(*g@S2?U3wv pYqJX)l5$ca?8n`s?D+1fVIOo)ws}1Y?adzfc2v)UHoDife*rALS|R`d diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 3f16f2b..fcd72b8 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: wira-risk-management\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-09-10 13:44+0200\n" +"POT-Creation-Date: 2025-09-11 10:17+0200\n" "PO-Revision-Date: 2025-09-09 13:45+0200\n" "Last-Translator: Kevin Heyer \n" "Language-Team: German\n" @@ -22,12 +22,12 @@ msgstr "Admin" #: risks/admin.py:15 risks/models.py:36 templates/risks/dashboard.html:75 #: templates/risks/dashboard.html:80 templates/risks/dashboard.html:85 -#: templates/risks/list_risks.html:76 +#: templates/risks/list_risks.html:97 msgid "Risks" msgstr "Risiken" -#: risks/admin.py:16 risks/models.py:190 templates/base.html:36 -#: templates/risks/list_risks.html:37 +#: risks/admin.py:16 risks/models.py:190 templates/base.html:38 +#: templates/risks/list_risks.html:31 msgid "Controls" msgstr "Maßnahmen" @@ -39,8 +39,8 @@ msgstr "Restrisiken" msgid "Reviews" msgstr "Prüfung" -#: risks/admin.py:19 risks/models.py:258 templates/base.html:37 -#: templates/risks/item_risk.html:248 +#: risks/admin.py:19 risks/models.py:258 templates/base.html:39 +#: templates/risks/item_risk.html:252 msgid "Incidents" msgstr "Vorfälle" @@ -48,13 +48,13 @@ msgstr "Vorfälle" msgid "Users" msgstr "Benutzer" -#: risks/admin.py:133 risks/models.py:302 templates/risks/item_risk.html:287 +#: risks/admin.py:133 risks/models.py:326 templates/risks/item_risk.html:291 msgid "User" msgstr "Benutzer" #: risks/admin.py:139 msgid "Message" -msgstr "" +msgstr "Nachricht" #: risks/admin.py:147 msgid "Mark selected as read" @@ -76,9 +76,10 @@ msgstr "%(n)d Benachrichtigungen wurden als ungelesen Markiert" #: risks/admin.py:157 msgid "Mark selected as sent" -msgstr "" +msgstr "Auswahl als gelesen markieren" #: risks/admin.py:160 +#, python-format msgid "%(n)d notifications marked as sent." msgstr "%(n)d Benachrichtigungen wurden als gelesen Markiert" @@ -87,18 +88,23 @@ msgid "Mark selected as unsent" msgstr "Auswahl als ungesendet markieren" #: risks/admin.py:165 +#, python-format msgid "%(n)d notifications marked as unsent." msgstr "%(n)d Benachrichtigungen wurden als gelesen Markiert" -#: risks/admin.py:177 +#: risks/admin.py:182 +msgid "Extra recipients" +msgstr "Zusätzliche Empfänger" + +#: risks/admin.py:190 msgid "SSO Information" msgstr "SSO-Informationen" -#: risks/admin.py:186 +#: risks/admin.py:199 msgid "Risks Owned" msgstr "Eigene Risiken" -#: risks/admin.py:190 +#: risks/admin.py:203 msgid "Controls Responsible" msgstr "Verantwortlich für Maßnahmen" @@ -107,17 +113,17 @@ msgid "Risk Management" msgstr "Risikomanagement" #: risks/forms.py:9 risks/forms.py:16 risks/forms.py:23 risks/models.py:73 -#: templates/risks/item_risk.html:64 templates/risks/item_risk.html:202 -#: templates/risks/item_risk.html:256 +#: templates/risks/item_risk.html:64 templates/risks/item_risk.html:206 +#: templates/risks/item_risk.html:260 msgid "Status" msgstr "Status" -#: risks/forms.py:30 risks/models.py:42 templates/risks/item_risk.html:177 +#: risks/forms.py:30 risks/models.py:42 templates/risks/item_risk.html:181 msgid "Review required" msgstr "Prüfung nötig" #: risks/models.py:35 templates/risks/item_risk.html:11 -#: templates/risks/list_risks.html:18 templates/risks/list_risks.html:83 +#: templates/risks/list_risks.html:18 templates/risks/list_risks.html:110 msgid "Risk" msgstr "Risiko" @@ -182,7 +188,7 @@ msgid "Availability" msgstr "Verfügbarkeit" #: risks/models.py:64 risks/models.py:200 risks/models.py:265 -#: templates/risks/item_risk.html:201 +#: templates/risks/item_risk.html:205 msgid "Title" msgstr "Titel" @@ -191,15 +197,17 @@ msgid "Description" msgstr "Beschreibung" #: risks/models.py:66 templates/risks/item_risk.html:53 +#: templates/risks/list_risks.html:57 msgid "Asset" msgstr "Asset" #: risks/models.py:67 templates/risks/item_risk.html:54 +#: templates/risks/list_risks.html:70 msgid "Process" msgstr "Prozess" #: risks/models.py:68 templates/risks/item_risk.html:55 -#: templates/risks/list_risks.html:85 +#: templates/risks/list_risks.html:44 templates/risks/list_risks.html:112 msgid "Category" msgstr "Kategorie" @@ -211,6 +219,10 @@ msgstr "Erstellt am" msgid "Updated at" msgstr "Aktualisiert am" +#: risks/models.py:71 +msgid "Effects" +msgstr "Auswirkungen" + #: risks/models.py:133 msgid "Residual Risk" msgstr "Restrisiko" @@ -251,7 +263,7 @@ msgstr "Audit-Log" msgid "Auditlogs" msgstr "Audit-Logs" -#: risks/models.py:257 templates/risks/item_risk.html:255 +#: risks/models.py:257 templates/risks/item_risk.html:259 msgid "Incident" msgstr "Vorfall" @@ -267,15 +279,120 @@ msgstr "Meldedatum" msgid "Reported by" msgstr "Gemeldet von" -#: risks/models.py:279 +#: risks/models.py:279 risks/utils.py:119 msgid "Notification" msgstr "Benachrichtigung" -#: risks/models.py:280 templates/base.html:78 +#: risks/models.py:280 templates/base.html:80 #: templates/risks/notifications.html:4 msgid "Notifications" msgstr "Nachrichten" +#: risks/models.py:295 +msgid "Risk created" +msgstr "Risiko erstellt" + +#: risks/models.py:296 +msgid "Risk updated" +msgstr "Risiko Aktualisiert" + +#: risks/models.py:297 +msgid "Risk deleted" +msgstr "Risiko gelöscht" + +#: risks/models.py:298 +msgid "Risk review required" +msgstr "Risikoprüfung nötig" + +#: risks/models.py:299 +msgid "Risk review completed" +msgstr "Risikoprüfung Abgeschlossen" + +#: risks/models.py:301 +msgid "Control created" +msgstr "Maßnahme erstellt" + +#: risks/models.py:302 +msgid "Control updated" +msgstr "Maßnahme Aktualisiert" + +#: risks/models.py:303 +msgid "Control deleted" +msgstr "Maßnahme '{title}' gelöscht" + +#: risks/models.py:305 +msgid "Residual created" +msgstr "Restrisiko erstellt" + +#: risks/models.py:306 +msgid "Residual updated" +msgstr "Restrisiko Aktualisiert" + +#: risks/models.py:307 +msgid "Residual deleted" +msgstr "Restrisiko gelöscht" + +#: risks/models.py:308 +msgid "Residual review required" +msgstr "Restrisikoprüfung nötig" + +#: risks/models.py:309 +msgid "Residual review completed" +msgstr "Restrisiko geprüft" + +#: risks/models.py:311 +msgid "Incident created" +msgstr "Vorfall erstellt" + +#: risks/models.py:312 +msgid "Incident updated" +msgstr "Vorfall Aktualisiert" + +#: risks/models.py:313 +msgid "Incident deleted" +msgstr "Vorfall gelöscht" + +#: risks/models.py:315 +msgid "User created" +msgstr "Benutzer erstellt" + +#: risks/models.py:316 +msgid "User deleted" +msgstr "Benutzer gelöscht" + +#: risks/models.py:371 +msgid "Notification rule" +msgstr "Benachrichtigungsregel" + +#: risks/models.py:372 +msgid "Notification rules" +msgstr "Benachrichtigungsregeln" + +#: risks/models.py:375 +msgid "Event" +msgstr "Aktion" + +#: risks/models.py:380 +msgid "Show in app" +msgstr "Zeige in der WebApp" + +#: risks/models.py:381 +msgid "Send via email" +msgstr "Sende via E-Mail" + +#: risks/models.py:385 +msgid "Send to owner/responsible/reporter (if available)" +msgstr "Sende an Risikoeigner/Verantwortliche/Melder (Wenn vorhanden)" + +#: risks/models.py:389 +msgid "Send to all staff" +msgstr "Sende an alle App-Mitarbeiter" + +#: risks/models.py:393 +msgid "Extra recipients (emails, comma or newline separated)" +msgstr "" +"Zusätzliche Empfänger (E-Mails, durch Komma oder Zeilenumbruch getrennt)" + #: risks/signals.py:57 #, python-brace-format msgid "User '{u}' created" @@ -291,13 +408,13 @@ msgstr "Benutzer '{u}' löschte" msgid "Risk '{title}' {state}" msgstr "Risiko '{title}' {state}" -#: risks/signals.py:72 risks/signals.py:147 risks/signals.py:240 -#: risks/signals.py:296 +#: risks/signals.py:72 risks/signals.py:166 risks/signals.py:212 +#: risks/signals.py:275 risks/signals.py:369 risks/signals.py:411 msgid "created" msgstr "erstellt" -#: risks/signals.py:72 risks/signals.py:147 risks/signals.py:240 -#: risks/signals.py:296 +#: risks/signals.py:72 risks/signals.py:166 risks/signals.py:212 +#: risks/signals.py:275 risks/signals.py:369 risks/signals.py:411 msgid "updated" msgstr "Aktualisiert" @@ -306,77 +423,137 @@ msgstr "Aktualisiert" msgid "Risk '{title}' deleted" msgstr "Risiko '{title}' gelöscht" -#: risks/signals.py:145 +#: risks/signals.py:117 +#, python-brace-format +msgid "Risk created: {t}" +msgstr "Risiko erstellt: {t}" + +#: risks/signals.py:123 +#, python-brace-format +msgid "Risk updated: {t}" +msgstr "Risiko Aktualisiert: {t}" + +#: risks/signals.py:143 +#, python-brace-format +msgid "Risk deleted: {t}" +msgstr "Risiko gelöscht: {t}" + +#: risks/signals.py:164 #, python-brace-format msgid "Control '{title}' {state}" msgstr "Maßnahme '{title}' {state}" -#: risks/signals.py:154 +#: risks/signals.py:173 #, python-brace-format msgid "Control '{title}' deleted" msgstr "Maßnahme '{title}' gelöscht" #: risks/signals.py:211 #, python-brace-format +msgid "Control {event}: {t}" +msgstr "Maßnahme {event}: {t}" + +#: risks/signals.py:231 +#, python-brace-format +msgid "Control deleted: {t}" +msgstr "Maßnahme gelöscht: {t}" + +#: risks/signals.py:246 +#, python-brace-format msgid "Review required for risk '{t}' due to control change" msgstr "Prüfung nötig für: '{t}', da Maßnahmen geändert wurden" -#: risks/signals.py:230 +#: risks/signals.py:265 #, python-brace-format msgid "Review required for risk '{t}'" msgstr "Prüfung benötigt für Risiko '{t}'" -#: risks/signals.py:235 +#: risks/signals.py:270 #, python-brace-format msgid "Review completed for risk '{t}'" msgstr "Prüfung Abgeschlossen für Risiko '{t}'" -#: risks/signals.py:239 +#: risks/signals.py:274 #, python-brace-format msgid "Residual risk {state} for '{t}'" msgstr "Restrisiko {state} für '{t}'" -#: risks/signals.py:244 +#: risks/signals.py:279 #, python-brace-format msgid "Residual risk deleted for '{t}'" msgstr "Restrisiko für '{t}' gelöscht" -#: risks/signals.py:296 +#: risks/signals.py:316 +#, python-brace-format +msgid "Residual created for risk: {t}" +msgstr "Restrisiko erstellt für das Risiko: {t}" + +#: risks/signals.py:328 +#, python-brace-format +msgid "Residual review required for risk: {t}" +msgstr "Restrisikoprüfung benötigt für Risiko {t}" + +#: risks/signals.py:334 +#, python-brace-format +msgid "Residual review completed for risk: {t}" +msgstr "Restrisikoprüfung Abgeschlossen für Risiko {t}" + +#: risks/signals.py:340 +#, python-brace-format +msgid "Residual updated for risk: {t}" +msgstr "Restrisiko Aktualisiert für das Risiko: {t}" + +#: risks/signals.py:358 +#, python-brace-format +msgid "Residual deleted for risk: {t}" +msgstr "Restrisiko gelöscht für das Risiko: {t}" + +#: risks/signals.py:369 #, python-brace-format msgid "Incident '{t}' {s}" -msgstr "Vorfälle '{t}' {s}" +msgstr "Vorfall '{t}' {s}" -#: risks/signals.py:301 +#: risks/signals.py:374 #, python-brace-format msgid "Incident '{t}' deleted" msgstr "Vorfall '{t}' gelöscht" -#: risks/utils.py:48 +#: risks/signals.py:410 +#, python-brace-format +msgid "Incident {event}: {t}" +msgstr "Vorfall {event}: {t}" + +#: risks/signals.py:442 +#, python-brace-format +msgid "Incident deleted: {t}" +msgstr "Vorfall gelöscht: {t}" + +#: risks/utils.py:53 risks/utils.py:67 #, python-brace-format msgid "Follow-up reached: review required for risk '{t}'" msgstr "Wiedervorlagedatum erreicht: Prüfung nötig für Risiko '{t}'" -#: risks/views.py:315 +#: risks/views.py:345 msgid "Notification marked as read." msgstr "Nachricht als gelesen markiert" -#: risks/views.py:323 +#: risks/views.py:353 msgid "All notifications marked as read." msgstr "Alle Benachrichtigungen wurden als gelesen Markiert" -#: risks/views.py:340 +#: risks/views.py:370 msgid "Risk status updated." msgstr "Risikostatus Aktualisiert" -#: risks/views.py:354 +#: risks/views.py:384 msgid "Control status updated." msgstr "Maßnahmenstatus Aktualisiert" -#: risks/views.py:368 +#: risks/views.py:398 msgid "Incident status updated." msgstr "Vorfallstatus Aktualisiert" -#: risks/views.py:384 +#: risks/views.py:414 msgid "Residual review flag updated." msgstr "Restrisiko geprüft" @@ -384,32 +561,37 @@ msgstr "Restrisiko geprüft" msgid "Dashboard" msgstr "Dashboard" -#: templates/base.html:35 templates/risks/item_risk.html:4 +#: templates/base.html:35 templates/risks/risk_matrix.html:4 +#: templates/risks/risk_matrix.html:12 templates/risks/risk_matrix.html:18 +msgid "Risk Matrix" +msgstr "Risikomatrix" + +#: templates/base.html:37 templates/risks/item_risk.html:4 #: templates/risks/list_risks.html:4 msgid "Risk analysis" msgstr "Risikoanalyse" -#: templates/base.html:73 +#: templates/base.html:75 msgid "AdminCP" msgstr "Adminbereich" -#: templates/base.html:86 +#: templates/base.html:88 msgid "Derk Mode" msgstr "Dark Mode" -#: templates/base.html:92 +#: templates/base.html:94 msgid "Logout" msgstr "Logout" -#: templates/base.html:107 +#: templates/base.html:109 msgid "Login" msgstr "Login" -#: templates/base.html:142 templates/base.html:149 +#: templates/base.html:144 templates/base.html:151 msgid "Light Mode" msgstr "Light Mode" -#: templates/base.html:152 +#: templates/base.html:154 msgid "Dark Mode" msgstr "Dark Mode" @@ -471,99 +653,100 @@ msgstr "Aktualisiert am" msgid "Resubmission" msgstr "Wiedervorlagedatum" -#: templates/risks/item_risk.html:76 +#: templates/risks/item_risk.html:80 msgid "Risk assessment" msgstr "Risikomanagement" -#: templates/risks/item_risk.html:84 +#: templates/risks/item_risk.html:88 msgid "Gross (before measures)" msgstr "Brutto (vor Maßnahmen)" -#: templates/risks/item_risk.html:89 templates/risks/item_risk.html:135 -#: templates/risks/list_risks.html:86 +#: templates/risks/item_risk.html:93 templates/risks/item_risk.html:139 +#: templates/risks/list_risks.html:118 templates/risks/list_risks.html:122 +#: templates/risks/risk_matrix.html:25 msgid "Likelihood" msgstr "Eintritt" -#: templates/risks/item_risk.html:90 templates/risks/item_risk.html:136 +#: templates/risks/item_risk.html:94 templates/risks/item_risk.html:140 msgid "Probability of occurrence" msgstr "Eintrittswahrscheinlichkeit" -#: templates/risks/item_risk.html:98 templates/risks/item_risk.html:144 -#: templates/risks/list_risks.html:87 +#: templates/risks/item_risk.html:102 templates/risks/item_risk.html:148 +#: templates/risks/list_risks.html:119 templates/risks/list_risks.html:123 +#: templates/risks/risk_matrix.html:25 msgid "Impact" msgstr "Schaden" -#: templates/risks/item_risk.html:99 templates/risks/item_risk.html:145 +#: templates/risks/item_risk.html:103 templates/risks/item_risk.html:149 msgid "Extent of damage" msgstr "Schadensausmaß" -#: templates/risks/item_risk.html:107 templates/risks/item_risk.html:108 -#: templates/risks/item_risk.html:153 templates/risks/item_risk.html:154 -#: templates/risks/list_risks.html:89 +#: templates/risks/item_risk.html:111 templates/risks/item_risk.html:112 +#: templates/risks/item_risk.html:157 templates/risks/item_risk.html:158 +#: templates/risks/list_risks.html:121 templates/risks/list_risks.html:125 msgid "Level" msgstr "Stufe" -#: templates/risks/item_risk.html:116 templates/risks/item_risk.html:117 -#: templates/risks/item_risk.html:161 templates/risks/item_risk.html:163 -#: templates/risks/list_risks.html:88 +#: templates/risks/item_risk.html:120 templates/risks/item_risk.html:121 +#: templates/risks/item_risk.html:165 templates/risks/item_risk.html:167 +#: templates/risks/list_risks.html:120 templates/risks/list_risks.html:124 +#: templates/risks/risk_matrix.html:39 msgid "Score" msgstr "Score" -#: templates/risks/item_risk.html:130 +#: templates/risks/item_risk.html:134 msgid "Net (after measures)" msgstr "Netto (nach Maßnahmen)" -#: templates/risks/item_risk.html:169 +#: templates/risks/item_risk.html:173 msgid "No net risk recorded yet." msgstr "Kein Restrisiko vergeben" -#: templates/risks/item_risk.html:180 +#: templates/risks/item_risk.html:184 msgid "Save" msgstr "Speichern" -#: templates/risks/item_risk.html:194 +#: templates/risks/item_risk.html:198 msgid "Measures" msgstr "Maßnahmen" -#: templates/risks/item_risk.html:203 +#: templates/risks/item_risk.html:207 msgid "Deadline" msgstr "Frist" -#: templates/risks/item_risk.html:204 +#: templates/risks/item_risk.html:208 msgid "Responsible" msgstr "Verantwortliche/r" -#: templates/risks/item_risk.html:205 +#: templates/risks/item_risk.html:209 msgid "Link" msgstr "Link" -#: templates/risks/item_risk.html:239 +#: templates/risks/item_risk.html:243 msgid "No measures recorded." msgstr "Keine Maßnahmen gefunden." -#: templates/risks/item_risk.html:257 -#, fuzzy -#| msgid "Reported by" +#: templates/risks/item_risk.html:261 msgid "Reported on" -msgstr "Gemeldet von" +msgstr "Gemeldet am" -#: templates/risks/item_risk.html:271 +#: templates/risks/item_risk.html:275 msgid "No incidents recorded." msgstr "Keine Vorfälle gefunden." -#: templates/risks/item_risk.html:279 +#: templates/risks/item_risk.html:283 msgid "History" msgstr "" -#: templates/risks/item_risk.html:286 +#: templates/risks/item_risk.html:290 msgid "Time" msgstr "Zeitpunkt" -#: templates/risks/item_risk.html:288 +#: templates/risks/item_risk.html:292 msgid "Action" msgstr "Aktion" -#: templates/risks/item_risk.html:302 +#: templates/risks/item_risk.html:306 msgid "No History found." msgstr "Keine Historie vorhanden" @@ -571,20 +754,33 @@ msgstr "Keine Historie vorhanden" msgid "Filter" msgstr "Filter" -#: templates/risks/list_risks.html:22 templates/risks/list_risks.html:41 -#: templates/risks/list_risks.html:60 templates/risks/notifications.html:13 -msgid "All" -msgstr "Alle" - -#: templates/risks/list_risks.html:56 templates/risks/list_risks.html:90 -msgid "Risk Owner" +#: templates/risks/list_risks.html:83 +#, fuzzy +#| msgid "Risk Owner" +msgid "Owner" msgstr "Risikoeigner" -#: templates/risks/list_risks.html:84 +#: templates/risks/list_risks.html:111 msgid "Asset / Process" msgstr "Asset / Prozess" -#: templates/risks/list_risks.html:152 +#: templates/risks/list_risks.html:113 +msgid "Risk Owner" +msgstr "Risikoeigner" + +#: templates/risks/list_risks.html:114 +msgid "Gross Risk" +msgstr "Bruttorisiko" + +#: templates/risks/list_risks.html:115 +msgid "Net Risk" +msgstr "Nettorisiko" + +#: templates/risks/list_risks.html:195 +msgid "No residual risk defined" +msgstr "Noch kein Restrisiko definiert" + +#: templates/risks/list_risks.html:201 msgid "No risks present" msgstr "Aktuell keine Risiken" @@ -592,6 +788,10 @@ msgstr "Aktuell keine Risiken" msgid "Unread" msgstr "Ungelesen" +#: templates/risks/notifications.html:13 +msgid "All" +msgstr "Alle" + #: templates/risks/notifications.html:20 msgid "Mark all as read" msgstr "Alle als gelesen Markieren" @@ -607,3 +807,23 @@ msgstr "Als gelesen markieren" #: templates/risks/notifications.html:53 msgid "No notifications." msgstr "Keine Nachrichten" + +#: templates/risks/risk_matrix.html:13 +msgid "Detail View" +msgstr "Detailansicht" + +#: templates/risks/risk_matrix.html:54 +msgid "Show" +msgstr "Zeige" + +#: templates/risks/risk_matrix.html:58 +msgid "Gross" +msgstr "Brutto" + +#: templates/risks/risk_matrix.html:61 +msgid "Net" +msgstr "Netto" + +#: templates/risks/risk_matrix.html:73 templates/risks/risk_matrix.html:115 +msgid "Impact \\\\ Likelihood" +msgstr "Schadensausmaß \\\\ Eintrittswahrscheinlichkeit" diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index d06e20b..bde0ace 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-09-10 13:44+0200\n" +"POT-Creation-Date: 2025-09-11 10:17+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -28,12 +28,12 @@ msgstr "" #: risks/admin.py:15 risks/models.py:36 templates/risks/dashboard.html:75 #: templates/risks/dashboard.html:80 templates/risks/dashboard.html:85 -#: templates/risks/list_risks.html:76 +#: templates/risks/list_risks.html:97 msgid "Risks" msgstr "" -#: risks/admin.py:16 risks/models.py:190 templates/base.html:36 -#: templates/risks/list_risks.html:37 +#: risks/admin.py:16 risks/models.py:190 templates/base.html:38 +#: templates/risks/list_risks.html:31 msgid "Controls" msgstr "" @@ -45,8 +45,8 @@ msgstr "" msgid "Reviews" msgstr "" -#: risks/admin.py:19 risks/models.py:258 templates/base.html:37 -#: templates/risks/item_risk.html:248 +#: risks/admin.py:19 risks/models.py:258 templates/base.html:39 +#: templates/risks/item_risk.html:252 msgid "Incidents" msgstr "" @@ -54,7 +54,7 @@ msgstr "" msgid "Users" msgstr "" -#: risks/admin.py:133 risks/models.py:302 templates/risks/item_risk.html:287 +#: risks/admin.py:133 risks/models.py:326 templates/risks/item_risk.html:291 msgid "User" msgstr "" @@ -98,15 +98,19 @@ msgstr "" msgid "%(n)d notifications marked as unsent." msgstr "" -#: risks/admin.py:177 -msgid "SSO Information" -msgstr "" - -#: risks/admin.py:186 -msgid "Risks Owned" +#: risks/admin.py:182 +msgid "Extra recipients" msgstr "" #: risks/admin.py:190 +msgid "SSO Information" +msgstr "" + +#: risks/admin.py:199 +msgid "Risks Owned" +msgstr "" + +#: risks/admin.py:203 msgid "Controls Responsible" msgstr "" @@ -115,17 +119,17 @@ msgid "Risk Management" msgstr "" #: risks/forms.py:9 risks/forms.py:16 risks/forms.py:23 risks/models.py:73 -#: templates/risks/item_risk.html:64 templates/risks/item_risk.html:202 -#: templates/risks/item_risk.html:256 +#: templates/risks/item_risk.html:64 templates/risks/item_risk.html:206 +#: templates/risks/item_risk.html:260 msgid "Status" msgstr "" -#: risks/forms.py:30 risks/models.py:42 templates/risks/item_risk.html:177 +#: risks/forms.py:30 risks/models.py:42 templates/risks/item_risk.html:181 msgid "Review required" msgstr "" #: risks/models.py:35 templates/risks/item_risk.html:11 -#: templates/risks/list_risks.html:18 templates/risks/list_risks.html:83 +#: templates/risks/list_risks.html:18 templates/risks/list_risks.html:110 msgid "Risk" msgstr "" @@ -190,7 +194,7 @@ msgid "Availability" msgstr "" #: risks/models.py:64 risks/models.py:200 risks/models.py:265 -#: templates/risks/item_risk.html:201 +#: templates/risks/item_risk.html:205 msgid "Title" msgstr "" @@ -199,15 +203,17 @@ msgid "Description" msgstr "" #: risks/models.py:66 templates/risks/item_risk.html:53 +#: templates/risks/list_risks.html:57 msgid "Asset" msgstr "" #: risks/models.py:67 templates/risks/item_risk.html:54 +#: templates/risks/list_risks.html:70 msgid "Process" msgstr "" #: risks/models.py:68 templates/risks/item_risk.html:55 -#: templates/risks/list_risks.html:85 +#: templates/risks/list_risks.html:44 templates/risks/list_risks.html:112 msgid "Category" msgstr "" @@ -219,6 +225,10 @@ msgstr "" msgid "Updated at" msgstr "" +#: risks/models.py:71 +msgid "Effects" +msgstr "" + #: risks/models.py:133 msgid "Residual Risk" msgstr "" @@ -259,7 +269,7 @@ msgstr "" msgid "Auditlogs" msgstr "" -#: risks/models.py:257 templates/risks/item_risk.html:255 +#: risks/models.py:257 templates/risks/item_risk.html:259 msgid "Incident" msgstr "" @@ -275,15 +285,119 @@ msgstr "" msgid "Reported by" msgstr "" -#: risks/models.py:279 +#: risks/models.py:279 risks/utils.py:119 msgid "Notification" msgstr "" -#: risks/models.py:280 templates/base.html:78 +#: risks/models.py:280 templates/base.html:80 #: templates/risks/notifications.html:4 msgid "Notifications" msgstr "" +#: risks/models.py:295 +msgid "Risk created" +msgstr "" + +#: risks/models.py:296 +msgid "Risk updated" +msgstr "" + +#: risks/models.py:297 +msgid "Risk deleted" +msgstr "" + +#: risks/models.py:298 +msgid "Risk review required" +msgstr "" + +#: risks/models.py:299 +msgid "Risk review completed" +msgstr "" + +#: risks/models.py:301 +msgid "Control created" +msgstr "" + +#: risks/models.py:302 +msgid "Control updated" +msgstr "" + +#: risks/models.py:303 +msgid "Control deleted" +msgstr "" + +#: risks/models.py:305 +msgid "Residual created" +msgstr "" + +#: risks/models.py:306 +msgid "Residual updated" +msgstr "" + +#: risks/models.py:307 +msgid "Residual deleted" +msgstr "" + +#: risks/models.py:308 +msgid "Residual review required" +msgstr "" + +#: risks/models.py:309 +msgid "Residual review completed" +msgstr "" + +#: risks/models.py:311 +msgid "Incident created" +msgstr "" + +#: risks/models.py:312 +msgid "Incident updated" +msgstr "" + +#: risks/models.py:313 +msgid "Incident deleted" +msgstr "" + +#: risks/models.py:315 +msgid "User created" +msgstr "" + +#: risks/models.py:316 +msgid "User deleted" +msgstr "" + +#: risks/models.py:371 +msgid "Notification rule" +msgstr "" + +#: risks/models.py:372 +msgid "Notification rules" +msgstr "" + +#: risks/models.py:375 +msgid "Event" +msgstr "" + +#: risks/models.py:380 +msgid "Show in app" +msgstr "" + +#: risks/models.py:381 +msgid "Send via email" +msgstr "" + +#: risks/models.py:385 +msgid "Send to owner/responsible/reporter (if available)" +msgstr "" + +#: risks/models.py:389 +msgid "Send to all staff" +msgstr "" + +#: risks/models.py:393 +msgid "Extra recipients (emails, comma or newline separated)" +msgstr "" + #: risks/signals.py:57 #, python-brace-format msgid "User '{u}' created" @@ -299,13 +413,13 @@ msgstr "" msgid "Risk '{title}' {state}" msgstr "" -#: risks/signals.py:72 risks/signals.py:147 risks/signals.py:240 -#: risks/signals.py:296 +#: risks/signals.py:72 risks/signals.py:166 risks/signals.py:212 +#: risks/signals.py:275 risks/signals.py:369 risks/signals.py:411 msgid "created" msgstr "" -#: risks/signals.py:72 risks/signals.py:147 risks/signals.py:240 -#: risks/signals.py:296 +#: risks/signals.py:72 risks/signals.py:166 risks/signals.py:212 +#: risks/signals.py:275 risks/signals.py:369 risks/signals.py:411 msgid "updated" msgstr "" @@ -314,77 +428,137 @@ msgstr "" msgid "Risk '{title}' deleted" msgstr "" -#: risks/signals.py:145 +#: risks/signals.py:117 +#, python-brace-format +msgid "Risk created: {t}" +msgstr "" + +#: risks/signals.py:123 +#, python-brace-format +msgid "Risk updated: {t}" +msgstr "" + +#: risks/signals.py:143 +#, python-brace-format +msgid "Risk deleted: {t}" +msgstr "" + +#: risks/signals.py:164 #, python-brace-format msgid "Control '{title}' {state}" msgstr "" -#: risks/signals.py:154 +#: risks/signals.py:173 #, python-brace-format msgid "Control '{title}' deleted" msgstr "" #: risks/signals.py:211 #, python-brace-format +msgid "Control {event}: {t}" +msgstr "" + +#: risks/signals.py:231 +#, python-brace-format +msgid "Control deleted: {t}" +msgstr "" + +#: risks/signals.py:246 +#, python-brace-format msgid "Review required for risk '{t}' due to control change" msgstr "" -#: risks/signals.py:230 +#: risks/signals.py:265 #, python-brace-format msgid "Review required for risk '{t}'" msgstr "" -#: risks/signals.py:235 +#: risks/signals.py:270 #, python-brace-format msgid "Review completed for risk '{t}'" msgstr "" -#: risks/signals.py:239 +#: risks/signals.py:274 #, python-brace-format msgid "Residual risk {state} for '{t}'" msgstr "" -#: risks/signals.py:244 +#: risks/signals.py:279 #, python-brace-format msgid "Residual risk deleted for '{t}'" msgstr "" -#: risks/signals.py:296 +#: risks/signals.py:316 +#, python-brace-format +msgid "Residual created for risk: {t}" +msgstr "" + +#: risks/signals.py:328 +#, python-brace-format +msgid "Residual review required for risk: {t}" +msgstr "" + +#: risks/signals.py:334 +#, python-brace-format +msgid "Residual review completed for risk: {t}" +msgstr "" + +#: risks/signals.py:340 +#, python-brace-format +msgid "Residual updated for risk: {t}" +msgstr "" + +#: risks/signals.py:358 +#, python-brace-format +msgid "Residual deleted for risk: {t}" +msgstr "" + +#: risks/signals.py:369 #, python-brace-format msgid "Incident '{t}' {s}" msgstr "" -#: risks/signals.py:301 +#: risks/signals.py:374 #, python-brace-format msgid "Incident '{t}' deleted" msgstr "" -#: risks/utils.py:48 +#: risks/signals.py:410 +#, python-brace-format +msgid "Incident {event}: {t}" +msgstr "" + +#: risks/signals.py:442 +#, python-brace-format +msgid "Incident deleted: {t}" +msgstr "" + +#: risks/utils.py:53 risks/utils.py:67 #, python-brace-format msgid "Follow-up reached: review required for risk '{t}'" msgstr "" -#: risks/views.py:315 +#: risks/views.py:345 msgid "Notification marked as read." msgstr "" -#: risks/views.py:323 +#: risks/views.py:353 msgid "All notifications marked as read." msgstr "" -#: risks/views.py:340 +#: risks/views.py:370 msgid "Risk status updated." msgstr "" -#: risks/views.py:354 +#: risks/views.py:384 msgid "Control status updated." msgstr "" -#: risks/views.py:368 +#: risks/views.py:398 msgid "Incident status updated." msgstr "" -#: risks/views.py:384 +#: risks/views.py:414 msgid "Residual review flag updated." msgstr "" @@ -392,32 +566,37 @@ msgstr "" msgid "Dashboard" msgstr "" -#: templates/base.html:35 templates/risks/item_risk.html:4 +#: templates/base.html:35 templates/risks/risk_matrix.html:4 +#: templates/risks/risk_matrix.html:12 templates/risks/risk_matrix.html:18 +msgid "Risk Matrix" +msgstr "" + +#: templates/base.html:37 templates/risks/item_risk.html:4 #: templates/risks/list_risks.html:4 msgid "Risk analysis" msgstr "" -#: templates/base.html:73 +#: templates/base.html:75 msgid "AdminCP" msgstr "" -#: templates/base.html:86 +#: templates/base.html:88 msgid "Derk Mode" msgstr "" -#: templates/base.html:92 +#: templates/base.html:94 msgid "Logout" msgstr "" -#: templates/base.html:107 +#: templates/base.html:109 msgid "Login" msgstr "" -#: templates/base.html:142 templates/base.html:149 +#: templates/base.html:144 templates/base.html:151 msgid "Light Mode" msgstr "" -#: templates/base.html:152 +#: templates/base.html:154 msgid "Dark Mode" msgstr "" @@ -477,97 +656,100 @@ msgstr "" msgid "Resubmission" msgstr "" -#: templates/risks/item_risk.html:76 +#: templates/risks/item_risk.html:80 msgid "Risk assessment" msgstr "" -#: templates/risks/item_risk.html:84 +#: templates/risks/item_risk.html:88 msgid "Gross (before measures)" msgstr "" -#: templates/risks/item_risk.html:89 templates/risks/item_risk.html:135 -#: templates/risks/list_risks.html:86 +#: templates/risks/item_risk.html:93 templates/risks/item_risk.html:139 +#: templates/risks/list_risks.html:118 templates/risks/list_risks.html:122 +#: templates/risks/risk_matrix.html:25 msgid "Likelihood" msgstr "" -#: templates/risks/item_risk.html:90 templates/risks/item_risk.html:136 +#: templates/risks/item_risk.html:94 templates/risks/item_risk.html:140 msgid "Probability of occurrence" msgstr "" -#: templates/risks/item_risk.html:98 templates/risks/item_risk.html:144 -#: templates/risks/list_risks.html:87 +#: templates/risks/item_risk.html:102 templates/risks/item_risk.html:148 +#: templates/risks/list_risks.html:119 templates/risks/list_risks.html:123 +#: templates/risks/risk_matrix.html:25 msgid "Impact" msgstr "" -#: templates/risks/item_risk.html:99 templates/risks/item_risk.html:145 +#: templates/risks/item_risk.html:103 templates/risks/item_risk.html:149 msgid "Extent of damage" msgstr "" -#: templates/risks/item_risk.html:107 templates/risks/item_risk.html:108 -#: templates/risks/item_risk.html:153 templates/risks/item_risk.html:154 -#: templates/risks/list_risks.html:89 +#: templates/risks/item_risk.html:111 templates/risks/item_risk.html:112 +#: templates/risks/item_risk.html:157 templates/risks/item_risk.html:158 +#: templates/risks/list_risks.html:121 templates/risks/list_risks.html:125 msgid "Level" msgstr "" -#: templates/risks/item_risk.html:116 templates/risks/item_risk.html:117 -#: templates/risks/item_risk.html:161 templates/risks/item_risk.html:163 -#: templates/risks/list_risks.html:88 +#: templates/risks/item_risk.html:120 templates/risks/item_risk.html:121 +#: templates/risks/item_risk.html:165 templates/risks/item_risk.html:167 +#: templates/risks/list_risks.html:120 templates/risks/list_risks.html:124 +#: templates/risks/risk_matrix.html:39 msgid "Score" msgstr "" -#: templates/risks/item_risk.html:130 +#: templates/risks/item_risk.html:134 msgid "Net (after measures)" msgstr "" -#: templates/risks/item_risk.html:169 +#: templates/risks/item_risk.html:173 msgid "No net risk recorded yet." msgstr "" -#: templates/risks/item_risk.html:180 +#: templates/risks/item_risk.html:184 msgid "Save" msgstr "" -#: templates/risks/item_risk.html:194 +#: templates/risks/item_risk.html:198 msgid "Measures" msgstr "" -#: templates/risks/item_risk.html:203 +#: templates/risks/item_risk.html:207 msgid "Deadline" msgstr "" -#: templates/risks/item_risk.html:204 +#: templates/risks/item_risk.html:208 msgid "Responsible" msgstr "" -#: templates/risks/item_risk.html:205 +#: templates/risks/item_risk.html:209 msgid "Link" msgstr "" -#: templates/risks/item_risk.html:239 +#: templates/risks/item_risk.html:243 msgid "No measures recorded." msgstr "" -#: templates/risks/item_risk.html:257 +#: templates/risks/item_risk.html:261 msgid "Reported on" msgstr "" -#: templates/risks/item_risk.html:271 +#: templates/risks/item_risk.html:275 msgid "No incidents recorded." msgstr "" -#: templates/risks/item_risk.html:279 +#: templates/risks/item_risk.html:283 msgid "History" msgstr "" -#: templates/risks/item_risk.html:286 +#: templates/risks/item_risk.html:290 msgid "Time" msgstr "" -#: templates/risks/item_risk.html:288 +#: templates/risks/item_risk.html:292 msgid "Action" msgstr "" -#: templates/risks/item_risk.html:302 +#: templates/risks/item_risk.html:306 msgid "No History found." msgstr "" @@ -575,20 +757,31 @@ msgstr "" msgid "Filter" msgstr "" -#: templates/risks/list_risks.html:22 templates/risks/list_risks.html:41 -#: templates/risks/list_risks.html:60 templates/risks/notifications.html:13 -msgid "All" +#: templates/risks/list_risks.html:83 +msgid "Owner" msgstr "" -#: templates/risks/list_risks.html:56 templates/risks/list_risks.html:90 -msgid "Risk Owner" -msgstr "" - -#: templates/risks/list_risks.html:84 +#: templates/risks/list_risks.html:111 msgid "Asset / Process" msgstr "" -#: templates/risks/list_risks.html:152 +#: templates/risks/list_risks.html:113 +msgid "Risk Owner" +msgstr "" + +#: templates/risks/list_risks.html:114 +msgid "Gross Risk" +msgstr "" + +#: templates/risks/list_risks.html:115 +msgid "Net Risk" +msgstr "" + +#: templates/risks/list_risks.html:195 +msgid "No residual risk defined" +msgstr "" + +#: templates/risks/list_risks.html:201 msgid "No risks present" msgstr "" @@ -596,6 +789,10 @@ msgstr "" msgid "Unread" msgstr "" +#: templates/risks/notifications.html:13 +msgid "All" +msgstr "" + #: templates/risks/notifications.html:20 msgid "Mark all as read" msgstr "" @@ -611,3 +808,23 @@ msgstr "" #: templates/risks/notifications.html:53 msgid "No notifications." msgstr "" + +#: templates/risks/risk_matrix.html:13 +msgid "Detail View" +msgstr "" + +#: templates/risks/risk_matrix.html:54 +msgid "Show" +msgstr "" + +#: templates/risks/risk_matrix.html:58 +msgid "Gross" +msgstr "" + +#: templates/risks/risk_matrix.html:61 +msgid "Net" +msgstr "" + +#: templates/risks/risk_matrix.html:73 templates/risks/risk_matrix.html:115 +msgid "Impact \\\\ Likelihood" +msgstr "" diff --git a/risks/templatetags/risk_extras.py b/risks/templatetags/risk_extras.py index f06fd65..1c8e003 100644 --- a/risks/templatetags/risk_extras.py +++ b/risks/templatetags/risk_extras.py @@ -10,6 +10,20 @@ _LIKELIHOOD_LABELS = dict(Risk.LIKELIHOOD_CHOICES) _IMPACT_LABELS = dict(Risk.IMPACT_CHOICES) LEVEL_ID_MAP = {"Low": 1, "Medium": 2, "High": 3, "Critical": 4} +@register.filter +def dict_get(d, key): + try: + return d.get(key) + except Exception: + return None + +@register.filter +def mul(a, b): + try: + return int(a) * int(b) + except (TypeError, ValueError): + return "" + @register.filter def level_id(level): return LEVEL_ID_MAP.get(str(level), "") @@ -38,7 +52,7 @@ def likelihood_id_label(val): return "" label = _LIKELIHOOD_LABELS.get(i, "") short = _short(str(label)) if label else "" - return format_html("{}
({})", i, short) if label else format_html("{}", i) + return format_html("{} ({})", i, short) if label else format_html("{}", i) @register.filter def impact_id_label(val): @@ -48,7 +62,7 @@ def impact_id_label(val): return "" label = _IMPACT_LABELS.get(i, "") short = _short(str(label)) if label else "" - return format_html("{}
({})", i, short) if label else format_html("{}", i) + return format_html("{} ({})", i, short) if label else format_html("{}", i) @register.filter @@ -131,4 +145,24 @@ def to_bg(css_class: str): css = str(css_class) except Exception: return "" - return css.replace("is-control-", "has-background-control-") if css.startswith("is-control-") else css \ No newline at end of file + return css.replace("is-control-", "has-background-control-") if css.startswith("is-control-") else css + +@register.filter +def score_bg_class(score): + """ + Mappt Score (1..20) auf deine Farbhintergründe. + """ + try: + s = int(score) + except (TypeError, ValueError): + return "" + if s <= 4: + return "has-background-control-verylow" + elif s <= 8: + return "has-background-control-low" + elif s <= 12: + return "has-background-control-mid" + elif s <= 16: + return "has-background-control-high" + else: + return "has-background-control-veryhigh" \ No newline at end of file diff --git a/risks/urls.py b/risks/urls.py index 031882e..a07da14 100644 --- a/risks/urls.py +++ b/risks/urls.py @@ -13,6 +13,7 @@ urlpatterns = [ path("risks/controls/", views.show_control, name="show_control"), path("risks/list_incidents", views.list_incidents, name="list_incidents"), path("risks/incidents/", views.show_incident, name="show_incident"), + path("risks/risk_matrix", views.risk_matrix, name="risk_matrix"), # Notifications path("notifications/", views.notifications, name="notifications"), diff --git a/risks/views.py b/risks/views.py index e0a4cc6..a5e3057 100644 --- a/risks/views.py +++ b/risks/views.py @@ -9,7 +9,7 @@ from django.shortcuts import redirect, render, get_object_or_404 from django.utils.translation import gettext_lazy as _ from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated -from collections import Counter +from collections import Counter, defaultdict from .forms import RiskStatusForm, ControlStatusForm, IncidentStatusForm, ResidualReviewForm from .models import Risk, Control, ResidualRisk, AuditLog, Incident, Notification from .serializers import ControlSerializer, RiskSerializer, ResidualRiskSerializer, UserSerializer, AuditSerializer, IncidentSerializer @@ -119,12 +119,15 @@ def list_risks(request): """ View for listing all Risks """ - qs = Risk.objects.all().select_related("owner") + qs = Risk.objects.all().select_related("owner", "residual_risk") # GET-Parameter lesen risk_id = request.GET.get("risk") control_id = request.GET.get("control") owner_id = request.GET.get("owner") + category = request.GET.get("category") + asset = request.GET.get("asset") + process = request.GET.get("process") if risk_id: qs = qs.filter(id=risk_id) @@ -132,16 +135,43 @@ def list_risks(request): qs = qs.filter(controls__id=control_id) if owner_id: qs = qs.filter(owner_id=owner_id) + if category: + qs = qs.filter(category=category) + if asset: + qs = qs.filter(asset=asset) + if process: + qs = qs.filter(process=process) risks = qs.order_by("title").distinct() controls = Control.objects.all().order_by("title") owners = User.objects.filter(owned_risks__isnull=False).distinct().order_by("username") + categories = (Risk.objects + .exclude(category__isnull=True) + .exclude(category__exact="") + .values_list("category", flat=True) + .distinct() + .order_by("category")) + assets = (Risk.objects + .exclude(asset__isnull=True) + .exclude(asset__exact="") + .values_list("asset", flat=True) + .distinct() + .order_by("asset")) + processes = (Risk.objects + .exclude(process__isnull=True) + .exclude(process__exact="") + .values_list("process", flat=True) + .distinct() + .order_by("process")) return render(request, "risks/list_risks.html", { "risks": risks, "controls": controls, "owners": owners, + "categories": categories, + "assets": assets, + "processes": processes, }) @login_required @@ -382,4 +412,31 @@ def update_residual_review(request, risk_id): obj._changed_by = request.user obj.save(update_fields=["review_required", "updated_at"]) messages.success(request, _("Residual review flag updated.")) - return redirect("risks:show_risk", id=risk.pk) \ No newline at end of file + return redirect("risks:show_risk", id=risk.pk) + + +def risk_matrix(request): + risks = (Risk.objects + .select_related("owner", "residual_risk") # wichtig fürs Netto + .all()) + + impacts = sorted(Risk.IMPACT_CHOICES, key=lambda x: x[0]) + likelihoods = sorted(Risk.LIKELIHOOD_CHOICES, key=lambda x: x[0]) + + gross_matrix = {i: {l: [] for l, _ in likelihoods} for i, _ in impacts} + net_matrix = {i: {l: [] for l, _ in likelihoods} for i, _ in impacts} + + for r in risks: + # Brutto platzieren + gross_matrix[r.impact][r.likelihood].append(r) + # Netto (falls vorhanden) platzieren + rr = getattr(r, "residual_risk", None) + if rr: + net_matrix[rr.impact][rr.likelihood].append(r) + + return render(request, "risks/risk_matrix.html", { + "impacts": impacts, + "likelihoods": likelihoods, + "gross_matrix": gross_matrix, + "net_matrix": net_matrix, + }) diff --git a/static/css/design.css b/static/css/design.css index 7392c06..c6454a3 100644 --- a/static/css/design.css +++ b/static/css/design.css @@ -1,10 +1,10 @@ /* Base palette */ :root{ - --c-verylow:#22c55e; --c-verylow-100:#dcfce7; --c-verylow-300:#86efac; --c-verylow-inv:#fff; + --c-verylow:#22c55e; --c-verylow-100:#dcfce7; --c-verylow-300:#86efac; --c-verylow-inv:#000; --c-low:#84cc16; --c-low-100:#ecfccb; --c-low-300:#bef264; --c-low-inv:#111; --c-mid:#eab308; --c-mid-100:#fef9c3; --c-mid-300:#fde047; --c-mid-inv:#111; --c-high:#f97316; --c-high-100:#ffedd5; --c-high-300:#fbbf24; --c-high-inv:#111; - --c-veryhigh:#dc2626; --c-veryhigh-100:#fee2e2;--c-veryhigh-300:#fca5a5; --c-veryhigh-inv:#fff; + --c-veryhigh:#dc2626; --c-veryhigh-100:#fee2e2;--c-veryhigh-300:#fca5a5; --c-veryhigh-inv:#000; } /* Helpers (wie Bulma) */ @@ -80,6 +80,9 @@ .progress.is-control-high::-moz-progress-bar{background:var(--c-high)} .progress.is-control-veryhigh::-moz-progress-bar{background:var(--c-veryhigh)} +abbr { + text-decoration: none; +} /* Topbar-Farbe erzwingen (Bulma überschreibt sonst mit weiß) */ .navbar.topbar-nav { diff --git a/templates/base.html b/templates/base.html index d872ddb..a5a907c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -32,6 +32,8 @@ {% trans "Risk Management" %}