[copr] master: Introduce Action model for recording special actions (copr renaming/deleting/etc) (bd4a81b)

bkabrda at fedoraproject.org bkabrda at fedoraproject.org
Thu Feb 21 09:13:40 UTC 2013


Repository : http://git.fedorahosted.org/cgit/copr.git

On branch  : master

>---------------------------------------------------------------

commit bd4a81bad1f13802b85a064a752c17dab4c94746
Author: Bohuslav Kabrda <bkabrda at redhat.com>
Date:   Wed Feb 20 13:28:43 2013 +0100

    Introduce Action model for recording special actions (copr renaming/deleting/etc)


>---------------------------------------------------------------

 .../alembic/versions/544873aa3ba1_add_action.py    |   37 +++++++++++++++++++
 coprs_frontend/coprs/exceptions.py                 |    5 +++
 coprs_frontend/coprs/forms.py                      |    2 +-
 coprs_frontend/coprs/helpers.py                    |    8 ++++
 coprs_frontend/coprs/logic/coprs_logic.py          |   38 ++++++++++++++++----
 coprs_frontend/coprs/models.py                     |   30 +++++++++++++++
 .../coprs/views/coprs_ns/coprs_general.py          |   14 +++++--
 7 files changed, 122 insertions(+), 12 deletions(-)

