[damned-lies/api_reserve_upload] Allowed reserve/upload translation by API
- From: Claude Paroz <claudep src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies/api_reserve_upload] Allowed reserve/upload translation by API
- Date: Sat, 30 Jan 2021 22:17:51 +0000 (UTC)
commit aa8ce42e6d755438806e21d4cf5c66ca9d1b0c30
Author: Claude Paroz <claude 2xlibre net>
Date: Sat Jan 30 23:16:25 2021 +0100
Allowed reserve/upload translation by API
Thanks Amanda Shafack for the initial patch.
api/tests.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
api/urls.py | 15 +++++++++++----
api/views.py | 49 ++++++++++++++++++++++++++++++++++++++++++-------
3 files changed, 105 insertions(+), 11 deletions(-)
---
diff --git a/api/tests.py b/api/tests.py
index fb7d67de..5997098b 100644
--- a/api/tests.py
+++ b/api/tests.py
@@ -1,6 +1,16 @@
+from pathlib import Path
+
+from django.core.files import File
from django.test import TestCase
from django.urls import reverse
+from languages.models import Language
+from people.models import Person
+from stats.models import Branch, Domain
+from teams.models import Role, Team
+from vertimus.models import State, StateTranslating
+from vertimus.views import get_vertimus_state
+
class APITests(TestCase):
fixtures = ['sample_data.json']
@@ -93,3 +103,45 @@ class APITests(TestCase):
self.assertEqual(result['statistics'], {'untrans': 0, 'fuzzy': 0, 'trans': 47})
self.assertEqual(result['pot_file'], '/POT/gnome-hello.master/gnome-hello.master.pot')
self.assertEqual(result['po_file'], '/POT/gnome-hello.master/gnome-hello.master.fr.po')
+
+ def test_reserve_translation(self):
+ translator = Person.objects.create(
+ first_name='John', last_name='Translator',
+ email='jt devnull com', username='translator'
+ )
+ Role.objects.create(team=Team.objects.get(name='fr'), person=translator)
+ url = reverse('api-reserve', args=['gnome-hello', 'master', 'po', 'fr'])
+ response = self.client.post(url, data={})
+ self.assertEqual(response.status_code, 302)
+ self.client.force_login(translator)
+ response = self.client.post(url, data={})
+ self.assertEqual(response.json(), {'result': 'OK'})
+ state = State.objects.get(person=translator)
+ self.assertEqual(state.name, 'Translating')
+
+ def test_upload_translation(self):
+ translator = Person.objects.create(
+ first_name='John', last_name='Translator',
+ email='jt devnull com', username='translator'
+ )
+ Role.objects.create(team=Team.objects.get(name='fr'), person=translator)
+ _, _, state = get_vertimus_state(
+ Branch.objects.get(module__name='gnome-hello'),
+ Domain.objects.get(module__name='gnome-hello', name='po'),
+ Language.objects.get(locale='fr')
+ )
+ url = reverse('api-upload', args=['gnome-hello', 'master', 'po', 'fr'])
+ test_po = Path(__file__).parent.parent / "stats" / "tests" / "test.po"
+ with (test_po).open('rb') as fh:
+ response = self.client.post(url, data={'file': File(fh)})
+ self.assertEqual(response.status_code, 302)
+
+ self.client.force_login(translator)
+ with (test_po).open('rb') as fh:
+ response = self.client.post(url, data={'file': File(fh)})
+ self.assertEqual(response.status_code, 403)
+
+ state.change_state(StateTranslating, person=translator)
+ with (test_po).open('rb') as fh:
+ response = self.client.post(url, data={'file': File(fh)})
+ self.assertEqual(response.json(), {'result': 'OK'})
diff --git a/api/urls.py b/api/urls.py
index 734be31f..93ea78bf 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -2,6 +2,11 @@ from django.urls import path
from . import views
+complete_translatable_path = (
+ 'modules/<name:module_name>/branches/<path:branch_name>/domains/<name:domain_name>'
+ '/languages/<locale:lang>'
+)
+
urlpatterns = [
path('', views.APIHomeView.as_view(), name='api-home'),
path('modules/', views.ModulesView.as_view(), name='api-modules'),
@@ -14,12 +19,14 @@ urlpatterns = [
path('releases/<name:release>/stats', views.ReleaseStatsView.as_view(), name='api-release-stats'),
path('releases/<name:release>/languages/<locale:lang>', views.ReleaseLanguageView.as_view(),
name='api-release-language'),
- path(
- 'modules/<name:module_name>/branches/<path:branch_name>/domains/<name:domain_name>'
- '/languages/<locale:lang>',
- views.ModuleLangStatsView.as_view(),
+ path(complete_translatable_path, views.ModuleLangStatsView.as_view(),
name='api-module-lang-stats'
),
+ # APIs to reserve translation and upload translation, as could be used by an external
+ # translation tool.
+ path(f'{complete_translatable_path}/reserve', views.ReserveTranslationView.as_view(),
name='api-reserve'),
+ path(f'{complete_translatable_path}/upload', views.UploadTranslationView.as_view(), name='api-upload'),
+
# Used by a GitLab webhook to signal a commit for that module/branch
path('modules/<name:module_name>/branches/<path:branch_name>/ping', views.rebuild_branch,
name='api-module-rebuild'),
diff --git a/api/views.py b/api/views.py
index c99c3bea..1f2d57c7 100644
--- a/api/views.py
+++ b/api/views.py
@@ -1,10 +1,13 @@
import traceback
from threading import Thread
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.core.exceptions import PermissionDenied
from django.core.mail import mail_admins
from django.http import Http404, HttpResponseForbidden, JsonResponse
from django.shortcuts import get_object_or_404
from django.urls import reverse
+from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View
@@ -12,6 +15,7 @@ from common.utils import check_gitlab_request
from languages.models import Language
from stats.models import Branch, Module, Release
from teams.models import Team
+from vertimus.models import ActionRT, ActionUT
from vertimus.views import get_vertimus_state
@@ -182,20 +186,25 @@ class ReleaseLanguageView(View):
})
-class ModuleLangStatsView(View):
- def get(self, *args, **kwargs):
+class VertimusPageMixin:
+ def get_state_from_kwargs(self):
module = get_object_or_404(Module, name=self.kwargs['module_name'])
branch = get_object_or_404(module.branch_set, name=self.kwargs['branch_name'])
domain = branch.get_domains().get(self.kwargs['domain_name'])
if not domain:
raise Http404
language = get_object_or_404(Language, locale=self.kwargs['lang'])
- pot_stats, stats, state = get_vertimus_state(branch, domain, language)
+ return get_vertimus_state(branch, domain, language)[1:]
+
+
+class ModuleLangStatsView(VertimusPageMixin, View):
+ def get(self, *args, **kwargs):
+ stats, state = self.get_state_from_kwargs()
return JsonResponse({
- 'module': module.name,
- 'branch': branch.name,
- 'domain': domain.name,
- 'language': language.locale,
+ 'module': state.branch.module.name,
+ 'branch': state.branch.name,
+ 'domain': state.domain.name,
+ 'language': state.language.locale,
'state': state.name,
'statistics': {
'trans': stats.translated(), 'fuzzy': stats.fuzzy(), 'untrans': stats.untranslated()
@@ -205,6 +214,32 @@ class ModuleLangStatsView(View):
})
+@method_decorator(csrf_exempt, name='dispatch')
+class ReserveTranslationView(LoginRequiredMixin, VertimusPageMixin, View):
+ def post(self, request, *args, **kwargs):
+ stats, state = self.get_state_from_kwargs()
+ actions = [x.name for x in state.get_available_actions(request.user.person)]
+ if 'RT' not in actions:
+ raise PermissionDenied('This module cannot be reserved')
+
+ action = ActionRT(person=request.user.person)
+ action.apply_on(state, {'comment': ''})
+ return JsonResponse({'result': 'OK'})
+
+
+@method_decorator(csrf_exempt, name='dispatch')
+class UploadTranslationView(LoginRequiredMixin, VertimusPageMixin, View):
+ def post(self, request, *args, **kwargs):
+ stats, state = self.get_state_from_kwargs()
+ actions = [x.name for x in state.get_available_actions(request.user.person)]
+ if 'UT' not in actions:
+ raise PermissionDenied('You cannot upload a translation to this module')
+
+ action = ActionUT(person=request.user.person, file=request.FILES.get('file'))
+ action.apply_on(state, {'comment': ''})
+ return JsonResponse({'result': 'OK'})
+
+
# CSRF skipped, verification using a secret token.
@csrf_exempt
def rebuild_branch(request, module_name, branch_name):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]