[damned-lies] Add stats for uploaded (merged) files



commit 9bb95310a60187b55a2a8d38e76e9d289e72999d
Author: Claude Paroz <claude 2xlibre net>
Date:   Sat Aug 6 18:55:19 2011 +0200

    Add stats for uploaded (merged) files

 people/tests/__init__.py                         |    1 -
 stats/models.py                                  |   13 ++
 stats/templatetags/stats_extras.py               |    8 +
 stats/tests/__init__.py                          |    3 +
 teams/tests.py                                   |    2 +-
 templates/vertimus/vertimus_detail.html          |    5 +-
 vertimus/migrations/0002_add_merged_file.py      |  184 ++++++++++++++++++++++
 vertimus/migrations/0003_populate_merged_file.py |  181 +++++++++++++++++++++
 vertimus/models.py                               |   58 +++----
 vertimus/tests/__init__.py                       |   36 +++--
 vertimus/views.py                                |    9 +-
 11 files changed, 443 insertions(+), 57 deletions(-)
---
diff --git a/people/tests/__init__.py b/people/tests/__init__.py
index b11b560..797a4d8 100644
--- a/people/tests/__init__.py
+++ b/people/tests/__init__.py
@@ -93,6 +93,5 @@ class PeopleTestCase(TestCase):
         # Test only p5 should be deleted
         self.assertEqual(Person.objects.all().count(), 5)
         Person.clean_obsolete_accounts()
-        import pdb; pdb.set_trace()
         self.assertEqual(Person.objects.all().count(), 4)
         self.assertEqual(set(Person.objects.all()), set([p1, p2, p3, p4]))
diff --git a/stats/models.py b/stats/models.py
index 77656f0..9838d9f 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -1205,6 +1205,12 @@ class PoFile(models.Model):
     def __unicode__(self):
         return "%s (%s/%s/%s)" % (self.path, self.translated, self.fuzzy, self.untranslated)
 
+    def url(self):
+        return utils.url_join(settings.MEDIA_URL, settings.UPLOAD_DIR, os.path.basename(self.path))
+
+    def filename(self):
+        return os.path.basename(self.path)
+
     def pot_size(self):
         return self.translated + self.fuzzy + self.untranslated
 
@@ -1230,6 +1236,13 @@ class PoFile(models.Model):
         else:
             return int(100*self.untranslated/self.pot_size())
 
+    def update_stats(self):
+        stats = utils.po_file_stats(self.path, msgfmt_checks=False)
+        self.translated   = stats['translated']
+        self.fuzzy        = stats['fuzzy']
+        self.untranslated = stats['untranslated']
+        self.save()
+
 
 class Statistics(models.Model):
     branch = models.ForeignKey(Branch)
diff --git a/stats/templatetags/stats_extras.py b/stats/templatetags/stats_extras.py
index 1612a02..07fad51 100644
--- a/stats/templatetags/stats_extras.py
+++ b/stats/templatetags/stats_extras.py
@@ -45,6 +45,14 @@ def num_stats(stat, scope='full'):
             'fuzzy':        stat.fuzzy(scope),
             'untranslated': stat.untranslated(scope),
         }
+    elif isinstance(stat, PoFile):
+        stats = {
+            'translated':   stat.translated,
+            'fuzzy':        stat.fuzzy,
+            'untranslated': stat.untranslated,
+        }
+        if scope != 'short':
+            stats['prc'] = stat.tr_percentage()
     else:
         stats = stat
     if 'prc' in stats:
diff --git a/stats/tests/__init__.py b/stats/tests/__init__.py
index 7a74096..abefe5d 100644
--- a/stats/tests/__init__.py
+++ b/stats/tests/__init__.py
@@ -37,9 +37,12 @@ def test_scratchdir(test_func):
     """ Decorator to temporarily use the scratchdir inside the test directory """
     def decorator(self):
         old_SCRATCHDIR = settings.SCRATCHDIR
+        old_POTDIR = settings.POTDIR
         settings.SCRATCHDIR = os.path.dirname(os.path.abspath(__file__))
+        settings.POTDIR = os.path.join(settings.SCRATCHDIR, "POT")
         test_func(self)
         settings.SCRATCHDIR = old_SCRATCHDIR
+        settings.POTDIR = old_POTDIR
     return decorator
 
 
