From 79c11523a328d9510242e6b164f7cdcfc65c33b5 Mon Sep 17 00:00:00 2001 From: Johan Castiblanco Date: Fri, 7 Jul 2023 17:44:48 -0500 Subject: [PATCH] feat: add functions to add extra fields refactor: change relationship expected data to tests This allow to match the expected new fields in the attributes relationship to the tests. style: clean pylint and add docstring refactor: change configuration for mapping style: pr recommendation for tests --- .../course_experience/api/v1/serializers.py | 77 +++++++++++++++++++ .../api/v1/tests/mixins_helpers.py | 27 ++++++- .../api/v1/tests/test_views.py | 75 ++---------------- 3 files changed, 108 insertions(+), 71 deletions(-) diff --git a/eox_nelp/course_experience/api/v1/serializers.py b/eox_nelp/course_experience/api/v1/serializers.py index 39bf3793..f87ff933 100644 --- a/eox_nelp/course_experience/api/v1/serializers.py +++ b/eox_nelp/course_experience/api/v1/serializers.py @@ -1,4 +1,6 @@ """Serializers used for the experience views.""" +from copy import copy + from django.contrib.auth import get_user_model from rest_framework_json_api import serializers @@ -13,6 +15,79 @@ from eox_nelp.edxapp_wrapper.course_overviews import CourseOverview User = get_user_model() +COURSE_OVERVIEW_EXTRA_FIELD_MAPPING = {"display_name": "display_name"} +USER_EXTRA_FIELD_MAPPING = { + "first_name": "first_name", + "last_name": "last_name", + "profile_name": "profile__name", + "username": "username", +} + + +def get_course_extra_attributes(value=None): + """Function to retrieve CourseOverview extra fields + + Args: + value (CourseOverview instance): CourseOverview that the relation analize. Defaults to None. + + Returns: + dict: dict object too add course extra fields + """ + return { + "attributes": map_attributes_from_instance_to_dict(value, COURSE_OVERVIEW_EXTRA_FIELD_MAPPING) + } + + +def get_user_extra_attributes(value=None): + """Function to retrieve User extra fields + + Args: + value (Userinstance): User that the relation analize. Defaults to None. + + Returns: + dict: dict object too add user extra fields + """ + return { + "attributes": map_attributes_from_instance_to_dict(value, USER_EXTRA_FIELD_MAPPING) + } + + +def map_attributes_from_instance_to_dict(instance, attributes_mapping): + """Create a dictionary that represents some fields or attributes of a instance based on + a attributes_mapping dictionary. This dict would have key, values which the key represent the + attribute to look in the instance and the value the key name in the output dict. + Based in the `attributes_mapping` you should use a dict with the following config: + { + "key_name": "field_name" + } + This would check in the instace instance.field_name and the value send it to output dict + like {"key_name": instance.field_name} + Also its is possible to check nested fields if you declarate the field of instance separated by `__` + eg: + { + "key_name": "field_level1__field_level2" + } + This example would check in the instace like instance.field_level1.field_level2 and in the output + dict like {"key_name": instance.field_level1.field_level2} + Args: + instance (instance class): Model or instance of class to retrieved fields. + attributes_mapping (dict): Dictionary map that has the fields to search and the keys name to output, + + Returns: + instance_dict: dict representing the instance + """ + instance_dict = {} + for extra_field, instance_field in attributes_mapping.items(): + extra_value = None + instance_level = copy(instance) + for instance_field in instance_field.split("__"): + if hasattr(instance_level, instance_field): + instance_level = getattr(instance_level, instance_field) + extra_value = instance_level + + instance_dict[extra_field] = extra_value + + return instance_dict class ExperienceSerializer(serializers.ModelSerializer): @@ -27,9 +102,11 @@ class ExperienceSerializer(serializers.ModelSerializer): ) course_id = ExperienceResourceRelatedField( queryset=CourseOverview.objects, + get_extra_fields=get_course_extra_attributes, ) author = ExperienceResourceRelatedField( queryset=User.objects, + get_extra_fields=get_user_extra_attributes, ) diff --git a/eox_nelp/course_experience/api/v1/tests/mixins_helpers.py b/eox_nelp/course_experience/api/v1/tests/mixins_helpers.py index 58c7ad0c..d381d352 100644 --- a/eox_nelp/course_experience/api/v1/tests/mixins_helpers.py +++ b/eox_nelp/course_experience/api/v1/tests/mixins_helpers.py @@ -12,6 +12,7 @@ from rest_framework import status from rest_framework.test import APIClient +from eox_nelp.course_experience.api.v1.serializers import get_course_extra_attributes, get_user_extra_attributes from eox_nelp.course_experience.api.v1.views import INVALID_KEY_ERROR from eox_nelp.edxapp_wrapper.course_overviews import CourseOverview @@ -35,6 +36,31 @@ def setUp(self): # pylint: disable=invalid-name self.my_course, _ = CourseOverview.objects.get_or_create(id=BASE_COURSE_ID) self.client.force_authenticate(self.user) + def make_relationships_data(self): + """ + Make the relationships dict with custom extra attributes based in the attributes of the serializers + withe variables COURSE_OVERVIEW_EXTRA_ATTRIBUTES and USER_EXTRA_ATTRIBUTES + + Returns: + dict: relationships dict with the corresponding shape, and key-values. + """ + return { + "author": { + "data": { + "type": "User", + "id": f"{self.user.id}", + **get_user_extra_attributes(self.user) + } + }, + "course_id": { + "data": { + "type": "CourseOverview", + "id": f"{self.my_course.id}", + **get_course_extra_attributes(self.my_course) + } + }, + } + def test_get_object_list_by_user(self): """ Test a get request to the list endpoint for the desired view. Expected behavior: @@ -85,7 +111,6 @@ def test_patch_object_json(self): expected_data["data"]["attributes"].update(self.patch_data) response = self.client.patch(url_endpoint, self.patch_data, format="json") - self.assertIn(response.headers["Content-Type"], RESPONSE_CONTENT_TYPES) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.json(), expected_data) diff --git a/eox_nelp/course_experience/api/v1/tests/test_views.py b/eox_nelp/course_experience/api/v1/tests/test_views.py index 2d8a500c..20c0891e 100644 --- a/eox_nelp/course_experience/api/v1/tests/test_views.py +++ b/eox_nelp/course_experience/api/v1/tests/test_views.py @@ -61,20 +61,7 @@ def setUp(self): "status": self.my_unit_like.status, "item_id": f"{self.my_unit_like.item_id}", }, - "relationships": { - "author": { - "data": { - "type": "User", - "id": f"{self.user.id}", - } - }, - "course_id": { - "data": { - "type": "CourseOverview", - "id": f"{self.my_course.id}", - } - } - } + "relationships": self.make_relationships_data() } } self.object_url_kwarg = {self.object_key: BASE_ITEM_ID} @@ -117,20 +104,7 @@ def setUp(self): "reason": f"{self.my_unit_report.reason}", "item_id": f"{self.my_unit_report.item_id}", }, - "relationships": { - "author": { - "data": { - "type": "User", - "id": f"{self.user.id}", - } - }, - "course_id": { - "data": { - "type": "CourseOverview", - "id": f"{self.my_course.id}", - } - } - } + "relationships": self.make_relationships_data() } } self.object_url_kwarg = {self.object_key: BASE_ITEM_ID} @@ -190,20 +164,7 @@ def setUp(self): "username": f"{self.user.username}", "status": self.my_course_like.status, }, - "relationships": { - "author": { - "data": { - "type": "User", - "id": f"{self.user.id}", - } - }, - "course_id": { - "data": { - "type": "CourseOverview", - "id": f"{self.my_course.id}", - } - } - } + "relationships": self.make_relationships_data() } } @@ -244,20 +205,7 @@ def setUp(self): "username": f"{self.user.username}", "reason": f"{self.my_course_report.reason}" }, - "relationships": { - "author": { - "data": { - "type": "User", - "id": f"{self.user.id}" - } - }, - "course_id": { - "data": { - "type": "CourseOverview", - "id": f"{self.my_course.id}" - } - } - } + "relationships": self.make_relationships_data() } } @@ -315,20 +263,7 @@ def setUp(self): "public": self.my_course_feedback.public, "recommended": self.my_course_feedback.recommended, }, - "relationships": { - "author": { - "data": { - "type": "User", - "id": f"{self.user.id}" - } - }, - "course_id": { - "data": { - "type": "CourseOverview", - "id": f"{self.my_course.id}" - } - } - } + "relationships": self.make_relationships_data() } }