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

✨ [#2112] Retrieve open tasks on login and store in userfeed #1057

Merged
merged 4 commits into from
Feb 29, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"url": "https://maykinmedia.nl",
"uuid": "fb72d8db-c3ee-4aa0-96c1-260b202cb208",
"identificatie": "1234-2023",
"naam": "Aanvullende informatie gewenst",
"startdatum": "2023-11-14",
"formulierLink": "https://maykinmedia.nl"
},
{
"url": "https://maykinmedia.nl",
"uuid": "d74f6a5c-297d-43a3-a923-1774164d852d",
"identificatie": "4321-2023",
"naam": "Aanvullende informatie gewenst",
"startdatum": "2023-10-11",
"formulierLink": "https://maykinmedia.nl"
}
]
2 changes: 1 addition & 1 deletion src/open_inwoner/apimock/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def get(self, request, *args, **kwargs):
with open(file_path, "r") as f:
data = json.load(f)
process_urls(data, prefix, self.url_replacers)
return JsonResponse(data)
return JsonResponse(data, safe=False)


def process_urls(data, prefix, url_replacers):
Expand Down
10 changes: 10 additions & 0 deletions src/open_inwoner/openzaak/api_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,13 @@ def process_data(self) -> dict:
"eind_datum_geldigheid": self.eind_datum_geldigheid or "Geen",
"case_type": "OpenSubmission",
}


@dataclass
class OpenTask(Model):
url: str
uuid: str
identificatie: str
naam: str
startdatum: date
formulier_link: str
19 changes: 19 additions & 0 deletions src/open_inwoner/openzaak/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .api_models import (
InformatieObjectType,
OpenSubmission,
OpenTask,
Resultaat,
ResultaatType,
Rol,
Expand Down Expand Up @@ -654,6 +655,24 @@ def fetch_open_submissions(self, bsn: str) -> List[OpenSubmission]:

return results

def fetch_open_tasks(self, bsn: str) -> List[OpenTask]:
if not bsn:
return []

try:
response = self.get(
"openstaande-taken",
params={"bsn": bsn},
)
data = get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return []

results = factory(OpenTask, data)

return results


def build_client(type_) -> Optional[APIClient]:
config = OpenZaakConfig.get_solo()
Expand Down
29 changes: 28 additions & 1 deletion src/open_inwoner/openzaak/tests/mocks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from open_inwoner.openzaak.tests.shared import FORMS_ROOT


class ESuiteData:
class ESuiteSubmissionData:
def __init__(self):
self.submission_1 = {
"url": "https://dmidoffice2.esuite-development.net/formulieren-provider/api/v1/8e3ae29c-7bc5-4f7d-a27c-b0c83c13328e",
Expand Down Expand Up @@ -34,3 +34,30 @@ def install_mocks(self, m):
json=self.response,
)
return self


class ESuiteTaskData:
def __init__(self):
self.task1 = {
"url": "https://maykinmedia.nl",
"uuid": "fb72d8db-c3ee-4aa0-96c1-260b202cb208",
"identificatie": "1234-2023",
"naam": "Aanvullende informatie gewenst",
"startdatum": "2023-11-14",
"formulierLink": "https://maykinmedia.nl",
}
self.task2 = {
"url": "https://maykinmedia.nl",
"uuid": "d74f6a5c-297d-43a3-a923-1774164d852d",
"identificatie": "4321-2023",
"naam": "Aanvullende informatie gewenst",
"startdatum": "2023-10-11",
"formulierLink": "https://maykinmedia.nl",
}

def install_mocks(self, m):
m.get(
f"{FORMS_ROOT}openstaande-taken",
json=[self.task1, self.task2],
)
return self
4 changes: 2 additions & 2 deletions src/open_inwoner/openzaak/tests/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
ZaakTypeStatusTypeConfigFactory,
)
from .helpers import generate_oas_component_cached
from .mocks import ESuiteData
from .mocks import ESuiteSubmissionData
from .shared import CATALOGI_ROOT, ZAKEN_ROOT

# Avoid redirects through `KvKLoginMiddleware`
Expand Down Expand Up @@ -868,7 +868,7 @@ def test_case_submission(self, m):
login_type=LoginTypeChoices.digid, bsn="900222086", email="[email protected]"
)

data = ESuiteData().install_mocks(m)
data = ESuiteSubmissionData().install_mocks(m)

