Skip to content

Commit

Permalink
✨ [#2149] Implement new answer indicator for questions in aanvraag de…
Browse files Browse the repository at this point in the history
…tail

replaced KlantCntactMomentLocal.klantcontactmoment_url with contactmoment_url, so that we can easily get the local mapping without having to fetch KlantContactMoment resources
  • Loading branch information
stevenbal committed Mar 15, 2024
1 parent 26a0ef1 commit 85fe1bd
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 34 deletions.
11 changes: 8 additions & 3 deletions src/open_inwoner/accounts/views/contactmoments.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ def get_kcm_data(
timedelta(days=settings.CONTACTMOMENT_NEW_DAYS),
)
if local_kcm_mapping:
is_seen = getattr(local_kcm_mapping.get(kcm.url), "is_seen", False)
is_seen = getattr(
local_kcm_mapping.get(kcm.contactmoment.url), "is_seen", False
)
else:
# In the detail view, this is automatically true
is_seen = True
Expand Down Expand Up @@ -178,7 +180,10 @@ def get_context_data(self, **kwargs):
)
ctx["contactmomenten"] = [
self.get_kcm_data(
kcm, local_kcm_mapping=get_local_kcm_mapping(kcms, self.request.user)
kcm,
local_kcm_mapping=get_local_kcm_mapping(
[kcm.contactmoment for kcm in kcms], self.request.user
),
)
for kcm in kcms
]
Expand Down Expand Up @@ -215,7 +220,7 @@ def get_context_data(self, **kwargs):
raise Http404()

local_kcm, is_created = KlantContactMomentLocal.objects.get_or_create( # noqa
user=self.request.user, klantcontactmoment_url=kcm.url
user=self.request.user, contactmoment_url=kcm.contactmoment.url
)
if not local_kcm.is_seen:
local_kcm.is_seen = True
Expand Down
12 changes: 11 additions & 1 deletion src/open_inwoner/cms/cases/views/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@

