-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add callback for Talpa robot (#2654)
* feat: add callback for Talpa robot * feat: utility for getting the ip from a request * feat: add audit log entry for applications * fix: single quotes, imports * fix: missing migration * feat: update related batch to rejected_by_talpa * fix: conflicting migrations * fix: failing tests * feat: add separate talpa_status for application * fix: conflicting migrations
- Loading branch information
Showing
14 changed files
with
360 additions
and
5 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
backend/benefit/applications/api/v1/serializers/talpa_callback.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from rest_framework import serializers | ||
|
||
|
||
class TalpaCallbackSerializer(serializers.Serializer): | ||
status = serializers.ChoiceField(choices=["Success", "Failure"]) | ||
successful_applications = serializers.ListField(child=serializers.IntegerField()) | ||
failed_applications = serializers.ListField(child=serializers.IntegerField()) | ||
|
||
def validate(self, data): | ||
""" | ||
Check that successful_applications and failed_applications are not both empty. | ||
""" | ||
if not data.get("successful_applications") and not data.get( | ||
"failed_applications" | ||
): | ||
raise serializers.ValidationError( | ||
"Both successful_applications and failed_applications cannot be empty." | ||
) | ||
|
||
return data |
91 changes: 91 additions & 0 deletions
91
backend/benefit/applications/api/v1/talpa_integration_views.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import logging | ||
from typing import List, Union | ||
|
||
from django.contrib.auth.models import AnonymousUser | ||
from django.db import transaction | ||
from rest_framework import status | ||
from rest_framework.permissions import AllowAny | ||
from rest_framework.response import Response | ||
from rest_framework.views import APIView | ||
|
||
from applications.api.v1.serializers.talpa_callback import TalpaCallbackSerializer | ||
from applications.enums import ApplicationBatchStatus, ApplicationTalpaStatus | ||
from applications.models import Application | ||
from common.authentications import RobotBasicAuthentication | ||
from common.utils import get_request_ip_address | ||
from shared.audit_log import audit_logging | ||
from shared.audit_log.enums import Operation | ||
|
||
LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
class TalpaCallbackView(APIView): | ||
authentication_classes = [RobotBasicAuthentication] | ||
permission_classes = [AllowAny] | ||
|
||
def post(self, request, *args, **kwargs): | ||
serializer = TalpaCallbackSerializer(data=request.data) | ||
|
||
if serializer.is_valid(): | ||
self.process_callback(serializer.validated_data, request) | ||
return Response({"message": "Callback received"}, status=status.HTTP_200_OK) | ||
else: | ||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
def process_callback(self, data, request): | ||
if data["status"] == "Success": | ||
self._handle_successful_applications( | ||
data["successful_applications"], get_request_ip_address(request) | ||
) | ||
self._handle_failed_applications(data["failed_applications"]) | ||
else: | ||
LOGGER.error(f"Received a talpa callback with status: {data['status']}") | ||
|
||
def _get_applications(self, application_numbers) -> Union[List[Application], None]: | ||
applications = Application.objects.filter( | ||
application_number__in=application_numbers | ||
) | ||
if not applications.exists() and application_numbers: | ||
LOGGER.error(f"No applications found with numbers: {application_numbers}") | ||
return None | ||
return applications | ||
|
||
def _handle_successful_applications( | ||
self, application_numbers: list, ip_address: str | ||
): | ||
"""Add audit log entries for applications which were processed successfully by TALPA""" | ||
successful_applications = self._get_applications(application_numbers) | ||
if successful_applications: | ||
successful_applications.update( | ||
talpa_status=ApplicationTalpaStatus.SUCCESSFULLY_SENT_TO_TALPA | ||
) | ||
|
||
for application in successful_applications: | ||
audit_logging.log( | ||
AnonymousUser, | ||
"", | ||
Operation.READ, | ||
application, | ||
ip_address=ip_address, | ||
additional_information="application was read succesfully by TALPA", | ||
) | ||
|
||
@transaction.atomic | ||
def _handle_failed_applications(self, application_numbers: list): | ||
"""Update applications and related batch which could not be processed with status REJECTED_BY_TALPA""" | ||
applications = self._get_applications(application_numbers) | ||
if applications: | ||
try: | ||
batch = applications.first().batch | ||
if batch: | ||
batch.status = ApplicationBatchStatus.REJECTED_BY_TALPA | ||
batch.save() | ||
applications.update( | ||
talpa_status=ApplicationTalpaStatus.REJECTED_BY_TALPA | ||
) | ||
else: | ||
LOGGER.error( | ||
f"No batch associated with applications: {applications.values_list('id', flat=True)}" | ||
) | ||
except Exception as e: | ||
LOGGER.error(f"Error updating batch and applications: {str(e)}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
backend/benefit/applications/migrations/0050_add_rejected_by_talpa_status.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Generated by Django 3.2.23 on 2024-01-08 07:17 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('applications', '0049_historicalemployee'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='application', | ||
name='status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected'), ('rejected_by_talpa', 'Rejected by Talpa')], default='draft', max_length=64, verbose_name='status'), | ||
), | ||
migrations.AlterField( | ||
model_name='applicationlogentry', | ||
name='from_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected'), ('rejected_by_talpa', 'Rejected by Talpa')], max_length=64), | ||
), | ||
migrations.AlterField( | ||
model_name='applicationlogentry', | ||
name='to_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected'), ('rejected_by_talpa', 'Rejected by Talpa')], max_length=64), | ||
), | ||
migrations.AlterField( | ||
model_name='historicalapplication', | ||
name='status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected'), ('rejected_by_talpa', 'Rejected by Talpa')], default='draft', max_length=64, verbose_name='status'), | ||
), | ||
migrations.AlterField( | ||
model_name='historicalapplicationlogentry', | ||
name='from_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected'), ('rejected_by_talpa', 'Rejected by Talpa')], max_length=64), | ||
), | ||
migrations.AlterField( | ||
model_name='historicalapplicationlogentry', | ||
name='to_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected'), ('rejected_by_talpa', 'Rejected by Talpa')], max_length=64), | ||
), | ||
] |
18 changes: 18 additions & 0 deletions
18
backend/benefit/applications/migrations/0051_alter_applicationbatch_status.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 3.2.23 on 2024-01-09 08:57 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('applications', '0050_add_rejected_by_talpa_status'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='applicationbatch', | ||
name='status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('exported_ahjo_report', 'Ahjo report created, not yet sent to AHJO'), ('awaiting_ahjo_decision', 'Sent to Ahjo, decision pending'), ('accepted', 'Accepted in Ahjo'), ('rejected', 'Rejected in Ahjo'), ('returned', 'Returned from Ahjo without decision'), ('sent_to_talpa', 'Sent to Talpa'), ('completed', 'Processing is completed'), ('rejected_by_talpa', 'Rejected by Talpa')], default='draft', max_length=64, verbose_name='status of batch'), | ||
), | ||
] |
53 changes: 53 additions & 0 deletions
53
backend/benefit/applications/migrations/0052_application_talpa_status.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Generated by Django 3.2.23 on 2024-01-15 08:53 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('applications', '0051_alter_applicationbatch_status'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='application', | ||
name='talpa_status', | ||
field=models.CharField(choices=[('not_sent_to_talpa', 'Not sent to Talpa'), ('rejected_by_talpa', 'Rejected by Talpa'), ('successfully_sent_to_talpa', 'Successfully sent to Talpa')], default='not_sent_to_talpa', max_length=64, verbose_name='talpa_status'), | ||
), | ||
migrations.AddField( | ||
model_name='historicalapplication', | ||
name='talpa_status', | ||
field=models.CharField(choices=[('not_sent_to_talpa', 'Not sent to Talpa'), ('rejected_by_talpa', 'Rejected by Talpa'), ('successfully_sent_to_talpa', 'Successfully sent to Talpa')], default='not_sent_to_talpa', max_length=64, verbose_name='talpa_status'), | ||
), | ||
migrations.AlterField( | ||
model_name='application', | ||
name='status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], default='draft', max_length=64, verbose_name='status'), | ||
), | ||
migrations.AlterField( | ||
model_name='applicationlogentry', | ||
name='from_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], max_length=64), | ||
), | ||
migrations.AlterField( | ||
model_name='applicationlogentry', | ||
name='to_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], max_length=64), | ||
), | ||
migrations.AlterField( | ||
model_name='historicalapplication', | ||
name='status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], default='draft', max_length=64, verbose_name='status'), | ||
), | ||
migrations.AlterField( | ||
model_name='historicalapplicationlogentry', | ||
name='from_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], max_length=64), | ||
), | ||
migrations.AlterField( | ||
model_name='historicalapplicationlogentry', | ||
name='to_status', | ||
field=models.CharField(choices=[('draft', 'Draft'), ('received', 'Received'), ('handling', 'Handling'), ('additional_information_needed', 'Additional information requested'), ('cancelled', 'Cancelled'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], max_length=64), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.