[damned-lies] Partially redesigned utils.generate_doc_pot_file and stats tests



commit f6db20dcfe3e784b470e5d78d4f98a04bf89a28e
Author: Claude Paroz <claude 2xlibre net>
Date:   Wed Aug 31 10:16:59 2011 +0200

    Partially redesigned utils.generate_doc_pot_file and stats tests

 common/fields.py                                   |    3 +
 stats/fixtures/sample_data.json                    |  177 ++++++++++++++------
 stats/models.py                                    |    9 +-
 stats/templatetags/stats_extras.py                 |    2 +-
 stats/tests/__init__.py                            |  143 +++++++---------
 stats/tests/fixture_factory.py                     |    8 +-
 .../help/C/figures/gnome-hello-logo.png            |  Bin 0 -> 3388 bytes
 .../help/fr/figures/gnome-hello-logo.png           |  Bin 0 -> 3388 bytes
 stats/utils.py                                     |  125 +++++++++-----
 teams/tests.py                                     |    2 +-
 10 files changed, 278 insertions(+), 191 deletions(-)
---
diff --git a/common/fields.py b/common/fields.py
index 4fb0ce8..1794c96 100644
--- a/common/fields.py
+++ b/common/fields.py
@@ -87,6 +87,9 @@ class JSONField(models.TextField):
 
         return super(JSONField, self).get_db_prep_save(value, connection=connection)
 
+    def value_to_string(self, obj):
+        return simplejson.dumps(self._get_val_from_obj(obj), cls=DjangoJSONEncoder)
+
 
 # rules for South migrations tool (for version >= 0.7)
 try:
diff --git a/stats/fixtures/sample_data.json b/stats/fixtures/sample_data.json
index 516fac1..2f8ad8a 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-08-08 22:23:57", 
+   "last_login": "2011-08-30 23:11:56", 
    "groups": [], 
    "user_permissions": [], 
    "password": "!", 
    "email": "bob example org", 
