Skip to content

Commit 7e684e8

Browse files
author
Bart van der Schoor
committed
[#2058] Added status indicator to case_status userfeed items
1 parent db9db6f commit 7e684e8

File tree

8 files changed

+172
-3
lines changed

8 files changed

+172
-3
lines changed

src/open_inwoner/openzaak/admin.py

+22
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin):
277277
"identificatie",
278278
"omschrijving",
279279
"num_infotypes",
280+
"num_statustypes",
281+
"num_resulttypes",
280282
]
281283
list_display = [
282284
"identificatie",
@@ -288,6 +290,8 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin):
288290
"contact_subject_code",
289291
"document_upload_enabled",
290292
"num_infotypes",
293+
"num_statustypes",
294+
"num_resulttypes",
291295
]
292296
list_display_links = [
293297
"identificatie",
@@ -316,6 +320,8 @@ def has_delete_permission(self, request, obj=None):
316320
def get_queryset(self, request):
317321
qs = super().get_queryset(request)
318322
qs = qs.annotate(num_infotypes=Count("zaaktypeinformatieobjecttypeconfig"))
323+
qs = qs.annotate(num_statustypes=Count("zaaktypestatustypeconfig"))
324+
qs = qs.annotate(num_resulttypes=Count("zaaktyperesultaattypeconfig"))
319325
qs = qs.annotate(
320326
num_doc_notify=Count(
321327
"zaaktypeinformatieobjecttypeconfig",
@@ -339,6 +345,22 @@ def num_infotypes(self, obj=None):
339345

340346
num_infotypes.admin_order_field = "num_infotypes"
341347

348+
def num_statustypes(self, obj=None):
349+
if not obj or not obj.pk:
350+
return "-"
351+
else:
352+
return getattr(obj, "num_statustypes", 0)
353+
354+
num_statustypes.admin_order_field = "num_statustypes"
355+
356+
def num_resulttypes(self, obj=None):
357+
if not obj or not obj.pk:
358+
return "-"
359+
else:
360+
return getattr(obj, "num_resulttypes", 0)
361+
362+
num_resulttypes.admin_order_field = "num_resulttypes"
363+
342364
def has_doc_notify(self, obj=None):
343365
if not obj or not obj.pk:
344366
return False

src/open_inwoner/openzaak/managers.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
from django.utils import timezone
77

88
from open_inwoner.accounts.models import User
9-
from open_inwoner.openzaak.api_models import ZaakType
9+
from open_inwoner.openzaak.api_models import Status, StatusType, Zaak, ZaakType
1010
from open_inwoner.utils.translate import TranslationLookup
1111

1212
if TYPE_CHECKING:
1313
from open_inwoner.openzaak.models import (
1414
UserCaseInfoObjectNotification,
1515
UserCaseStatusNotification,
16+
ZaakTypeConfig,
1617
)
1718

1819

@@ -139,6 +140,20 @@ def filter_case_type(self, case_type: ZaakType):
139140
identificatie=case_type.identificatie,
140141
)
141142

143+
def filter_from_str(self, catalogus_url: str, case_type_identificatie: str):
144+
qs = self
145+
if catalogus_url:
146+
qs = qs.filter(
147+
catalogus__url=catalogus_url,
148+
)
149+
else:
150+
qs = qs.filter(
151+
catalogus__isnull=True,
152+
)
153+
return qs.filter(
154+
identificatie=case_type_identificatie,
155+
)
156+
142157
def filter_enabled_for_case_type(self, case_type: ZaakType):
143158
"""
144159
Returns all ZaakTypeConfig instances which allow external documents
@@ -165,6 +180,31 @@ def filter_questions_enabled_for_case_type(self, case_type: ZaakType):
165180
return self.filter_case_type(case_type).filter(questions_enabled=True)
166181

167182

183+
class ZaakTypeStatusTypeConfigQuerySet(models.QuerySet):
184+
def find_for(self, case: Zaak, status: Status):
185+
return self.find_for_types(case.zaaktype, status.statustype)
186+
187+
def find_for_types(self, case_type: ZaakType, status_type: StatusType):
188+
from .models import ZaakTypeConfig
189+
190+
ztc = ZaakTypeConfig.objects.filter_case_type(case_type)
191+
return self.filter(
192+
zaaktype_config__in=ztc, statustype_url=status_type.url
193+
).first()
194+
195+
def find_for_types_from_str(
196+
self, catalogus_url: str, case_type_identificatie: str, status_type_url: str
197+
):
198+
from .models import ZaakTypeConfig
199+
200+
ztc = ZaakTypeConfig.objects.filter_from_str(
201+
catalogus_url, case_type_identificatie
202+
)
203+
return self.filter(
204+
zaaktype_config__in=ztc, statustype_url=status_type_url
205+
).first()
206+
207+
168208
class StatusTranslationQuerySet(models.QuerySet):
169209
def get_lookup(self):
170210
return TranslationLookup(self.values_list("status", "translation"))

src/open_inwoner/openzaak/models.py

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
UserCaseStatusNotificationManager,
1818
ZaakTypeConfigQueryset,
1919
ZaakTypeInformatieObjectTypeConfigQueryset,
20+
ZaakTypeStatusTypeConfigQuerySet,
2021
)
2122

2223
from .constants import StatusIndicators
@@ -465,6 +466,8 @@ class ZaakTypeStatusTypeConfig(models.Model):
465466
),
466467
)
467468

469+
objects = ZaakTypeStatusTypeConfigQuerySet.as_manager()
470+
468471
class Meta:
469472
verbose_name = _("Zaaktype Statustype Configuration")
470473

src/open_inwoner/templates/cms/plugins/userfeed/userfeed.html

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ <h2 class="h2 {% if userfeed.action_required %}indicator{% endif %}">
1919
<a href="{{ item.action_url }}" class="card {% if item.action_required %}card--status{% endif %} {% if item.is_completed %}card--completed{% endif %}">
2020
<div class="card__body card__body--tabled">
2121
<p class="p tabled__value">{{ item.title }}</p>
22+
23+
<p class="p" style="color: rebeccapurple; font-size: 300%;">
24+
{{ item.status_indicator }}
25+
</p>
26+
2227
<h2 class="userfeed__heading">
2328
<span class="">{{ item.message }}</span>
2429
{# Status gewijzigd naar &lsquo;<span class="status">Documenten nodig</span>&rsquo;</span>#}

src/open_inwoner/userfeed/adapter.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from open_inwoner.openzaak.constants import StatusIndicators
12
from open_inwoner.userfeed.models import FeedItemData
23

34

@@ -12,6 +13,8 @@ class FeedItem:
1213
base_message = ""
1314
base_action_text = ""
1415
base_action_url = ""
16+
base_status_indicator = StatusIndicators.info
17+
base_status_indicator_action = StatusIndicators.warning
1518

1619
cms_apps: list[str] = []
1720

@@ -41,6 +44,12 @@ def title(self) -> str:
4144
def message(self) -> str:
4245
return self.get_data("message") or self.base_message
4346

47+
@property
48+
def status_indicator(self) -> str:
49+
if self.action_required:
50+
return self.base_status_indicator_action
51+
return self.base_status_indicator
52+
4453
@property
4554
def action_text(self) -> str:
4655
return self.base_action_text

src/open_inwoner/userfeed/hooks/case_status.py

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import timedelta
2+
from typing import Optional
23

34
from django.db.models import Q
45
from django.urls import reverse
@@ -8,6 +9,7 @@
89

910
from open_inwoner.accounts.models import User
1011
from open_inwoner.openzaak.api_models import Status, Zaak
12+
from open_inwoner.openzaak.models import ZaakTypeConfig, ZaakTypeStatusTypeConfig
1113
from open_inwoner.openzaak.utils import translate_single_status
1214
from open_inwoner.userfeed.adapter import FeedItem
1315
from open_inwoner.userfeed.adapters import register_item_adapter
@@ -23,8 +25,17 @@ def case_status_notification_received(user: User, case: Zaak, status: Status):
2325
"case_identificatie": case.identificatie,
2426
"case_omschrijving": case.omschrijving,
2527
"status_omschrijving": status.statustype.omschrijving,
28+
# new for actionable
29+
"catalogus_url": case.zaaktype.catalogus,
30+
"case_type_identificatie": case.zaaktype.identificatie,
31+
"status_type_url": status.statustype.url,
2632
}
2733

34+
action_required = False
35+
status_config = ZaakTypeStatusTypeConfig.objects.find_for(case, status)
36+
if status_config:
37+
action_required = status_config.action_required
38+
2839
# let's try to update last record if change happened recently
2940
qs = FeedItemData.objects.filter(
3041
user=user,
@@ -38,12 +49,14 @@ def case_status_notification_received(user: User, case: Zaak, status: Status):
3849
display_at=timezone.now(),
3950
completed_at=None,
4051
type_data=data,
52+
action_required=action_required,
4153
):
4254
FeedItemData.objects.create(
4355
user=user,
4456
type=FeedItemType.case_status_changed,
4557
ref_uuid=case.uuid,
4658
type_data=data,
59+
action_required=action_required,
4760
)
4861

4962

@@ -59,9 +72,23 @@ class CaseStatusUpdateFeedItem(FeedItem):
5972

6073
cms_apps = ["cases"]
6174

75+
status_config: Optional[ZaakTypeStatusTypeConfig] = None
76+
77+
def __init__(self, data: FeedItemData):
78+
super().__init__(data)
79+
80+
self.status_config = ZaakTypeStatusTypeConfig.objects.find_for_types_from_str(
81+
self.get_data("catalogus_url"),
82+
self.get_data("case_type_identificatie"),
83+
self.get_data("status_type_url"),
84+
)
85+
6286
@property
6387
def title(self) -> str:
64-
return self.get_data("case_omschrijving", super().title)
88+
if self.status_config and self.status_config.status_indicator_text:
89+
return self.status_config.status_indicator_text
90+
else:
91+
return self.get_data("case_omschrijving", super().title)
6592

6693
@property
6794
def message(self) -> str:
@@ -80,5 +107,12 @@ def action_url(self) -> str:
80107
uuid = self.get_data("case_uuid")
81108
return reverse("cases:case_detail", kwargs={"object_id": uuid})
82109

110+
@property
111+
def status_indicator(self) -> str:
112+
if self.status_config and self.status_config.status_indicator:
113+
return self.status_config.status_indicator
114+
else:
115+
return super().status_indicator
116+
83117

84118
register_item_adapter(CaseStatusUpdateFeedItem, FeedItemType.case_status_changed)

src/open_inwoner/userfeed/tests/hooks/test_case_status.py

+55-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
from zgw_consumers.api_models.base import factory
99

1010
from open_inwoner.openzaak.api_models import Status, StatusType, Zaak, ZaakType
11-
from open_inwoner.openzaak.tests.factories import StatusTranslationFactory
11+
from open_inwoner.openzaak.constants import StatusIndicators
12+
from open_inwoner.openzaak.tests.factories import (
13+
StatusTranslationFactory,
14+
ZaakTypeConfigFactory,
15+
ZaakTypeStatusTypeConfigFactory,
16+
)
1217
from open_inwoner.openzaak.tests.test_notification_data import MockAPIData
1318
from open_inwoner.userfeed.choices import FeedItemType
1419
from open_inwoner.userfeed.feed import get_feed
@@ -43,6 +48,7 @@ def test_status_update(self, mock_get_active_app_names: Mock):
4348
self.assertEqual(item.type, FeedItemType.case_status_changed)
4449
self.assertEqual(item.action_required, False)
4550
self.assertEqual(item.is_completed, False)
51+
self.assertEqual(item.status_indicator, StatusIndicators.info)
4652
self.assertEqual(
4753
strip_tags(item.message),
4854
escape(_("Case status has been changed to 'initial'")),
@@ -109,3 +115,51 @@ def test_status_update(self, mock_get_active_app_names: Mock):
109115
# doesn't break on repeat
110116
case_status_seen(user, case)
111117
self.assertEqual(get_feed(user).total_items, 0)
118+
119+
@patch("open_inwoner.userfeed.feed.get_active_app_names", return_value=["cases"])
120+
def test_status_update_with_status_type_config(
121+
self, mock_get_active_app_names: Mock
122+
):
123+
data = MockAPIData()
124+
user = data.user_initiator
125+
126+
case = factory(Zaak, data.zaak)
127+
case.zaaktype = factory(ZaakType, data.zaak_type)
128+
129+
status = factory(Status, data.status_initial)
130+
status.statustype = factory(StatusType, data.status_type_initial)
131+
132+
ztc = ZaakTypeConfigFactory(
133+
identificatie=case.zaaktype.identificatie,
134+
catalogus__url=case.zaaktype.catalogus,
135+
)
136+
status_config = ZaakTypeStatusTypeConfigFactory(
137+
zaaktype_config=ztc,
138+
statustype_url=status.statustype.url,
139+
status_indicator=StatusIndicators.warning,
140+
status_indicator_text="my_status_text",
141+
action_required=True,
142+
)
143+
144+
case_status_notification_received(user, case, status)
145+
146+
# check feed
147+
feed = get_feed(user)
148+
self.assertEqual(feed.total_items, 1)
149+
self.assertEqual(len(feed.summary), 2)
150+
151+
# check item
152+
item = feed.items[0]
153+
self.assertEqual(item.type, FeedItemType.case_status_changed)
154+
self.assertEqual(item.action_required, True)
155+
self.assertEqual(item.is_completed, False)
156+
self.assertEqual(item.status_indicator, StatusIndicators.warning)
157+
self.assertEqual(
158+
strip_tags(item.message),
159+
escape(_("Case status has been changed to 'initial'")),
160+
)
161+
self.assertEqual(item.title, "my_status_text")
162+
self.assertEqual(
163+
item.action_url,
164+
reverse("cases:case_detail", kwargs={"object_id": case.uuid}),
165+
)

src/open_inwoner/userfeed/tests/test_feed.py

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from freezegun import freeze_time
77

88
from open_inwoner.accounts.tests.factories import UserFactory
9+
from open_inwoner.openzaak.constants import StatusIndicators
910
from open_inwoner.userfeed.choices import FeedItemType
1011
from open_inwoner.userfeed.feed import ACTION_COMPLETED_HISTORY_RANGE, get_feed
1112
from open_inwoner.userfeed.hooks.common import simple_message
@@ -39,6 +40,7 @@ def test_get_feed__and__simple_message(self):
3940
self.assertEqual(item.message, "Hello")
4041
self.assertEqual(item.title, "Test message")
4142
self.assertEqual(item.action_url, "http://foo.bar")
43+
self.assertEqual(item.status_indicator, StatusIndicators.info)
4244

4345
# check summary
4446
summary = feed.summary[0]

0 commit comments

Comments
 (0)