diff --git a/teams/tests.py b/teams/tests.py
index 21d029d..da4ce2e 100644
--- a/teams/tests.py
+++ b/teams/tests.py
@@ -203,7 +203,7 @@ class RoleTest(TeamsAndRolesTests):
         self.assertTrue(self.role.is_active)
         self.assertTrue(self.role2.is_active)
         self.assertTrue(self.role_limit_date.is_active)
-        self.assertTrue(self.role_inactive.is_active)        
+        self.assertTrue(self.role_inactive.is_active)
 
         Role.inactivate_unused_roles()
 
diff --git a/templates/vertimus/vertimus_detail.html b/templates/vertimus/vertimus_detail.html
index 025176d..c315e79 100644
--- a/templates/vertimus/vertimus_detail.html
+++ b/templates/vertimus/vertimus_detail.html
@@ -154,9 +154,8 @@ $(document).ready(function() {
         {% if action.has_po_file %}
           {% if action.merged_file.url %}
             <a href="{{ action.merged_file.url }}">
-              <img src="{{ MEDIA_URL }}img/download.png"/>&nbsp;{{ action.merged_file.filename }}
-            </a>
-            <br/>
+              <img src="{{ MEDIA_URL }}img/download.png"/>&nbsp;{{ action.merged_file.filename }}</a>
+            {{ action.merged_file|num_stats:'short' }}<br/>
           {% endif %}
           <div class="right_actions">{% trans "diff with:" %}
             {% for f in files %}
diff --git a/vertimus/migrations/0002_add_merged_file.py b/vertimus/migrations/0002_add_merged_file.py
new file mode 100644
index 0000000..97fa059
--- /dev/null
+++ b/vertimus/migrations/0002_add_merged_file.py
@@ -0,0 +1,184 @@
+# 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):
+        
+        # Adding field 'ActionArchived.merged_file'
+        db.add_column('action_archived', 'merged_file', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['stats.PoFile'], unique=True, null=True), keep_default=False)
+
+        # Adding field 'Action.merged_file'
+        db.add_column('action', 'merged_file', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['stats.PoFile'], unique=True, null=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'ActionArchived.merged_file'
+        db.delete_column('action_archived', 'merged_file_id')
+
+        # Deleting field 'Action.merged_file'
+        db.delete_column('action', 'merged_file_id')
+
+
+    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.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.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'})
+        },
+        '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'})
+        },
+        'vertimus.action': {
+            'Meta': {'object_name': 'Action', 'db_table': "'action'"},
+            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merged_file': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['stats.PoFile']", 'unique': 'True', 'null': 'True'}),
+            'name': ('django.db.models.fields.SlugField', [], {'max_length': '8', 'db_index': 'True'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Person']"}),
+            'state_db': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vertimus.State']"})
+        },
+        'vertimus.actionarchived': {
+            'Meta': {'object_name': 'ActionArchived', 'db_table': "'action_archived'"},
+            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merged_file': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['stats.PoFile']", 'unique': 'True', 'null': 'True'}),
+            'name': ('django.db.models.fields.SlugField', [], {'max_length': '8', 'db_index': 'True'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Person']"}),
+            'sequence': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'state_db': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vertimus.State']"})
+        },
+        'vertimus.state': {
+            'Meta': {'unique_together': "(('branch', 'domain', 'language'),)", 'object_name': 'State', 'db_table': "'state'"},
+            'branch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Branch']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Domain']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['languages.Language']"}),
+            'name': ('django.db.models.fields.SlugField', [], {'default': "'None'", 'max_length': '20', 'db_index': 'True'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['people.Person']", 'null': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['vertimus']
diff --git a/vertimus/migrations/0003_populate_merged_file.py b/vertimus/migrations/0003_populate_merged_file.py
new file mode 100644
index 0000000..7af9905
--- /dev/null
+++ b/vertimus/migrations/0003_populate_merged_file.py
@@ -0,0 +1,181 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+from vertimus.models import Action
+from stats.models import PoFile
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Write your forwards methods here."
+        for action in Action.objects.all():
+            if action.has_po_file() and not action.merged_file:
+                merged_path = "%s.merged.po" % action.file.path[:-3]
+                action.merged_file = PoFile.objects.create(path=merged_path)
+                action.save()
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+        pass
+
+    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.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.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'})
+        },
+        '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'})
+        },
+        'vertimus.action': {
+            'Meta': {'object_name': 'Action', 'db_table': "'action'"},
+            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merged_file': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['stats.PoFile']", 'unique': 'True', 'null': 'True'}),
+            'name': ('django.db.models.fields.SlugField', [], {'max_length': '8', 'db_index': 'True'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Person']"}),
+            'state_db': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vertimus.State']"})
+        },
+        'vertimus.actionarchived': {
+            'Meta': {'object_name': 'ActionArchived', 'db_table': "'action_archived'"},
+            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merged_file': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['stats.PoFile']", 'unique': 'True', 'null': 'True'}),
+            'name': ('django.db.models.fields.SlugField', [], {'max_length': '8', 'db_index': 'True'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['people.Person']"}),
+            'sequence': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'state_db': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vertimus.State']"})
+        },
+        'vertimus.state': {
+            'Meta': {'unique_together': "(('branch', 'domain', 'language'),)", 'object_name': 'State', 'db_table': "'state'"},
+            'branch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Branch']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stats.Domain']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['languages.Language']"}),
+            'name': ('django.db.models.fields.SlugField', [], {'default': "'None'", 'max_length': '20', 'db_index': 'True'}),
+            'person': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['people.Person']", 'null': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['vertimus']
diff --git a/vertimus/models.py b/vertimus/models.py
index 7fb6bf0..06214a2 100644
--- a/vertimus/models.py
+++ b/vertimus/models.py
@@ -28,7 +28,7 @@ from django.contrib.sites.models import Site
 from django.core import mail, urlresolvers
 from django.db import models
 from django.db.models import Max
-from django.db.models.signals import post_save, post_delete
+from django.db.models.signals import post_save, pre_delete
 from django.utils.translation import get_language, activate, ugettext, ugettext_lazy as _
 
 from stats.models import Branch, Domain, Statistics, PoFile
@@ -315,7 +315,7 @@ class ActionAbstract(models.Model):
     comment = models.TextField(blank=True, null=True)
     file = models.FileField(upload_to=generate_upload_filename, blank=True, null=True)
     #up_file     = models.OneToOneField(PoFile, null=True, related_name='action_p')
-    #merged_file = models.OneToOneField(PoFile, null=True, related_name='action_m')
+    merged_file = models.OneToOneField(PoFile, null=True) #, related_name='action_m')
 
     # A comment or a file is required
     arg_is_required = False
@@ -345,17 +345,6 @@ class ActionAbstract(models.Model):
         except:
             return False
 
-    def merged_file(self):
-        """If available, returns the merged file as a dict: {'url':'path':'filename'}"""
-        mfile_url = mfile_path = mfile_name = None
-        if self.file:
-            mfile_url = self.file.url[:-3] + ".merged.po"
-            mfile_path = self.file.path[:-3] + ".merged.po"
-            mfile_name = os.path.basename(mfile_path)
-            if not os.access(mfile_path, os.R_OK):
-                mfile_url = mfile_path = mfile_name = None
-        return {'url': mfile_url, 'path': mfile_path, 'filename': mfile_name}
-
     @classmethod
     def get_action_history(cls, state=None, sequence=None):
         """
@@ -436,20 +425,27 @@ class Action(ActionAbstract):
 
     def merge_file_with_pot(self, pot_file):
         """Merge the uploaded translated file with current pot."""
-        if self.file:
+        if not self.file:
+            return
+        if not self.merged_file:
             merged_path = "%s.merged.po" % self.file.path[:-3]
-            command = "msgmerge --previous -o %(out_po)s %(po_file)s %(pot_file)s" % {
-                'out_po':   merged_path,
-                'po_file':  self.file.path,
-                'pot_file': pot_file
-            }
-            run_shell_command(command)
-            # If uploaded file is reduced, run po_grep *after* merge
-            if is_po_reduced(self.file):
-                temp_path = "%s.temp.po" % self.file.path[:-3]
-                shutil.copy(merged_path, temp_path)
-                po_grep(temp_path, merged_path, self.state_db.domain.red_filter)
-                os.remove(temp_path)
+            self.merged_file = PoFile.objects.create(path=merged_path)
+            self.save()
+            return # post_save will call merge_file_with_pot again
+        merged_path = self.merged_file.path
+        command = "msgmerge --previous -o %(out_po)s %(po_file)s %(pot_file)s" % {
+            'out_po':   merged_path,
+            'po_file':  self.file.path,
+            'pot_file': pot_file
+        }
+        run_shell_command(command)
+        # If uploaded file is reduced, run po_grep *after* merge
+        if is_po_reduced(self.file):
+            temp_path = "%s.temp.po" % self.file.path[:-3]
+            shutil.copy(merged_path, temp_path)
+            po_grep(temp_path, merged_path, self.state_db.domain.red_filter)
+            os.remove(temp_path)
+        self.merged_file.update_stats()
 
     def send_mail_new_state(self, state, recipient_list):
         # Remove None and empty string items from the list
@@ -757,18 +753,18 @@ post_save.connect(merge_uploaded_file)
 
 def delete_action_files(sender, instance, **kwargs):
     """
-    post_delete callback for Action that deletes the file + the merged file from upload
+    pre_delete callback for Action that deletes the file + the merged file from upload
     directory.
     """
     if not isinstance(instance, ActionAbstract) or not getattr(instance, 'file'):
         return
     if instance.file.path.endswith('.po'):
-        merged_file = instance.file.path[:-3] + ".merged.po"
-        if os.access(merged_file, os.W_OK):
-             os.remove(merged_file)
+        if instance.merged_file:
+            if os.access(instance.merged_file.path, os.W_OK):
+                 os.remove(instance.merged_file.path)
     if os.access(instance.file.path, os.W_OK):
          os.remove(instance.file.path)
-post_delete.connect(delete_action_files)
+pre_delete.connect(delete_action_files)
 
 def reactivate_role(sender, instance, **kwargs):
     # Reactivating the role if needed
diff --git a/vertimus/tests/__init__.py b/vertimus/tests/__init__.py
index 327c645..4b01580 100644
--- a/vertimus/tests/__init__.py
+++ b/vertimus/tests/__init__.py
@@ -30,6 +30,7 @@ from django.utils.datastructures import MultiValueDict
 
 from teams.tests import TeamsAndRolesTests
 from stats.models import Module, Branch, Release, Category, Domain, Statistics
+from stats.tests import test_scratchdir
 from vertimus.models import *
 from vertimus.forms import ActionForm
 
@@ -38,30 +39,32 @@ class VertimusTest(TeamsAndRolesTests):
     def setUp(self):
         super(VertimusTest, self).setUp()
 
-        self.m = Module(name='gedit', description='GNOME Editor',
+        self.m = Module.objects.create(name='gedit', description='GNOME Editor',
             bugs_base="http://bugzilla.gnome.org/";,
             bugs_product='gedit', bugs_component='general',
             vcs_type='svn', vcs_root="http://svn.gnome.org/svn";,
             vcs_web="http://svn.gnome.org/viewvc/gedit";)
-        self.m.save()
 
         Branch.checkout_on_creation = False
         self.b = Branch(name='gnome-2-24', module=self.m)
         # Block the update of Statistics by the thread
         self.b.save(update_statistics=False)
 
-        self.r = Release(name='gnome-2-24', status='official',
+        self.r = Release.objects.create(name='gnome-2-24', status='official',
             description='GNOME 2.24 (stable)',
             string_frozen=True)
-        self.r.save()
 
-        self.c = Category(release=self.r, branch=self.b, name='desktop')
-        self.c.save()
+        self.c = Category.objects.create(release=self.r, branch=self.b, name='desktop')
 
-        self.d = Domain(module=self.m, name='po',
+        self.d = Domain.objects.create(module=self.m, name='po',
             description='UI translations',
             dtype='ui', directory='po')
-        self.d.save()
+        pot_stat = Statistics.objects.create(language=None, branch=self.b, domain=self.d)
+        self.files_to_clean = []
+
+    def tearDown(self):
+        for path in self.files_to_clean:
+            os.remove(path)
 
     def test_state_none(self):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
@@ -204,6 +207,7 @@ class VertimusTest(TeamsAndRolesTests):
         action.apply_on(state)
         self.assertTrue(isinstance(state, StateTranslating))
 
+    @test_scratchdir
     def test_action_ut(self):
         # Disabling the role
         role = Role.objects.get(person=self.pt, team=self.l.team)
@@ -213,11 +217,14 @@ class VertimusTest(TeamsAndRolesTests):
         state = StateTranslating(branch=self.b, domain=self.d, language=self.l, person=self.pt)
         state.save()
 
-        test_file = ContentFile('test content')
-        test_file.name = 'mytestfile.po'
-        
-        action = Action.new_by_name('UT', person=self.pt, comment="Done by translator.", file=test_file)
+        test_file = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "valid_po.po"), 'r')
+
+        action = Action.new_by_name('UT', person=self.pt, comment="Done by translator.", file=File(test_file))
         action.apply_on(state)
+        self.assertEqual(action.file.url, '/media/upload/gedit-gnome-2-24-po-fr-%d.po' % state.id)
+        self.assertEqual(action.merged_file.url(), '/media/upload/gedit-gnome-2-24-po-fr-%d.merged.po' % state.id)
+        self.files_to_clean.extend([action.file.path, action.merged_file.path])
+
         self.assertTrue(isinstance(state, StateTranslated))
         # Mail sent to mailing list
         self.assertEquals(len(mail.outbox), 1)
@@ -245,6 +252,7 @@ class VertimusTest(TeamsAndRolesTests):
 
         action = Action.new_by_name('UP', person=self.pr, comment="Done.", file=test_file)
         action.apply_on(state)
+        self.files_to_clean.append(action.file.path)
         self.assertTrue(isinstance(state, StateProofread))
 
     def test_action_tc(self):
@@ -374,9 +382,6 @@ class VertimusTest(TeamsAndRolesTests):
         self.assertEqual(Action.objects.all().count(), 0)
 
     def test_vertimus_view(self):
-        pot_stat = Statistics(language=None, branch=self.b, domain=self.d)
-        pot_stat.save()
-
         url = reverse('vertimus_by_ids', args=[self.b.id, self.d.id, self.l.id])
         response = self.client.get(url)
         self.assertNotContains(response, '<option value="WC">')
@@ -397,7 +402,6 @@ class VertimusTest(TeamsAndRolesTests):
         f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "valid_po.po"), 'r')
         post_file = MultiValueDict({'file': [File(f)]})
         form = ActionForm([('WC', u'Write a comment')], post_content, post_file)
-
         self.assert_(form.is_valid())
 
         # Test form without file
diff --git a/vertimus/views.py b/vertimus/views.py
index 590d3ef..e8d5553 100644
--- a/vertimus/views.py
+++ b/vertimus/views.py
@@ -147,7 +147,7 @@ def vertimus_diff(request, action_id_1, action_id_2, level):
     action_1 = get_object_or_404(ActionReal, pk=action_id_1)
     state = action_1.state_db
 
-    file_path_1 = action_1.merged_file()['path'] or action_1.file.path
+    file_path_1 = action_1.merged_file and action_1.merged_file.path or action_1.file.path
     reduced = is_po_reduced(file_path_1)
 
     try:
@@ -160,7 +160,7 @@ def vertimus_diff(request, action_id_1, action_id_2, level):
     if action_id_2 not in (None, "0"):
         # 1) id_2 specified in URL
         action_2 = get_object_or_404(ActionReal, pk=action_id_2)
-        file_path_2 = action_2.merged_file()['path'] or action_2.file.path
+        file_path_2 = action_2.merged_file and action_2.merged_file.path or action_2.file.path
         descr_2 = _("Uploaded file by %(name)s on %(date)s") % { 'name': action_2.person.name,
                                                                  'date': action_2.created }
     else:
@@ -170,7 +170,7 @@ def vertimus_diff(request, action_id_1, action_id_2, level):
             action_2 = action_1.get_previous_action_with_po()
 
         if action_2:
-            file_path_2 = action_2.merged_file()['path'] or action_2.file.path
+            file_path_2 = action_2.merged_file and action_2.merged_file.path or action_2.file.path
             descr_2 = _("Uploaded file by %(name)s on %(date)s") % { 'name': action_2.person.name,
                                                                      'date': action_2.created }
         else:
@@ -208,5 +208,4 @@ def latest_uploaded_po(request, module_name, branch_name, domain_name, locale_na
                                           file__endswith=".po").order_by('-created')[:1]
     if not latest_upload:
         raise Http404
-    merged_file = latest_upload[0].merged_file()
-    return HttpResponseRedirect(merged_file['url'])
+    return HttpResponseRedirect(latest_upload[0].merged_file.url())



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