Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [#2149] Display new answer header for mijn vragen list #1075

Merged
merged 6 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/open_inwoner/accounts/views/contactmoments.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
from open_inwoner.openklant.api_models import KlantContactMoment
from open_inwoner.openklant.clients import build_client
from open_inwoner.openklant.constants import Status
from open_inwoner.openklant.models import ContactFormSubject
from open_inwoner.openklant.models import ContactFormSubject, KlantContactMomentLocal
from open_inwoner.openklant.views.contactform import ContactFormView
from open_inwoner.openklant.wrap import (
contactmoment_has_new_answer,
fetch_klantcontactmoment,
fetch_klantcontactmomenten,
get_fetch_parameters,
get_local_kcm_mapping,
)
from open_inwoner.openzaak.clients import build_client as build_client_openzaak
from open_inwoner.utils.mixins import PaginationMixin
Expand Down Expand Up @@ -71,12 +73,17 @@ class KCMDict(TypedDict):
onderwerp: str
status: str
antwoord: str
new_answer_available: bool


class KlantContactMomentBaseView(
CommonPageMixin, BaseBreadcrumbMixin, KlantContactMomentAccessMixin, TemplateView
):
def get_kcm_data(self, kcm: KlantContactMoment) -> KCMDict:
def get_kcm_data(
self,
kcm: KlantContactMoment,
local_kcm_mapping: Optional[dict[str, KlantContactMomentLocal]] = None,
) -> KCMDict:
data = {
"registered_date": kcm.contactmoment.registratiedatum,
"channel": kcm.contactmoment.kanaal.title(),
Expand All @@ -87,6 +94,9 @@ def get_kcm_data(self, kcm: KlantContactMoment) -> KCMDict:
"type": kcm.contactmoment.type,
"status": Status.safe_label(kcm.contactmoment.status, _("Onbekend")),
"antwoord": kcm.contactmoment.antwoord,
"new_answer_available": contactmoment_has_new_answer(
kcm.contactmoment, local_kcm_mapping=local_kcm_mapping
),
}

# replace e_suite_subject_code with OIP configured subject, if applicable
Expand Down Expand Up @@ -155,7 +165,15 @@ def get_context_data(self, **kwargs):
kcms = fetch_klantcontactmomenten(
**get_fetch_parameters(self.request, use_vestigingsnummer=True)
)
ctx["contactmomenten"] = [self.get_kcm_data(kcm) for kcm in kcms]
ctx["contactmomenten"] = [
self.get_kcm_data(
kcm,
local_kcm_mapping=get_local_kcm_mapping(
[kcm.contactmoment for kcm in kcms], self.request.user
),
)
for kcm in kcms
]
paginator_dict = self.paginate_with_context(ctx["contactmomenten"])
ctx.update(paginator_dict)
return ctx
Expand Down Expand Up @@ -188,6 +206,13 @@ def get_context_data(self, **kwargs):
if not kcm:
raise Http404()

local_kcm, is_created = KlantContactMomentLocal.objects.get_or_create( # noqa
user=self.request.user, contactmoment_url=kcm.contactmoment.url
)
if not local_kcm.is_seen:
local_kcm.is_seen = True
local_kcm.save()

if client := build_client("contactmomenten"):
zaken_client = build_client_openzaak("zaak")
ocm = client.retrieve_objectcontactmoment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"vorigContactmoment": null,
"volgendContactmoment": null,
"bronorganisatie": "WwXGZrg",
"registratiedatum": "2022-05-15T23:12:13+00:00",
"registratiedatum": "2030-05-15T23:12:13+00:00",
"voorkeurskanaal": "xyz",
"voorkeurstaal": "vzT",
"tekst": "Mag ik mijn garage veranderen in een kantoor?\n\n",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
"previous": null,
"next": null,
"results": [
{
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-cccccccccccc",
"url": "https://contactmomenten.nl/api/v1/klantcontactmomenten/aaaaaaaa-aaaa-aaaa-aaaa-cccccccccccc",
"klant": "https://klanten.nl/api/v1/klant/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"contactmoment": "https://contactmomenten.nl/api/v1/contactmoment/aaaaaaaa-aaaa-aaaa-aaaa-bbbbbbbbbbbb",
"rol": "gesprekspartner",
"gelezen": true
},
{
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-ccccccccccc2",
"url": "https://contactmomenten.nl/api/v1/klantcontactmomenten/aaaaaaaa-aaaa-aaaa-aaaa-ccccccccccc2",
Expand Down

This file was deleted.

This file was deleted.

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
6 changes: 4 additions & 2 deletions src/open_inwoner/components/templatetags/file_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from filer.models.filemodels import File

from open_inwoner.cms.cases.views.status import SimpleFile
from open_inwoner.utils.time import is_new
from open_inwoner.utils.time import instance_is_new

register = template.Library()

Expand Down Expand Up @@ -147,7 +147,9 @@ def file(file, **kwargs):
created = getattr(file, "created", None)
kwargs["created"] = created

if is_new(file, "created", datetime.timedelta(days=settings.DOCUMENT_RECENT_DAYS)):
if instance_is_new(
file, "created", datetime.timedelta(days=settings.DOCUMENT_RECENT_DAYS)
):
kwargs["recently_added"] = True

if "show_download" not in kwargs:
Expand Down
3 changes: 3 additions & 0 deletions src/open_inwoner/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,9 @@
# recent documents: created/added no longer than n days in the past
DOCUMENT_RECENT_DAYS = config("DOCUMENT_RECENT_DAYS", default=1)

# recent answers to contactmomenten: no longer than n days in the past
CONTACTMOMENT_NEW_DAYS = config("CONTACTMOMENT_NEW_DAYS", default=7)

#
# Maykin 2FA
#
Expand Down
4 changes: 4 additions & 0 deletions src/open_inwoner/conf/fixtures/django-admin-index.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@
[
"filer",
"folderpermission"
],
[
"openklant",
"klantcontactmomentlocal"
stevenbal marked this conversation as resolved.
Show resolved Hide resolved
]
]
}
Expand Down
14 changes: 13 additions & 1 deletion src/open_inwoner/openklant/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ordered_model.admin import OrderedInlineModelAdminMixin, OrderedTabularInline
from solo.admin import SingletonModelAdmin

from .models import ContactFormSubject, OpenKlantConfig
from .models import ContactFormSubject, KlantContactMomentLocal, OpenKlantConfig


class ContactFormSubjectInlineAdmin(OrderedTabularInline):
Expand Down Expand Up @@ -70,3 +70,15 @@ class OpenKlantConfigAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin):
},
),
]


