Repository navigation
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 6 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 |
|---|---|---|
|
|
@@ -5,10 +5,14 @@ | |
|
|
||
| import os | ||
|
|
||
| from django.http import FileResponse, Http404 | ||
| from django.http import FileResponse, Http404, HttpResponse, HttpResponseBadRequest | ||
| from rest_framework.exceptions import PermissionDenied | ||
| from rest_framework.request import Request | ||
| from rest_framework.views import APIView | ||
|
|
||
| from ...import_export import api | ||
| from .serializers import TaxonomyImportBodySerializer | ||
|
|
||
|
|
||
| class TemplateView(APIView): | ||
| """ | ||
|
|
@@ -49,3 +53,51 @@ def get(self, request: Request, file_ext: str, *args, **kwargs) -> FileResponse: | |
| response = FileResponse(fh, content_type=content_type) | ||
| response['Content-Disposition'] = content_disposition | ||
| return response | ||
|
|
||
|
|
||
| class ImportView(APIView): | ||
| """ | ||
| View to import taxonomies | ||
|
|
||
| **Example Requests** | ||
| POST /tagging/rest_api/v1/import/ | ||
| { | ||
| "taxonomy_name": "Taxonomy Name", | ||
| "taxonomy_description": "This is a description", | ||
| "file": <file>, | ||
| } | ||
|
|
||
| **Query Returns** | ||
| * 200 - Success | ||
| * 400 - Bad request | ||
| * 405 - Method not allowed | ||
| """ | ||
| http_method_names = ['post'] | ||
|
pomegranited marked this conversation as resolved.
Outdated
|
||
|
|
||
| def post(self, request: Request, *args, **kwargs) -> HttpResponse: | ||
| """ | ||
| Imports the taxonomy from the uploaded file. | ||
| """ | ||
| perm = "oel_tagging.import_taxonomy" | ||
| if not request.user.has_perm(perm): | ||
| raise PermissionDenied("You do not have permission to import taxonomies") | ||
|
|
||
| body = TaxonomyImportBodySerializer(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"] | ||
|
|
||
| import_success = api.create_taxonomy_and_import_tags( | ||
| taxonomy_name=taxonomy_name, | ||
| taxonomy_description=taxonomy_description, | ||
| file=file, | ||
| parser_format=parser_format, | ||
| ) | ||
|
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.. the import/export tags API was designed to use async celery tasks so it could run in the background (in case there's a lot of tags to import). @bradenmacdonald do you want us to use these async tasks here, or are we doing all imports synchronously?
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. I suspect synchronous will be totally fine for the short term, but we might as well do it async since we've done most of the work already. How much work will that add to make the REST API layer async too?
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. What do we mean to make the call async here?
The 2 is simple (but I don't know if we have some gain that way).
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. I meant (1). But with simple polling for now, no need for websockets or anything fancy. For (2) it actually ties up much more server resources than just directly doing the import synchronously. If that's going to take a while to implement though we can just to sync for now.
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. Got it! If the import fails, it is easy to remove the created taxonomy in the sync call. Doing async, we can create the taxonomy, return the id to the client, and make calls waiting for the results (the result is associated with the taxonomy). But I need to figure out how to delete the freshly created taxonomy after a failed attempt. Would you happen to have any ideas on this? This is not urgent for this task, but we will need to handle it if we want to go async sometime in the future. One approach is to change the import flow in the front-end: we first create the taxonomy and always call import inside it (then we don't need to handle the delete). This will impact our designs and this task a bit. Another approach is to handle the create+import sync and use async for importing on an already created taxonomy. But it will also impact the UX, having different flows. I think the appropriate solution will be to refactor the import to let it create the taxonomy and change the results to be tied to some kind of "job-id", and not the taxonomy itself.
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. I think the right approach is to create an additional async task which creates the taxonomy, then synchronously calls the existing import task (which is a celery task so can be called either sync or async, but since we're already in an async task, we call it sync). If that succeeds, it returns the taxonomy ID, and if it fails, it deletes the taxonomy and returns an error message. The main thing is that this "wrapper" task which does create+import returns an import ID not a taxonomy ID. And then the frontend polls the import ID until it gets either a success or failure message. Only then does it get the taxonomy ID. But that's all getting too complicated. Just do it synchronously for now an we'll make it async if it's too slow in practice. Explicitly put a comment in the REST API for the import that "this is an unstable API and may change if we make it async." |
||
|
|
||
| if import_success: | ||
| return HttpResponse(status=200) | ||
| else: | ||
| return HttpResponseBadRequest("Error importing taxonomy") | ||
Uh oh!
There was an error while loading. Please reload this page.