-   "date_joined": "2011-08-08 22:23:57"
+   "date_joined": "2011-08-30 23:11:56"
   }
  }, 
  {
@@ -27,12 +27,12 @@
    "is_active": true, 
    "is_superuser": false, 
    "is_staff": false, 
-   "last_login": "2011-08-08 22:23:57", 
+   "last_login": "2011-08-30 23:11:56", 
    "groups": [], 
    "user_permissions": [], 
    "password": "!", 
    "email": "coord example org", 
-   "date_joined": "2011-08-08 22:23:57"
+   "date_joined": "2011-08-30 23:11:56"
   }
  }, 
  {
@@ -45,12 +45,12 @@
    "is_active": true, 
    "is_superuser": false, 
    "is_staff": false, 
-   "last_login": "2011-08-08 22:23:57", 
+   "last_login": "2011-08-30 23:11:56", 
    "groups": [], 
    "user_permissions": [], 
    "password": "!", 
    "email": "alessio example org", 
-   "date_joined": "2011-08-08 22:23:57"
+   "date_joined": "2011-08-30 23:11:56"
   }
  }, 
  {
@@ -199,7 +199,7 @@
    "vcs_web": "http://git.gnome.org/browse/gnome-hello/";, 
    "name": "gnome-hello", 
    "vcs_root": "git://git.gnome.org/gnome-hello", 
-   "bugs_product": "gnome-hello", 
+   "bugs_product": "test", 
    "ext_platform": null, 
    "maintainers": [], 
    "bugs_component": "test", 
@@ -446,8 +446,11 @@
   "pk": 1, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 47, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -458,8 +461,11 @@
   "pk": 2, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 47, 
@@ -470,8 +476,11 @@
   "pk": 3, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 7, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 10, 
    "translated": 30, 
@@ -482,9 +491,12 @@
   "pk": 4, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 20, 
-   "figures": null, 
+   "translated_words": 0, 
+   "figures": "[{\"path\": \"figures/gnome-hello-new.png\", \"hash\": \"8a1fcc6f46a22a1f500cfef9ca51b481\"}, {\"path\": \"figures/gnome-hello-logo.png\", \"hash\": \"1ae47b7a7c4fbeb1f6bb72c0cf18d389\"}]", 
    "fuzzy": 0, 
    "translated": 0, 
    "path": null
@@ -494,9 +506,12 @@
   "pk": 5, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
-   "figures": null, 
+   "translated_words": 0, 
+   "figures": "[{\"translated\": false, \"path\": \"figures/gnome-hello-new.png\", \"hash\": \"8a1fcc6f46a22a1f500cfef9ca51b481\", \"fuzzy\": true}, {\"translated\": false, \"path\": \"figures/gnome-hello-logo.png\", \"hash\": \"1ae47b7a7c4fbeb1f6bb72c0cf18d389\", \"fuzzy\": false}]", 
    "fuzzy": 0, 
    "translated": 20, 
    "path": null
@@ -506,8 +521,11 @@
   "pk": 6, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 20, 
@@ -518,8 +536,11 @@
   "pk": 7, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-08 22:23:57", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 136, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -530,8 +551,11 @@
   "pk": 8, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 128, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -542,8 +566,11 @@
   "pk": 9, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 136, 
@@ -554,8 +581,11 @@
   "pk": 10, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 6, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 130, 
@@ -566,8 +596,11 @@
   "pk": 11, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 28, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 100, 
@@ -578,8 +611,11 @@
   "pk": 12, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 259, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -590,8 +626,11 @@
   "pk": 13, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 259, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -602,8 +641,11 @@
   "pk": 14, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 259, 
@@ -614,8 +656,11 @@
   "pk": 15, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 149, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -626,8 +671,11 @@
   "pk": 16, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 4, 
    "translated": 255, 
@@ -638,8 +686,11 @@
   "pk": 17, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 259, 
@@ -650,8 +701,11 @@
   "pk": 18, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 259, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -662,8 +716,11 @@
   "pk": 19, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 259, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -674,8 +731,11 @@
   "pk": 20, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 259, 
@@ -686,8 +746,11 @@
   "pk": 21, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 626, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 0, 
    "translated": 0, 
@@ -698,8 +761,11 @@
   "pk": 22, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 2, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 20, 
    "translated": 598, 
@@ -710,8 +776,11 @@
   "pk": 23, 
   "model": "stats.pofile", 
   "fields": {
-   "updated": "2011-08-10 14:15:49", 
+   "updated": "2011-08-30 23:11:56", 
+   "untranslated_words": 0, 
+   "fuzzy_words": 0, 
    "untranslated": 0, 
+   "translated_words": 0, 
    "figures": null, 
    "fuzzy": 6, 
    "translated": 620, 
@@ -724,7 +793,7 @@
   "fields": {
    "domain": 1, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 1, 
@@ -739,7 +808,7 @@
   "fields": {
    "domain": 1, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 2, 
@@ -754,7 +823,7 @@
   "fields": {
    "domain": 1, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 3, 
@@ -769,7 +838,7 @@
   "fields": {
    "domain": 2, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 4, 
@@ -784,7 +853,7 @@
   "fields": {
    "domain": 2, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 5, 
@@ -799,7 +868,7 @@
   "fields": {
    "domain": 2, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 6, 
@@ -814,7 +883,7 @@
   "fields": {
    "domain": 3, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 7, 
@@ -829,7 +898,7 @@
   "fields": {
    "domain": 3, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 9, 
@@ -844,7 +913,7 @@
   "fields": {
    "domain": 3, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 10, 
@@ -859,7 +928,7 @@
   "fields": {
    "domain": 4, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 12, 
@@ -874,7 +943,7 @@
   "fields": {
    "domain": 4, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 13, 
@@ -889,7 +958,7 @@
   "fields": {
    "domain": 4, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 14, 
@@ -904,7 +973,7 @@
   "fields": {
    "domain": 3, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 15, 
@@ -919,7 +988,7 @@
   "fields": {
    "domain": 3, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 16, 
@@ -934,7 +1003,7 @@
   "fields": {
    "domain": 3, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 17, 
@@ -949,7 +1018,7 @@
   "fields": {
    "domain": 4, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 18, 
@@ -964,7 +1033,7 @@
   "fields": {
    "domain": 4, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 19, 
@@ -979,7 +1048,7 @@
   "fields": {
    "domain": 4, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 20, 
@@ -994,7 +1063,7 @@
   "fields": {
    "domain": 5, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 21, 
@@ -1009,7 +1078,7 @@
   "fields": {
    "domain": 5, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 22, 
@@ -1024,7 +1093,7 @@
   "fields": {
    "domain": 5, 
    "old_translated": 0, 
-   "old_date": "2011-08-10 14:15:49", 
+   "old_date": "2011-08-30 23:11:56", 
    "old_fuzzy": 0, 
    "old_untranslated": 0, 
    "full_po": 23, 
@@ -1042,4 +1111,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/models.py b/stats/models.py
index 5b87de3..9b47625 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -490,13 +490,8 @@ class Branch(models.Model):
                     fig_stats = None
                     if dom.dtype == "doc":
                         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):
-                                fig_file = open(trans_path, 'rb').read()
-                                trans_hash = hashlib.md5(fig_file).hexdigest()
-                                if fig['hash'] == trans_hash:
-                                    langstats['errors'].append(("warn-ext", "Figures should not be copied when identical to original (%s)." % trans_path))
+                        fig_errors = utils.check_identical_figures(fig_stats, domain_path, lang)
+                        langstats['errors'].extend(fig_errors)
 
                     if settings.DEBUG: print >>sys.stderr, lang + ":\n" + str(langstats)
                     # Save in DB
diff --git a/stats/templatetags/stats_extras.py b/stats/templatetags/stats_extras.py
index 19ce6a1..fd612e8 100644
--- a/stats/templatetags/stats_extras.py
+++ b/stats/templatetags/stats_extras.py
@@ -62,7 +62,7 @@ def num_stats(stat, scope='full'):
         result = '<pre class="stats"><b>%(prc)3s%%</b> %(translated)6s %(fuzzy)5s %(untranslated)5s </pre>' % stats
         result = result.replace(" 0 ", '<span class="zero"> 0 </span>')
     else:
-        result = "(%(translated)s/%(fuzzy)s/%(untranslated)s)"
+        result = "(%(translated)s/%(fuzzy)s/%(untranslated)s)" % stats
     return mark_safe(result)
 
 @register.filter
diff --git a/stats/tests/__init__.py b/stats/tests/__init__.py
index 0aafa22..461716a 100644
--- a/stats/tests/__init__.py
+++ b/stats/tests/__init__.py
@@ -47,6 +47,7 @@ def test_scratchdir(test_func):
 
 
 class ModuleTestCase(TestCase):
+    fixtures = ['sample_data.json']
     SYS_DEPENDENCIES = (
         ('gettext', 'msgfmt'),
         ('intltool', 'intltool-update'),
@@ -63,28 +64,9 @@ class ModuleTestCase(TestCase):
             shutil.rmtree(self.co_path)
 
     def setUp(self):
-        # TODO: load bulk data from fixtures
         Branch.checkout_on_creation = False
-        self.mod = Module.objects.create(
-            name="gnome-hello",
-            bugs_base="http://bugzilla.gnome.org";,
-            bugs_product="test", # This product really exists
-            bugs_component="test",
-            vcs_type="git",
-            vcs_root="git://git.gnome.org/gnome-hello",
-            vcs_web="http://git.gnome.org/browse/gnome-hello/";)
-        self.mod.save()
-        dom = Domain.objects.create(module=self.mod, name='po', description='UI Translations', dtype='ui', directory='po')
-        dom = Domain.objects.create(module=self.mod, name='help', description='User Guide', dtype='doc', directory='help')
-
-        self.b = Branch(name='master', module=self.mod)
-        self.b.save(update_statistics=False)
-
-        self.rel = Release.objects.create(
-            name='gnome-2-24', status='official',
-            description='GNOME 2.24 (stable)', string_frozen=True)
-
-        self.cat = Category.objects.create(release=self.rel, branch=self.b, name='desktop')
+        self.mod = Module.objects.get(name="gnome-hello")
+        self.branch = self.mod.branch_set.get(name="master")
 
     def tearDown(self):
         if os.access(self.co_path, os.X_OK):
@@ -92,20 +74,23 @@ class ModuleTestCase(TestCase):
             run_shell_command(command, raise_on_error=True)
 
     def testModuleFunctions(self):
-        self.assertEquals(self.mod.get_description(), 'gnome-hello')
+        self.assertEqual(self.mod.get_description(), 'gnome-hello')
 
     def testBranchFunctions(self):
-        self.assertTrue(self.b.is_head())
-        self.assertEquals(self.b.get_vcs_url(), "git://git.gnome.org/gnome-hello")
-        self.assertEquals(self.b.get_vcs_web_url(), "http://git.gnome.org/browse/gnome-hello/";)
+        self.assertTrue(self.branch.is_head())
+        self.assertEqual(self.branch.get_vcs_url(), "git://git.gnome.org/gnome-hello")
+        self.assertEqual(self.branch.get_vcs_web_url(), "http://git.gnome.org/browse/gnome-hello/";)
 
     def testBranchStats(self):
         # Check stats
-        self.b.update_stats(force=True)
-        fr_po_stat = Statistics.objects.get(branch=self.b, domain__name='po', language__locale='fr')
+        self.branch.update_stats(force=True)
+        fr_po_stat = Statistics.objects.get(branch=self.branch, domain__name='po', language__locale='fr')
         self.assertEqual(fr_po_stat.translated(), 44)
-        fr_doc_stat = Statistics.objects.get(branch=self.b, domain__name='help', language__locale='fr')
+        fr_doc_stat = Statistics.objects.get(branch=self.branch, domain__name='help', language__locale='fr')
         self.assertEqual(fr_doc_stat.translated(), 16)
+        self.assertEqual(fr_po_stat.po_url(), u"/POT/gnome-hello.master/gnome-hello.master.fr.po")
+        self.assertEqual(fr_po_stat.pot_url(), u"/POT/gnome-hello.master/gnome-hello.master.pot")
+        self.assertEqual(fr_doc_stat.po_url(), u"/POT/gnome-hello.master/docs/gnome-hello-help.master.fr.po")
 
     def testCreateAndDeleteBranch(self):
         Branch.checkout_on_creation = True
@@ -124,54 +109,52 @@ class ModuleTestCase(TestCase):
         b1.save(update_statistics=False)
         b2 = Branch(name='p-branch', module=self.mod)
         b2.save(update_statistics=False)
-        self.assertEquals([b.name for b in sorted(self.mod.branch_set.all())], ['master','p-branch','a-branch'])
+        self.assertEqual([b.name for b in sorted(self.mod.branch_set.all())], ['master','p-branch','a-branch'])
         b1.weight = -1
         b1.save(update_statistics=False)
-        self.assertEquals([b.name for b in sorted(self.mod.branch_set.all())], ['master','a-branch','p-branch'])
+        self.assertEqual([b.name for b in sorted(self.mod.branch_set.all())], ['master','a-branch','p-branch'])
 
     def testStringFrozenMail(self):
         """ String change for a module of a string_frozen release should generate a message """
         mail.outbox = []
-        self.rel.string_frozen = True
-        self.rel.save()
-        self.b.update_stats(force=False)
+        self.branch.update_stats(force=False)
 
         # Create a new file with translation
-        new_file_path = os.path.join(self.b.co_path(), "dummy_file.py")
+        new_file_path = os.path.join(self.branch.co_path(), "dummy_file.py")
         new_string = "Dummy string for D-L tests"
         f = open(new_file_path,'w')
         f.write("a = _('%s')\n" % new_string)
         f.close()
         # Add the new file to POTFILES.in
-        f = open(os.path.join(self.b.co_path(), "po", "POTFILES.in"), 'a')
+        f = open(os.path.join(self.branch.co_path(), "po", "POTFILES.in"), 'a')
         f.write("dummy_file.py\n")
         f.close()
         # Regenerate stats (mail should be sent)
-        self.b.update_stats(force=False, checkout=False)
+        self.branch.update_stats(force=False, checkout=False)
         # Assertions
-        self.assertEquals(len(mail.outbox), 1);
-        self.assertEquals(mail.outbox[0].subject, "String additions to 'gnome-hello.master'")
+        self.assertEqual(len(mail.outbox), 1);
+        self.assertEqual(mail.outbox[0].subject, "String additions to 'gnome-hello.master'")
         self.assertTrue(mail.outbox[0].message().as_string().find(new_string)>-1)
 
     def testReadFileVariable(self):
         from stats.utils import search_variable_in_file
         file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "help_docbook", "Makefile.am")
         var_content = search_variable_in_file(file_path, "DOC_INCLUDES")
-        self.assertEquals(var_content.split(), ['rnusers.xml', 'rnlookingforward.xml', '$(NULL)'])
+        self.assertEqual(var_content.split(), ['rnusers.xml', 'rnlookingforward.xml', '$(NULL)'])
 
     def testGenerateDocPotfile(self):
-        from stats.utils import generate_doc_pot_file, get_fig_stats
+        from stats.utils import generate_doc_pot_file, get_fig_stats, DocFormat
         # 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')
+        potfile, errs, doc_format = 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')
+        res = get_fig_stats(pot_path, doc_format=doc_format)
         self.assertEqual(len(res), 1)
         os.remove(pot_path)
         # Mallard-style help (with itstool)
         pot_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "help_mallard", "gnome-help-itstool.pot")
-        res = get_fig_stats(pot_path, image_method='itstool')
+        res = get_fig_stats(pot_path, doc_format=DocFormat(True, True))
         self.assertEqual(len(res), 2)
         self.assertEqual(res[0]['path'], "figures/gnome.png")
 
@@ -180,43 +163,10 @@ class ModuleTestCase(TestCase):
             module=self.mod, name='http-po',
             description='UI Translations', dtype='ui',
             pot_method='http://l10n.gnome.org/POT/damned-lies.master/damned-lies.master.pot')
-        self.b.checkout()
-        potfile, errs = dom.generate_pot_file(self.b)
+        self.branch.checkout()
+        potfile, errs = dom.generate_pot_file(self.branch)
         self.assertTrue(os.path.exists(potfile))
 
-    def testIdenticalFigureWarning(self):
-        """ 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", "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='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='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.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='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/cs/figures/gnome-hello-new.png?h=master')
-
-    def testFigureView(self):
-        self.b.update_stats(force=True)
-        url = reverse('stats.views.docimages', args=[self.mod.name, 'help', self.b.name, 'fr'])
-        response = self.client.get(url)
-        self.assertContains(response, "gnome-hello-new.png")
-        # Same for a non-existing language
-        Language.objects.create(name='Afrikaans', locale='af')
-        url = reverse('stats.views.docimages', args=[self.mod.name, 'help', self.b.name, 'af'])
-        response = self.client.get(url)
-        self.assertContains(response, "gnome-hello-new.png")
-
     def testCreateUnexistingBranch(self):
         """ Try to create a non-existing branch """
         Branch.checkout_on_creation = True
@@ -227,7 +177,7 @@ class ModuleTestCase(TestCase):
     def testDynamicPO(self):
         """ Test the creation of a blank po file for a new language """
         lang = Language.objects.create(name="Tamil", locale="ta")
-        self.b.update_stats(force=True) # At least POT stats needed
+        self.branch.update_stats(force=False) # At least POT stats needed
         response = self.client.get('/module/po/gnome-hello.po.master.ta.po')
         self.assertContains(response, """# Tamil translation for gnome-hello.
 # Copyright (C) %s gnome-hello's COPYRIGHT HOLDER
@@ -251,15 +201,15 @@ class ModuleTestCase(TestCase):
         pers.save()
         self.mod.maintainers.add(pers)
         update_doap_infos(self.mod)
-        self.assertEquals(self.mod.maintainers.count(), 6)
+        self.assertEqual(self.mod.maintainers.count(), 6)
         claude = self.mod.maintainers.get(email='claude 2xlibre net')
-        self.assertEquals(claude.username, 'claudep')
+        self.assertEqual(claude.username, 'claudep')
 
     @test_scratchdir
     def testUpdateDoapInfos(self):
         from stats.doap import update_doap_infos
         update_doap_infos(self.mod)
-        self.assertEquals(self.mod.homepage, "http://git.gnome.org/browse/gnome-hello";)
+        self.assertEqual(self.mod.homepage, "http://git.gnome.org/browse/gnome-hello";)
 
 
 class StatisticsTests(TestCase):
@@ -288,3 +238,32 @@ class StatisticsTests(TestCase):
         stats = FakeLangStatistics(pot_stats, Language.objects.get(locale='bem'))
         self.assertEqual(stats.po_url(), "/module/po/zenity.po.gnome-2-30.bem.po")
         self.assertEqual(stats.po_url(reduced=True), "/module/po/zenity.po.gnome-2-30.bem-reduced.po")
+
+class FigureTests(TestCase):
+    fixtures = ['sample_data.json']
+    def testFigureView(self):
+        url = reverse('stats.views.docimages', args=['gnome-hello', 'help', 'master', 'fr'])
+        response = self.client.get(url)
+        self.assertContains(response, "gnome-hello-new.png")
+        # Same for a non-existing language
+        Language.objects.create(name='Afrikaans', locale='af')
+        url = reverse('stats.views.docimages', args=['gnome-hello', 'help', 'master', 'af'])
+        response = self.client.get(url)
+        self.assertContains(response, "gnome-hello-new.png")
+
+    def testFigureURLs(self):
+        """ Test if figure urls are properly constructed """
+        stat = Statistics.objects.get(branch__module__name='gnome-hello', branch__name='master', domain__dtype='doc', language__locale='fr')
+        figs = stat.get_figures()
+        self.assertEqual(figs[0]['orig_remote_url'], 'http://git.gnome.org/browse/gnome-hello/plain/help/C/figures/gnome-hello-new.png?h=master')
+        self.assertFalse('trans_remote_url' in figs[0])
+
+    @test_scratchdir
+    def testIdenticalFigureWarning(self):
+        """ Detect warning if translated figure is identical to original figure """
+        from stats.utils import check_identical_figures
+        branch = Branch.objects.get(module__name='gnome-hello', name='master')
+        doc_stat = Statistics.objects.get(branch=branch, domain__name='help', language__locale='fr')
+        errs = check_identical_figures(doc_stat.get_figures(), os.path.join(branch.co_path(), 'help'), 'fr')
+        self.assertEqual(len(errs), 1)
+        self.assertTrue(errs[0][1].startswith("Figures should not be copied"))
diff --git a/stats/tests/fixture_factory.py b/stats/tests/fixture_factory.py
index 9563df8..166e2b7 100644
--- a/stats/tests/fixture_factory.py
+++ b/stats/tests/fixture_factory.py
@@ -55,7 +55,7 @@ class FixtureFactory(TestCase):
             vcs_root="git://git.gnome.org/gnome-hello",
             vcs_web="http://git.gnome.org/browse/gnome-hello/";,
             bugs_base="http://bugzilla.gnome.org";,
-            bugs_product="gnome-hello",
+            bugs_product="test", # This product really exists
             bugs_component="test")
         zenity = Module.objects.create(name="zenity", vcs_type="git",
             vcs_root="git://git.gnome.org/zenity",
@@ -114,9 +114,11 @@ class FixtureFactory(TestCase):
         Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=l_fr, full_po=pofile, part_po=pofile)
         pofile = PoFile.objects.create(translated=30, fuzzy=10, untranslated=7)
         Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=l_it, full_po=pofile, part_po=pofile)
-        pofile = PoFile.objects.create(untranslated=20)
+        pofile = PoFile.objects.create(untranslated=20,
+            figures = [{"path": "figures/gnome-hello-new.png", "hash": "8a1fcc6f46a22a1f500cfef9ca51b481"}, {"path": "figures/gnome-hello-logo.png", "hash": "1ae47b7a7c4fbeb1f6bb72c0cf18d389"}])
         Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=None, full_po=pofile, part_po=pofile)
-        pofile = PoFile.objects.create(translated=20)
+        pofile = PoFile.objects.create(translated=20,
+            figures = [{"translated": False, "path": "figures/gnome-hello-new.png", "hash": "8a1fcc6f46a22a1f500cfef9ca51b481", "fuzzy": True}, {"translated": False, "path": "figures/gnome-hello-logo.png", "hash": "1ae47b7a7c4fbeb1f6bb72c0cf18d389", "fuzzy": False}])
         Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=l_fr, full_po=pofile, part_po=pofile)
         pofile = PoFile.objects.create(translated=20)
         Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=l_it, full_po=pofile, part_po=pofile)
diff --git a/stats/tests/git/gnome-hello/help/C/figures/gnome-hello-logo.png b/stats/tests/git/gnome-hello/help/C/figures/gnome-hello-logo.png
new file mode 100644
index 0000000..b52af4b
Binary files /dev/null and b/stats/tests/git/gnome-hello/help/C/figures/gnome-hello-logo.png differ
diff --git a/stats/tests/git/gnome-hello/help/fr/figures/gnome-hello-logo.png b/stats/tests/git/gnome-hello/help/fr/figures/gnome-hello-logo.png
new file mode 100644
index 0000000..b52af4b
Binary files /dev/null and b/stats/tests/git/gnome-hello/help/fr/figures/gnome-hello-logo.png differ
diff --git a/stats/utils.py b/stats/utils.py
index ba50284..dacfdff 100644
--- a/stats/utils.py
+++ b/stats/utils.py
@@ -47,25 +47,61 @@ 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:",
+
+class DocFormat(object):
+    itstool_regex = re.compile("^msgid \"external ref=\'(?P<path>[^\']*)\' md5=\'(?P<hash>[^\']*)\'\"")
+    xml2po_regex = re.compile("^msgid \"@@image: \'(?P<path>[^\']*)\'; md5=(?P<hash>[^\"]*)\"")
+    def __init__(self, is_itstool, is_mallard):
+        self.format = is_mallard and "mallard" or "docbook"
+        self.tool = is_itstool and "itstool" or "xml2po"
+
+    @property
+    def command(self):
+        if self.tool == "itstool":
+            return "cd \"%%(dir)s\" && %sitstool -o %%(potfile)s %%(files)s" % ITSTOOL_PATH
+        elif self.format == "mallard":
+            return "cd \"%(dir)s\" && xml2po -m mallard -o %(potfile)s -e %(files)s"
+        else:
+            return "cd \"%(dir)s\" && xml2po -o %(potfile)s -e %(files)s"
+
+    @property
+    def module_var(self):
+        if self.tool == "itstool":
+            return "HELP_ID"
+        elif self.format == "mallard":
+            return "DOC_ID"
+        else:
+            return "DOC_MODULE"
+
+    @property
+    def include_var(self):
+        if self.tool == "itstool":
+            return "HELP_FILES"
+        elif self.format == "mallard":
+            return "DOC_PAGES"
+        else:
+            return "DOC_INCLUDES"
+
+    @property
+    def img_grep(self):
+        if self.tool == "itstool":
+            return "^msgid \"external ref="
+        else:
+            return "^msgid \"@@image:"
+
+    @property
+    def img_grep(self):
+        return self.tool == "itstool" and "^msgid \"external ref=" or "^msgid \"@@image:"
+
+    @property
+    def bef_line(self):
         # Lines to keep before matched grep to catch the ,fuzzy or #|msgid line
-        'bef_line': 1,
-        '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=",
-        'bef_line': 2,
-        'img_regex': re.compile("^msgid \"external ref=\'(?P<path>[^\']*)\' md5=\'(?P<hash>[^\']*)\'\""),
-    },
-}
+        return self.tool == "itstool" and 2 or 1
+
+    @property
+    def img_regex(self):
+        return self.tool == "itstool" and self.itstool_regex or self.xml2po_regex
+
 
 def sort_object_list(lst, sort_meth):
     """ Sort an object list with sort_meth (which should return a translated string) """
@@ -173,25 +209,18 @@ def generate_doc_pot_file(vcs_path, potbase, moduleid):
     errors = []
 
     doc_id = read_makefile_variable([vcs_path], "HELP_ID")
-    if doc_id:
-        tool = "itstool"
-    else:
-        tool = "xml2po"
+    has_index_page = os.access(os.path.join(vcs_path, "C", "index.page"), os.R_OK)
+    doc_format = DocFormat(bool(doc_id), has_index_page)
 
-    options = ""
-    if os.access(os.path.join(vcs_path, "C", "index.page"), os.R_OK):
-        # a Mallard document
+    if doc_format.format == "mallard":
         files = ["index.page"]
-        if tool == "xml2po":
-            options = "-m mallard"
-        incl_var = extract_tools[tool]['incl_var']
     else:
-        modulename = read_makefile_variable([vcs_path], "DOC_MODULE")
+        modulename = read_makefile_variable([vcs_path], doc_format.module_var)
         if not modulename:
-            return "", (("error", ugettext_noop("Module %s doesn't look like gnome-doc-utils module.") % moduleid),), tool
+            return "", (("error", ugettext_noop("Module %s doesn't look like gnome-doc-utils module.") % moduleid),), doc_format
         if not os.access(os.path.join(vcs_path, "C", modulename + ".xml"), os.R_OK):
             if os.access(os.path.join(vcs_path, "C", moduleid + ".xml"), os.R_OK):
-                errors.append(("warn", ugettext_noop("DOC_MODULE doesn't resolve to a real file, using '%s.xml'.") % (moduleid)))
+                errors.append(("warn", ugettext_noop("%s doesn't resolve to a real file, using '%s.xml'.") % (doc_format.module_var, moduleid)))
                 modulename = moduleid
             else:
                 # Last try: only one xml file in C/...
@@ -199,17 +228,16 @@ def generate_doc_pot_file(vcs_path, potbase, moduleid):
                 if len(xml_files) == 1:
                     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, tool
+                    errors.append(("error", ugettext_noop("%s doesn't point to a real file, probably a macro.") % doc_format.module_var))
+                    return "", errors, doc_format
         files = [modulename + ".xml"]
-        incl_var = "DOC_INCLUDES"
 
-    includes = read_makefile_variable([vcs_path], incl_var)
+    includes = read_makefile_variable([vcs_path], doc_format.include_var)
     if includes:
         files.extend(filter(lambda x:x not in ("", "$(NULL)"), includes.split()))
     files = " ".join([os.path.join("C", f) for f in files])
     potfile = os.path.join(vcs_path, "C", potbase + ".pot")
-    command = extract_tools[tool]['command'] % {'dir': vcs_path, 'opts': options, 'potfile': potfile, 'files': files}
+    command = doc_format.command % {'dir': vcs_path, 'potfile': potfile, 'files': files}
     (status, output, errs) = run_shell_command(command)
 
     if status != STATUS_OK:
@@ -222,9 +250,9 @@ def generate_doc_pot_file(vcs_path, potbase, moduleid):
         potfile = ""
 
     if not os.access(potfile, os.R_OK):
-        return "", errors, tool
+        return "", errors, doc_format
     else:
-        return potfile, errors, tool
+        return potfile, errors, doc_format
 
 def read_makefile_variable(vcs_paths, variable):
     """ vcs_paths is a list of potential path where Makefile.am could be found """
@@ -408,15 +436,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, image_method, trans_stats=True):
+def get_fig_stats(pofile, doc_format, 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'):
+    if not isinstance(doc_format, DocFormat):
         return []
     # Extract image strings: beforeline/msgid/msgstr/grep auto output a fourth line
-    before_lines = extract_tools[image_method]['bef_line']
+    before_lines = doc_format.bef_line
     command = "msgcat --no-wrap %(pofile)s| grep -A 1 -B %(before)s '%(grep)s'" % {
-        'pofile': pofile, 'grep': extract_tools[image_method]['img_grep'], 'before': before_lines,
+        'pofile': pofile, 'grep': doc_format.img_grep, 'before': before_lines,
     }
     (status, output, errs) = run_shell_command(command)
     if status != STATUS_OK:
@@ -430,7 +458,7 @@ def get_fig_stats(pofile, image_method, trans_stats=True):
     for i, line in islice(enumerate(lines), 0, None, 3+before_lines):
         # TODO: add image size
         fig = {"path": '', "hash": ''}
-        m = extract_tools[image_method]['img_regex'].match(lines[i+before_lines])
+        m = doc_format.img_regex.match(lines[i+before_lines])
         if m:
             fig["path"] = m.group('path')
             fig["hash"] = m.group('hash')
@@ -440,6 +468,17 @@ def get_fig_stats(pofile, image_method, trans_stats=True):
         figures.append(fig)
     return figures
 
+def check_identical_figures(fig_stats, base_path, lang):
+    errors = []
+    for fig in fig_stats:
+        trans_path = os.path.join(base_path, lang, fig['path'])
+        if os.access(trans_path, os.R_OK):
+            fig_file = open(trans_path, 'rb').read()
+            trans_hash = hashlib.md5(fig_file).hexdigest()
+            if fig['hash'] == trans_hash:
+                errors.append(("warn-ext", "Figures should not be copied when identical to original (%s)." % trans_path))
+    return errors
+
 def add_custom_header(po_path, header, value):
     """ Add a custom po file header """
     grep_cmd = """grep "%s" %s""" % (header, po_path)
diff --git a/teams/tests.py b/teams/tests.py
index e7293be..a5d1b54 100644
--- a/teams/tests.py
+++ b/teams/tests.py
@@ -187,7 +187,7 @@ class RoleTest(TeamsAndRolesTests):
         self.pt.last_login = datetime.now()-timedelta(days=10) # active person
         self.pt.save()
 
-        self.pr.last_login = datetime.now()-timedelta(days=30*6) # inactive person
+        self.pr.last_login = datetime.now()-timedelta(days=30*6+1) # inactive person
         self.pr.save()
 
         self.pc.last_login = datetime.now()-timedelta(days=30*6-1) #active person, but in limit date



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]