Repository :
http://git.fedorahosted.org/cgit/copr.git
On branch : master
---------------------------------------------------------------
commit bd4a81bad1f13802b85a064a752c17dab4c94746
Author: Bohuslav Kabrda <bkabrda(a)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)