diff --git a/coprs_frontend/alembic/versions/544873aa3ba1_add_action.py b/coprs_frontend/alembic/versions/544873aa3ba1_add_action.py
new file mode 100644
index 0000000..c3cf2ac
--- /dev/null
+++ b/coprs_frontend/alembic/versions/544873aa3ba1_add_action.py
@@ -0,0 +1,37 @@
+"""empty message
+
+Revision ID: 544873aa3ba1
+Revises: 1ee4b45f5476
+Create Date: 2013-02-20 13:20:34.778470
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '544873aa3ba1'
+down_revision = '1ee4b45f5476'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    ### commands auto generated by Alembic - please adjust! ###
+    op.create_table('action',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('action_type', sa.Integer(), nullable=False),
+    sa.Column('object_type', sa.String(length=20), nullable=True),
+    sa.Column('object_id', sa.Integer(), nullable=True),
+    sa.Column('old_value', sa.String(length=255), nullable=True),
+    sa.Column('new_value', sa.String(length=255), nullable=True),
+    sa.Column('backend_result', sa.Integer(), nullable=True),
+    sa.Column('backend_message', sa.Text(), nullable=True),
+    sa.Column('created_on', sa.Integer(), nullable=True),
+    sa.PrimaryKeyConstraint('id')
+    )
+    ### end Alembic commands ###
+
+
+def downgrade():
+    ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table('action')
+    ### end Alembic commands ###
diff --git a/coprs_frontend/coprs/exceptions.py b/coprs_frontend/coprs/exceptions.py
index 09888e2..da744b2 100644
--- a/coprs_frontend/coprs/exceptions.py
+++ b/coprs_frontend/coprs/exceptions.py
@@ -12,3 +12,8 @@ class DuplicateException(BaseException):
 
 class InsufficientRightsException(BaseException):
     pass
+
+class ActionInProgressException(BaseException):
+    def __init__(self, msg, action):
+        self.msg = msg
+        self.action = action
diff --git a/coprs_frontend/coprs/forms.py b/coprs_frontend/coprs/forms.py
index 6d14b60..5c80c33 100644
--- a/coprs_frontend/coprs/forms.py
+++ b/coprs_frontend/coprs/forms.py
@@ -41,7 +41,7 @@ class CoprUniqueNameValidator(object):
         self.message = message
 
     def __call__(self, form, field):
-        existing = coprs_logic.CoprsLogic.exists_for_current_user(flask.g.user, field.data).first()
+        existing = coprs_logic.CoprsLogic.exists_for_user(flask.g.user, field.data).first()
 
         if existing and str(existing.id) != form.id.data:
             raise wtf.ValidationError(self.message.format(field.data))
diff --git a/coprs_frontend/coprs/helpers.py b/coprs_frontend/coprs/helpers.py
index ac105ec..15e942e 100644
--- a/coprs_frontend/coprs/helpers.py
+++ b/coprs_frontend/coprs/helpers.py
@@ -35,6 +35,14 @@ class PermissionEnum(object):
     def choices_list(cls, without = -1):
         return [(n, k) for k, n in cls.vals.items() if n != without]
 
+class ActionTypeEnum(object):
+    __metaclass__ = EnumType
+    vals = {'delete': 0, 'rename': 1}
+
+class BackendResultEnum(object):
+    __metaclass__ = EnumType
+    vals = {'waiting': 0, 'success': 1, 'failure': 2}
+
 class Paginator(object):
     def __init__(self, query, total_count, page = 1, per_page_override = None, urls_count_override = None):
         self.query = query
diff --git a/coprs_frontend/coprs/logic/coprs_logic.py b/coprs_frontend/coprs/logic/coprs_logic.py
index aee017d..4b4e48c 100644
--- a/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/coprs_frontend/coprs/logic/coprs_logic.py
@@ -78,7 +78,7 @@ class CoprsLogic(object):
 
     @classmethod
     def new(cls, user, copr, check_for_duplicates = True):
-        if check_for_duplicates and cls.exists_for_current_user(user, copr.name).all():
+        if check_for_duplicates and cls.exists_for_user(user, copr.name).all():
             raise exceptions.DuplicateException(
                 'Copr: "{0}" already exists'.format(copr.name))
         signals.copr_created.send(cls, copr=copr)
@@ -86,15 +86,31 @@ class CoprsLogic(object):
 
     @classmethod
     def update(cls, user, copr, check_for_duplicates = True):
-        if check_for_duplicates and cls.exists_for_current_user(user, copr.name).all():
-            raise exceptions.DuplicateException(
-                'Copr: "{0}" already exists'.format(copr.name))
+        action = cls.unfinished_actions(user, copr).first()
+        if action:
+            raise exceptions.ActionInProgressException('Action in progress on this copr.', action)
+
+        existing = cls.exists_for_user(copr.owner, copr.name).first()
+        if existing:
+            if check_for_duplicates and existing.id != copr.id:
+                raise exceptions.DuplicateException('Copr: "{0}" already exists'.format(copr.name))
+        else: # we're renaming
+            # if we fire a models.Copr.query, it will use the modified copr in session
+            # -> workaround this by just getting the name
+            old_copr_name = db.session.query(models.Copr.name).filter(models.Copr.id==copr.id).first()[0]
+            action = models.Action(action_type='rename',
+                                   object_type='copr',
+                                   object_id=copr.id,
+                                   old_value='{0}/{1}'.format(copr.owner.name, old_copr_name),
+                                   new_value='{0}/{1}'.format(copr.owner.name, copr.name),
+                                   created_on=int(time.time()))
+            db.session.add(action)
         db.session.add(copr)
 
     @classmethod
-    def exists_for_current_user(cls, user, coprname):
-        existing = models.Copr.query.filter(models.Copr.name == coprname).\
-                                     filter(models.Copr.owner_id == user.id)
+    def exists_for_user(cls, user, coprname):
+        existing = models.Copr.query.filter(models.Copr.name==coprname).\
+                                     filter(models.Copr.owner_id==user.id)
 
         return existing
 
@@ -103,6 +119,14 @@ class CoprsLogic(object):
         models.Copr.query.filter(models.Copr.id == copr.id).\
                           update({models.Copr.build_count: models.Copr.build_count + 1})
 
+    @classmethod
+    def unfinished_actions(cls, user, copr):
+        actions = models.Action.query.filter(models.Action.object_type=='copr').\
+                                      filter(models.Action.object_id==copr.id).\
+                                      filter(models.Action.backend_result==0)
+
+        return actions
+
 class CoprPermissionsLogic(object):
     @classmethod
     def get(cls, user, copr, searched_user):
diff --git a/coprs_frontend/coprs/models.py b/coprs_frontend/coprs/models.py
index 0f81c85..8c20f86 100644
--- a/coprs_frontend/coprs/models.py
+++ b/coprs_frontend/coprs/models.py
@@ -1,4 +1,5 @@
 import datetime
+import time
 
 import sqlalchemy
 from sqlalchemy.ext.associationproxy import association_proxy
@@ -211,3 +212,32 @@ class CoprChroot(db.Model, Serializer):
     copr = db.relationship('Copr', backref = db.backref('copr_chroots',
                                                         single_parent=True,
                                                         cascade='all,delete,delete-orphan'))
+
+class Action(db.Model, Serializer):
+    id = db.Column(db.Integer, primary_key=True)
+    # delete, rename,
+    action_type = db.Column(db.Integer, nullable=False)
+    # copr,
+    object_type = db.Column(db.String(20))
+    object_id = db.Column(db.Integer)
+    old_value = db.Column(db.String(255))
+    new_value = db.Column(db.String(255))
+    backend_result = db.Column(db.Integer, default=helpers.BackendResultEnum('waiting'))
+    backend_message = db.Column(db.Text)
+    created_on = db.Column(db.Integer)
+
+    def __str__(self):
+        return self.__unicode__()
+
+    def __unicode__(self):
+        if self.action_type == 'delete':
+            return 'Deleting {0} {1}'.format(self.object_type, self.old_value)
+        elif self.action_type == 'rename':
+            return 'Renaming {0} from {1} to {2}.'.format(self.object_type,
+                                                          self.old_value,
+                                                          self.new_value)
+
+        return 'Action {0} on {1}, old value: {2}, new value: {3}.'.format(self.action_type,
+                                                                           self.object_type,
+                                                                           self.old_value,
+                                                                           self.new_value)
diff --git a/coprs_frontend/coprs/views/coprs_ns/coprs_general.py b/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
index fb00e4c..fc1a0ad 100644
--- a/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
+++ b/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
@@ -177,10 +177,16 @@ def copr_update(username, coprname):
         copr.instructions = form.instructions.data
         coprs_logic.CoprChrootsLogic.update_from_names(flask.g.user, copr, form.selected_chroots)
 
-        coprs_logic.CoprsLogic.update(flask.g.user, copr, check_for_duplicates = False) # form validation checks for duplicates
-        db.session.commit()
-        flask.flash('Copr was updated successfully.')
-        return flask.redirect(flask.url_for('coprs_ns.copr_detail', username = username, coprname = form.name.data))
+        try:
+            coprs_logic.CoprsLogic.update(flask.g.user, copr, check_for_duplicates = False) # form validation checks for duplicates
+        except exceptions.ActionInProgressException as e:
+            flask.flash('Can\'t change this Copr name, there is another operation in progress: {0}'.format(e.action))
+            db.session.rollback()
+        else:
+            flask.flash('Copr was updated successfully.')
+            db.session.commit()
+
+        return flask.redirect(flask.url_for('coprs_ns.copr_detail', username = username, coprname = copr.name))
     else:
         return copr_edit(username, coprname, form)
 



More information about the copr-devel mailing list