Skip to content

Commit

Permalink
feat: add candidate field to RTEN model
Browse files Browse the repository at this point in the history
  • Loading branch information
andrey-canon committed Jul 8, 2024
1 parent 65c41a0 commit 196e88f
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 7 deletions.
21 changes: 21 additions & 0 deletions eox_nelp/migrations/0010_pearsonrtenevent_candidate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.2.21 on 2024-07-05 21:33

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


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('eox_nelp', '0009_pearsonrtenevent_appointments'),
]

operations = [
migrations.AddField(
model_name='pearsonrtenevent',
name='candidate',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]
6 changes: 3 additions & 3 deletions eox_nelp/pearson_vue/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ class PearsonRTENEventAdmin(admin.ModelAdmin):
list_display (list): List of fields to display in the admin list view.
readonly_fields (tuple): Tuple of fields that are read-only in the admin interface.
"""
list_display = ("event_type", "created_at")
readonly_fields = ("created_at",)
list_filter = ["event_type",]
list_display = ("event_type", "created_at", "candidate")
readonly_fields = ("created_at", "candidate", "event_type")
list_filter = ["event_type"]


admin.site.register(PearsonRTENEvent, PearsonRTENEventAdmin)
2 changes: 1 addition & 1 deletion eox_nelp/pearson_vue/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class PearsonRTENSerializer(serializers.ModelSerializer):
class Meta:
"""Meta class"""
model = PearsonRTENEvent
fields = ["event_type", "content", "created_at"]
fields = ["event_type", "content", "candidate", "created_at"]
read_only_fields = ["event_type", "created_at"]

def to_internal_value(self, data):
Expand Down
21 changes: 20 additions & 1 deletion eox_nelp/pearson_vue/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from eox_nelp.edxapp_wrapper.student import AnonymousUserId
from eox_nelp.pearson_vue.api.v1.serializers import PearsonRTENSerializer
from eox_nelp.pearson_vue.constants import (
CANCEL_APPOINTMENT,
Expand Down Expand Up @@ -102,7 +103,7 @@ def perform_create(self, serializer):
serializer (Serializer): The serializer instance used for data validation and saving.
"""
content_data = self.request.data.copy()
serializer.save(event_type=self.event_type, content=content_data)
serializer.save(event_type=self.event_type, candidate=self.get_candidate(), content=content_data)

def create(self, request, *args, **kwargs):
"""
Expand All @@ -123,6 +124,24 @@ def create(self, request, *args, **kwargs):
headers = self.get_success_headers(serializer.data)
return Response({}, status=status.HTTP_200_OK, headers=headers)

def get_candidate(self):
"""
Retrieves the candidate user based on the client candidate ID provided in the request data.
This method extracts the `clientCandidateID` from the request data, removes the "NELC" prefix,
and attempts to retrieve the corresponding user from the `AnonymousUserId` model. If the candidate
ID does not exist, it returns `None`.
Returns:
user (object or None): The user object associated with the anonymous user ID, or `None` if not found.
"""
anonymous_user_id = self.request.data.get("clientCandidateID", "").replace("NELC", "")

try:
return AnonymousUserId.objects.get(anonymous_user_id=anonymous_user_id).user
except AnonymousUserId.DoesNotExist:
return None


class ResultNotificationView(PearsonRTENBaseView):
"""
Expand Down
3 changes: 3 additions & 0 deletions eox_nelp/pearson_vue/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
EVENT_TYPE_CHOICES: A list of tuples representing the possible choices
for an event type. Each choice represents a different type of event.
"""
from django.contrib.auth import get_user_model
from django.db import models

from eox_nelp.pearson_vue.constants import (
Expand All @@ -33,6 +34,7 @@
(MODIFY_APPOINTMENT, "Modify Appointment"),
(CANCEL_APPOINTMENT, "Cancel Appointment"),
]
User = get_user_model()


class PearsonRTENEvent(models.Model):
Expand All @@ -47,3 +49,4 @@ class PearsonRTENEvent(models.Model):
content = models.JSONField()
created_at = models.DateTimeField(auto_now_add=True)
event_type = models.CharField(max_length=20, choices=EVENT_TYPE_CHOICES)
candidate = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
43 changes: 41 additions & 2 deletions eox_nelp/pearson_vue/tests/api/v1/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.test import override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient

from eox_nelp.edxapp_wrapper.student import AnonymousUserId
from eox_nelp.pearson_vue.constants import (
CANCEL_APPOINTMENT,
MODIFY_APPOINTMENT,
Expand Down Expand Up @@ -48,6 +50,13 @@ def setUp(self): # pylint: disable=invalid-name
self.user, _ = User.objects.get_or_create(username='testuser', password='12345')
self.client.force_authenticate(user=self.user)

def tearDown(self): # pylint: disable=invalid-name
"""
Reset the mocked objects after each test.
"""
AnonymousUserId.reset_mock()
AnonymousUserId.objects.get.side_effect = None

@override_settings(ENABLE_CERTIFICATE_PUBLISHER=False)
def test_create_result_notification_event(self):
"""
Expand All @@ -57,18 +66,48 @@ def test_create_result_notification_event(self):
- The number of records has increased in 1.
- Response returns a 200 status code.
- Response data is empty.
- AnonymousUserId.objects.get has been called with the expected data.
"""
# pylint: disable=no-member
initial_count = PearsonRTENEvent.objects.filter(event_type=self.event_type, candidate=self.user).count()
AnonymousUserId.objects.get.return_value.user = self.user

response = self.client.post(
reverse(f"pearson-vue-api:v1:{self.event_type}"),
{"clientCandidateID": "NELC123456"},
format="json",
)

final_count = PearsonRTENEvent.objects.filter(event_type=self.event_type, candidate=self.user).count()

self.assertEqual(final_count, initial_count + 1)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {})
AnonymousUserId.objects.get.assert_called_once_with(anonymous_user_id="123456")

def test_create_result_notification_event_without_user(self):
"""
Test creating an event without clienCandidateID.
Expected behavior:
- The number of records has increased in 1.
- Response returns a 200 status code.
- Response data is empty.
- AnonymousUserId.objects.get has been called with the expected data.
"""
# pylint: disable=no-member
initial_count = PearsonRTENEvent.objects.filter(event_type=self.event_type).count()
initial_count = PearsonRTENEvent.objects.filter(event_type=self.event_type, candidate=None).count()
AnonymousUserId.DoesNotExist = ObjectDoesNotExist
AnonymousUserId.objects.get.side_effect = AnonymousUserId.DoesNotExist

response = self.client.post(reverse(f"pearson-vue-api:v1:{self.event_type}"), {}, format="json")

final_count = PearsonRTENEvent.objects.filter(event_type=self.event_type).count()
final_count = PearsonRTENEvent.objects.filter(event_type=self.event_type, candidate=None).count()

self.assertEqual(final_count, initial_count + 1)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {})
AnonymousUserId.objects.get.assert_called_once_with(anonymous_user_id="")

def test_get_event(self):
"""
Expand Down

0 comments on commit 196e88f

Please sign in to comment.