[damned-lies] Figure stats computed during update and stored in pofile.figures
- From: Claude Paroz <claudep src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies] Figure stats computed during update and stored in pofile.figures
- Date: Thu, 4 Aug 2011 11:18:33 +0000 (UTC)
commit 8d61b672b23c4cd262d1c904226d1b49b740bf14
Author: Claude Paroz <claude 2xlibre net>
Date: Thu Aug 4 13:14:50 2011 +0200
Figure stats computed during update and stored in pofile.figures
This is also adding support for detecting images in itstool-generated
po files.
common/fields.py | 38 ++++
docs/DataModel.odg | Bin 27476 -> 31369 bytes
stats/fixtures/sample_data.json | 178 ++++++++---------
stats/migrations/0009_add_figure_field.py | 214 ++++++++++++++++++++
stats/models.py | 71 ++++---
stats/tests/__init__.py | 20 +-
stats/tests/fixture_factory.py | 6 +-
.../help_docbook/C/figures/rnusers.nautilus.png | Bin 0 -> 844 bytes
stats/tests/help_docbook/C/rnusers.xml | 6 +
stats/utils.py | 85 ++++----
vertimus/views.py | 11 +-
11 files changed, 439 insertions(+), 190 deletions(-)
---
diff --git a/common/fields.py b/common/fields.py
index 817472f..4fb0ce8 100644
--- a/common/fields.py
+++ b/common/fields.py
@@ -3,6 +3,7 @@
from django import forms
from django.core import exceptions
+from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils import simplejson
@@ -51,9 +52,46 @@ class DictionaryField(models.Field):
defaults.update(kwargs)
return super(DictionaryField, self).formfield(**defaults)
+
+# From http://djangosnippets.org/snippets/1478/
+
+class JSONField(models.TextField):
+ """JSONField is a generic textfield that neatly serializes/unserializes
+ JSON objects seamlessly"""
+
+ # Used so to_python() is called
+ __metaclass__ = models.SubfieldBase
+
+ def to_python(self, value):
+ """Convert our string value to JSON after we load it from the DB"""
+
+ if value == "":
+ return None
+
+ try:
+ if isinstance(value, basestring):
+ return simplejson.loads(value)
+ except ValueError:
+ pass
+
+ return value
+
+ def get_db_prep_save(self, value, connection):
+ """Convert our JSON object to a string before we save"""
+
+ if value == "":
+ return None
+
+ if isinstance(value, (dict, list)):
+ value = simplejson.dumps(value, cls=DjangoJSONEncoder)
+
+ return super(JSONField, self).get_db_prep_save(value, connection=connection)
+
+
# rules for South migrations tool (for version >= 0.7)
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^common\.fields\.DictionaryField"])
+ add_introspection_rules([], ["^common\.fields\.JSONField"])
except ImportError:
pass
diff --git a/docs/DataModel.odg b/docs/DataModel.odg
index 33e730f..c96e3d2 100644
Binary files a/docs/DataModel.odg and b/docs/DataModel.odg differ
diff --git a/stats/fixtures/sample_data.json b/stats/fixtures/sample_data.json
index 8e85b00..c6c73e2 100644
--- a/stats/fixtures/sample_data.json
+++ b/stats/fixtures/sample_data.json
@@ -9,12 +9,12 @@
"is_active": true,
"is_superuser": false,
"is_staff": false,
- "last_login": "2011-02-17 22:15:33",
+ "last_login": "2011-08-04 12:03:53",
"groups": [],
"user_permissions": [],
"password": "!",
"email": "bob example org",
- "date_joined": "2011-02-17 22:15:33"
+ "date_joined": "2011-08-04 12:03:53"
}
},
{
@@ -27,12 +27,12 @@
"is_active": true,
"is_superuser": false,
"is_staff": false,
- "last_login": "2011-02-17 22:15:33",
+ "last_login": "2011-08-04 12:03:53",
"groups": [],
"user_permissions": [],
"password": "!",
"email": "coord example org",
- "date_joined": "2011-02-17 22:15:33"
+ "date_joined": "2011-08-04 12:03:53"
}
},
{
@@ -45,12 +45,12 @@
"is_active": true,
"is_superuser": false,
"is_staff": false,
- "last_login": "2011-02-17 22:15:33",
+ "last_login": "2011-08-04 12:03:53",
"groups": [],
"user_permissions": [],
"password": "!",
"email": "alessio example org",
- "date_joined": "2011-02-17 22:15:33"
+ "date_joined": "2011-08-04 12:03:53"
}
},
{
@@ -190,6 +190,7 @@
"name": "gnome-hello",
"vcs_root": "git://git.gnome.org/gnome-hello",
"bugs_product": "gnome-hello",
+ "ext_platform": null,
"maintainers": [],
"bugs_component": "test",
"bugs_base": "http://bugzilla.gnome.org",
@@ -207,6 +208,7 @@
"name": "shared-mime-info",
"vcs_root": "git://anongit.freedesktop.org/xdg/shared-mime-info",
"bugs_product": "shared-mime-info",
+ "ext_platform": null,
"maintainers": [],
"bugs_component": "general",
"bugs_base": "https://bugs.freedesktop.org/",
@@ -224,6 +226,7 @@
"name": "zenity",
"vcs_root": "git://git.gnome.org/zenity",
"bugs_product": "zenity",
+ "ext_platform": null,
"maintainers": [],
"bugs_component": "general",
"bugs_base": "http://bugzilla.gnome.org",
@@ -284,8 +287,8 @@
"dtype": "ui",
"pot_method": null,
"module": 1,
- "linguas_location": null,
- "red_filter": null,
+ "linguas_location": null,
+ "red_filter": null,
"directory": "po",
"description": "UI Translations"
}
@@ -299,7 +302,7 @@
"pot_method": null,
"module": 2,
"linguas_location": null,
- "red_filter": null,
+ "red_filter": null,
"directory": "po",
"description": "UI Translations"
}
@@ -313,7 +316,7 @@
"pot_method": null,
"module": 3,
"linguas_location": null,
- "red_filter": null,
+ "red_filter": null,
"directory": "po",
"description": "UI Translations"
}
@@ -327,7 +330,7 @@
"pot_method": null,
"module": 1,
"linguas_location": null,
- "red_filter": null,
+ "red_filter": null,
"directory": "help",
"description": "User Guide"
}
@@ -341,7 +344,7 @@
"pot_method": null,
"module": 2,
"linguas_location": null,
- "red_filter": null,
+ "red_filter": null,
"directory": "help",
"description": "User Guide"
}
@@ -355,7 +358,7 @@
"pot_method": null,
"module": 3,
"linguas_location": null,
- "red_filter": null,
+ "red_filter": null,
"directory": "help",
"description": "User Guide"
}
@@ -433,9 +436,9 @@
"pk": 1,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 47,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -445,9 +448,9 @@
"pk": 2,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 47,
"path": null
@@ -457,9 +460,9 @@
"pk": 3,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 7,
+ "figures": null,
"fuzzy": 10,
"translated": 30,
"path": null
@@ -469,9 +472,9 @@
"pk": 4,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 1,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 20,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -481,9 +484,9 @@
"pk": 5,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 20,
"path": null
@@ -493,9 +496,9 @@
"pk": 6,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 20,
"path": null
@@ -505,9 +508,9 @@
"pk": 7,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 136,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -517,9 +520,9 @@
"pk": 8,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 136,
"path": null
@@ -529,9 +532,9 @@
"pk": 9,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 6,
+ "figures": null,
"fuzzy": 0,
"translated": 130,
"path": null
@@ -541,9 +544,9 @@
"pk": 10,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 11,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 259,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -553,9 +556,9 @@
"pk": 11,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 259,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -565,9 +568,9 @@
"pk": 12,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 259,
"path": null
@@ -577,9 +580,9 @@
"pk": 13,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 149,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -589,9 +592,9 @@
"pk": 14,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 4,
"translated": 255,
"path": null
@@ -601,9 +604,9 @@
"pk": 15,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 259,
"path": null
@@ -613,9 +616,9 @@
"pk": 16,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 11,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 259,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -625,9 +628,9 @@
"pk": 17,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 259,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -637,9 +640,9 @@
"pk": 18,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 0,
"translated": 259,
"path": null
@@ -649,9 +652,9 @@
"pk": 19,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 626,
+ "figures": null,
"fuzzy": 0,
"translated": 0,
"path": null
@@ -661,9 +664,9 @@
"pk": 20,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 2,
+ "figures": null,
"fuzzy": 20,
"translated": 598,
"path": null
@@ -673,9 +676,9 @@
"pk": 21,
"model": "stats.pofile",
"fields": {
- "updated": "2011-03-23 13:47:59",
- "num_figures": 0,
+ "updated": "2011-08-04 12:03:53",
"untranslated": 0,
+ "figures": null,
"fuzzy": 6,
"translated": 620,
"path": null
@@ -687,10 +690,9 @@
"fields": {
"domain": 1,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 1,
"language": null,
"part_po": null,
@@ -703,10 +705,9 @@
"fields": {
"domain": 1,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 2,
"language": 2,
"part_po": null,
@@ -719,10 +720,9 @@
"fields": {
"domain": 1,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 3,
"language": 3,
"part_po": null,
@@ -735,10 +735,9 @@
"fields": {
"domain": 2,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 4,
"language": null,
"part_po": null,
@@ -751,10 +750,9 @@
"fields": {
"domain": 2,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 5,
"language": 2,
"part_po": null,
@@ -767,10 +765,9 @@
"fields": {
"domain": 2,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 6,
"language": 3,
"part_po": null,
@@ -783,10 +780,9 @@
"fields": {
"domain": 3,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 7,
"language": null,
"part_po": null,
@@ -799,10 +795,9 @@
"fields": {
"domain": 3,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 8,
"language": 2,
"part_po": null,
@@ -815,10 +810,9 @@
"fields": {
"domain": 3,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 9,
"language": 3,
"part_po": null,
@@ -831,10 +825,9 @@
"fields": {
"domain": 4,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 10,
"language": null,
"part_po": null,
@@ -847,10 +840,9 @@
"fields": {
"domain": 4,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 11,
"language": 2,
"part_po": null,
@@ -863,10 +855,9 @@
"fields": {
"domain": 4,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 12,
"language": 3,
"part_po": null,
@@ -879,10 +870,9 @@
"fields": {
"domain": 3,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 13,
"language": null,
"part_po": null,
@@ -895,10 +885,9 @@
"fields": {
"domain": 3,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 14,
"language": 2,
"part_po": null,
@@ -911,10 +900,9 @@
"fields": {
"domain": 3,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 15,
"language": 3,
"part_po": null,
@@ -927,10 +915,9 @@
"fields": {
"domain": 4,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 16,
"language": null,
"part_po": null,
@@ -943,10 +930,9 @@
"fields": {
"domain": 4,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 17,
"language": 2,
"part_po": null,
@@ -959,10 +945,9 @@
"fields": {
"domain": 4,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 18,
"language": 3,
"part_po": null,
@@ -975,10 +960,9 @@
"fields": {
"domain": 5,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 19,
"language": null,
"part_po": null,
@@ -991,10 +975,9 @@
"fields": {
"domain": 5,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 20,
"language": 2,
"part_po": null,
@@ -1007,10 +990,9 @@
"fields": {
"domain": 5,
"old_translated": 0,
- "old_date": "2011-03-23 13:47:59",
+ "old_date": "2011-08-04 12:03:53",
"old_fuzzy": 0,
"old_untranslated": 0,
- "old_num_figures": 0,
"full_po": 21,
"language": 3,
"part_po": null,
@@ -1026,4 +1008,4 @@
"description": "Error regenerating POT file for zenity:\n<pre>intltool-update -g 'zenity' -p\nERROR: xgettext failed to generate PO template file.</pre>"
}
}
-]
+]
\ No newline at end of file
diff --git a/stats/migrations/0009_add_figure_field.py b/stats/migrations/0009_add_figure_field.py
new file mode 100644
index 0000000..a860df7
--- /dev/null
+++ b/stats/migrations/0009_add_figure_field.py
@@ -0,0 +1,214 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting field 'PoFile.num_figures'
+ db.delete_column('pofile', 'num_figures')
+
+ # Adding field 'PoFile.figures'
+ db.add_column('pofile', 'figures', self.gf('common.fields.JSONField')(null=True, blank=True), keep_default=False)
+
+ # Deleting field 'Statistics.old_num_figures'
+ db.delete_column('statistics', 'old_num_figures')
+
+
+ def backwards(self, orm):
+
+ # Adding field 'PoFile.num_figures'
+ db.add_column('pofile', 'num_figures', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
+
+ # Deleting field 'PoFile.figures'
+ db.delete_column('pofile', 'figures')
+
+ # Adding field 'Statistics.old_num_figures'
+ db.add_column('statistics', 'old_num_figures', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'languages.language': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Language', 'db_table': "'language'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'locale': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '15'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
+ 'plurals': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+ 'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['teams.Team']", 'null': 'True', 'blank': 'True'})
+ },
+ 'people.person': {
+ 'Meta': {'ordering': "('username',)", 'object_name': 'Person', 'db_table': "'person'", '_ormbases': ['auth.User']},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'bugzilla_account': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'irc_nick': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}),
+ 'svn_account': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
+ 'webpage_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'stats.branch': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'module'),)", 'object_name': 'Branch', 'db_table': "'branch'"},
+ 'file_hashes': ('common.fields.DictionaryField', [], {'default': "''", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Module']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'vcs_subpath': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+ },
+ 'stats.category': {
+ 'Meta': {'unique_together': "(('release', 'branch'),)", 'object_name': 'Category', 'db_table': "'category'"},
+ 'branch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Branch']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '30'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Release']"})
+ },
+ 'stats.domain': {
+ 'Meta': {'ordering': "('-dtype', 'name')", 'object_name': 'Domain', 'db_table': "'domain'"},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'dtype': ('django.db.models.fields.CharField', [], {'default': "'ui'", 'max_length': '5'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'linguas_location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'module': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Module']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'pot_method': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'red_filter': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'stats.information': {
+ 'Meta': {'object_name': 'Information', 'db_table': "'information'"},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'statistics': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Statistics']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
+ },
+ 'stats.informationarchived': {
+ 'Meta': {'object_name': 'InformationArchived', 'db_table': "'information_archived'"},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'statistics': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.StatisticsArchived']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
+ },
+ 'stats.module': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Module', 'db_table': "'module'"},
+ 'bugs_base': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'bugs_component': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'bugs_product': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'ext_platform': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'maintainers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'maintains_modules'", 'blank': 'True', 'db_table': "'module_maintainer'", 'to': "orm['people.Person']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'vcs_root': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'vcs_type': ('django.db.models.fields.CharField', [], {'max_length': '5'}),
+ 'vcs_web': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'stats.pofile': {
+ 'Meta': {'object_name': 'PoFile', 'db_table': "'pofile'"},
+ 'figures': ('common.fields.JSONField', [], {'null': 'True', 'blank': 'True'}),
+ 'fuzzy': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'translated': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'untranslated': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ 'stats.release': {
+ 'Meta': {'ordering': "('status', '-name')", 'object_name': 'Release', 'db_table': "'release'"},
+ 'branches': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'releases'", 'symmetrical': 'False', 'through': "orm['stats.Category']", 'to': "orm['stats.Branch']"}),
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '20', 'db_index': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
+ 'string_frozen': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+ },
+ 'stats.statistics': {
+ 'Meta': {'unique_together': "(('branch', 'domain', 'language'),)", 'object_name': 'Statistics', 'db_table': "'statistics'"},
+ 'branch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Branch']"}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Domain']"}),
+ 'full_po': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'stat_f'", 'unique': 'True', 'null': 'True', 'to': "orm['stats.PoFile']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['languages.Language']", 'null': 'True'}),
+ 'old_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'old_fuzzy': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'old_translated': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'old_untranslated': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'part_po': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'stat_p'", 'unique': 'True', 'null': 'True', 'to': "orm['stats.PoFile']"})
+ },
+ 'stats.statisticsarchived': {
+ 'Meta': {'object_name': 'StatisticsArchived', 'db_table': "'statistics_archived'"},
+ 'branch': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'domain': ('django.db.models.fields.TextField', [], {}),
+ 'fuzzy': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '15'}),
+ 'module': ('django.db.models.fields.TextField', [], {}),
+ 'translated': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
+ 'untranslated': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+ },
+ 'teams.role': {
+ 'Meta': {'unique_together': "(('team', 'person'),)", 'object_name': 'Role', 'db_table': "'role'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Person']"}),
+ 'role': ('django.db.models.fields.CharField', [], {'default': "'translator'", 'max_length': '15'}),
+ 'team': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['teams.Team']"})
+ },
+ 'teams.team': {
+ 'Meta': {'ordering': "('description',)", 'object_name': 'Team', 'db_table': "'team'"},
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mailing_list': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'mailing_list_subscribe': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'members': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'teams'", 'symmetrical': 'False', 'through': "orm['teams.Role']", 'to': "orm['people.Person']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'presentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'use_workflow': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'webpage_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['stats']
diff --git a/stats/models.py b/stats/models.py
index 7a43c01..e1a3e80 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (c) 2008-2010 Claude Paroz <claude 2xlibre net>.
+# Copyright (c) 2008-2011 Claude Paroz <claude 2xlibre net>.
# Copyright (c) 2008 Stephane Raimbault <stephane raimbault gmail com>.
#
# This file is part of Damned Lies.
@@ -27,12 +27,13 @@ from datetime import datetime
from django.conf import settings
from django.core.exceptions import ValidationError
+from django.core.urlresolvers import reverse
from django.utils.translation import ungettext, ugettext as _, ugettext_noop
from django.utils import dateformat
from django.utils.datastructures import SortedDict
from django.db import models, connection
-from common.fields import DictionaryField
+from common.fields import DictionaryField, JSONField
from common.utils import is_site_admin
from stats import utils, signals
from stats.doap import update_doap_infos
@@ -394,6 +395,7 @@ class Branch(models.Model):
# 3. Generate a fresh pot file
# ****************************
+ pot_method = dom.pot_method
if dom.dtype == 'ui':
potfile, errs = dom.generate_pot_file(self)
elif dom.dtype == 'doc':
@@ -401,7 +403,8 @@ class Branch(models.Model):
potfile, errs = dom.generate_pot_file(self)
else:
# Standard gnome-doc-utils pot generation
- potfile, errs = utils.generate_doc_pot_file(domain_path, dom.potbase(), self.module.name, settings.DEBUG)
+ potfile, errs, pot_method = utils.generate_doc_pot_file(
+ domain_path, dom.potbase(), self.module.name)
else:
print >> sys.stderr, "Unknown domain type '%s', ignoring domain '%s'" % (dom.dtype, dom.name)
continue
@@ -444,12 +447,17 @@ class Branch(models.Model):
# 6. Generate pot stats and update DB
# ***********************************
- pot_stats = utils.po_file_stats(potfile, False)
+ pot_stats = utils.po_file_stats(potfile, msgfmt_checks=False)
+ fig_stats = utils.get_fig_stats(potfile, pot_method, trans_stats=False)
errors.extend(pot_stats['errors'])
if potfile != previous_pot and not utils.copy_file(potfile, previous_pot):
errors.append(('error', ugettext_noop("Can't copy new POT file to public location.")))
- pot_stat.set_translation_stats(previous_pot, untranslated=int(pot_stats['untranslated']), num_figures=int(pot_stats['num_figures']))
+ pot_stat.set_translation_stats(
+ previous_pot,
+ untranslated=int(pot_stats['untranslated']),
+ figstats = fig_stats,
+ )
pot_stat.set_errors(errors)
# Send pot_has_changed signal
@@ -475,11 +483,12 @@ class Branch(models.Model):
}
utils.run_shell_command(realcmd)
- langstats = utils.po_file_stats(outpo, msgfmt_checks=True, count_images=(dom.dtype == "doc"))
+ langstats = utils.po_file_stats(outpo, msgfmt_checks=True)
if linguas['langs'] is not None and lang not in linguas['langs']:
langstats['errors'].append(("warn-ext", linguas['error']))
+ fig_stats = None
if dom.dtype == "doc":
- fig_stats = utils.get_fig_stats(outpo)
+ fig_stats = utils.get_fig_stats(outpo, pot_method)
for fig in fig_stats:
trans_path = os.path.join(domain_path, lang, fig['path'])
if os.access(trans_path, os.R_OK):
@@ -509,7 +518,7 @@ class Branch(models.Model):
translated = int(langstats['translated']),
fuzzy = int(langstats['fuzzy']),
untranslated = int(langstats['untranslated']),
- num_figures = int(langstats.get('num_figures', 0)))
+ figstats=fig_stats)
for err in langstats['errors']:
stat.information_set.add(Information(type=err[0], description=err[1]))
# Check if doap file changed
@@ -1174,8 +1183,8 @@ class PoFile(models.Model):
translated = models.IntegerField(default=0)
fuzzy = models.IntegerField(default=0)
untranslated = models.IntegerField(default=0)
- # Number of figures in doc templates
- num_figures = models.IntegerField(default=0)
+ # List of figure dict
+ figures = JSONField(blank=True, null=True)
class Meta:
db_table = 'pofile'
@@ -1188,7 +1197,7 @@ class PoFile(models.Model):
def fig_count(self):
""" If stat of a document type, get the number of figures in the document """
- return self.num_figures
+ return len(self.figures)
def tr_percentage(self):
if self.pot_size() == 0:
@@ -1219,8 +1228,6 @@ class Statistics(models.Model):
old_translated = models.IntegerField(default=0) # obsolete
old_fuzzy = models.IntegerField(default=0) # obsolete
old_untranslated = models.IntegerField(default=0) # obsolete
- # Number of figures in doc templates
- old_num_figures = models.IntegerField(default=0) # obsolete
full_po = models.OneToOneField(PoFile, null=True, related_name='stat_f')
part_po = models.OneToOneField(PoFile, null=True, related_name='stat_p')
@@ -1233,7 +1240,6 @@ class Statistics(models.Model):
def __init__(self, *args, **kwargs):
models.Model.__init__(self, *args, **kwargs)
- self.figures = None
self.modname = None
self.moddescription = None
self.partial_po = False # True if part of a multiple po module
@@ -1329,30 +1335,32 @@ class Statistics(models.Model):
return text
def get_figures(self):
- """ self.figures is a list of dicts:
+ """ Return an enriched list of figure dicts (used in module_images.html):
[{'path':, 'hash':, 'fuzzy':, 'translated':, 'translated_file':}, ...] """
- if self.figures is None and self.domain.dtype == 'doc':
- self.figures = utils.get_fig_stats(self.po_path())
+ figures = []
+ if self.full_po and self.domain.dtype == 'doc':
# something like: "http://git.gnome.org/browse/vinagre / plain / help / %s / %s ?h=master"
url_model = utils.url_join(self.branch.get_vcs_web_url(), self.branch.img_url_prefix,
self.domain.directory, '%s', '%s') + self.branch.img_url_suffix
- for fig in self.figures:
- fig['orig_remote_url'] = url_model % ('C', fig['path'])
- fig['translated_file'] = False
+ for fig in self.full_po.figures:
+ fig2 = fig.copy()
+ fig2['orig_remote_url'] = url_model % ('C', fig['path'])
+ fig2['translated_file'] = False
# Check if a translated figure really exists or if the English one is used
if (self.language and
os.path.exists(os.path.join(self.branch.co_path(), self.domain.directory, self.language.locale, fig['path']))):
- fig['trans_remote_url'] = url_model % (self.language.locale, fig['path'])
- fig['translated_file'] = True
- return self.figures
+ fig2['trans_remote_url'] = url_model % (self.language.locale, fig['path'])
+ fig2['translated_file'] = True
+ figures.append(fig2)
+ return figures
def fig_stats(self):
stats = {'fuzzy':0, 'translated':0, 'total':0, 'prc':0}
- for fig in self.get_figures():
+ for fig in self.full_po.figures:
stats['total'] += 1
- if fig['fuzzy']: stats['fuzzy'] += 1
+ if fig.get('fuzzy', 0): stats['fuzzy'] += 1
else:
- if fig['translated']: stats['translated'] += 1
+ if fig.get('translated', 0): stats['translated'] += 1
stats['untranslated'] = stats['total'] - (stats['translated'] + stats['fuzzy'])
if stats['total'] > 0:
stats['prc'] = 100*stats['translated']/stats['total']
@@ -1386,7 +1394,7 @@ class Statistics(models.Model):
def pot_url(self):
return self.po_url(potfile=True)
- def set_translation_stats(self, po_path, translated=0, fuzzy=0, untranslated=0, num_figures=0):
+ def set_translation_stats(self, po_path, translated=0, fuzzy=0, untranslated=0, figstats=None):
if not self.full_po:
self.full_po = PoFile.objects.create(path=po_path)
self.save()
@@ -1394,7 +1402,7 @@ class Statistics(models.Model):
self.full_po.translated = translated
self.full_po.fuzzy = fuzzy
self.full_po.untranslated = untranslated
- self.full_po.num_figures = num_figures
+ self.full_po.figures = figstats
self.full_po.updated = datetime.now()
self.full_po.save()
if self.domain.dtype == "ui":
@@ -1413,7 +1421,7 @@ class Statistics(models.Model):
else:
part_po_path = self.full_po.path[:-3] + ".reduced.po"
utils.po_grep(self.full_po.path, part_po_path, self.domain.red_filter)
- part_stats = utils.po_file_stats(part_po_path, msgfmt_checks=False, count_images=False)
+ part_stats = utils.po_file_stats(part_po_path, msgfmt_checks=False)
if part_stats['translated'] + part_stats['fuzzy'] + part_stats['untranslated'] == translated + fuzzy + untranslated:
# No possible gain, set part_po = full_po so it is possible to compute complete stats at database level
part_po_equals_full_po()
@@ -1622,6 +1630,11 @@ class FakeLangStatistics(object):
'lang_locale': self.language.locale
}
+ def po_url(self, potfile=False, reduced=False):
+ return reverse(
+ 'dynamic_po',
+ args=("%s.%s.%s.%s.po" % (self.branch.module.name, self.domain.name, self.branch.name, self.language.locale),)
+ )
class FakeSummaryStatistics(object):
""" Statistics class that sums up an entire module stats """
diff --git a/stats/tests/__init__.py b/stats/tests/__init__.py
index 92f6246..b4f97aa 100644
--- a/stats/tests/__init__.py
+++ b/stats/tests/__init__.py
@@ -160,12 +160,14 @@ class ModuleTestCase(TestCase):
self.assertEquals(var_content.split(), ['rnusers.xml', 'rnlookingforward.xml', '$(NULL)'])
def testGenerateDocPotfile(self):
- from stats.utils import generate_doc_pot_file
+ from stats.utils import generate_doc_pot_file, get_fig_stats
# Docbook-style help
help_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "help_docbook")
- generate_doc_pot_file(help_path, 'release-notes', 'release-notes', None)
+ generate_doc_pot_file(help_path, 'release-notes', 'release-notes')
pot_path = os.path.join(help_path, "C", "release-notes.pot")
self.assertTrue(os.access(pot_path, os.R_OK))
+ res = get_fig_stats(pot_path, image_method='xml2po')
+ self.assertEqual(len(res), 1)
os.remove(pot_path)
# TODO: Mallard-style help
@@ -173,23 +175,23 @@ class ModuleTestCase(TestCase):
""" Detect warning if translated figure is identical to original figure """
self.b.checkout()
orig_figure = os.path.join(self.b.co_path(), "help", "C", "figures", "gnome-hello-new.png")
- shutil.copy(orig_figure, os.path.join(self.b.co_path(), "help", "fr", "figures", "gnome-hello-new.png"))
+ shutil.copy(orig_figure, os.path.join(self.b.co_path(), "help", "cs", "figures", "gnome-hello-new.png"))
self.b.update_stats(force=True, checkout=False)
- doc_stat = Statistics.objects.get(branch=self.b, domain__name='help', language__locale='fr')
+ doc_stat = Statistics.objects.get(branch=self.b, domain__name='help', language__locale='cs')
warn_infos = Information.objects.filter(statistics=doc_stat, type='warn-ext')
self.assertEquals(len(warn_infos), 1);
- ui_stat = Statistics.objects.get(branch=self.b, domain__name='po', language__locale='fr')
- self.assertEquals(ui_stat.po_url(), u"/POT/gnome-hello.master/gnome-hello.master.fr.po");
+ ui_stat = Statistics.objects.get(branch=self.b, domain__name='po', language__locale='cs')
+ self.assertEquals(ui_stat.po_url(), u"/POT/gnome-hello.master/gnome-hello.master.cs.po");
self.assertEquals(ui_stat.pot_url(), u"/POT/gnome-hello.master/gnome-hello.master.pot");
- self.assertEquals(doc_stat.po_url(), u"/POT/gnome-hello.master/docs/gnome-hello-help.master.fr.po");
+ self.assertEquals(doc_stat.po_url(), u"/POT/gnome-hello.master/docs/gnome-hello-help.master.cs.po");
def testFigureURLs(self):
""" Test if figure urls are properly constructed """
self.b.update_stats(force=True)
- stat = Statistics.objects.get(branch=self.b, domain__dtype='doc', language__locale='fr')
+ stat = Statistics.objects.get(branch=self.b, domain__dtype='doc', language__locale='cs')
figs = stat.get_figures()
self.assertEquals(figs[0]['orig_remote_url'], 'http://git.gnome.org/browse/gnome-hello/plain/help/C/figures/gnome-hello-new.png?h=master')
- self.assertEquals(figs[0]['trans_remote_url'], 'http://git.gnome.org/browse/gnome-hello/plain/help/fr/figures/gnome-hello-new.png?h=master')
+ self.assertEquals(figs[0]['trans_remote_url'], 'http://git.gnome.org/browse/gnome-hello/plain/help/cs/figures/gnome-hello-new.png?h=master')
def testFigureView(self):
self.b.update_stats(force=True)
diff --git a/stats/tests/fixture_factory.py b/stats/tests/fixture_factory.py
index de416f6..ca7c40c 100644
--- a/stats/tests/fixture_factory.py
+++ b/stats/tests/fixture_factory.py
@@ -109,20 +109,20 @@ class FixtureFactory(TestCase):
Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=None, full_po=PoFile.objects.create(untranslated=47))
Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=l_fr, full_po=PoFile.objects.create(translated=47))
Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=l_it, full_po=PoFile.objects.create(translated=30, fuzzy=10, untranslated=7))
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=None, full_po=PoFile.objects.create(untranslated=20, num_figures=1))
+ Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=None, full_po=PoFile.objects.create(untranslated=20))
Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=l_fr, full_po=PoFile.objects.create(translated=20))
Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=l_it, full_po=PoFile.objects.create(translated=20))
# zenity ui 2.30, zenity doc 2.30, zenity ui master, zenity doc master (POT, fr, it)
Statistics.objects.create(branch=b2, domain=dom['zenity-ui'], language=None, full_po=PoFile.objects.create(untranslated=136))
Statistics.objects.create(branch=b2, domain=dom['zenity-ui'], language=l_fr, full_po=PoFile.objects.create(translated=136))
Statistics.objects.create(branch=b2, domain=dom['zenity-ui'], language=l_it, full_po=PoFile.objects.create(translated=130, untranslated=6))
- Statistics.objects.create(branch=b2, domain=dom['zenity-doc'], language=None, full_po=PoFile.objects.create(untranslated=259, num_figures=11))
+ Statistics.objects.create(branch=b2, domain=dom['zenity-doc'], language=None, full_po=PoFile.objects.create(untranslated=259))
Statistics.objects.create(branch=b2, domain=dom['zenity-doc'], language=l_fr, full_po=PoFile.objects.create(untranslated=259))
Statistics.objects.create(branch=b2, domain=dom['zenity-doc'], language=l_it, full_po=PoFile.objects.create(translated=259))
stat1 = Statistics.objects.create(branch=b3, domain=dom['zenity-ui'], language=None, full_po=PoFile.objects.create(untranslated=149))
Statistics.objects.create(branch=b3, domain=dom['zenity-ui'], language=l_fr, full_po=PoFile.objects.create(translated=255, fuzzy=4))
Statistics.objects.create(branch=b3, domain=dom['zenity-ui'], language=l_it, full_po=PoFile.objects.create(translated=259))
- Statistics.objects.create(branch=b3, domain=dom['zenity-doc'], language=None, full_po=PoFile.objects.create(untranslated=259, num_figures=11))
+ Statistics.objects.create(branch=b3, domain=dom['zenity-doc'], language=None, full_po=PoFile.objects.create(untranslated=259))
Statistics.objects.create(branch=b3, domain=dom['zenity-doc'], language=l_fr, full_po=PoFile.objects.create(untranslated=259))
Statistics.objects.create(branch=b3, domain=dom['zenity-doc'], language=l_it, full_po=PoFile.objects.create(translated=259))
# shared-mime-info ui (POT, fr, it)
diff --git a/stats/tests/help_docbook/C/figures/rnusers.nautilus.png b/stats/tests/help_docbook/C/figures/rnusers.nautilus.png
new file mode 100644
index 0000000..17ddf78
Binary files /dev/null and b/stats/tests/help_docbook/C/figures/rnusers.nautilus.png differ
diff --git a/stats/tests/help_docbook/C/rnusers.xml b/stats/tests/help_docbook/C/rnusers.xml
index 75515b3..8448ee1 100644
--- a/stats/tests/help_docbook/C/rnusers.xml
+++ b/stats/tests/help_docbook/C/rnusers.xml
@@ -6,4 +6,10 @@
<sect1 id="rnusers">
<title>What's New for Users</title>
+ <figure id="fig.rnusers.nautilus">
+ <title><application>Nautilus</application></title>
+ <screenshot><mediaobject><imageobject>
+ <imagedata fileref="figures/rnusers.nautilus.png" format="PNG"/>
+ </imageobject></mediaobject></screenshot>
+ </figure>
</sect1>
diff --git a/stats/utils.py b/stats/utils.py
index 86c587f..89152d0 100644
--- a/stats/utils.py
+++ b/stats/utils.py
@@ -31,11 +31,11 @@ try:
except ImportError:
has_toolkit = False
-from django.utils.translation import ugettext_noop
+from django.conf import settings
from django.contrib.sites.models import Site
-from django.core.mail import send_mail
from django.core.files.base import File
-from django.conf import settings
+from django.core.mail import send_mail
+from django.utils.translation import ugettext_noop
import potdiff
@@ -46,6 +46,24 @@ CHANGED_ONLY_FORMATTING = 1
CHANGED_WITH_ADDITIONS = 2
CHANGED_NO_ADDITIONS = 3
+ITSTOOL_PATH = getattr(settings, 'ITSTOOL_PATH', '')
+extract_tools = {
+ 'xml2po': {
+ 'command' : "cd \"%(dir)s\" && xml2po %(opts)s -o %(potfile)s -e %(files)s",
+ 'mod_var' : "DOC_ID",
+ 'incl_var': "DOC_PAGES",
+ 'img_grep': "^msgid \"@@image:",
+ 'img_regex': re.compile("^msgid \"@@image: \'(?P<path>[^\']*)\'; md5=(?P<hash>[^\"]*)\""),
+ },
+ 'itstool': {
+ 'command' : "cd \"%%(dir)s\" && %sitstool -o %%(potfile)s %%(files)s" % ITSTOOL_PATH,
+ 'mod_var' : "HELP_ID",
+ 'incl_var': "HELP_FILES",
+ 'img_grep': "^msgid \"external ref=",
+ 'img_regex': re.compile("^msgid \"external ref=\'(?P<path>[^\']*)\'; md5=\'(?P<hash>[^\']*)\'\""),
+ },
+}
+
def sort_object_list(lst, sort_meth):
""" Sort an object list with sort_meth (which should return a translated string) """
templist = [(getattr(obj_, sort_meth)().lower(), obj_) for obj_ in lst]
@@ -147,22 +165,8 @@ def check_potfiles(po_path):
+ "</li>\n</ul>")))
return errors
-def generate_doc_pot_file(vcs_path, potbase, moduleid, verbose):
+def generate_doc_pot_file(vcs_path, potbase, moduleid):
""" Return the pot file for a document-type domain, and the error if any """
-
- itstool_path = getattr(settings, 'ITSTOOL_PATH', '')
- extract_tools = {
- 'xml2po': {
- 'command' : "cd \"%(dir)s\" && xml2po %(opts)s -o %(potfile)s -e %(files)s",
- 'mod_var' : "DOC_ID",
- 'incl_var': "DOC_PAGES",
- },
- 'itstool': {
- 'command' : "cd \"%%(dir)s\" && %sitstool -o %%(potfile)s %%(files)s" % itstool_path,
- 'mod_var' : "HELP_ID",
- 'incl_var': "HELP_FILES",
- },
- }
errors = []
doc_id = read_makefile_variable([vcs_path], "HELP_ID")
@@ -192,7 +196,7 @@ def generate_doc_pot_file(vcs_path, potbase, moduleid, verbose):
modulename = os.path.basename(xml_files[0])[:-4]
else:
errors.append(("error", ugettext_noop("DOC_MODULE doesn't point to a real file, probably a macro.")))
- return "", errors
+ return "", errors, tool
files = [modulename + ".xml"]
extract_tools[tool]['incl_var'] = "DOC_INCLUDES"
@@ -214,9 +218,9 @@ def generate_doc_pot_file(vcs_path, potbase, moduleid, verbose):
potfile = ""
if not os.access(potfile, os.R_OK):
- return "", errors
+ return "", errors, tool
else:
- return potfile, errors
+ return potfile, errors, tool
def read_makefile_variable(vcs_paths, variable):
""" vcs_paths is a list of potential path where Makefile.am could be found """
@@ -276,13 +280,12 @@ def pot_diff_status(pota, potb):
else:
return CHANGED_NO_ADDITIONS, result_all
-def po_file_stats(pofile, msgfmt_checks=True, count_images=True):
+def po_file_stats(pofile, msgfmt_checks=True):
""" Compute pofile translation statistics, and proceed to some validity checks if msgfmt_checks is True """
res = {
'translated' : 0,
'fuzzy' : 0,
'untranslated' : 0,
- 'num_figures' : 0,
'errors' : [],
}
c_env = {"LC_ALL": "C", "LANG": "C", "LANGUAGE": "C"}
@@ -345,12 +348,6 @@ def po_file_stats(pofile, msgfmt_checks=True, count_images=True):
if status != STATUS_OK:
res['errors'].append(("warn",
ugettext_noop("PO file '%s' is not UTF-8 encoded.") % (filename)))
- # Count number of figures in PO(T) file
- if count_images:
- command = "grep '^msgid \"@@image:' \"%s\" | wc -l" % pofile
- (status, output, errs) = run_shell_command(command)
- res['num_figures'] = int(output)
-
return res
def read_linguas_file(full_path):
@@ -397,11 +394,15 @@ def get_doc_linguas(module_path, po_path):
return {'langs': linguas.split(),
'error': ugettext_noop("DOC_LINGUAS list doesn't include this language.") }
-def get_fig_stats(pofile):
+def get_fig_stats(pofile, image_method, trans_stats=True):
""" Extract image strings from pofile and return a list of figures dict:
[{'path':, 'hash':, 'fuzzy':, 'translated':}, ...] """
+ if image_method not in ('xml2po', 'itstool'):
+ return []
# Extract image strings: beforeline/msgid/msgstr/grep auto output a fourth line
- command = "msgcat --no-wrap %(pofile)s| grep -A 1 -B 1 '^msgid \"@@image:'" % locals()
+ command = "msgcat --no-wrap %(pofile)s| grep -A 1 -B 1 '%(grep)s'" % {
+ 'pofile': pofile, 'grep': extract_tools[image_method]['img_grep']
+ }
(status, output, errs) = run_shell_command(command)
if status != STATUS_OK:
# FIXME: something should be logged here
@@ -409,20 +410,18 @@ def get_fig_stats(pofile):
lines = output.split('\n')
while lines[0][0] != "#":
lines = lines[1:] # skip warning messages at the top of the output
- re_path = re.compile('^msgid \"@@image: \'([^\']*)\'')
- re_hash = re.compile('.*md5=(.*)\"')
- figures = []
+ figures = []
for i, line in islice(enumerate(lines), 0, None, 4):
- fig = {'path': '', 'hash': ''}
- fig['fuzzy'] = (line=='#, fuzzy' or line[:8]=='#| msgid')
- path_match = re_path.match(lines[i+1])
- if path_match and len(path_match.groups()):
- fig['path'] = path_match.group(1)
- hash_match = re_hash.match(lines[i+1])
- if hash_match and len(hash_match.groups()):
- fig['hash'] = hash_match.group(1)
- fig['translated'] = len(lines[i+2])>9 and not fig['fuzzy']
+ # TODO: add image size
+ fig = {"path": '', "hash": ''}
+ m = extract_tools[image_method]['img_regex'].match(lines[i+1])
+ if m:
+ fig["path"] = m.group('path')
+ fig["hash"] = m.group('hash')
+ if trans_stats:
+ fig["fuzzy"] = (line=='#, fuzzy' or line[:8]=='#| msgid')
+ fig["translated"] = len(lines[i+2])>9 and not fig['fuzzy']
figures.append(fig)
return figures
diff --git a/vertimus/views.py b/vertimus/views.py
index eb86eda..69d84ec 100644
--- a/vertimus/views.py
+++ b/vertimus/views.py
@@ -25,7 +25,7 @@ from django.http import HttpResponseRedirect, Http404
from django.shortcuts import render, get_object_or_404
from django.utils.translation import ugettext as _
-from stats.models import Statistics, Module, Branch, Domain, Language
+from stats.models import Statistics, FakeLangStatistics, Module, Branch, Domain, Language
from stats.utils import is_po_reduced
from vertimus.models import State, Action, ActionArchived
from vertimus.forms import ActionForm
@@ -62,13 +62,8 @@ def vertimus(request, branch, domain, language, stats=None, level="0"):
if not stats:
try:
stats = Statistics.objects.get(branch=branch, domain=domain, language=language)
- po_url = stats.po_url()
except Statistics.DoesNotExist:
- stats = pot_stats
- po_url = urlresolvers.reverse('dynamic_po',
- args=("%s.%s.%s.%s.po" % (branch.module.name, domain.name, branch.name, language.locale),))
- else:
- po_url = stats.po_url()
+ stats = FakeLangStatistics(pot_stats, language)
# Get the state of the translation
(state, created) = State.objects.get_or_create(
@@ -122,7 +117,7 @@ def vertimus(request, branch, domain, language, stats=None, level="0"):
'pageSection': 'module',
'stats': stats,
'pot_stats': pot_stats,
- 'po_url': po_url,
+ 'po_url': stats.po_url(),
'po_url_reduced': stats.has_reducedstat() and stats.po_url(reduced=True) or '',
'branch': branch,
'other_states': other_branch_states,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]