from open_inwoner.openklant.clients import build_client as build_client_openklant
from open_inwoner.openklant.models import OpenKlantConfig
from open_inwoner.openklant.wrap import get_fetch_parameters
from open_inwoner.openklant.wrap import (
contactmoment_has_new_answer,
get_fetch_parameters,
get_local_kcm_mapping,
)
from open_inwoner.openzaak.api_models import Status, StatusType, Zaak
from open_inwoner.openzaak.clients import (
ZakenClient,
Expand Down Expand Up @@ -155,6 +159,12 @@ def get_context_data(self, **kwargs):
questions = [ocm.contactmoment for ocm in objectcontactmomenten]
questions.sort(key=lambda q: q.registratiedatum, reverse=True)

local_mapping = get_local_kcm_mapping(questions, self.request.user)
for question in questions:
question.new_answer_available = contactmoment_has_new_answer(
question, local_mapping
)

statustypen = []
if catalogi_client := build_client_openzaak("catalogi"):
statustypen = catalogi_client.fetch_status_types_no_cache(
Expand Down
4 changes: 2 additions & 2 deletions src/open_inwoner/openklant/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class KlantContactMomentLocalAdmin(admin.ModelAdmin):
"user__first_name",
"user__last_name",
"user__email",
"klantcontactmoment_url",
"contactmoment_url",
]
list_filter = ["is_seen"]
list_display = ["user", "klantcontactmoment_url", "is_seen"]
list_display = ["user", "contactmoment_url", "is_seen"]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.10 on 2024-03-15 09:43
# Generated by Django 4.2.10 on 2024-03-15 12:57

from django.conf import settings
from django.db import migrations, models
Expand Down Expand Up @@ -26,12 +26,8 @@ class Migration(migrations.Migration):
),
),
(
"klantcontactmoment_url",
models.URLField(
max_length=1000,
unique=True,
verbose_name="KlantContactMoment URL",
),
"contactmoment_url",
models.URLField(max_length=1000, verbose_name="ContactMoment URL"),
),
(
"is_seen",
Expand All @@ -55,6 +51,7 @@ class Migration(migrations.Migration):
options={
"verbose_name": "KlantContactMoment",
"verbose_name_plural": "KlantContactMomenten",
"unique_together": {("user", "contactmoment_url")},
},
),
]
5 changes: 3 additions & 2 deletions src/open_inwoner/openklant/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ class KlantContactMomentLocal(models.Model):
"This is the user that asked the question to which this is an answer."
),
)
klantcontactmoment_url = models.URLField(
verbose_name=_("KlantContactMoment URL"), unique=True, max_length=1000
contactmoment_url = models.URLField(
verbose_name=_("ContactMoment URL"), max_length=1000
)
is_seen = models.BooleanField(
verbose_name=_("Is seen"),
Expand All @@ -151,3 +151,4 @@ class KlantContactMomentLocal(models.Model):
class Meta:
verbose_name = _("KlantContactMoment")
verbose_name_plural = _("KlantContactMomenten")
unique_together = [["user", "contactmoment_url"]]
2 changes: 1 addition & 1 deletion src/open_inwoner/openklant/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ class KlantContactMomentLocalFactory(factory.django.DjangoModelFactory):
class Meta:
model = "openklant.KlantContactMomentLocal"

klantcontactmoment_url = factory.Faker("url")
contactmoment_url = factory.Faker("url")
user = factory.SubFactory(UserFactory)
14 changes: 9 additions & 5 deletions src/open_inwoner/openklant/tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,23 @@ def test_get_local_kcm_mapping(self, m):
kcms = fetch_klantcontactmomenten(user_bsn=data.user.bsn)

with self.subTest("running the first time will create KlantContactMomentLocal"):
mapping = get_local_kcm_mapping(kcms, data.user)
mapping = get_local_kcm_mapping(
[kcm.contactmoment for kcm in kcms], data.user
)

self.assertEqual(KlantContactMomentLocal.objects.count(), 1)

kcm_local = KlantContactMomentLocal.objects.get()

self.assertEqual(mapping, {kcms[0].url: kcm_local})
self.assertEqual(mapping, {kcms[0].contactmoment.url: kcm_local})
self.assertEqual(kcm_local.user, data.user)
self.assertEqual(kcm_local.klantcontactmoment_url, kcms[0].url)
self.assertEqual(kcm_local.contactmoment_url, kcms[0].contactmoment.url)
self.assertEqual(kcm_local.is_seen, False)

with self.subTest("running function again will ignore existing entries"):
mapping = get_local_kcm_mapping(kcms, data.user)
mapping = get_local_kcm_mapping(
[kcm.contactmoment for kcm in kcms], data.user
)

self.assertEqual(KlantContactMomentLocal.objects.count(), 1)
self.assertEqual(mapping, {kcms[0].url: kcm_local})
self.assertEqual(mapping, {kcms[0].contactmoment.url: kcm_local})
12 changes: 8 additions & 4 deletions src/open_inwoner/openklant/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ def test_list_for_bsn(self, m, mock_get_local_kcm_mapping):
kcm.contactmoment = factory(ContactMoment, data.contactmoment)
kcm.klant = factory(Klant, data.klant)

mock_get_local_kcm_mapping.assert_called_once_with([kcm], data.user)
mock_get_local_kcm_mapping.assert_called_once_with(
[kcm.contactmoment], data.user
)

status_item = response.pyquery.find(f"p:contains('{_('Status')}')").parent()

Expand All @@ -100,7 +102,7 @@ def test_list_for_bsn_new_answer_available(self, m, mock_get_local_kcm_mapping):
mock_get_local_kcm_mapping.return_value = {
data.klant_contactmoment["url"]: KlantContactMomentLocalFactory.create(
user=data.user,
klantcontactmoment_url=data.klant_contactmoment["url"],
contactmoment_url=data.klant_contactmoment["contactmoment"],
is_seen=False,
)
}
Expand Down Expand Up @@ -136,7 +138,9 @@ def test_list_for_bsn_new_answer_available(self, m, mock_get_local_kcm_mapping):
kcm.contactmoment = factory(ContactMoment, data.contactmoment)
kcm.klant = factory(Klant, data.klant)

mock_get_local_kcm_mapping.assert_called_once_with([kcm], data.user)
mock_get_local_kcm_mapping.assert_called_once_with(
[kcm.contactmoment], data.user
)

status_item = response.pyquery.find(f"p:contains('{_('Status')}')").parent()

Expand Down Expand Up @@ -300,7 +304,7 @@ def test_show_detail_for_bsn_with_zaak(self, m, mock_get_local_kcm_mapping):
)

kcm_local = KlantContactMomentLocal.objects.get(
klantcontactmoment_url=data.klant_contactmoment["url"]
contactmoment_url=data.klant_contactmoment["contactmoment"]
)

self.assertEqual(kcm_local.user, data.user)
Expand Down
34 changes: 25 additions & 9 deletions src/open_inwoner/openklant/wrap.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import logging
from datetime import timedelta
from typing import List, Optional

from django.conf import settings

from open_inwoner.accounts.models import User
from open_inwoner.kvk.branches import get_kvk_branch_number
from open_inwoner.openklant.api_models import KlantContactMoment
from open_inwoner.openklant.api_models import ContactMoment, KlantContactMoment
from open_inwoner.openklant.clients import build_client
from open_inwoner.openklant.models import KlantContactMomentLocal, OpenKlantConfig
from open_inwoner.utils.time import instance_is_new

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -99,28 +103,40 @@ def get_fetch_parameters(request, use_vestigingsnummer: bool = False) -> dict:


def get_local_kcm_mapping(
kcms: list[KlantContactMoment], user: User
contactmomenten: list[ContactMoment],
user: User,
) -> dict[str, KlantContactMomentLocal]:
to_create = []
existing_kcms = set(
KlantContactMomentLocal.objects.filter(user=user).values_list(
"klantcontactmoment_url", flat=True
"contactmoment_url", flat=True
)
)

to_create = []
for kcm in kcms:
if kcm.url in existing_kcms:
for contactmoment in contactmomenten:
if contactmoment.url in existing_kcms:
continue

to_create.append(
KlantContactMomentLocal(user=user, klantcontactmoment_url=kcm.url)
KlantContactMomentLocal(user=user, contactmoment_url=contactmoment.url)
)

KlantContactMomentLocal.objects.bulk_create(to_create)

kcm_answer_mapping = {
kcm_answer.klantcontactmoment_url: kcm_answer
kcm_answer.contactmoment_url: kcm_answer
for kcm_answer in KlantContactMomentLocal.objects.filter(user=user)
}

return kcm_answer_mapping


def contactmoment_has_new_answer(
contactmoment: ContactMoment, local_kcm_mapping: dict[str, KlantContactMomentLocal]
) -> bool:
is_new = instance_is_new(
contactmoment,
"registratiedatum",
timedelta(days=settings.CONTACTMOMENT_NEW_DAYS),
)
is_seen = getattr(local_kcm_mapping.get(contactmoment.url), "is_seen", False)
return bool(contactmoment.antwoord) and is_new and not is_seen
4 changes: 4 additions & 0 deletions src/open_inwoner/templates/pages/questions/questions.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ <h2 class="h3 contactmomenten__title">{% trans "Vragen" %}</h2>
{% for contactmoment in questions %}
{# redirect to fetch klantcontactmoment from contactmoment #}
<a href="{% url 'cases:kcm_redirect' contactmoment.uuid %}" class="card card--compact card--stretch card--toggle-hide contactmomenten__link">
{% if contactmoment.new_answer_available %}
{% translate "Nieuw antwoord beschikbaar" as new_answer_text %}
{% include "components/StatusIndicator/StatusIndicator.html" with status_indicator="info" status_indicator_text=new_answer_text only %}
{% endif %}
<div class="card__body">
{% render_list %}
<span class="contactmomenten-list">
Expand Down

0 comments on commit 85fe1bd

Please sign in to comment.