Skip to content

Commit

Permalink
Merge pull request #1574 from maykinmedia/task/2969-openklant2-vragen
Browse files Browse the repository at this point in the history
[#2969] Create questions with OpenKlant2
  • Loading branch information
swrichards authored Feb 5, 2025
2 parents a6d78fa + 4352a67 commit ff61863
Show file tree
Hide file tree
Showing 17 changed files with 498 additions and 125 deletions.
32 changes: 17 additions & 15 deletions src/open_inwoner/accounts/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from open_inwoner.haalcentraal.models import HaalCentraalConfig
from open_inwoner.haalcentraal.utils import update_brp_data_in_db
from open_inwoner.kvk.client import KvKClient
from open_inwoner.openklant.constants import KlantenServiceType
from open_inwoner.openklant.models import KlantenSysteemConfig
from open_inwoner.openklant.services import OpenKlant2Service, eSuiteKlantenService
from open_inwoner.utils.logentry import user_action

Expand Down Expand Up @@ -40,21 +42,21 @@ def update_user_on_login(sender, user, request, *args, **kwargs):
if user.login_type == LoginTypeChoices.eherkenning:
_update_eherkenning_user_from_kvk_api(user=user)

# OpenKlant2
try:
service = OpenKlant2Service()
except Exception:
logger.error("OpenKlant2 service failed to build")
else:
_update_user_from_openklant2(user=user, service=service, request=request)

# eSuite
try:
service = eSuiteKlantenService()
except Exception:
logger.error("eSuiteKlantenService failed to build")
else:
_update_user_from_esuite(user=user, service=service, request=request)
config = KlantenSysteemConfig.get_solo()
if config.primary_backend == KlantenServiceType.OPENKLANT2.value:
try:
service = OpenKlant2Service()
except Exception:
logger.error("OpenKlant2 service failed to build")
else:
_update_user_from_openklant2(user=user, service=service, request=request)
if config.primary_backend == KlantenServiceType.ESUITE.value:
try:
service = eSuiteKlantenService()
except Exception:
logger.error("eSuiteKlantenService failed to build")
else:
_update_user_from_esuite(user=user, service=service, request=request)


def _update_user_from_openklant2(
Expand Down
6 changes: 6 additions & 0 deletions src/open_inwoner/accounts/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from open_inwoner.haalcentraal.tests.mixins import HaalCentraalMixin
from open_inwoner.kvk.branches import get_kvk_branch_number
from open_inwoner.kvk.tests.factories import CertificateFactory
from open_inwoner.openklant.constants import KlantenServiceType
from open_inwoner.openklant.models import KlantenSysteemConfig
from open_inwoner.openklant.tests.data import MockAPIReadPatchData
from open_inwoner.utils.tests.helpers import AssertTimelineLogMixin

Expand Down Expand Up @@ -2039,6 +2041,10 @@ def setUpTestData(cls):
config.enable_notification_channel_choice = True
config.save()

klant_config = KlantenSysteemConfig.get_solo()
klant_config.primary_backend = KlantenServiceType.ESUITE.value
klant_config.save()

def test_update_hook_is_registered_on_login(self, m):
connected_functions = [receiver[1]() for receiver in user_logged_in.receivers]
self.assertIn(update_user_on_login, connected_functions)
Expand Down
2 changes: 1 addition & 1 deletion src/open_inwoner/cms/cases/views/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,7 @@ def _register_via_openklant2(self, form, config: OpenKlant2Config):
raise NotImplementedError

def _register_via_esuite(self, form, config: ESuiteKlantConfig):
assert config.has_api_configuration()
assert config.has_api_configuration

try:
ztc = ZaakTypeConfig.objects.filter_case_type(self.case.zaaktype).get()
Expand Down
2 changes: 0 additions & 2 deletions src/open_inwoner/cms/footer/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@
class FooterPagesPlugin(CMSPluginBase):
name = _("Pages List")
render_template = "cms/footer/footer_pages_plugin.html"

# cache = False

def render(self, context, instance, placeholder):
# TODO move options to plugin model
config = SiteConfiguration.get_solo()
klant_config = KlantenSysteemConfig.get_solo()
context["flatpages"] = config.get_ordered_flatpages
Expand Down
4 changes: 2 additions & 2 deletions src/open_inwoner/cms/footer/tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
class ContactFormTestCase(ClearCachesMixin, TestCase):
def setUp(self):
super().setUp()

# clear esuite_config
esuite_config = ESuiteKlantConfig.get_solo()
esuite_config.klanten_service = None
esuite_config.contactmomenten_service = None
esuite_config.register_bronorganisatie_rsin = ""
esuite_config.register_type = ""
esuite_config.register_employee_id = ""
esuite_config.send_email_confirmation = True
esuite_config.save()

def test_no_form_link_shown_in_footer_if_not_has_configuration(self):
Expand All @@ -39,7 +39,7 @@ def test_form_link_is_shown_in_footer_when_has_configuration(self):
self.assertFalse(klant_config.has_contactform_configuration)

esuite_config = ESuiteKlantConfig.get_solo()
ContactFormSubjectFactory(config=esuite_config)
ContactFormSubjectFactory(esuite_config=esuite_config)

klant_config.register_contact_email = "example@example.com"
klant_config.save()
Expand Down
23 changes: 17 additions & 6 deletions src/open_inwoner/openklant/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,35 @@
class ContactFormSubjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["subject_code"].widget.attrs["placeholder"] = _(
"Must be configured if E-suite is used"
)
if esuite_subject_code := self.fields.get("esuite_subject_code", None):
esuite_subject_code.widget.attrs["placeholder"] = _(
"Must be configured if E-suite is used"
)

class Meta:
model = ContactFormSubject
fields = (
"subject",
"subject_code",
"esuite_subject_code",
)


class ContactFormSubjectInlineAdmin(OrderedTabularInline):
model = ContactFormSubject
form = ContactFormSubjectForm
fields = ("subject", "subject_code", "order", "move_up_down_links")
readonly_fields = ("order", "move_up_down_links")
ordering = ("order",)
extra = 0


class ContactFormSubjectInlineAdminESuite(ContactFormSubjectInlineAdmin):
fields = ("subject", "esuite_subject_code", "order", "move_up_down_links")


class ContactFormSubjectInlineAdminOpenKlant(ContactFormSubjectInlineAdmin):
fields = ("subject", "order", "move_up_down_links")


class ESuiteKlantConfigAdminForm(forms.ModelForm):
class Meta:
model = ESuiteKlantConfig
Expand All @@ -48,7 +56,7 @@ class Meta:
class ESuiteKlantConfigAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin):
form = ESuiteKlantConfigAdminForm
inlines = [
ContactFormSubjectInlineAdmin,
ContactFormSubjectInlineAdminESuite,
]
fieldsets = [
(
Expand Down Expand Up @@ -110,6 +118,9 @@ class Meta:
class OpenKlant2ConfigAdmin(SingletonModelAdmin):
model = OpenKlant2Config
form = OpenKlant2ConfigAdminForm
inlines = [
ContactFormSubjectInlineAdminOpenKlant,
]
fieldsets = [
(
_("API configuration"),
Expand Down
19 changes: 15 additions & 4 deletions src/open_inwoner/openklant/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from django.utils.translation import gettext_lazy as _

from open_inwoner.accounts.models import User
from open_inwoner.openklant.models import ContactFormSubject, ESuiteKlantConfig
from open_inwoner.openklant.constants import KlantenServiceType
from open_inwoner.openklant.models import ContactFormSubject, KlantenSysteemConfig
from open_inwoner.openklant.views.utils import generate_question_answer_pair
from open_inwoner.utils.validators import DutchPhoneNumberValidator

Expand Down Expand Up @@ -57,9 +58,19 @@ def __init__(self, user, request_session, *args, **kwargs):
self.user = user
self.request_session = request_session

# TODO: check for primary_backend? What in case of OK2?
config = ESuiteKlantConfig.get_solo()
self.fields["subject"].queryset = config.contactformsubject_set.all()
klant_config = KlantenSysteemConfig.get_solo()
if klant_config.primary_backend == KlantenServiceType.OPENKLANT2.value:
subjects = ContactFormSubject.objects.filter(
esuite_subject_code__isnull=True
)
elif klant_config.primary_backend == KlantenServiceType.ESUITE.value:
subjects = ContactFormSubject.objects.filter(
esuite_subject_code__isnull=False
)
else:
subjects = ContactFormSubject.objects.none()

self.fields["subject"].queryset = subjects

if self.user.is_authenticated:
del self.fields["first_name"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Generated by Django 4.2.18 on 2025-01-29 09:48

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


class Migration(migrations.Migration):

dependencies = [("openklant", "0024_alter_klantensysteemconfig_primary_backend")]

operations = [
migrations.RenameField(
model_name="contactformsubject",
old_name="subject_code",
new_name="esuite_subject_code",
),
migrations.RemoveField(
model_name="contactformsubject",
name="config",
),
migrations.AddField(
model_name="contactformsubject",
name="esuite_config",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="openklant.esuiteklantconfig",
),
),
migrations.AlterField(
model_name="contactformsubject",
name="esuite_subject_code",
field=models.CharField(
blank=True,
max_length=255,
null=True,
verbose_name="e-Suite 'onderwerp' code",
),
),
migrations.AddField(
model_name="contactformsubject",
name="openklant_config",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="openklant.openklant2config",
),
),
]
49 changes: 31 additions & 18 deletions src/open_inwoner/openklant/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class ESuiteKlantConfig(SingletonModel):
class Meta:
verbose_name = _("eSuite Klant configuration")

@property
def has_api_configuration(self):
return all(getattr(self, f, "") for f in self.register_api_required_fields)

Expand All @@ -117,17 +118,24 @@ class ContactFormSubject(OrderedModel):
verbose_name=_("Onderwerp"),
max_length=255,
)
subject_code = models.CharField(
esuite_subject_code = models.CharField(
verbose_name=_("e-Suite 'onderwerp' code"),
max_length=255,
null=True,
blank=True,
)
config = models.ForeignKey(
esuite_config = models.ForeignKey(
"ESuiteKlantConfig",
null=True,
on_delete=models.CASCADE,
)
openklant_config = models.ForeignKey(
"OpenKlant2Config",
null=True,
on_delete=models.CASCADE,
)

order_with_respect_to = "config"
order_with_respect_to = "esuite_config"

objects = OrderedModelManager()

Expand Down Expand Up @@ -213,6 +221,12 @@ class OpenKlant2Config(SingletonModel):
),
)

register_api_required_fields = ("service",)

@property
def has_api_configuration(self):
return all(getattr(self, f, False) for f in self.register_api_required_fields)

class Meta:
verbose_name = _("OpenKlant2 configuration")

Expand Down Expand Up @@ -309,25 +323,24 @@ def __str__(self):

@property
def has_api_configuration(self) -> bool:
if self.primary_backend == KlantenServiceType.ESUITE.value:
esuite_config = ESuiteKlantConfig.get_solo()
return esuite_config.has_api_configuration()

# TODO: support `has_api_configuration` check for OK2?
return False
match self.primary_backend:
case KlantenServiceType.ESUITE.value:
config = ESuiteKlantConfig.get_solo()
case KlantenServiceType.OPENKLANT2.value:
config = OpenKlant2Config.get_solo()
case _:
config = None
return getattr(config, "has_api_configuration", False)

@property
def contact_registration_enabled(self) -> bool:
return bool(self.register_contact_email or self.has_api_configuration)

@property
def has_contactform_configuration(self) -> bool:
if not self.contact_registration_enabled:
return False

if self.primary_backend == KlantenServiceType.ESUITE.value:
esuite_config = ESuiteKlantConfig.get_solo()
return esuite_config.contactformsubject_set.exists()

# TODO: check conditions for OK2
return False
contactform_subjects = ContactFormSubject.objects.filter(
esuite_subject_code__isnull=False
if self.primary_backend == KlantenServiceType.ESUITE.value
else True
)
return self.contact_registration_enabled and contactform_subjects.exists()
20 changes: 17 additions & 3 deletions src/open_inwoner/openklant/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,20 @@ def get_fetch_parameters(

return None

@property
def supports_anonymous_questions(self):
match self.config:
case ESuiteKlantConfig():
return True
case OpenKlant2Config():
return False
case _:
raise ValueError("Unsupported backend for KlantenService")


class eSuiteKlantenService(KlantenService):
config: ESuiteKlantConfig
supports_anonymous_questions: bool = True

def __init__(self, config: ESuiteKlantConfig | None = None):
self.config = config or ESuiteKlantConfig.get_solo()
Expand Down Expand Up @@ -574,10 +585,12 @@ def _get_kcm_subject(
e-suite code or
3. return the the e-suite subject code if no mapping exists
"""
e_suite_subject_code = getattr(kcm.contactmoment, "onderwerp", "")
esuite_subject_code = getattr(kcm.contactmoment, "onderwerp", "")

try:
subject = ContactFormSubject.objects.get(subject_code=e_suite_subject_code)
subject = ContactFormSubject.objects.get(
esuite_subject_code=esuite_subject_code
)
except ContactFormSubject.MultipleObjectsReturned as exc:
logger.warning(
"Multiple OIP subjects mapped to the same e-suite subject code for ",
Expand All @@ -593,7 +606,7 @@ def _get_kcm_subject(
kcm.contactmoment.url,
exc_info=exc,
)
return e_suite_subject_code
return esuite_subject_code

return subject.subject

Expand Down Expand Up @@ -843,6 +856,7 @@ def from_klantcontact_and_answer(
class OpenKlant2Service(KlantenService):
config: OpenKlant2Config
client: OpenKlant2Client
supports_anonymous_questions: bool = False

def __init__(self, config: OpenKlant2Config | None = None):
self.config = config or OpenKlant2Config.get_solo()
Expand Down
Loading

0 comments on commit ff61863

Please sign in to comment.