-
Notifications
You must be signed in to change notification settings - Fork 18
feat: add import taxonomy endpoint #112
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
c1d5944
05105bc
a6919a1
f030767
4fe2457
06efd55
d400a54
a50e08f
e1b1789
d9950f6
7450737
ae76400
62e2593
a827178
730b73f
4e52c57
a8a0c7e
c4fe95d
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 |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| """ | ||
| Open edX Learning ("Learning Core"). | ||
| """ | ||
| __version__ = "0.3.2" | ||
| __version__ = "0.3.3" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| from rest_framework.decorators import action | ||
| from rest_framework.exceptions import MethodNotAllowed, PermissionDenied, ValidationError | ||
| from rest_framework.generics import ListAPIView, RetrieveUpdateDestroyAPIView | ||
| from rest_framework.request import Request | ||
| from rest_framework.response import Response | ||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet | ||
|
|
||
|
|
@@ -26,7 +27,7 @@ | |
| update_tag_in_taxonomy, | ||
| ) | ||
| from ...data import TagDataQuerySet | ||
| from ...import_export.api import export_tags | ||
| from ...import_export.api import export_tags, get_last_import_log, import_tags | ||
| from ...import_export.parsers import ParserFormat | ||
| from ...models import ObjectTag, Taxonomy | ||
| from ...rules import ObjectTagPermissionItem | ||
|
|
@@ -40,6 +41,8 @@ | |
| ObjectTagUpdateQueryParamsSerializer, | ||
| TagDataSerializer, | ||
| TaxonomyExportQueryParamsSerializer, | ||
| TaxonomyImportBodySerializer, | ||
| TaxonomyImportNewBodySerializer, | ||
| TaxonomyListQueryParamsSerializer, | ||
| TaxonomySerializer, | ||
| TaxonomyTagCreateBodySerializer, | ||
|
|
@@ -52,7 +55,7 @@ | |
| @view_auth_classes | ||
| class TaxonomyView(ModelViewSet): | ||
| """ | ||
| View to list, create, retrieve, update, delete or export Taxonomies. | ||
| View to list, create, retrieve, update, delete, export or import Taxonomies. | ||
|
|
||
| **List Query Parameters** | ||
| * enabled (optional) - Filter by enabled status. Valid values: true, | ||
|
|
@@ -167,7 +170,29 @@ class TaxonomyView(ModelViewSet): | |
| * 400 - Invalid query parameter | ||
| * 403 - Permission denied | ||
|
|
||
| **Import/Create Taxonomy Example Requests** | ||
| POST /tagging/rest_api/v1/taxonomy/import/ | ||
| { | ||
| "taxonomy_name": "Taxonomy Name", | ||
| "taxonomy_description": "This is a description", | ||
| "file": <file>, | ||
| } | ||
|
|
||
| **Import/Create Taxonomy Query Returns** | ||
| * 200 - Success | ||
| * 400 - Bad request | ||
| * 403 - Permission denied | ||
|
|
||
| **Import/Update Taxonomy Example Requests** | ||
| PUT /tagging/rest_api/v1/taxonomy/:pk/tags/import/ | ||
| { | ||
| "file": <file>, | ||
| } | ||
|
|
||
| **Import/Update Taxonomy Query Returns** | ||
| * 200 - Success | ||
| * 400 - Bad request | ||
| * 403 - Permission denied | ||
| """ | ||
|
|
||
| lookup_value_regex = r"\d+" | ||
|
|
@@ -215,9 +240,6 @@ def export(self, request, **_kwargs) -> HttpResponse: | |
| Export a taxonomy. | ||
| """ | ||
| taxonomy = self.get_object() | ||
| perm = "oel_tagging.export_taxonomy" | ||
| if not request.user.has_perm(perm, taxonomy): | ||
| raise PermissionDenied("You do not have permission to export this taxonomy.") | ||
| query_params = TaxonomyExportQueryParamsSerializer( | ||
| data=request.query_params.dict() | ||
| ) | ||
|
|
@@ -242,6 +264,57 @@ def export(self, request, **_kwargs) -> HttpResponse: | |
|
|
||
| return HttpResponse(tags, content_type=content_type) | ||
|
|
||
| @action(detail=False, url_path="import", methods=["post"]) | ||
| def create_import(self, request: Request, **_kwargs) -> Response: | ||
| """ | ||
| Creates a new taxonomy and imports the tags from the uploaded file. | ||
| """ | ||
| body = TaxonomyImportNewBodySerializer(data=request.data) | ||
| body.is_valid(raise_exception=True) | ||
|
|
||
| taxonomy_name = body.validated_data["taxonomy_name"] | ||
| taxonomy_description = body.validated_data["taxonomy_description"] | ||
| file = body.validated_data["file"].file | ||
| parser_format = body.validated_data["parser_format"] | ||
|
|
||
| taxonomy = create_taxonomy(taxonomy_name, taxonomy_description) | ||
| try: | ||
| import_success = import_tags(taxonomy, file, parser_format) | ||
|
|
||
| if import_success: | ||
| serializer = self.get_serializer(taxonomy) | ||
| return Response(serializer.data, status=status.HTTP_201_CREATED) | ||
| else: | ||
| import_error = get_last_import_log(taxonomy) | ||
|
Contributor
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. Hmm.. This works fine in a serial environment, but if we've got multiple imports happening at the same time, this won't necessarily be the import log we're interested in here. Really, the only way to get that is if we have the task ID after import. But I don't know if it's worth refactoring
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. This is not as alarming as I made it sound: |
||
| taxonomy.delete() | ||
| return Response(import_error, status=status.HTTP_400_BAD_REQUEST) | ||
| except ValueError as e: | ||
| return Response(str(e), status=status.HTTP_400_BAD_REQUEST) | ||
|
|
||
| @action(detail=True, url_path="tags/import", methods=["put"]) | ||
| def update_import(self, request: Request, **_kwargs) -> Response: | ||
| """ | ||
| Imports tags from the uploaded file to an already created taxonomy. | ||
| """ | ||
| body = TaxonomyImportBodySerializer(data=request.data) | ||
| body.is_valid(raise_exception=True) | ||
|
|
||
| file = body.validated_data["file"].file | ||
| parser_format = body.validated_data["parser_format"] | ||
|
|
||
| taxonomy = self.get_object() | ||
| try: | ||
| import_success = import_tags(taxonomy, file, parser_format) | ||
|
|
||
| if import_success: | ||
| serializer = self.get_serializer(taxonomy) | ||
| return Response(serializer.data) | ||
| else: | ||
| import_error = get_last_import_log(taxonomy) | ||
| return Response(import_error, status=status.HTTP_400_BAD_REQUEST) | ||
| except ValueError as e: | ||
| return Response(str(e), status=status.HTTP_400_BAD_REQUEST) | ||
|
|
||
|
|
||
| @view_auth_classes | ||
| class ObjectTagView( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
It's true that "if you can view the taxonomy, you can export it", but are we still checking that the user has "view" permission here? I don't see it. (Though it looks like it's covered by the tests. I'm just wondering where that check is happening).
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.
The
get_object()function handles that:https://github.com/encode/django-rest-framework/blob/1db19f4b2d1500894448634852e02f73043382e4/rest_framework/generics.py#L103