Skip to content

Instantly share code, notes, and snippets.

@mathjazz
Created June 29, 2017 13:31
Show Gist options
  • Select an option

  • Save mathjazz/811fce95c270f1e665ad52a751ddc0ca to your computer and use it in GitHub Desktop.

Select an option

Save mathjazz/811fce95c270f1e665ad52a751ddc0ca to your computer and use it in GitHub Desktop.
ActiveEntityTranslation experiment
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