[damned-lies] [vertimus] Use proxy models for Action classes



commit 268a2e96f48d6dd553afefacd4c317b43d4ed96c
Author: Claude Paroz <claude 2xlibre net>
Date:   Sat May 21 16:52:22 2011 +0200

    [vertimus] Use proxy models for Action classes

 people/views.py                              |    2 +-
 stats/management/commands/run-maintenance.py |    4 +-
 stats/models.py                              |    4 +-
 templates/vertimus/vertimus_detail.html      |    2 +-
 vertimus/admin.py                            |    8 +-
 vertimus/feeds.py                            |   16 +-
 vertimus/forms.py                            |    4 +-
 vertimus/models.py                           |  593 ++++++++++++--------------
 vertimus/tests/__init__.py                   |  131 +++---
 vertimus/views.py                            |   30 +-
 10 files changed, 380 insertions(+), 414 deletions(-)
---
diff --git a/people/views.py b/people/views.py
index 428204a..e3fce1d 100644
--- a/people/views.py
+++ b/people/views.py
@@ -54,7 +54,7 @@ class PersonDetailView(DetailView):
 
     def get_context_data(self, **kwargs):
         context = super(PersonDetailView, self).get_context_data(**kwargs)
-        states = State.objects.filter(actiondb__person=self.object).distinct()
+        states = State.objects.filter(action__person=self.object).distinct()
         all_languages = [(lg[0], LANG_INFO.get(lg[0], {'name_local': lg[1]})['name_local']) for lg in settings.LANGUAGES]
         all_languages.sort(key=itemgetter(1))
         context.update({
diff --git a/stats/management/commands/run-maintenance.py b/stats/management/commands/run-maintenance.py
index 706c7ec..fc5cd70 100644
--- a/stats/management/commands/run-maintenance.py
+++ b/stats/management/commands/run-maintenance.py
@@ -2,7 +2,7 @@ from django.core.management.base import BaseCommand
 
 from people.models import Person
 from teams.models import Role
-from vertimus.models import ActionDbArchived
+from vertimus.models import ActionArchived
 from languages.views import clean_tar_files
 
 class Command(BaseCommand):
@@ -11,5 +11,5 @@ class Command(BaseCommand):
     def handle(self, *args, **options):
         Person.clean_unactivated_accounts()
         Role.inactivate_unused_roles()
-        ActionDbArchived.clean_old_actions(365)
+        ActionArchived.clean_old_actions(365)
         clean_tar_files()
diff --git a/stats/models.py b/stats/models.py
index 6171473..02dd7a8 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -1497,7 +1497,7 @@ class Statistics(models.Model):
             }
         """
         # Import here to prevent a circular dependency
-        from vertimus.models import State, ActionDb
+        from vertimus.models import State, Action
 
         if dtype.endswith('-part'):
             dtype = dtype[:-5]
@@ -1533,7 +1533,7 @@ class Statistics(models.Model):
         vt_states_dict = dict([("%d-%d" % (vt.branch.id, vt.domain.id),vt) for vt in vt_states])
 
         # Get comments from last action of State objects
-        actions = ActionDb.objects.filter(state_db__in=vt_states, comment__isnull=False).order_by('created')
+        actions = Action.objects.filter(state_db__in=vt_states, comment__isnull=False).order_by('created')
         actions_dict = dict([(act.state_db_id, act) for act in actions])
         for vt_state in vt_states_dict.values():
             if vt_state.id in actions_dict:
diff --git a/templates/vertimus/vertimus_detail.html b/templates/vertimus/vertimus_detail.html
index 566cc30..73c57e9 100644
--- a/templates/vertimus/vertimus_detail.html
+++ b/templates/vertimus/vertimus_detail.html
@@ -139,7 +139,7 @@ $(document).ready(function() {
       {% endif %}
       </div>
       <a href="{{ action.person.get_absolute_url }}">{{ action.person.name }}</a>
-      , <strong>{{ action }}</strong>, {{ action.created|date:_("Y-m-d g:i a O") }}
+      , <strong>{{ action.description }}</strong>, {{ action.created|date:_("Y-m-d g:i a O") }}
     </div>
     <div class="vertimus_action_content">
       {% if action.file %}
diff --git a/vertimus/admin.py b/vertimus/admin.py
index a24473f..b3a56eb 100644
--- a/vertimus/admin.py
+++ b/vertimus/admin.py
@@ -1,13 +1,15 @@
 # -*- coding: utf-8 -*-
 from django.contrib import admin
 
-from vertimus.models import State, ActionDb
+from vertimus.models import State, Action
 
 class StateAdmin(admin.ModelAdmin):
     raw_id_fields = ('branch', 'domain', 'person',)
 
-class ActionDbAdmin(admin.ModelAdmin):
+class ActionAdmin(admin.ModelAdmin):
     list_display = ('__unicode__', 'state_db')
+    raw_id_fields = ('state_db', 'person')
+    search_fields = ('comment',)
 
 admin.site.register(State, StateAdmin)
-admin.site.register(ActionDb, ActionDbAdmin)
+admin.site.register(Action, ActionAdmin)
diff --git a/vertimus/feeds.py b/vertimus/feeds.py
index 7015a24..cce91b0 100644
--- a/vertimus/feeds.py
+++ b/vertimus/feeds.py
@@ -25,7 +25,7 @@ from django.utils.translation import ugettext_lazy as _
 from django.contrib.sites.models import Site
 from languages.models import Language
 from teams.models import Team
-from vertimus.models import ActionDb, ActionDbArchived
+from vertimus.models import Action, ActionArchived
 from common.utils import imerge_sorted_by_field
 
 class LatestActionsByLanguage(Feed):
@@ -51,11 +51,11 @@ class LatestActionsByLanguage(Feed):
     def items(self, obj):
         # The Django ORM doesn't provide the UNION SQL feature :-(
         # so we need to fetch twice more objects than required
-        actions_db = ActionDb.objects.filter(state_db__language=obj.id).select_related('state').order_by('-created')[:20]
-        archived_actions_db = ActionDbArchived.objects.filter(state_db__language=obj.id).select_related('state').order_by('-created')[:20]
+        actions = Action.objects.filter(state_db__language=obj.id).select_related('state').order_by('-created')[:20]
+        archived_actions = ActionArchived.objects.filter(state_db__language=obj.id).select_related('state').order_by('-created')[:20]
 
         # islice avoid to fetch too many objects
-        return (action_db.get_action() for action_db in islice(imerge_sorted_by_field(actions_db, archived_actions_db, '-created'), 20))
+        return islice(imerge_sorted_by_field(actions, archived_actions, '-created'), 20)
 
     def item_link(self, item):
         link = urlresolvers.reverse('vertimus_by_names',
@@ -95,13 +95,11 @@ class LatestActionsByTeam(Feed):
     def items(self, obj):
         # The Django ORM doesn't provide the UNION SQL feature :-(
         # so we need to fetch twice more objects than required
-        actions_db = ActionDb.objects.filter(state_db__language__team=obj.id).select_related('state').order_by('-created')[:20]
-        archived_actions_db = ActionDbArchived.objects.filter(state_db__language__team=obj.id).select_related('state').order_by('-created')[:20]
+        actions = Action.objects.filter(state_db__language__team=obj.id).select_related('state').order_by('-created')[:20]
+        archived_actions = ActionArchived.objects.filter(state_db__language__team=obj.id).select_related('state').order_by('-created')[:20]
 
-        a = imerge_sorted_by_field(actions_db, archived_actions_db, '-created')
-        b = islice(imerge_sorted_by_field(actions_db, archived_actions_db, '-created'), 20)
         # islice avoid to fetch too many objects
-        return (action_db.get_action() for action_db in islice(imerge_sorted_by_field(actions_db, archived_actions_db, '-created'), 20))
+        return islice(imerge_sorted_by_field(actions, archived_actions, '-created'), 20)
 
     def item_link(self, item):
         link = urlresolvers.reverse('vertimus_by_names',
diff --git a/vertimus/forms.py b/vertimus/forms.py
index cb801d3..a660a73 100644
--- a/vertimus/forms.py
+++ b/vertimus/forms.py
@@ -23,7 +23,7 @@ import os
 
 from django import forms
 from django.utils.translation import ugettext_lazy as _
-from vertimus.models import ActionAbstract
+from vertimus.models import Action
 from stats.utils import po_file_stats
 
 class ActionWidget(forms.Select):
@@ -69,9 +69,9 @@ class ActionForm(forms.Form):
         action_code = cleaned_data.get('action')
         if action_code is None:
             raise forms.ValidationError(_("Invalid action. Someone probably posted another action just before you."))
-        action = ActionAbstract.new_by_name(action_code)
         comment = cleaned_data.get('comment')
         file = cleaned_data.get('file')
+        action = Action.new_by_name(action_code, comment=comment, file=file)
 
         if action.comment_is_required and not comment:
             raise forms.ValidationError(_("A comment is needed for this action."))
diff --git a/vertimus/models.py b/vertimus/models.py
index e9c2114..13dd9db 100644
--- a/vertimus/models.py
+++ b/vertimus/models.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 #
 # Copyright (c) 2008-2009 Stéphane Raimbault <stephane raimbault gmail com>
+# Copyright (c) 2011 Claude Paroz <claude 2xlibre net>
 #
 # This file is part of Damned Lies.
 #
@@ -81,8 +82,9 @@ class State(models.Model):
     def get_absolute_url(self):
         return ('vertimus_by_ids', [self.branch.id, self.domain.id, self.language.id])
 
-    def change_state(self, state_class):
+    def change_state(self, state_class, person=None):
         self.name = state_class.name
+        self.person = person
         self.__class__ = state_class
         self.save()
 
@@ -94,26 +96,12 @@ class State(models.Model):
                 action_names.append('AA')
         return [eval('Action' + action_name)() for action_name in action_names]
 
-    def apply_action(self, action, person, comment=None, file=None):
-        # Check the permission to use this action
-        if action.name in (a.name for a in self.get_available_actions(person)):
-            action.apply(self, person, comment, file)
-            if not isinstance(self, StateNone):
-                self.person = person
-                self.save()
-
-                if isinstance(self, StateCommitted):
-                    # Committed is the last state of the workflow, archive actions
-                    self.apply_action(ActionAA(), person)
-        else:
-            raise Exception('Not allowed')
-
     def get_action_sequence_from_level(self, level):
         """Get the sequence corresponding to the requested level.
            The first level is 1."""
         assert level > 0, "Level must be greater than 0"
 
-        query = self.actiondbarchived_set.all().values('sequence').distinct().order_by('-sequence')[level-1:level]
+        query = ActionArchived.objects.filter(state_db=self).values('sequence').distinct().order_by('-sequence')[level-1:level]
         sequence = None
         if len(query) > 0:
             sequence = query[0]['sequence']
@@ -282,14 +270,27 @@ class StateCommitted(State):
 #
 
 ACTION_NAMES = (
-    'WC',
-    'RT', 'UT',
-    'RP', 'UP',
-    'TC', 'CI', 'RC',
-    'IC', 'TR',
-    'AA', 'UNDO')
+    ('WC', _('Write a comment')),
+    ('RT', _('Reserve for translation')),
+    ('UT', _('Upload the new translation')),
+    ('RP', _('Reserve for proofreading')),
+    ('UP', _('Upload the proofread translation')),
+    # Translators: this means the file is ready to be committed in repository
+    ('TC', _('Ready for submission')),
+    ('CI', _('Submit to repository')),
+    # Translators: this indicates a committer is going to commit the file in the repository
+    ('RC', _('Reserve to submit')),
+    # Translators: this is used to indicate the file has been committed in the repository
+    ('IC', _('Inform of submission')),
+    # Translators: regardless of the translation completion, this file need to be reviewed
+    ('TR', _('Review required')),
+    ('AA', _('Archive the actions')),
+    ('UNDO', _('Undo the last state change')),
+)
 
 def generate_upload_filename(instance, filename):
+    if isinstance(instance, ActionArchived):
+        return "%s/%s" % (settings.UPLOAD_ARCHIVED_DIR, os.path.basename(filename))
     # Extract the first extension (with the point)
     root, ext = os.path.splitext(filename)
     # Check if a second extension is present
@@ -304,66 +305,132 @@ def generate_upload_filename(instance, filename):
         ext)
     return "%s/%s" % (settings.UPLOAD_DIR, new_filename)
 
-def action_db_get_action_history(cls, state_db=None, sequence=None):
-    """
-    Return action history as a list of tuples (action, file_history),
-    file_history is a list of previous po files, used in vertimus view to
-    generate diff links
-    """
-    history = []
-    if state_db or sequence:
-        file_history = [{'action_id':0, 'title': ugettext("File in repository")}]
-        if not sequence:
-            query = cls.objects.filter(state_db__id=state_db.id)
-        else:
-            # Not necessary to filter on state_db with a sequence (unique)
-            query = cls.objects.filter(sequence=sequence)
-        for action_db in query.order_by('id'):
-            history.append((action_db.get_action(), list(file_history)))
-            if action_db.file and action_db.file.path.endswith('.po'):
-                # Action.id and ActionDb.id are identical (inheritance)
-                file_history.insert(0, {
-                    'action_id': action_db.id,
-                    'title': ugettext("Uploaded file by %(name)s on %(date)s") % {
-                        'name': action_db.person.name,
-                        'date': action_db.created },
-                    })
-    return history
-
-class ActionDb(models.Model):
+class ActionAbstract(models.Model):
+    """ Common model for Action and ActionArchived """
     state_db = models.ForeignKey(State)
     person = models.ForeignKey(Person)
 
     name = models.SlugField(max_length=8)
-    description = None
-    created = models.DateTimeField(auto_now_add=True, editable=False)
+    created = models.DateTimeField(editable=False)
     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')
 
+    # A comment or a file is required
+    arg_is_required = False
+    comment_is_required = False
+    file_is_required = False
+    file_is_prohibited = False
+
+    class Meta:
+        abstract = True
+
+    def __unicode__(self):
+        return u"%s (%s) - %s" % (self.name, self.description, self.id)
+
+    @property
+    def description(self):
+        return dict(ACTION_NAMES).get(self.name, None)
+
+    def get_filename(self):
+        if self.file:
+            return os.path.basename(self.file.name)
+        else:
+            return None
+
+    def has_po_file(self):
+        try:
+            return self.file.name[-3:] == ".po"
+        except:
+            return False
+
+    @classmethod
+    def get_action_history(cls, state=None, sequence=None):
+        """
+        Return action history as a list of tuples (action, file_history),
+        file_history is a list of previous po files, used in vertimus view to
+        generate diff links
+        sequence argument is only valid on ActionArchived instances
+        """
+        history = []
+        if state or sequence:
+            file_history = [{'action_id':0, 'title': ugettext("File in repository")}]
+            if not sequence:
+                query = cls.objects.filter(state_db__id=state.id)
+            else:
+                # Not necessary to filter on state with a sequence (unique)
+                query = cls.objects.filter(sequence=sequence)
+            for action in query.order_by('id'):
+                history.append((action, list(file_history)))
+                if action.file and action.file.path.endswith('.po'):
+                    file_history.insert(0, {
+                        'action_id': action.id,
+                        'title': ugettext("Uploaded file by %(name)s on %(date)s") % {
+                            'name': action.person.name,
+                            'date': action.created },
+                        })
+        return history
+
+
+class Action(ActionAbstract):
     class Meta:
         db_table = 'action'
         verbose_name = 'action'
 
-    def __unicode__(self):
-        return "%s (%s)" % (self.name, self.id)
+    def __init__(self, *args, **kwargs):
+        super(Action, self).__init__(*args, **kwargs)
+        if self.name:
+            self.__class__ = eval('Action' + self.name)
+        else:
+            if getattr(self.__class__, 'name'):
+                self.name = self.__class__.name
+
+    @classmethod
+    def new_by_name(cls, action_name, **kwargs):
+         return eval('Action' + action_name)(**kwargs)
+
+    def save(self, *args, **kwargs):
+        if not self.id and not self.created:
+            self.created = datetime.today()
+        super(Action, self).save(*args, **kwargs)
 
-    def get_action(self):
-        action = eval('Action' + self.name)()
-        action._action_db = self
-        return action
+    def apply_on(self, state):
+        if not self in state.get_available_actions(self.person):
+            raise Exception('Not allowed')
+        self.state_db = state
+        if self.file:
+            self.file.save(self.file.name, self.file, save=False)
+        self.save()
+        if not isinstance(self, ActionWC):
+            # All actions change state except Writing a comment
+            self.state_db.change_state(self.target_state, self.person)
+            if self.target_state == StateCommitted:
+                # Committed is the last state of the workflow, archive actions
+                arch_action = self.new_by_name('AA', person=self.person)
+                arch_action.apply_on(self.state_db)
+
+    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}
 
-    def get_previous_action_db_with_po(self):
+    def get_previous_action_with_po(self):
         """
-        Return the previous ActionDb with an uploaded file related to the
+        Return the previous Action with an uploaded file related to the
         same state.
         """
         try:
-            action_db = ActionDb.objects.filter(file__endswith=".po", state_db=self.state_db,
+            action = Action.objects.filter(file__endswith=".po", state_db=self.state_db,
                 id__lt=self.id).latest('id')
-            return action_db
-        except ActionDb.DoesNotExist:
+            return action
+        except Action.DoesNotExist:
             return None
 
     def merge_file_with_pot(self, pot_file):
@@ -383,148 +450,6 @@ class ActionDb(models.Model):
                 po_grep(temp_path, merged_path, self.state_db.domain.red_filter)
                 os.remove(temp_path)
 
-    @classmethod
-    def get_action_history(cls, state_db):
-        return action_db_get_action_history(cls, state_db=state_db)
-
-def generate_archive_filename(instance, original_filename):
-    return "%s/%s" % (settings.UPLOAD_ARCHIVED_DIR, os.path.basename(original_filename))
-
-class ActionDbArchived(models.Model):
-    state_db = models.ForeignKey(State)
-    person = models.ForeignKey(Person)
-
-    name = models.SlugField(max_length=8)
-    # Datetime copied from ActionDb
-    created = models.DateTimeField(editable=False)
-    comment = models.TextField(blank=True, null=True)
-    file = models.FileField(upload_to=generate_archive_filename, blank=True, null=True)
-    # The first element of each cycle is null at creation time (and defined
-    # afterward).
-    sequence = models.IntegerField(null=True)
-
-    class Meta:
-        db_table = 'action_archived'
-
-    def __unicode__(self):
-        return "%s (%s)" % (self.name, self.id)
-
-    def get_action(self):
-        action = eval('Action' + self.name)()
-        action._action_db = self
-        return action
-
-    @classmethod
-    def get_action_history(cls, sequence):
-        return action_db_get_action_history(cls, state_db=None, sequence=sequence)
-
-    @classmethod
-    def clean_old_actions(cls, days):
-        """ Delete old archived actions after some (now-days) time """
-        # In each sequence, test date of the latest action, to delete whole sequences instead of individual actions
-        for action in ActionDbArchived.objects.values('sequence'
-            ).annotate(max_created=Max('created')
-            ).filter(max_created__lt=datetime.now()-timedelta(days=days)):
-            # Call each action delete() so as file is also deleted
-            for act in ActionDbArchived.objects.filter(sequence=action['sequence']):
-                act.delete()
-
-class ActionAbstract(object):
-    """Abstract class"""
-
-    # A comment or a file is required
-    arg_is_required = False
-    comment_is_required = False
-    file_is_required = False
-    file_is_prohibited = False
-
-    @classmethod
-    def new_by_name(cls, action_name):
-         return eval('Action' + action_name)()
-
-    @classmethod
-    def get_all(cls):
-        """Reserved to the admins to access all actions"""
-        return [eval('Action' + action_name)() for action_name in ACTION_NAMES]
-
-    @property
-    def id(self):
-        return self._action_db.id
-
-    @property
-    def person(self):
-        return self._action_db.person
-
-    @property
-    def comment(self):
-        return self._action_db.comment
-
-    @property
-    def created(self):
-        return self._action_db.created
-
-    @property
-    def file(self):
-        return self._action_db.file
-
-    @property
-    def state(self):
-        return self._action_db.state_db
-
-    def get_previous_action_with_po(self):
-        """
-        Return the previous Action with an uploaded file related to the same
-        state.
-        """
-        action_db = self._action_db.get_previous_action_db_with_po()
-        if action_db:
-            return action_db.get_action()
-        else:
-            return None
-
-    def save_action_db(self, state, person, comment=None, file=None):
-        """Used by apply"""
-        self._action_db = ActionDb(state_db=state, person=person,
-            name=self.name, comment=comment, file=file)
-        if file:
-            self._action_db.file.save(file.name, file, save=False)
-        self._action_db.save()
-
-        # Reactivating the role if needed
-        try:
-            role = person.role_set.get(team=state.language.team)
-            if not role.is_active:
-                role.is_active = True
-                role.save()
-        except Role.DoesNotExist:
-            pass
-
-    def __unicode__(self):
-        return unicode(self.description) # needs unicode() because description is lazy
-
-    def get_filename(self):
-        if self._action_db.file:
-            return os.path.basename(self._action_db.file.name)
-        else:
-            return None
-
-    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._action_db.file:
-            mfile_url = self._action_db.file.url[:-3] + ".merged.po"
-            mfile_path = self._action_db.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}
-
-    def has_po_file(self):
-        try:
-            return self._action_db.file.name[-3:] == ".po"
-        except:
-            return False
-
     def send_mail_new_state(self, state, recipient_list):
         # Remove None and empty string items from the list
         recipient_list = filter(lambda x: x and x is not None, recipient_list)
@@ -551,7 +476,7 @@ The new state of %(module)s - %(branch)s - %(domain)s (%(language)s) is now '%(n
                 'branch': state.branch.name,
                 'domain': state.domain.name,
                 'language': state.language.get_name(),
-                'new_state': state,
+                'new_state': state.description,
                 'url': url
             }
             message += self.comment or ugettext("Without comment")
@@ -561,17 +486,42 @@ The new state of %(module)s - %(branch)s - %(domain)s (%(language)s) is now '%(n
             activate(current_lang)
 
 
-class ActionWC(ActionAbstract):
+def generate_archive_filename(instance, original_filename):
+    return "%s/%s" % (settings.UPLOAD_ARCHIVED_DIR, os.path.basename(original_filename))
+
+class ActionArchived(ActionAbstract):
+    # The first element of each cycle is null at creation time (and defined
+    # afterward).
+    sequence = models.IntegerField(null=True)
+
+    class Meta:
+        db_table = 'action_archived'
+
+    @classmethod
+    def clean_old_actions(cls, days):
+        """ Delete old archived actions after some (now-days) time """
+        # In each sequence, test date of the latest action, to delete whole sequences instead of individual actions
+        for action in ActionArchived.objects.values('sequence'
+            ).annotate(max_created=Max('created')
+            ).filter(max_created__lt=datetime.now()-timedelta(days=days)):
+            # Call each action delete() so as file is also deleted
+            for act in ActionArchived.objects.filter(sequence=action['sequence']):
+                act.delete()
+
+
+class ActionWC(Action):
     name = 'WC'
-    description = _('Write a comment')
     comment_is_required = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
+
+    def apply_on(self, state):
+        super(ActionWC, self).apply_on(state)
 
         # Send an email to all translators of the page
         translator_emails = set()
-        for d in Person.objects.filter(actiondb__state_db=state).values('email'):
+        for d in Person.objects.filter(action__state_db=state).values('email'):
             translator_emails.add(d['email'])
 
         # Remove None items from the list
@@ -601,181 +551,179 @@ A new comment has been left on %(module)s - %(branch)s - %(domain)s (%(language)
                 'language': state.language.get_name(),
                 'url': url
             }
-            message += comment or ugettext("Without comment")
-            message += "\n\n" + person.name
+            message += self.comment or ugettext("Without comment")
+            message += "\n\n" + self.person.name
             message += "\n--\n" + _(u"This is an automated message sent from %s.") % current_site.domain
             mail.send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, translator_emails)
             activate(current_lang)
 
-class ActionRT(ActionAbstract):
+class ActionRT(Action):
     name = 'RT'
-    description = _('Reserve for translation')
     target_state = StateTranslating
     file_is_prohibited = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
-        state.change_state(self.target_state)
+    class Meta:
+        proxy = True
 
-class ActionUT(ActionAbstract):
+class ActionUT(Action):
     name = 'UT'
-    description = _('Upload the new translation')
     target_state = StateTranslated
     file_is_required = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
 
-        state.change_state(self.target_state)
+    def apply_on(self, state):
+        super(ActionUT, self).apply_on(state)
         self.send_mail_new_state(state, (state.language.team.mailing_list,))
 
-class ActionRP(ActionAbstract):
+class ActionRP(Action):
     name = 'RP'
-    description = _('Reserve for proofreading')
     target_state = StateProofreading
     file_is_prohibited = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
-        state.change_state(self.target_state)
+    class Meta:
+        proxy = True
 
-class ActionUP(ActionAbstract):
+class ActionUP(Action):
     name = 'UP'
-    description = _('Upload the proofread translation')
     target_state = StateProofread
     file_is_required = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
 
-        state.change_state(self.target_state)
+    def apply_on(self, state):
+        super(ActionUP, self).apply_on(state)
         self.send_mail_new_state(state, (state.language.team.mailing_list,))
 
-class ActionTC(ActionAbstract):
+class ActionTC(Action):
     name = 'TC'
-    # Translators: this means the file is ready to be committed in repository
-    description = _('Ready for submission')
     target_state = StateToCommit
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
 
-        state.change_state(self.target_state)
+    def apply_on(self, state):
+        super(ActionTC, self).apply_on(state)
         # Send an email to all committers of the team
         committers = [c.email for c in state.language.team.get_committers()]
         self.send_mail_new_state(state, committers)
 
-class ActionCI(ActionAbstract):
+class ActionCI(Action):
     name = 'CI'
-    description = _('Submit to repository')
     target_state = StateCommitted
     file_is_prohibited = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
+
+    def apply_on(self, state):
         action_with_po = self.get_previous_action_with_po()
         try:
-            state.branch.commit_po(action_with_po.file.path, state.domain, state.language, person)
-            state.change_state(self.target_state)
+            state.branch.commit_po(action_with_po.file.path, state.domain, state.language, self.person)
         except:
             # Commit failed, state unchanged
-            self._action_db.delete()
             # FIXME: somewhere the error should be catched and handled properly
             raise Exception(_("The commit failed. The error was: '%s'") % sys.exc_info()[1])
 
+        super(ActionCI, self).apply_on(state)
         self.send_mail_new_state(state, (state.language.team.mailing_list,))
 
-class ActionRC(ActionAbstract):
+class ActionRC(Action):
     name = 'RC'
-    # Translators: this indicates a committer is going to commit the file in the repository
-    description = _('Reserve to submit')
     target_state = StateCommitting
     file_is_prohibited = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
-        state.change_state(self.target_state)
+    class Meta:
+        proxy = True
 
-class ActionIC(ActionAbstract):
+class ActionIC(Action):
     name = 'IC'
-    # Translators: this is used to indicate the file has been committed in the repository
-    description = _('Inform of submission')
     target_state = StateCommitted
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
 
-        state.change_state(self.target_state)
+    def apply(self, state):
+        super(ActionIC, self).apply_on(state)
         self.send_mail_new_state(state, (state.language.team.mailing_list,))
 
-class ActionTR(ActionAbstract):
+class ActionTR(Action):
     name = 'TR'
-    # Translators: regardless of the translation completion, this file need to be reviewed
-    description = _('Review required')
     target_state = StateToReview
     arg_is_required = True
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
 
-        state.change_state(self.target_state)
+    def apply_on(self, state):
+        super(ActionTR, self).apply_on(state)
         self.send_mail_new_state(state, (state.language.team.mailing_list,))
 
-class ActionAA(ActionAbstract):
+class ActionAA(Action):
     name = 'AA'
-    description = _('Archive the actions')
     target_state = StateNone
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
 
-        actions_db = ActionDb.objects.filter(state_db=state).order_by('id').all()
+    def apply_on(self, state):
+        super(ActionAA, self).apply_on(state)
+        all_actions = Action.objects.filter(state_db=state).order_by('id').all()
 
         sequence = None
-        for action_db in actions_db:
+        for action in all_actions:
             file_to_archive = None
-            if action_db.file:
-                file_to_archive = action_db.file.file # get a file object, not a filefield
-            action_db_archived = ActionDbArchived(
-                state_db=action_db.state_db,
-                person=action_db.person,
-                name=action_db.name,
-                created=action_db.created,
-                comment=action_db.comment,
+            if action.file:
+                try:
+                    file_to_archive = action.file.file # get a file object, not a filefield
+                except IOError:
+                    pass
+            action_archived = ActionArchived(
+                state_db=action.state_db,
+                person=action.person,
+                name=action.name,
+                created=action.created,
+                comment=action.comment,
                 file=file_to_archive)
             if file_to_archive:
-                action_db_archived.file.save(action_db.file.name, file_to_archive, save=False)
+                action_archived.file.save(action.file.name, file_to_archive, save=False)
 
-            if sequence == None:
+            if sequence is None:
                 # The ID is available after the save()
-                action_db_archived.save()
-                sequence = action_db_archived.id
+                action_archived.save()
+                sequence = action_archived.id
 
-            action_db_archived.sequence = sequence
-            action_db_archived.save()
+            action_archived.sequence = sequence
+            action_archived.save()
 
-            action_db.delete() # The file is also automatically deleted, if it is not referenced elsewhere
-        state.change_state(self.target_state)
+            action.delete() # The file is also automatically deleted, if it is not referenced elsewhere
 
-class ActionUNDO(ActionAbstract):
+class ActionUNDO(Action):
     name = 'UNDO'
-    description = _('Undo the last state change')
 
-    def apply(self, state, person, comment=None, file=None):
-        self.save_action_db(state, person, comment, file)
+    class Meta:
+        proxy = True
+
+    def apply_on(self, state):
+        self.state_db = state
+        self.save()
 
         # Exclude WC because this action is a noop on State
-        actions_db = ActionDb.objects.filter(state_db__id=state.id).exclude(name='WC').order_by('-id')
-        i = 0
-        while (i < len(actions_db)):
-            if actions_db[i].name == 'UNDO':
+        actions = Action.objects.filter(state_db__id=state.id).exclude(name='WC').order_by('-id')
+        skip_next = False
+        for action in actions:
+            if skip_next:
+                skip_next = False
+                continue
+            if action.name == 'UNDO':
                 # Skip Undo and the associated action
-                i = i + 2
-            else:
-                # Found
-                action = actions_db[i].get_action()
-                state.change_state(action.target_state)
-                return
+                skip_next = True
+                continue
+            # Found action to revert
+            state.change_state(action.target_state, action.person)
+            return
         state.change_state(StateNone)
 
 class ActionSeparator(object):
@@ -783,20 +731,25 @@ class ActionSeparator(object):
     name = None
     description = "--------"
 
+#
+# Signal actions
+#
 def update_uploaded_files(sender, **kwargs):
     """Callback to handle pot_file_changed signal"""
-    actions = ActionDb.objects.filter(state_db__branch=kwargs['branch'],
-                                      state_db__domain=kwargs['domain'],
-                                      file__endswith=".po")
+    actions = Action.objects.filter(state_db__branch=kwargs['branch'],
+                                    state_db__domain=kwargs['domain'],
+                                    file__endswith=".po")
     for action in actions:
         action.merge_file_with_pot(kwargs['potfile'])
 pot_has_changed.connect(update_uploaded_files)
 
 def merge_uploaded_file(sender, instance, **kwargs):
     """
-    post_save callback for ActionDb that automatically merge uploaded file
+    post_save callback for Action that automatically merge uploaded file
     with latest pot file.
     """
+    if not isinstance(instance, Action):
+        return
     if instance.file and instance.file.path.endswith('.po'):
         try:
             stat = Statistics.objects.get(branch=instance.state_db.branch, domain=instance.state_db.domain, language=None)
@@ -804,22 +757,34 @@ def merge_uploaded_file(sender, instance, **kwargs):
             return
         potfile = stat.po_path()
         instance.merge_file_with_pot(potfile)
-post_save.connect(merge_uploaded_file, sender=ActionDb)
+post_save.connect(merge_uploaded_file)
 
 def delete_action_files(sender, instance, **kwargs):
     """
-    post_delete callback for ActionDb that deletes the file + the merged file from upload
+    post_delete callback for Action that deletes the file + the merged file from upload
     directory.
     """
-    if instance.file:
-        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 os.access(instance.file.path, os.W_OK):
-             os.remove(instance.file.path)
-post_delete.connect(delete_action_files, sender=ActionDb)
-post_delete.connect(delete_action_files, sender=ActionDbArchived)
+    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 os.access(instance.file.path, os.W_OK):
+         os.remove(instance.file.path)
+post_delete.connect(delete_action_files)
+
+def reactivate_role(sender, instance, **kwargs):
+    # Reactivating the role if needed
+    if not isinstance(instance, Action):
+        return
+    try:
+        role = instance.person.role_set.get(team=instance.state_db.language.team, is_active=False)
+        role.is_active = True
+        role.save()
+    except Role.DoesNotExist:
+        pass
+post_save.connect(reactivate_role)
 
 """ The following string is just reproduced from a template so as a translator comment
     can be added (comments are not supported in templates) """
diff --git a/vertimus/tests/__init__.py b/vertimus/tests/__init__.py
index 3c0a62a..71c6d23 100644
--- a/vertimus/tests/__init__.py
+++ b/vertimus/tests/__init__.py
@@ -190,8 +190,8 @@ class VertimusTest(TeamsAndRolesTests):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('WC')
-        state.apply_action(action, self.pt, "Hi!", None)
+        action = Action.new_by_name('WC', person=self.pt, comment="Hi!")
+        action.apply_on(state)
         # Test that submitting a comment without text generates a validation error
         form = ActionForm([('WC', u'Write a comment')], QueryDict('action=WC&comment='))
         self.assertTrue("A comment is needed" in str(form.errors))
@@ -200,8 +200,8 @@ class VertimusTest(TeamsAndRolesTests):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pt, "Reserved!", None)
+        action = Action.new_by_name('RT', person=self.pt, comment="Reserved!")
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateTranslating))
 
     def test_action_ut(self):
@@ -216,8 +216,8 @@ class VertimusTest(TeamsAndRolesTests):
         test_file = ContentFile('test content')
         test_file.name = 'mytestfile.po'
         
-        action = ActionAbstract.new_by_name('UT')
-        state.apply_action(action, self.pt, "Done by translator.", test_file)
+        action = Action.new_by_name('UT', person=self.pt, comment="Done by translator.", file=test_file)
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateTranslated))
         # Mail sent to mailing list
         self.assertEquals(len(mail.outbox), 1)
@@ -232,8 +232,8 @@ class VertimusTest(TeamsAndRolesTests):
         state = StateTranslated(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('RP')
-        state.apply_action(action, self.pr, "Reserved by a reviewer!")
+        action = Action.new_by_name('RP', person=self.pr, comment="Reserved by a reviewer!")
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateProofreading))
 
     def test_action_up(self):
@@ -243,24 +243,24 @@ class VertimusTest(TeamsAndRolesTests):
         test_file = ContentFile('test content')
         test_file.name = 'mytestfile.po'
 
-        action = ActionAbstract.new_by_name('UP')
-        state.apply_action(action, self.pr, "Done.", test_file)
+        action = Action.new_by_name('UP', person=self.pr, comment="Done.", file=test_file)
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateProofread))
 
     def test_action_tc(self):
         state = StateProofread(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('TC')
-        state.apply_action(action, self.pr, "Ready!")
+        action = Action.new_by_name('TC', person=self.pr, comment="Ready!")
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateToCommit))
 
     def test_action_rc(self):
         state = StateToCommit(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('RC')
-        state.apply_action(action, self.pc, "This work is mine!")
+        action = Action.new_by_name('RC', person=self.pc, comment="This work is mine!")
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateCommitting))
 
     def test_action_ic(self):
@@ -271,84 +271,85 @@ class VertimusTest(TeamsAndRolesTests):
         test_file = ContentFile('test content')
         test_file.name = 'mytestfile.po'
 
-        action = ActionAbstract.new_by_name('UP')
-        state.apply_action(action, self.pr, "Done.", test_file)
+        action = Action.new_by_name('UP', person=self.pr, comment="Done.", file=test_file)
+        action.apply_on(state)
 
         file_path = os.path.join(settings.MEDIA_ROOT, action.file.name)
         self.assertTrue(os.access(file_path, os.W_OK))
 
-        action = ActionAbstract.new_by_name('TC')
-        state.apply_action(action, self.pc, "To commit.")
+        action = Action.new_by_name('TC', person=self.pc, comment="To commit.")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('RC')
-        state.apply_action(action, self.pc, "Reserved commit.")
+        action = Action.new_by_name('RC', person=self.pc, comment="Reserved commit.")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('IC')
-        state.apply_action(action, self.pc, "Committed.")
+        action = Action.new_by_name('IC', person=self.pc, comment="Committed.")
+        action.apply_on(state)
 
         self.assertTrue(not os.access(file_path, os.F_OK), "%s not deleted" % file_path)
 
         # Remove test file
-        action_db_archived = ActionDbArchived.objects.get(comment="Done.")
-        filename_archived = os.path.join(settings.MEDIA_ROOT, action_db_archived.file.name)
-        action_db_archived.delete()
+        action_archived = ActionArchived.objects.get(comment="Done.")
+        filename_archived = os.path.join(settings.MEDIA_ROOT, action_archived.file.name)
+        action_archived.delete()
         self.assertTrue(not os.access(filename_archived, os.F_OK), "%s not deleted" % filename_archived)
 
     def test_action_tr(self):
         state = StateTranslated(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('TR')
-        state.apply_action(action, self.pc, "Bad work :-/")
+        action = Action.new_by_name('TR', person=self.pc, comment="Bad work :-/")
+        action.apply_on(state)
         self.assertTrue(isinstance(state, StateToReview))
 
-    def test_action_ba(self):
+    def test_action_aa(self):
         state = StateCommitted(branch=self.b, domain=self.d, language=self.l, person=self.pr)
         state.save()
 
-        action = ActionAbstract.new_by_name('AA')
-        state.apply_action(action, self.pc, comment="I don't want to disappear :)")
+        action = Action.new_by_name('AA', person=self.pc, comment="I don't want to disappear :)")
+        action.apply_on(state)
 
         state = State.objects.get(branch=self.b, domain=self.d, language=self.l)
         self.assertTrue(isinstance(state, StateNone))
+        self.assertEquals(state.action_set.count(), 0)
 
     def test_action_undo(self):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pt, "Reserved!")
+        action = Action.new_by_name('RT', person=self.pt, comment="Reserved!")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UNDO')
-        state.apply_action(action, self.pt, "Ooops! I don't want to do that. Sorry.")
+        action = Action.new_by_name('UNDO', person=self.pt, comment="Ooops! I don't want to do that. Sorry.")
+        action.apply_on(state)
 
         self.assertEqual(state.name, 'None')
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pt, "Translating")
+        action = Action.new_by_name('RT', person=self.pt, comment="Translating")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UT')
-        state.apply_action(action, self.pt, "Translated")
+        action = Action.new_by_name('UT', person=self.pt, comment="Translated")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pt, "Reserved!")
+        action = Action.new_by_name('RT', person=self.pt, comment="Reserved!")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UNDO')
-        state.apply_action(action, self.pt, "Ooops! I don't want to do that. Sorry.")
+        action = Action.new_by_name('UNDO', person=self.pt, comment="Ooops! I don't want to do that. Sorry.")
+        action.apply_on(state)
 
         self.assertEqual(state.name, 'Translated')
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pt, "Translating 1")
+        action = Action.new_by_name('RT', person=self.pt, comment="Translating 1")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UNDO')
-        state.apply_action(action, self.pt, "Undo 1")
+        action = Action.new_by_name('UNDO', person=self.pt, comment="Undo 1")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pt, "Translating 2")
+        action = Action.new_by_name('RT', person=self.pt, comment="Translating 2")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UNDO')
-        state.apply_action(action, self.pt, "Undo 2")
+        action = Action.new_by_name('UNDO', person=self.pt, comment="Undo 2")
+        action.apply_on(state)
 
         self.assertEqual(state.name, 'Translated')
 
@@ -388,29 +389,29 @@ class VertimusTest(TeamsAndRolesTests):
         state = StateNone(branch=self.b, domain=self.d, language=self.l)
         state.save()
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pr, "Reserved!")
+        action = Action.new_by_name('RT', person=self.pr, comment="Reserved!")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UNDO')
-        state.apply_action(action, self.pr, "Ooops! I don't want to do that. Sorry.")
+        action = Action.new_by_name('UNDO', person=self.pr, comment="Ooops! I don't want to do that. Sorry.")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('RT')
-        state.apply_action(action, self.pr, "Translating")
+        action = Action.new_by_name('RT', person=self.pr, comment="Translating")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UT')
-        state.apply_action(action, self.pr, "Translated")
+        action = Action.new_by_name('UT', person=self.pr, comment="Translated")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('RP')
-        state.apply_action(action, self.pr, "Proofreading")
+        action = Action.new_by_name('RP', person=self.pr, comment="Proofreading")
+        action.apply_on(state)
 
-        action = ActionAbstract.new_by_name('UNDO')
-        state.apply_action(action, self.pr, "Ooops! I don't want to do that. Sorry.")
+        action = Action.new_by_name('UNDO', person=self.pr, comment="Ooops! I don't want to do that. Sorry.")
+        action.apply_on(state)
 
-        actions_db = ActionDb.objects.filter(state_db__id=state.id).exclude(name='WC').order_by('-id')
+        actions_db = Action.objects.filter(state_db__id=state.id).exclude(name='WC').order_by('-id')
 
         # So the last action is UNDO
-        self.assert_(isinstance(actions_db[0].get_action(), ActionUNDO))
+        self.assert_(isinstance(actions_db[0], ActionUNDO))
 
         # Here be dragons! A call to len() workaround the Django/MySQL bug!
         len(actions_db)
-        self.assert_(isinstance(actions_db[0].get_action(), ActionUNDO))
+        self.assert_(isinstance(actions_db[0], ActionUNDO))
diff --git a/vertimus/views.py b/vertimus/views.py
index a04e038..f06e704 100644
--- a/vertimus/views.py
+++ b/vertimus/views.py
@@ -27,7 +27,7 @@ from django.utils.translation import ugettext as _
 
 from stats.models import Statistics, Module, Branch, Domain, Language
 from stats.utils import is_po_reduced
-from vertimus.models import State, ActionDb, ActionDbArchived, ActionAbstract
+from vertimus.models import State, Action, ActionArchived
 from vertimus.forms import ActionForm
 
 def vertimus_by_stats_id(request, stats_id, lang_id):
@@ -80,10 +80,10 @@ def vertimus(request, branch, domain, language, stats=None, level="0"):
 
     if level == 0:
         # Current actions
-        action_history = ActionDb.get_action_history(state)
+        action_history = Action.get_action_history(state=state)
     else:
         sequence = state.get_action_sequence_from_level(level)
-        action_history = ActionDbArchived.get_action_history(sequence)
+        action_history = ActionArchived.get_action_history(sequence=sequence)
 
     # Get the sequence of the grandparent to know if exists a previous action
     # history
@@ -105,9 +105,9 @@ def vertimus(request, branch, domain, language, stats=None, level="0"):
                 action = action_form.cleaned_data['action']
                 comment = action_form.cleaned_data['comment']
 
-                action = ActionAbstract.new_by_name(action)
-                state.apply_action(action, person, comment,
-                                   request.FILES.get('file', None))
+                action = Action.new_by_name(action, person=person, comment=comment,
+                    file=request.FILES.get('file', None))
+                action.apply_on(state)
 
                 return HttpResponseRedirect(
                     urlresolvers.reverse('vertimus_by_names',
@@ -143,10 +143,10 @@ def vertimus_diff(request, action_id_1, action_id_2, level):
     """Show a diff between current action po file and previous file"""
     import difflib
     if int(level) != 0:
-        ActionDbReal = ActionDbArchived
+        ActionReal = ActionArchived
     else:
-        ActionDbReal = ActionDb
-    action_1 = get_object_or_404(ActionDbReal, pk=action_id_1).get_action()
+        ActionReal = Action
+    action_1 = get_object_or_404(ActionReal, pk=action_id_1)
     state = action_1.state
 
     file_path_1 = action_1.merged_file()['path'] or action_1.file.path
@@ -161,7 +161,7 @@ def vertimus_diff(request, action_id_1, action_id_2, level):
                                                              'date': action_1.created }
     if action_id_2 not in (None, "0"):
         # 1) id_2 specified in URL
-        action_2 = get_object_or_404(ActionDbReal, pk=action_id_2).get_action()
+        action_2 = get_object_or_404(ActionReal, pk=action_id_2)
         file_path_2 = 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 }
@@ -204,11 +204,11 @@ def latest_uploaded_po(request, module_name, branch_name, domain_name, locale_na
     branch = get_object_or_404(Branch, module__name=module_name, name=branch_name)
     domain = get_object_or_404(Domain, module__name=module_name, name=domain_name)
     lang   = get_object_or_404(Language, locale=locale_name)
-    latest_upload = ActionDb.objects.filter(state_db__branch=branch,
-                                            state_db__domain=domain,
-                                            state_db__language=lang,
-                                            file__endswith=".po").order_by('-created')[:1]
+    latest_upload = Action.objects.filter(state_db__branch=branch,
+                                          state_db__domain=domain,
+                                          state_db__language=lang,
+                                          file__endswith=".po").order_by('-created')[:1]
     if not latest_upload:
         raise Http404
-    merged_file = latest_upload[0].get_action().merged_file()
+    merged_file = latest_upload[0].merged_file()
     return HttpResponseRedirect(merged_file['url'])



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