@admin.register(KlantContactMomentLocal)
class KlantContactMomentLocalAdmin(admin.ModelAdmin):
search_fields = [
"user__first_name",
"user__last_name",
"user__email",
"contactmoment_url",
]
list_filter = ["is_seen"]
list_display = ["user", "contactmoment_url", "is_seen"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Generated by Django 4.2.10 on 2024-03-15 12:57

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("openklant", "0008_openklantconfig_use_rsin_for_innnnpid_query_parameter"),
]

operations = [
migrations.CreateModel(
name="KlantContactMomentLocal",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"contactmoment_url",
models.URLField(max_length=1000, verbose_name="ContactMoment URL"),
),
(
"is_seen",
models.BooleanField(
default=False,
help_text="Whether or not the user has seen the answer",
verbose_name="Is seen",
),
),
(
"user",
models.ForeignKey(
help_text="This is the user that asked the question to which this is an answer.",
on_delete=django.db.models.deletion.CASCADE,
related_name="contactmoment_answers",
to=settings.AUTH_USER_MODEL,
verbose_name="User",
),
),
],
options={
"verbose_name": "KlantContactMoment",
"verbose_name_plural": "KlantContactMomenten",
"unique_together": {("user", "contactmoment_url")},
},
),
]
25 changes: 25 additions & 0 deletions src/open_inwoner/openklant/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,28 @@ class Meta(OrderedModel.Meta):

def __str__(self):
return self.subject


class KlantContactMomentLocal(models.Model):
stevenbal marked this conversation as resolved.
Show resolved Hide resolved
user = models.ForeignKey(
"accounts.User",
verbose_name=_("User"),
on_delete=models.CASCADE,
related_name="contactmoment_answers",
help_text=_(
"This is the user that asked the question to which this is an answer."
),
)
contactmoment_url = models.URLField(
verbose_name=_("ContactMoment URL"), max_length=1000
)
is_seen = models.BooleanField(
verbose_name=_("Is seen"),
help_text=_("Whether or not the user has seen the answer"),
default=False,
)

class Meta:
verbose_name = _("KlantContactMoment")
verbose_name_plural = _("KlantContactMomenten")
unique_together = [["user", "contactmoment_url"]]
3 changes: 2 additions & 1 deletion src/open_inwoner/openklant/tests/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ def __init__(self):
type="SomeType",
kanaal="MAIL",
status=Status.afgehandeld,
antwoord="",
antwoord="foo",
onderwerp="e_suite_subject_code",
registratiedatum="2022-01-01T12:00:00Z",
)
self.contactmoment2 = generate_oas_component_cached(
"cmc",
Expand Down
9 changes: 9 additions & 0 deletions src/open_inwoner/openklant/tests/factories.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import factory
from zgw_consumers.api_models.base import factory as zgw_factory

from open_inwoner.accounts.tests.factories import UserFactory
from open_inwoner.openklant.api_models import ContactMoment


Expand All @@ -14,3 +15,11 @@ class Meta:

def make_contactmoment(contact_moment_data: dict):
return zgw_factory(ContactMoment, contact_moment_data)


class KlantContactMomentLocalFactory(factory.django.DjangoModelFactory):
class Meta:
model = "openklant.KlantContactMomentLocal"

contactmoment_url = factory.Faker("url")
user = factory.SubFactory(UserFactory)
46 changes: 46 additions & 0 deletions src/open_inwoner/openklant/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.test import TestCase

import requests_mock

from open_inwoner.openklant.models import KlantContactMomentLocal
from open_inwoner.openklant.tests.data import MockAPIReadData
from open_inwoner.openklant.wrap import (
fetch_klantcontactmomenten,
get_local_kcm_mapping,
)
from open_inwoner.utils.test import ClearCachesMixin, DisableRequestLogMixin


@requests_mock.Mocker()
class HelpersTestCase(ClearCachesMixin, DisableRequestLogMixin, TestCase):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
MockAPIReadData.setUpServices()

def test_get_local_kcm_mapping(self, m):
data = MockAPIReadData().install_mocks(m)

kcms = fetch_klantcontactmomenten(user_bsn=data.user.bsn)

with self.subTest("running the first time will create KlantContactMomentLocal"):
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].contactmoment.url: kcm_local})
self.assertEqual(kcm_local.user, data.user)
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(
[kcm.contactmoment for kcm in kcms], data.user
)

self.assertEqual(KlantContactMomentLocal.objects.count(), 1)
self.assertEqual(mapping, {kcms[0].contactmoment.url: kcm_local})
Loading
Loading