response = self.app.get(
self.inner_url, user=user, headers={"HX-Request": "true"}
Expand Down
6 changes: 5 additions & 1 deletion src/open_inwoner/userfeed/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from django.contrib import admin

from .models import FeedItemData
Expand Down Expand Up @@ -27,7 +28,10 @@ class FeedItemDataAdmin(admin.ModelAdmin):
]

def has_change_permission(self, request, obj=None):
if settings.DEBUG:
return super().has_change_permission(request, obj=obj)
return False

def has_add_permission(self, request):
pass
if settings.DEBUG:
return super().has_add_permission(request)
1 change: 1 addition & 0 deletions src/open_inwoner/userfeed/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class FeedItemType(models.TextChoices):
case_status_changed = "case_status_change", _("Case status changed")
case_document_added = "case_document_added", _("Case document added")
plan_expiring = "plan_expiring", _("Plan nears deadline")
external_task = "external_task", _("External task")
3 changes: 3 additions & 0 deletions src/open_inwoner/userfeed/feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
get_item_adapter_class,
get_types_for_unpublished_cms_apps,
)
from open_inwoner.userfeed.hooks.external_task import update_user_tasks
from open_inwoner.userfeed.models import FeedItemData
from open_inwoner.userfeed.summarize import SUMMARIES

Expand Down Expand Up @@ -48,6 +49,8 @@ def get_feed(user: User, with_history: bool = False) -> Feed:
# empty feed
return Feed()

update_user_tasks(user)

# core filters
display_filter = Q(completed_at__isnull=True)
if with_history:
Expand Down
86 changes: 86 additions & 0 deletions src/open_inwoner/userfeed/hooks/external_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from typing import List

from django.utils.translation import gettext_lazy as _

from open_inwoner.accounts.choices import LoginTypeChoices
from open_inwoner.accounts.models import User
from open_inwoner.openzaak.api_models import OpenTask
from open_inwoner.openzaak.clients import build_client
from open_inwoner.userfeed.adapter import FeedItem
from open_inwoner.userfeed.choices import FeedItemType
from open_inwoner.userfeed.models import FeedItemData

from ..adapters import register_item_adapter


class OpenTaskFeedItem(FeedItem):
base_title = _("Open task")
base_message = _("Open task that is yet to be completed")

@property
def title(self) -> str:
return f"{self.base_title} ({self.get_data('task_identificatie')})"

@property
def message(self) -> str:
return self.get_data("task_name", super().message)


def update_external_task_items(user: User, openstaande_taken: List[OpenTask]):
"""
Creates items for OpenTasks if they do not exist yet, updates existing items if the
data changed and marks existing items as complete if no OpenTask exists for that
uuid anymore
"""
existing_uuid_mapping = {
str(item.ref_uuid): item
for item in FeedItemData.objects.filter(
type=FeedItemType.external_task,
user=user,
)
}
existing_uuids = set(existing_uuid_mapping.keys())

update_data = []
create_data = []
for task in openstaande_taken:
type_data = {
"action_url": task.formulier_link,
"task_name": task.naam,
"task_identificatie": task.identificatie,
}
if existing_item := existing_uuid_mapping.get(task.uuid):
if existing_item.type_data != type_data:
existing_item.type_data = type_data
update_data.append(existing_item)
continue

create_data.append(
FeedItemData(
user=user,
type=FeedItemType.external_task,
ref_uuid=task.uuid,
action_required=True,
type_data=type_data,
)
)

# TODO we could maybe use `bulk_create` once we upgraded to Django 4.x
FeedItemData.objects.bulk_update(update_data, ["type_data"], batch_size=100)
FeedItemData.objects.bulk_create(create_data)

# Mark all tasks with UUIDs not occurring in the fetched results as completed
completed_uuids = existing_uuids - set(task.uuid for task in openstaande_taken)
FeedItemData.objects.filter(
type=FeedItemType.external_task, user=user, ref_uuid__in=completed_uuids
).mark_completed()


def update_user_tasks(user: User):
if user.login_type == LoginTypeChoices.digid:
if client := build_client("form"):
tasks = client.fetch_open_tasks(user.bsn)
update_external_task_items(user, tasks)


register_item_adapter(OpenTaskFeedItem, FeedItemType.external_task)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.24 on 2024-02-27 11:40

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("userfeed", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="feeditemdata",
name="type",
field=models.CharField(
choices=[
("message_simple", "Simple text message"),
("case_status_change", "Case status changed"),
("case_document_added", "Case document added"),
("plan_expiring", "Plan nears deadline"),
("external_task", "External task"),
],
max_length=64,
verbose_name="Type",
),
),
]
Loading
Loading