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

[#1050] Implemented basic zaakinformationobject notifications #459

Merged
merged 5 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 5 additions & 5 deletions src/open_inwoner/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,15 +911,15 @@
],
},
"case_notification": {
"name": _("Case status update notification"),
"name": _("Case update notification"),
"description": _(
"This email is used to notify people a status update happened to their case"
"This email is used to notify people an update happened to their case"
),
"subject_default": "Status update to your case at {{ site_name }}",
"subject_default": "Update to your case at {{ site_name }}",
"body_default": """
<p>Beste</p>

<p>You are receiving this email because one of your cases received a status update.</p>
<p>You are receiving this email because one of your cases received a new status update or document attachment.</p>

<table>
<tr>
Expand Down Expand Up @@ -962,7 +962,7 @@
},
{
"name": "case_link",
"description": _("The link to your case details."),
"description": _("The link to the case details."),
},
{
"name": "site_name",
Expand Down
76 changes: 73 additions & 3 deletions src/open_inwoner/openzaak/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib import admin, messages
from django.core.exceptions import ValidationError
from django.db.models import Count
from django.db.models import BooleanField, Count, Exists, ExpressionWrapper, Q
from django.forms.models import BaseInlineFormSet
from django.utils.translation import gettext_lazy as _, ngettext

Expand All @@ -9,6 +9,7 @@
from .models import (
CatalogusConfig,
OpenZaakConfig,
UserCaseInfoObjectNotification,
UserCaseStatusNotification,
ZaakTypeConfig,
ZaakTypeInformatieObjectTypeConfig,
Expand Down Expand Up @@ -41,6 +42,25 @@ class CatalogusConfigAdmin(admin.ModelAdmin):
ordering = ("domein", "rsin")


class HasDocNotifyListFilter(admin.SimpleListFilter):
title = _("notify document attachment")
parameter_name = "doc_notify"

def lookups(self, request, model_admin):
return [
("yes", _("Yes")),
("no", _("No")),
]

def queryset(self, request, queryset):
v = self.value()
if v == "yes":
queryset = queryset.filter(has_doc_notify=True)
elif v == "no":
queryset = queryset.filter(has_doc_notify=False)
return queryset


class CatalogUsedListFilter(admin.SimpleListFilter):
title = _("Catalogus")
parameter_name = "catalogus"
Expand Down Expand Up @@ -81,12 +101,14 @@ class ZaakTypeInformatieObjectTypeConfigInline(admin.TabularInline):
fields = [
"omschrijving",
"document_upload_enabled",
"informatieobjecttype_url",
"document_notification_enabled",
"informatieobjecttype_uuid",
"zaaktype_uuids",
]
readonly_fields = [
"omschrijving",
"informatieobjecttype_url",
"informatieobjecttype_uuid",
"zaaktype_uuids",
]
ordering = ("omschrijving",)
Expand Down Expand Up @@ -126,6 +148,7 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin):
"omschrijving",
"catalogus",
"notify_status_changes",
"has_doc_notify",
"document_upload_enabled",
"num_infotypes",
]
Expand All @@ -135,6 +158,7 @@ class ZaakTypeConfigAdmin(admin.ModelAdmin):
]
list_filter = [
"notify_status_changes",
HasDocNotifyListFilter,
CatalogUsedListFilter,
]
search_fields = [
Expand All @@ -154,6 +178,19 @@ 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_doc_notify=Count(
"zaaktypeinformatieobjecttypeconfig",
filter=Q(
zaaktypeinformatieobjecttypeconfig__document_notification_enabled=True
),
)
)
qs = qs.annotate(
has_doc_notify=ExpressionWrapper(
Q(num_doc_notify__gt=0), output_field=BooleanField()
)
)
return qs

def num_infotypes(self, obj=None):
Expand All @@ -162,6 +199,17 @@ def num_infotypes(self, obj=None):
else:
return getattr(obj, "num_infotypes", 0)

num_infotypes.admin_order_field = "num_infotypes"

def has_doc_notify(self, obj=None):
if not obj or not obj.pk:
return False
else:
return getattr(obj, "has_doc_notify", False)

has_doc_notify.boolean = True
has_doc_notify.admin_order_field = "has_doc_notify"

@admin.action(description="Set selected Zaaktypes to notify on status changes")
def mark_as_notify_status_changes(self, request, qs):
count = qs.update(notify_status_changes=True)
Expand Down Expand Up @@ -206,7 +254,29 @@ class UserCaseStatusNotificationAdmin(admin.ModelAdmin):
"user",
"case_uuid",
"status_uuid",
"created",
"created_on",
]

def has_change_permission(self, request, obj=None):
return False


@admin.register(UserCaseInfoObjectNotification)
class UserCaseInfoObjectNotificationAdmin(admin.ModelAdmin):
raw_id_fields = ["user"]
search_fields = [
"user__first_name",
"user__last_name",
"user__email",
"user__id",
"case_uuid",
"zaak_info_object_uuid",
]
list_display = [
"user",
"case_uuid",
"zaak_info_object_uuid",
"created_on",
]

def has_change_permission(self, request, obj=None):
Expand Down
23 changes: 23 additions & 0 deletions src/open_inwoner/openzaak/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,29 @@ def fetch_single_case(case_uuid: str) -> Optional[Zaak]:
return case


@cache_result(
"single_case_information_object:{url}", timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT
)
def fetch_single_case_information_object(url: str) -> Optional[ZaakInformatieObject]:
client = build_client("zaak")

if client is None:
return

try:
response = client.retrieve("zaakinformatieobject", url=url)
except RequestException as e:
logger.exception("exception while making request", exc_info=e)
return
except ClientError as e:
logger.exception("exception while making request", exc_info=e)
return

case = factory(ZaakInformatieObject, response)

return case


def fetch_case_by_url_no_cache(case_url: str) -> Optional[Zaak]:
client = build_client("zaak")
try:
Expand Down
2 changes: 1 addition & 1 deletion src/open_inwoner/openzaak/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
ZaakTypeInformatieObjectTypeConfig,
)

from .api_models import Zaak
from .utils import cache as cache_result

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -117,6 +116,7 @@ def upload_document(
"inhoud": base64.b64encode(file.read()).decode("utf-8"),
"bestandsomvang": file.size,
"bestandsnaam": file.name,
# "status": "definitief",
"taal": "nld",
"informatieobjecttype": ZaakTypeInformatieObjectTypeConfig.objects.get(
id=user_choice
Expand Down
102 changes: 87 additions & 15 deletions src/open_inwoner/openzaak/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,16 @@
from django.db import IntegrityError, models, transaction

from open_inwoner.accounts.models import User
from open_inwoner.openzaak.api_models import Zaak, ZaakType

if TYPE_CHECKING:
from open_inwoner.openzaak.models import UserCaseStatusNotification
from open_inwoner.openzaak.models import (
UserCaseInfoObjectNotification,
UserCaseStatusNotification,
)


class UserCaseStatusNotificationQueryset(models.QuerySet):
def get_user_case_notifications(self, user, case_uuid):
return self.filter(user=user, case_uuid=case_uuid)

def has_notification(self, user, case_uuid, status_uuid):
return self.filter(
user=user, case_uuid=case_uuid, status_uuid=status_uuid
).exists()


class UserCaseStatusNotificationManager(
models.Manager.from_queryset(UserCaseStatusNotificationQueryset)
):
class UserCaseStatusNotificationManager(models.Manager):
def record_if_unique_notification(
self,
user: User,
Expand All @@ -44,12 +36,60 @@ def record_if_unique_notification(
return None


class UserCaseInfoObjectNotificationManager(models.Manager):
def record_if_unique_notification(
self,
user: User,
case_uuid: UUID,
zaak_info_object_uuid: UUID,
) -> Optional["UserCaseInfoObjectNotification"]:
"""
assume this is the first delivery but depend on the unique constraint
"""
kwargs = {
"user": user,
"case_uuid": case_uuid,
"zaak_info_object_uuid": zaak_info_object_uuid,
}
try:
with transaction.atomic():
note = self.create(**kwargs)
return note
except IntegrityError:
return None


class ZaakTypeInformatieObjectTypeConfigQueryset(models.QuerySet):
def get_visible_ztiot_configs_for_case(self, case):
def filter_catalogus(self, case_type: ZaakType):
if case_type.catalogus:
# support both url and resolved dataclass
catalogus_url = (
case_type.catalogus
if isinstance(case_type.catalogus, str)
else case_type.catalogus.url
)
return self.filter(
zaaktype_config__catalogus__url=catalogus_url,
)
else:
return self.filter(
zaaktype_config__catalogus__isnull=True,
)

def filter_case_type(self, case_type: ZaakType):
return self.filter_catalogus(case_type).filter(
zaaktype_uuids__contains=[case_type.uuid],
zaaktype_config__identificatie=case_type.identificatie,
)

def get_visible_ztiot_configs_for_case(self, case: Zaak):
"""
Returns all ZaakTypeInformatieObjectTypeConfig instances which allow
documents upload and are based on a specific case and case type.
"""
# TODO rename to 'filter_visible_for_case'
# TODO change signature to accept case_type/ZaakType
# TODO refactor to use self.filter_case_type(case_type)
if not case:
return self.none()

Expand All @@ -59,8 +99,36 @@ def get_visible_ztiot_configs_for_case(self, case):
document_upload_enabled=True,
)

def get_for_case_and_info_type(
self, case_type: ZaakType, info_object_type_url: str
):
return self.filter_case_type(case_type).get(
informatieobjecttype_url=info_object_type_url,
)


class ZaakTypeConfigQueryset(models.QuerySet):
def filter_catalogus(self, case_type: ZaakType):
if case_type.catalogus:
# support both url and resolved dataclass
catalogus_url = (
case_type.catalogus
if isinstance(case_type.catalogus, str)
else case_type.catalogus.url
)
return self.filter(
catalogus__url=catalogus_url,
)
else:
return self.filter(
catalogus__isnull=True,
)

def filter_case_type(self, case_type: ZaakType):
return self.filter_catalogus(case_type).filter(
identificatie=case_type.identificatie,
)

def get_visible_zt_configs_for_case_type_identification(
self, case_type_identification
):
Expand All @@ -71,6 +139,10 @@ def get_visible_zt_configs_for_case_type_identification(
if not case_type_identification:
return self.none()

# TODO rename to 'filter_visible_for_case_type'
# TODO change signature to accept case_type
# TODO refactor to use self.filter_case_type(case_type)

return self.filter(
identificatie=case_type_identification,
document_upload_enabled=True,
Expand Down
Loading