-
Notifications
You must be signed in to change notification settings - Fork 3
fix list duplication when editing lists #1749
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -550,3 +550,57 @@ def test_content_file_serializer(settings, expected_types, has_channels): | |
| ), | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| def test_set_learning_path_request_serializer(): | ||
| """Test serializer for setting learning path relationships""" | ||
| lists = factories.LearningPathFactory.create_batch(2) | ||
| resource = factories.LearningResourceFactory.create() | ||
|
|
||
| serializer = serializers.SetLearningPathsRequestSerializer() | ||
|
|
||
| data1 = { | ||
| "learning_path_ids": [ | ||
| str(lists[0].learning_resource.id), | ||
| lists[1].learning_resource.id, | ||
| ], | ||
| "learning_resource_id": str(resource.id), | ||
| } | ||
| assert serializer.to_internal_value(data1) == { | ||
| "learning_path_ids": [ | ||
| lists[0].learning_resource.id, | ||
| lists[1].learning_resource.id, | ||
| ], | ||
| "learning_resource_id": resource.id, | ||
| } | ||
|
|
||
| invalid = serializers.SetLearningPathsRequestSerializer( | ||
| data={"learning_path_ids": [1, 2], "learning_resource_id": 3} | ||
| ) | ||
| assert invalid.is_valid() is False | ||
| assert "learning_path_ids" in invalid.errors | ||
| assert "learning_resource_id" in invalid.errors | ||
|
|
||
|
|
||
| def test_set_userlist_request_serializer(): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add. docstring |
||
| """Test serializer for setting userlist relationships""" | ||
| lists = factories.UserListFactory.create_batch(2) | ||
| resource = factories.LearningResourceFactory.create() | ||
|
|
||
| serializer = serializers.SetUserListsRequestSerializer() | ||
|
|
||
| data1 = { | ||
| "userlist_ids": [str(lists[0].id), lists[1].id], | ||
| "learning_resource_id": str(resource.id), | ||
| } | ||
| assert serializer.to_internal_value(data1) == { | ||
| "userlist_ids": [lists[0].id, lists[1].id], | ||
| "learning_resource_id": resource.id, | ||
| } | ||
|
|
||
| invalid = serializers.SetUserListsRequestSerializer( | ||
| data={"userlist_ids": [1, 2], "learning_resource_id": 3} | ||
| ) | ||
| assert invalid.is_valid() is False | ||
| assert "userlist_ids" in invalid.errors | ||
| assert "learning_resource_id" in invalid.errors | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,8 @@ | |
| PodcastEpisodeResourceSerializer, | ||
| PodcastResourceSerializer, | ||
| ProgramResourceSerializer, | ||
| SetLearningPathsRequestSerializer, | ||
| SetUserListsRequestSerializer, | ||
| UserListRelationshipSerializer, | ||
| UserListSerializer, | ||
| VideoPlaylistResourceSerializer, | ||
|
|
@@ -433,10 +435,16 @@ def userlists(self, request, *args, **kwargs): # noqa: ARG002 | |
| """ | ||
| Set User List relationships for a given Learning Resource | ||
| """ | ||
| learning_resource_id = kwargs.get("pk") | ||
| user_list_ids = request.query_params.getlist("userlist_id") | ||
| req_data = SetUserListsRequestSerializer().to_internal_value( | ||
| { | ||
| "userlist_ids": request.query_params.getlist("userlist_id"), | ||
| "learning_resource_id": kwargs.get("pk"), | ||
| } | ||
| ) | ||
| learning_resource_id = req_data["learning_resource_id"] | ||
| userlist_ids = req_data["userlist_ids"] | ||
| if ( | ||
| UserList.objects.filter(pk__in=user_list_ids) | ||
| UserList.objects.filter(pk__in=userlist_ids) | ||
| .exclude(author=request.user) | ||
| .exists() | ||
| ): | ||
|
|
@@ -445,9 +453,14 @@ def userlists(self, request, *args, **kwargs): # noqa: ARG002 | |
| current_relationships = UserListRelationship.objects.filter( | ||
| parent__author=request.user, child_id=learning_resource_id | ||
| ) | ||
| current_relationships.exclude(parent_id__in=user_list_ids).delete() | ||
| for userlist_id in user_list_ids: | ||
|
|
||
| # Remove the resource from lists it WAS in before but is not in now | ||
| current_relationships.exclude(parent_id__in=userlist_ids).delete() | ||
| current_parent_lists = current_relationships.values_list("parent_id", flat=True) | ||
|
|
||
| for userlist_id in userlist_ids: | ||
| last_index = 0 | ||
| # re-number the positions for surviving items | ||
| for index, relationship in enumerate( | ||
| UserListRelationship.objects.filter( | ||
| parent__author=request.user, parent__id=userlist_id | ||
|
|
@@ -456,11 +469,13 @@ def userlists(self, request, *args, **kwargs): # noqa: ARG002 | |
| relationship.position = index | ||
| relationship.save() | ||
| last_index = index | ||
| UserListRelationship.objects.create( | ||
| parent_id=userlist_id, | ||
| child_id=learning_resource_id, | ||
| position=last_index + 1, | ||
| ) | ||
| # Add new items as necessary | ||
| if userlist_id not in list(current_parent_lists): | ||
| UserListRelationship.objects.create( | ||
| parent_id=userlist_id, | ||
| child_id=learning_resource_id, | ||
| position=last_index + 1, | ||
| ) | ||
| SerializerClass = self.get_serializer_class() | ||
| serializer = SerializerClass(current_relationships, many=True) | ||
| return Response(serializer.data) | ||
|
|
@@ -489,14 +504,25 @@ def learning_paths(self, request, *args, **kwargs): # noqa: ARG002 | |
| """ | ||
| Set Learning Path relationships for a given Learning Resource | ||
| """ | ||
| learning_resource_id = kwargs.get("pk") | ||
| learning_path_ids = request.query_params.getlist("learning_path_id") | ||
| req_data = SetLearningPathsRequestSerializer().to_internal_value( | ||
| { | ||
| "learning_path_ids": request.query_params.getlist("learning_path_id"), | ||
| "learning_resource_id": kwargs.get("pk"), | ||
| } | ||
| ) | ||
| learning_resource_id = req_data["learning_resource_id"] | ||
| learning_path_ids = req_data["learning_path_ids"] | ||
| current_relationships = LearningResourceRelationship.objects.filter( | ||
| child_id=learning_resource_id | ||
| ) | ||
| # Remove the resource from lists it WAS in before but is not in now | ||
| current_relationships.exclude(parent_id__in=learning_path_ids).delete() | ||
| for learning_path_id in learning_path_ids: | ||
| current_parent_lists = current_relationships.values_list("parent_id", flat=True) | ||
|
|
||
| for learning_path_id_str in learning_path_ids: | ||
| learning_path_id = int(learning_path_id_str) | ||
| last_index = 0 | ||
| # re-number the positions for surviving items | ||
| for index, relationship in enumerate( | ||
| LearningResourceRelationship.objects.filter( | ||
| parent__id=learning_path_id | ||
|
|
@@ -505,12 +531,15 @@ def learning_paths(self, request, *args, **kwargs): # noqa: ARG002 | |
| relationship.position = index | ||
| relationship.save() | ||
| last_index = index | ||
| LearningResourceRelationship.objects.create( | ||
| parent_id=learning_path_id, | ||
| child_id=learning_resource_id, | ||
| relation_type=LearningResourceRelationTypes.LEARNING_PATH_ITEMS, | ||
| position=last_index + 1, | ||
| ) | ||
|
|
||
| # Add new items as necessary | ||
| if learning_path_id not in list(current_parent_lists): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I spend a while trying to debug why my conditional was not working, and it ended up being because (originally) That motivated me to make the two new serializers for validating request data. |
||
| LearningResourceRelationship.objects.create( | ||
| parent_id=learning_path_id, | ||
| child_id=learning_resource_id, | ||
| relation_type=LearningResourceRelationTypes.LEARNING_PATH_ITEMS, | ||
| position=last_index + 1, | ||
| ) | ||
| SerializerClass = self.get_serializer_class() | ||
| serializer = SerializerClass(current_relationships, many=True) | ||
| return Response(serializer.data) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -350,3 +350,34 @@ def assign_userlists(course, userlists): | |
| assert ( | ||
| UserListRelationship.objects.filter(child=course.learning_resource).count() == 3 | ||
| ) | ||
|
|
||
|
|
||
| def test_adding_to_userlist_not_effect_existing_membership(client, user): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing docstring |
||
| """ | ||
| Given L1 (existing parent), L2 (new parent), and R (resource), | ||
| test that adding R to L2 does not affect L1. | ||
| """ | ||
| course = factories.CourseFactory.create() | ||
|
|
||
| existing_parent = factories.UserListFactory.create(author=user) | ||
| factories.UserListRelationshipFactory.create( | ||
| parent=existing_parent, child=course.learning_resource | ||
| ) | ||
| new_additional_parent = factories.UserListFactory.create(author=user) | ||
|
|
||
| prev_parent_count = existing_parent.resources.count() | ||
| new_additional_parent_count = new_additional_parent.resources.count() | ||
|
|
||
| url = reverse( | ||
| "lr:v1:learning_resource_relationships_api-userlists", | ||
| args=[course.learning_resource.id], | ||
| ) | ||
| client.force_login(user) | ||
| lists = [existing_parent, new_additional_parent] | ||
| resp = client.patch( | ||
| f"{url}?{"".join([f"userlist_id={userlist.id}&" for userlist in lists])}" | ||
| ) | ||
|
|
||
| assert resp.status_code == 200 | ||
| assert prev_parent_count == existing_parent.resources.count() | ||
| assert new_additional_parent_count + 1 == new_additional_parent.resources.count() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add. docstring