Skip to content

Commit

Permalink
Merge pull request #459 from maykinmedia/feature/1050-document-notifi…
Browse files Browse the repository at this point in the history
…cation

[#1050] Implemented basic zaakinformationobject notifications
  • Loading branch information
alextreme authored Feb 9, 2023
2 parents 3646af1 + 79210f2 commit 3bdf500
Show file tree
Hide file tree
Showing 19 changed files with 1,928 additions and 862 deletions.
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

0 comments on commit 3bdf500

Please sign in to comment.