Skip to content

Commit

Permalink
Merge pull request #1058 from maykinmedia/feature/2058-actionable-use…
Browse files Browse the repository at this point in the history
…rfeed

[#2058] Added status indicator to case_status userfeed items
  • Loading branch information
alextreme authored Feb 29, 2024
2 parents d11a962 + 2c5d122 commit a410dd2
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 3 deletions.
22 changes: 22 additions & 0 deletions src/open_inwoner/openzaak/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin):
"identificatie",
"omschrijving",
"num_infotypes",
"num_statustypes",
"num_resulttypes",
]
list_display = [
"identificatie",
Expand All @@ -288,6 +290,8 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin):
"contact_subject_code",
"document_upload_enabled",
"num_infotypes",
"num_statustypes",
"num_resulttypes",
]
list_display_links = [
"identificatie",
Expand Down Expand Up @@ -316,6 +320,8 @@ def has_delete_permission(self, request, obj=None):
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.annotate(num_infotypes=Count("zaaktypeinformatieobjecttypeconfig"))
qs = qs.annotate(num_statustypes=Count("zaaktypestatustypeconfig"))
qs = qs.annotate(num_resulttypes=Count("zaaktyperesultaattypeconfig"))
qs = qs.annotate(
num_doc_notify=Count(
"zaaktypeinformatieobjecttypeconfig",
Expand All @@ -339,6 +345,22 @@ def num_infotypes(self, obj=None):

num_infotypes.admin_order_field = "num_infotypes"

def num_statustypes(self, obj=None):
if not obj or not obj.pk:
return "-"
else:
return getattr(obj, "num_statustypes", 0)

num_statustypes.admin_order_field = "num_statustypes"

def num_resulttypes(self, obj=None):
if not obj or not obj.pk:
return "-"
else:
return getattr(obj, "num_resulttypes", 0)

num_resulttypes.admin_order_field = "num_resulttypes"

def has_doc_notify(self, obj=None):
if not obj or not obj.pk:
return False
Expand Down
41 changes: 40 additions & 1 deletion src/open_inwoner/openzaak/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils import timezone

from open_inwoner.accounts.models import User
from open_inwoner.openzaak.api_models import ZaakType
from open_inwoner.openzaak.api_models import Status, StatusType, Zaak, ZaakType
from open_inwoner.utils.translate import TranslationLookup

if TYPE_CHECKING:
Expand Down Expand Up @@ -139,6 +139,20 @@ def filter_case_type(self, case_type: ZaakType):
identificatie=case_type.identificatie,
)

def filter_from_str(self, catalogus_url: str, case_type_identificatie: str):
qs = self
if catalogus_url:
qs = qs.filter(
catalogus__url=catalogus_url,
)
else:
qs = qs.filter(
catalogus__isnull=True,
)
return qs.filter(
identificatie=case_type_identificatie,
)

def filter_enabled_for_case_type(self, case_type: ZaakType):
"""
Returns all ZaakTypeConfig instances which allow external documents
Expand All @@ -165,6 +179,31 @@ def filter_questions_enabled_for_case_type(self, case_type: ZaakType):
return self.filter_case_type(case_type).filter(questions_enabled=True)


class ZaakTypeStatusTypeConfigQuerySet(models.QuerySet):
def find_for(self, case: Zaak, status: Status):
return self.find_for_types(case.zaaktype, status.statustype)

def find_for_types(self, case_type: ZaakType, status_type: StatusType):
from .models import ZaakTypeConfig

ztc = ZaakTypeConfig.objects.filter_case_type(case_type)
return self.filter(
zaaktype_config__in=ztc, statustype_url=status_type.url
).first()

def find_for_types_from_str(
self, catalogus_url: str, case_type_identificatie: str, status_type_url: str
):
from .models import ZaakTypeConfig

ztc = ZaakTypeConfig.objects.filter_from_str(
catalogus_url, case_type_identificatie
)
return self.filter(
zaaktype_config__in=ztc, statustype_url=status_type_url
).first()


class StatusTranslationQuerySet(models.QuerySet):
def get_lookup(self):
return TranslationLookup(self.values_list("status", "translation"))
3 changes: 3 additions & 0 deletions src/open_inwoner/openzaak/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
UserCaseStatusNotificationManager,
ZaakTypeConfigQueryset,
ZaakTypeInformatieObjectTypeConfigQueryset,
ZaakTypeStatusTypeConfigQuerySet,
)

from .constants import StatusIndicators
Expand Down Expand Up @@ -465,6 +466,8 @@ class ZaakTypeStatusTypeConfig(models.Model):
),
)

objects = ZaakTypeStatusTypeConfigQuerySet.as_manager()

class Meta:
verbose_name = _("Zaaktype Statustype Configuration")

Expand Down
5 changes: 5 additions & 0 deletions src/open_inwoner/templates/cms/plugins/userfeed/userfeed.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ <h2 class="h2 {% if userfeed.action_required %}indicator{% endif %}">
<a href="{{ item.action_url }}" class="card {% if item.action_required %}card--status{% endif %} {% if item.is_completed %}card--completed{% endif %}">
<div class="card__body card__body--tabled">
<p class="p tabled__value">{{ item.title }}</p>

<p class="p" style="color: rebeccapurple; font-size: 300%;">
{{ item.status_indicator }}
</p>

<h2 class="userfeed__heading">
<span class="">{{ item.message }}</span>
{# Status gewijzigd naar &lsquo;<span class="status">Documenten nodig</span>&rsquo;</span>#}
Expand Down
9 changes: 9 additions & 0 deletions src/open_inwoner/userfeed/adapter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from open_inwoner.openzaak.constants import StatusIndicators
from open_inwoner.userfeed.models import FeedItemData


Expand All @@ -12,6 +13,8 @@ class FeedItem:
base_message = ""
base_action_text = ""
base_action_url = ""
base_status_indicator = StatusIndicators.info
base_status_indicator_action = StatusIndicators.warning

cms_apps: list[str] = []

Expand Down Expand Up @@ -41,6 +44,12 @@ def title(self) -> str:
def message(self) -> str:
return self.get_data("message") or self.base_message

@property
def status_indicator(self) -> str:
if self.action_required:
return self.base_status_indicator_action
return self.base_status_indicator

@property
def action_text(self) -> str:
return self.base_action_text
Expand Down
36 changes: 35 additions & 1 deletion src/open_inwoner/userfeed/hooks/case_status.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import timedelta
from typing import Optional

from django.db.models import Q
from django.urls import reverse
Expand All @@ -8,6 +9,7 @@

from open_inwoner.accounts.models import User
from open_inwoner.openzaak.api_models import Status, Zaak
from open_inwoner.openzaak.models import ZaakTypeStatusTypeConfig
from open_inwoner.openzaak.utils import translate_single_status
from open_inwoner.userfeed.adapter import FeedItem
from open_inwoner.userfeed.adapters import register_item_adapter
Expand All @@ -23,8 +25,17 @@ def case_status_notification_received(user: User, case: Zaak, status: Status):
"case_identificatie": case.identificatie,
"case_omschrijving": case.omschrijving,
"status_omschrijving": status.statustype.omschrijving,
# new for actionable
"catalogus_url": case.zaaktype.catalogus,
"case_type_identificatie": case.zaaktype.identificatie,
"status_type_url": status.statustype.url,
}

action_required = False
status_config = ZaakTypeStatusTypeConfig.objects.find_for(case, status)
if status_config:
action_required = status_config.action_required

# let's try to update last record if change happened recently
qs = FeedItemData.objects.filter(
user=user,
Expand All @@ -38,12 +49,14 @@ def case_status_notification_received(user: User, case: Zaak, status: Status):
display_at=timezone.now(),
completed_at=None,
type_data=data,
action_required=action_required,
):
FeedItemData.objects.create(
user=user,
type=FeedItemType.case_status_changed,
ref_uuid=case.uuid,
type_data=data,
action_required=action_required,
)


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

cms_apps = ["cases"]

status_config: Optional[ZaakTypeStatusTypeConfig] = None

def __init__(self, data: FeedItemData):
super().__init__(data)

self.status_config = ZaakTypeStatusTypeConfig.objects.find_for_types_from_str(
self.get_data("catalogus_url"),
self.get_data("case_type_identificatie"),
self.get_data("status_type_url"),
)

@property
def title(self) -> str:
return self.get_data("case_omschrijving", super().title)
if self.status_config and self.status_config.status_indicator_text:
return self.status_config.status_indicator_text
else:
return self.get_data("case_omschrijving", super().title)

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

@property
def status_indicator(self) -> str:
if self.status_config and self.status_config.status_indicator:
return self.status_config.status_indicator
else:
return super().status_indicator


register_item_adapter(CaseStatusUpdateFeedItem, FeedItemType.case_status_changed)
56 changes: 55 additions & 1 deletion src/open_inwoner/userfeed/tests/hooks/test_case_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
from zgw_consumers.api_models.base import factory

from open_inwoner.openzaak.api_models import Status, StatusType, Zaak, ZaakType
from open_inwoner.openzaak.tests.factories import StatusTranslationFactory
from open_inwoner.openzaak.constants import StatusIndicators
from open_inwoner.openzaak.tests.factories import (
StatusTranslationFactory,
ZaakTypeConfigFactory,
ZaakTypeStatusTypeConfigFactory,
)
from open_inwoner.openzaak.tests.test_notification_data import MockAPIData
from open_inwoner.userfeed.choices import FeedItemType
from open_inwoner.userfeed.feed import get_feed
Expand Down Expand Up @@ -43,6 +48,7 @@ def test_status_update(self, mock_get_active_app_names: Mock):
self.assertEqual(item.type, FeedItemType.case_status_changed)
self.assertEqual(item.action_required, False)
self.assertEqual(item.is_completed, False)
self.assertEqual(item.status_indicator, StatusIndicators.info)
self.assertEqual(
strip_tags(item.message),
escape(_("Case status has been changed to 'initial'")),
Expand Down Expand Up @@ -109,3 +115,51 @@ def test_status_update(self, mock_get_active_app_names: Mock):
# doesn't break on repeat
case_status_seen(user, case)
self.assertEqual(get_feed(user).total_items, 0)

@patch("open_inwoner.userfeed.feed.get_active_app_names", return_value=["cases"])
def test_status_update_with_status_type_config(
self, mock_get_active_app_names: Mock
):
data = MockAPIData()
user = data.user_initiator

case = factory(Zaak, data.zaak)
case.zaaktype = factory(ZaakType, data.zaak_type)

status = factory(Status, data.status_initial)
status.statustype = factory(StatusType, data.status_type_initial)

ztc = ZaakTypeConfigFactory(
identificatie=case.zaaktype.identificatie,
catalogus__url=case.zaaktype.catalogus,
)
status_config = ZaakTypeStatusTypeConfigFactory(
zaaktype_config=ztc,
statustype_url=status.statustype.url,
status_indicator=StatusIndicators.warning,
status_indicator_text="my_status_text",
action_required=True,
)

case_status_notification_received(user, case, status)

# check feed
feed = get_feed(user)
self.assertEqual(feed.total_items, 1)
self.assertEqual(len(feed.summary), 2)

# check item
item = feed.items[0]
self.assertEqual(item.type, FeedItemType.case_status_changed)
self.assertEqual(item.action_required, True)
self.assertEqual(item.is_completed, False)
self.assertEqual(item.status_indicator, StatusIndicators.warning)
self.assertEqual(
strip_tags(item.message),
escape(_("Case status has been changed to 'initial'")),
)
self.assertEqual(item.title, "my_status_text")
self.assertEqual(
item.action_url,
reverse("cases:case_detail", kwargs={"object_id": case.uuid}),
)
2 changes: 2 additions & 0 deletions src/open_inwoner/userfeed/tests/test_feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from freezegun import freeze_time

from open_inwoner.accounts.tests.factories import UserFactory
from open_inwoner.openzaak.constants import StatusIndicators
from open_inwoner.userfeed.choices import FeedItemType
from open_inwoner.userfeed.feed import ACTION_COMPLETED_HISTORY_RANGE, get_feed
from open_inwoner.userfeed.hooks.common import simple_message
Expand Down Expand Up @@ -39,6 +40,7 @@ def test_get_feed__and__simple_message(self):
self.assertEqual(item.message, "Hello")
self.assertEqual(item.title, "Test message")
self.assertEqual(item.action_url, "http://foo.bar")
self.assertEqual(item.status_indicator, StatusIndicators.info)

# check summary
summary = feed.summary[0]
Expand Down

0 comments on commit a410dd2

Please sign in to comment.