Add action to resolve Community Library Submissions#5178
Conversation
02827cf to
c6d2207
Compare
AlexVelezLl
left a comment
There was a problem hiding this comment.
Thanks @Jakoma02! This is looking great so far! I have left a couple of things I noted, mostly nitpicks, but the most important thing is to ensure the consistency in the returned value of the resolve action. Apart from that eveything looks good!
| ("contentcuration", "0156_communitylibrarysubmission_admin_fields"), | ||
| ] | ||
|
|
||
| operations = [ |
There was a problem hiding this comment.
I think we can collapse this migration with the previous one, as I see the only value that is being altered is the `blank=True)
There was a problem hiding this comment.
You are, of course, right. I incorrectly thought the previous migration was created in an earlier PR, so it was necessary to alter it. It definitely does not make sense to create and alter the field in the same PR. I fixed this in 9cddb63.
There was a problem hiding this comment.
Seems like we still need to remove this file, as its not needed anymore 😅
| ) | ||
| router.register( | ||
| r"communitylibrary_submission_admin", | ||
| CommunityLibrarySubmissionAdminViewSet, |
There was a problem hiding this comment.
Just for consistency with other admin viewsets, I think it may be a better idea to name this viewset AdminCommunityLibrarySubmissionViewset and add admin as prefix of the route.
There was a problem hiding this comment.
That makes sense, I changed this in 9cddb63. I am not sure if underscores or dashes should be preferred because I see both in the codebase, I leave underscores for now because that is what was approved for the user-facing community library viewset.
There was a problem hiding this comment.
I dont think there is a preferred way, I think thats okay :)
| return super().update(instance, validated_data) | ||
|
|
||
|
|
||
| def timezoned_datetime_now(): |
There was a problem hiding this comment.
Hmm, did you tried using django's timezone.now method instead? And then mock "django.utils.timezone.now" in the tests? Just gave it a quick try and seemed to be working okay
There was a problem hiding this comment.
I was not aware of django.utils.timezone. Directly mocking that function works well, thanks! Changed in 9cddb63.
| ) | ||
|
|
||
| if ( | ||
| "status" not in validated_data |
There was a problem hiding this comment.
(nitpick) If we reach to this point, we have already ensured that status is in validated_data thanks to the previous check.
There was a problem hiding this comment.
I was aware that status will always be in the validated data after the first if statement, but I intentionally left it because it seemed easier for the reader to understand that this is indeed not accessing a potentially undefined value, and the performance impact is practically non-existent. But I have no strong feelings about this. We can leave the explicit check out if that seems better to you.
There was a problem hiding this comment.
No strong feelings neither, if it was intentional, then its fine!
| or validated_data["status"] | ||
| == community_library_submission_constants.STATUS_REJECTED | ||
| ): | ||
| if "resolution_reason" not in validated_data: |
There was a problem hiding this comment.
Could we also check if "resolution_reason" and "feedback_notes" are not empty strings?
There was a problem hiding this comment.
You are right, I missed that possibility. Added in 9cddb63.
| field_map = CommunityLibrarySubmissionViewSet.field_map.copy() | ||
| field_map.update( | ||
| { | ||
| "resolved_by_first_name": "resolved_by__first_name", |
There was a problem hiding this comment.
Now that I think more about this, I think it would be better to just map both first and last name to a resolved_by_name field, as this is what we will always show in the frontend. We can pass functions to field_map fields, just like this example. If you can also update the author field in the CommunityLibrarySubmissionViewSet that'd be nice :)
| if submission.status == community_library_submission_constants.STATUS_APPROVED: | ||
| self._mark_previous_pending_submissions_as_superseded(submission) | ||
|
|
||
| return Response(serializer.data) |
There was a problem hiding this comment.
Since we use serializers just for write-only changes, we don't set read-only values to the serializer, therefore, here we are not returning the fields date_resolved, resolved_by_id, etc. So here we can instead return the self.serialize_object() value to get the standard response taking into account the values viewset property
There was a problem hiding this comment.
I did not think of doing it this way, thanks for suggesting this! Changed in 9cddb63.
| } | ||
| ) | ||
|
|
||
| def create(self, request, *args, **kwargs): |
There was a problem hiding this comment.
Here if we want to block all these requests, then it will be cleaner to create a Mixin that defines the common properties and methods we want to share between these two viewsets, and then have the CommunityLibrarySubmissionViewSet inherit from that mixin + the other REST operations. And have the CommunityLibrarySubmissionViewSet just inherit from the mixin and the ReadOnlyValuesViewset. That is a cleaner way to not override these methods if we don't want to allow them in the first place.
In Kolibri we have an example of this: https://github.com/learningequality/kolibri/blob/538fb63ddc7218d849e5fc7df1bf515d301eac4a/kolibri/core/auth/api.py#L542
There was a problem hiding this comment.
I have thought about separating the common functionality and then reusing it in both viewsets, but it seemed to be a bit too complicated and confusing and it seemed simpler to just override the methods. But I can see your point that extracting the common pieces into a mixin is somewhat cleaner, I refactored it that way in 9cddb63.
| self.patcher.stop() | ||
| super().tearDown() | ||
|
|
||
| def _manually_resolve_submission(self): |
There was a problem hiding this comment.
We can name this helper to _manually_reject_submission, so that is clearer why we then check the status == REJECTED in the tests.
| r"communitylibrary-submission", | ||
| CommunityLibrarySubmissionViewSet, | ||
| basename="community-library-submission", | ||
| ) | ||
| router.register( | ||
| r"admin_communitylibrary_submission", |
There was a problem hiding this comment.
Either if we use snake case or kebab case for these new routes, I think we should use the same case for both routes.
There was a problem hiding this comment.
Right, this was not intentional. Fixed in ff53d60.
| if submission.status == community_library_submission_constants.STATUS_APPROVED: | ||
| self._mark_previous_pending_submissions_as_superseded(submission) | ||
|
|
||
| return Response(self.serialize_object(pk=submission.id)) |
There was a problem hiding this comment.
(AFAIK 😅) In theory we shouldn't need to pass the pk argument to this method, it should behave the same as the get_object methods for detail=True actions.
There was a problem hiding this comment.
That makes sense, I removed the explicit pk argument in 73ad4cb.
| ReadOnlyValuesViewset, | ||
| ): | ||
| filter_backends = [DjangoFilterBackend] | ||
| filterset_fields = ["channel"] |
There was a problem hiding this comment.
The AdminCommunityLibrarySubmissionViewSet should also allow filtering by channels, and provide paginated responses, so I think we can make these fields part of the CommunityLibrarySubmissionViewSetMixin too.
There was a problem hiding this comment.
You are right that it makes sense to support the same filtering and pagination for the admin viewset as well, changed in 72bbef6.
AlexVelezLl
left a comment
There was a problem hiding this comment.
Thanks @Jakoma02! We are almost there! Just one little cleanup! After this we can proceed with the merge :)
| ("contentcuration", "0156_communitylibrarysubmission_admin_fields"), | ||
| ] | ||
|
|
||
| operations = [ |
There was a problem hiding this comment.
Seems like we still need to remove this file, as its not needed anymore 😅
You are of course right, done in 7a3c46e. |
AlexVelezLl
left a comment
There was a problem hiding this comment.
Thanks @Jakoma02! Great work! This looks good to me! Merging!
4863c93
into
learningequality:community-channels
Summary
This PR extends the
CommunityLibrarySubmissionmodel with new internal fields, adds a newCommunityLibraryAdminViewSetthat exposes these fields and introduces aresolveaction for administrators.Detailed changes:
date_resolved,resolved_by,resolved_reason,feedback_notesandinternal_notesto theCommunityLibrarySubmissionmodelresolved_reason,feedback_notesanddate_resolvedin the userCommunityLibrarySubmissionViewsetSupersededsubmission stateresolution_reason_choicesconstantsCommunityLibrarySubmissionAdminViewSetextendingCommunityLibrarySubmissionViewSet, also exposingresolved_by_id,resolved_by_first_name,resolved_by_last_nameandinternal_notesresolveaction with semantics as in the linked issueCommunityLibrarySubmissionResolveSerializerfor deserializing resolution requests, extendingCommunityLibrarySubmissionSerializerAdminViewSetTestCasetesting the functionalityNo manual testing besides designing the tests and making sure that they pass was performed.
References
Solves #5170.
This PR depends on unmerged changes from #5167, so it should only be merged after that PR is merged and should be rebased to incorporate any changes to that PR introduced during the review process.Reviewer guidance
See the note above about the merging order.Otherwise, not any.