Created
June 29, 2017 13:31
-
-
Save mathjazz/811fce95c270f1e665ad52a751ddc0ca to your computer and use it in GitHub Desktop.
ActiveEntityTranslation experiment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/pontoon/base/admin.py b/pontoon/base/admin.py | |
| index d5a79e7..60f2d56 100644 | |
| --- a/pontoon/base/admin.py | |
| +++ b/pontoon/base/admin.py | |
| @@ -181,3 +181,4 @@ admin.site.register(models.Entity, EntityAdmin) | |
| admin.site.register(models.Translation, TranslationAdmin) | |
| admin.site.register(models.TranslationMemoryEntry, TranslationMemoryEntryAdmin) | |
| admin.site.register(models.ChangedEntityLocale, ChangedEntityLocaleAdmin) | |
| +admin.site.register(models.ActiveTranslation) | |
| diff --git a/pontoon/base/migrations/0095_activetranslation.py b/pontoon/base/migrations/0095_activetranslation.py | |
| new file mode 100644 | |
| index 0000000..fa6e04a | |
| --- /dev/null | |
| +++ b/pontoon/base/migrations/0095_activetranslation.py | |
| @@ -0,0 +1,41 @@ | |
| +# -*- coding: utf-8 -*- | |
| +# Generated by Django 1.10.7 on 2017-06-21 15:33 | |
| +from __future__ import unicode_literals | |
| + | |
| +from django.db import migrations, models | |
| +import django.db.models.deletion | |
| +import jsonfield.fields | |
| + | |
| + | |
| +class Migration(migrations.Migration): | |
| + | |
| + dependencies = [ | |
| + ('base', '0094_improve_calculate_stats_performance'), | |
| + ] | |
| + | |
| + operations = [ | |
| + migrations.CreateModel( | |
| + name='ActiveTranslation', | |
| + fields=[ | |
| + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | |
| + ('resource_path', models.TextField()), | |
| + ('resource_format', models.CharField(max_length=20)), | |
| + ('entity_string', models.TextField()), | |
| + ('entity_key', models.TextField(blank=True)), | |
| + ('entity_comment', models.TextField(blank=True)), | |
| + ('entity_order', models.PositiveIntegerField()), | |
| + ('entity_source', jsonfield.fields.JSONField(blank=True, default=list)), | |
| + ('translation_string', models.TextField(blank=True, null=True)), | |
| + ('translation_author', models.TextField(blank=True, null=True)), | |
| + ('translation_date', models.DateTimeField(blank=True, null=True)), | |
| + ('status', models.CharField(choices=[(b'translated', b'translated'), (b'suggested', b'suggested'), (b'fuzzy', b'fuzzy'), (b'missing', b'missing')], max_length=10)), | |
| + ('has_suggestions', models.BooleanField(default=False)), | |
| + ('unchanged', models.BooleanField(default=False)), | |
| + ('entity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Entity')), | |
| + ('locale', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Locale')), | |
| + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Project')), | |
| + ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Resource')), | |
| + ('translation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base.Translation')), | |
| + ], | |
| + ), | |
| + ] | |
| diff --git a/pontoon/base/migrations/0096_populate_active_translation.py b/pontoon/base/migrations/0096_populate_active_translation.py | |
| new file mode 100644 | |
| index 0000000..4d89f6f | |
| --- /dev/null | |
| +++ b/pontoon/base/migrations/0096_populate_active_translation.py | |
| @@ -0,0 +1,67 @@ | |
| +# -*- coding: utf-8 -*- | |
| +# Generated by Django 1.10.7 on 2017-06-21 18:33 | |
| +from __future__ import unicode_literals | |
| + | |
| +from django.db import migrations | |
| + | |
| + | |
| +def populate_active_translation(apps, schema_editor): | |
| + ActiveTranslation = apps.get_model('base', 'ActiveTranslation') | |
| + Entity = apps.get_model('base', 'Entity') | |
| + Locale = apps.get_model('base', 'Locale') | |
| + | |
| + for locale in Locale.objects.all(): | |
| + entities_array = [] | |
| + entities = ( | |
| + Entity.objects | |
| + .filter(resource__translatedresources__locale=locale, obsolete=False) | |
| + .prefetch_resources_translations(locale) | |
| + ) | |
| + | |
| + for entity in entities: | |
| + if entity.string_plural == "": | |
| + translation = entity.get_translation() | |
| + if not translation['pk']: | |
| + status = 'missing' | |
| + elif translation['approved']: | |
| + status = 'approved' | |
| + elif translation['fuzzy']: | |
| + status = 'fuzzy' | |
| + else: | |
| + status = 'suggested' | |
| + else: | |
| + continue | |
| + | |
| + entities_array.append(ActiveTranslation(**{ | |
| + 'locale_id': locale.pk, | |
| + 'project_id': entity.resource.project.pk, | |
| + 'resource_id': entity.resource.pk, | |
| + 'entity_id': entity.pk, | |
| + 'translation_id': translation['pk'], | |
| + 'resource_path': entity.resource.path, | |
| + 'resource_format': entity.resource.format, | |
| + 'entity_string': entity.string, | |
| + 'entity_key': entity.cleaned_key, | |
| + 'entity_comment': entity.comment, | |
| + 'entity_order': entity.order, | |
| + 'entity_source': entity.source, | |
| + 'translation_string': translation['string'], | |
| + 'translation_author': translation['author'], | |
| + 'translation_date': translation['date'], | |
| + 'status': status, | |
| + 'has_suggestions': len(entity.fetched_translations) > 1, | |
| + 'unchanged': translation['string'] == entity.string, | |
| + })) | |
| + | |
| + ActiveTranslation.objects.bulk_create(entities_array) | |
| + | |
| + | |
| +class Migration(migrations.Migration): | |
| + | |
| + dependencies = [ | |
| + ('base', '0095_activetranslation'), | |
| + ] | |
| + | |
| + operations = [ | |
| + migrations.RunPython(populate_active_translation, migrations.RunPython.noop), | |
| + ] | |
| diff --git a/pontoon/base/models.py b/pontoon/base/models.py | |
| index 9f578b4..6f74d4e 100644 | |
| --- a/pontoon/base/models.py | |
| +++ b/pontoon/base/models.py | |
| @@ -1645,6 +1645,7 @@ class EntityQuerySet(models.QuerySet): | |
| """ | |
| return self.prefetch_related( | |
| 'resource', | |
| + 'resource__project', | |
| Prefetch( | |
| 'translation_set', | |
| queryset=Translation.objects.filter(locale=locale), | |
| @@ -1731,6 +1732,8 @@ class Entity(DirtyFieldsMixin, models.Model): | |
| 'string': None, | |
| 'approved': False, | |
| 'pk': None, | |
| + 'author': None, | |
| + 'date': None, | |
| } | |
| @classmethod | |
| @@ -2098,9 +2101,47 @@ class Translation(DirtyFieldsMixin, models.Model): | |
| 'string': self.string, | |
| 'approved': self.approved, | |
| 'fuzzy': self.fuzzy, | |
| + 'author': self.user.email if self.user else None, | |
| + 'date': self.date, | |
| } | |
| +class ActiveTranslation(models.Model): | |
| + locale = models.ForeignKey(Locale) | |
| + project = models.ForeignKey(Project) | |
| + resource = models.ForeignKey(Resource) | |
| + entity = models.ForeignKey(Entity) | |
| + translation = models.ForeignKey(Translation, blank=True, null=True) | |
| + | |
| + resource_path = models.TextField() | |
| + resource_format = models.CharField(max_length=20) | |
| + | |
| + entity_string = models.TextField() | |
| + entity_key = models.TextField(blank=True) | |
| + entity_comment = models.TextField(blank=True) | |
| + entity_order = models.PositiveIntegerField() | |
| + entity_source = JSONField(blank=True, default=list) | |
| + | |
| + translation_string = models.TextField(blank=True, null=True) | |
| + translation_author = models.TextField(blank=True, null=True) # Email address | |
| + translation_date = models.DateTimeField(blank=True, null=True) | |
| + | |
| + STATUSES = ( | |
| + ('translated', 'translated'), | |
| + ('suggested', 'suggested'), | |
| + ('fuzzy', 'fuzzy'), | |
| + ('missing', 'missing'), | |
| + ) | |
| + | |
| + status = models.CharField(max_length=10, choices=STATUSES) | |
| + has_suggestions = models.BooleanField(default=False) | |
| + unchanged = models.BooleanField(default=False) | |
| + | |
| + """ | |
| + TODO: Plurals (entity_string_plural, translation_plural_form) | |
| + """ | |
| + | |
| + | |
| class TranslationMemoryEntryManager(models.Manager): | |
| def minimum_levenshtein_ratio(self, text, min_quality=0.7): | |
| """ | |
| diff --git a/pontoon/base/views.py b/pontoon/base/views.py | |
| index 6fe96b5..8ad7a41 100755 | |
| --- a/pontoon/base/views.py | |
| +++ b/pontoon/base/views.py | |
| @@ -33,6 +33,7 @@ from django.views.decorators.http import ( | |
| from pontoon.base import forms | |
| from pontoon.base import utils | |
| from pontoon.base.models import ( | |
| + ActiveTranslation, | |
| ChangedEntityLocale, | |
| Entity, | |
| Locale, | |
| @@ -190,9 +191,49 @@ def entities(request): | |
| 'stats': TranslatedResource.objects.stats(project, paths, locale), | |
| }, safe=False) | |
| - entities = Entity.for_project_locale( | |
| - project, locale, paths, status, search, exclude_entities, extra, time, author | |
| - ) | |
| + """ | |
| + """ | |
| + """ | |
| + """ | |
| + | |
| + entities = ActiveTranslation.objects.filter(locale=locale) | |
| + | |
| + if project: | |
| + entities = entities.filter(project=project) | |
| + | |
| + if paths: | |
| + entities = entities.filter(resource_path__in=project.parts_to_paths(paths)) | |
| + | |
| + if status: | |
| + available_statuses = [x[0] for x in ActiveTranslation.STATUSES] | |
| + sanitized_statuses = filter(lambda s: s in available_statuses, status.split(',')) | |
| + | |
| + if sanitized_statuses: | |
| + entities = entities.filter(status__in=sanitized_statuses) | |
| + | |
| + if extra: | |
| + available_extras = ('has-suggestions', 'unchanged') | |
| + for sanitized_extra in filter(lambda s: s in available_extras, extra.split(',')): | |
| + entities = entities.filter(status__in=sanitized_extras) | |
| + | |
| + if time: | |
| + pass | |
| + | |
| + if author: | |
| + pass | |
| + | |
| + if search: | |
| + entities = entities.filter( | |
| + Q(translation_string__icontains=search) | Q(entity_string__icontains=search) | Q(entity_comment__icontains=search) | Q(entity_key__icontains=search) | |
| + ) | |
| + | |
| + if exclude_entities: | |
| + entities = entities.exclude(pk__in=exclude_entities) | |
| + | |
| + """ | |
| + """ | |
| + """ | |
| + """ | |
| # Only return a list of entity PKs (batch editing: select all) | |
| if request.POST.get('pkOnly', None): | |
| @@ -240,8 +281,31 @@ def entities(request): | |
| if entity_pk in entities.values_list('pk', flat=True): | |
| entities_to_map = list(entities_to_map) + list(entities.filter(pk=entity_pk)) | |
| + active_translations = [] | |
| + for active_translation in entities_to_map: | |
| + active_translations.append({ | |
| + 'pk': active_translation.entity.pk, | |
| + 'original': active_translation.entity_string, | |
| + 'marked': utils.mark_placeables(active_translation.entity_string), | |
| + 'original_plural': '', | |
| + 'marked_plural': '', | |
| + 'key': active_translation.entity_key, | |
| + 'path': active_translation.resource_path, | |
| + 'format': active_translation.resource_format, | |
| + 'comment': active_translation.entity_comment, | |
| + 'order': active_translation.entity_order, | |
| + 'source': active_translation.entity_source, | |
| + 'translation': [{ | |
| + 'pk': active_translation.translation.pk if active_translation.translation else None, | |
| + 'approved': active_translation.status == 'approved', | |
| + 'fuzzy': active_translation.status == 'fuzzy', | |
| + 'string': active_translation.translation_string, | |
| + }], | |
| + 'visible': True | |
| + }) | |
| + | |
| return JsonResponse({ | |
| - 'entities': Entity.map_entities(locale, entities_to_map, visible_entities), | |
| + 'entities': active_translations, | |
| 'has_next': has_next, | |
| 'stats': TranslatedResource.objects.stats(project, paths, locale), | |
| }, safe=False) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment