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