diff --git a/api/actions/serializers.py b/api/actions/serializers.py index 398bec892c9..027480a8354 100644 --- a/api/actions/serializers.py +++ b/api/actions/serializers.py @@ -149,13 +149,13 @@ def create(self, validated_data): visible = validated_data.get('visible', '') try: if trigger == DefaultTriggers.ACCEPT.db_name: - return target.run_accept(user=user, comment=comment, permissions=permissions, visible=visible) + target.accept(user=user, comment=comment, permissions=permissions, visible=visible) if trigger == DefaultTriggers.REJECT.db_name: - return target.run_reject(user, comment) + target.reject(user=user, comment=comment) if trigger == DefaultTriggers.EDIT_COMMENT.db_name: - return target.run_edit_comment(user, comment) + target.edit_comment(user=user, comment=comment) if trigger == DefaultTriggers.SUBMIT.db_name: - return target.run_submit(user) + target.submit(user=user, comment=comment) except InvalidTriggerError as e: # Invalid transition from the current state raise Conflict(str(e)) diff --git a/osf/models/mixins.py b/osf/models/mixins.py index 7981d95f82e..e9fbef94a7b 100644 --- a/osf/models/mixins.py +++ b/osf/models/mixins.py @@ -786,11 +786,18 @@ def get_extra_log_params(self, comment): class MachineableMixin(models.Model): - TriggersClass = DefaultTriggers class Meta: abstract = True + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.state_machine = self.MachineClass( + model=self, + active_state=self.state, + state_property_name='state' + ) + machine_state = models.CharField( max_length=15, db_index=True, @@ -799,35 +806,21 @@ class Meta: ) date_last_transitioned = models.DateTimeField(null=True, blank=True, db_index=True) - def validate_transition(self, transition_name, user, **kwargs): - """ - Run the specified state transition and create a corresponding Action. - - Params: - transition_name: The name of the transition to run. - user: The user triggering this transition. - kwargs: Additional parameters required by the transition. - """ - machine = self.MachineClass(self, 'machine_state') - transition = getattr(machine, transition_name)(user=user, **kwargs) - - if not transition: - valid_triggers = machine.get_triggers(self.machine_state) - raise InvalidTriggerError(transition_name, self.machine_state, valid_triggers) - - return machine.action - - def run_submit(self, user): - return self.validate_transition('submit', user=user) + @property + def MachineClass(self): + raise NotImplementedError() - def run_accept(self, user, comment, **kwargs): - return self.validate_transition('accept', user=user, comment=comment, **kwargs) + @property + def States(self): + raise NotImplementedError() - def run_reject(self, user, comment): - return self.validate_transition('reject', user=user, comment=comment) + @property + def state(self): + return self.States(self.machine_state) - def run_edit_comment(self, user, comment): - return self.validate_transition('edit_comment', user=user, comment=comment) + @state.setter + def state(self, new_state): + self.machine_state = new_state.db_name class NodeRequestableMixin(MachineableMixin): @@ -838,7 +831,14 @@ class NodeRequestableMixin(MachineableMixin): class Meta: abstract = True - MachineClass = NodeRequestMachine + @property + def MachineClass(self): + return NodeRequestMachine + + @property + def States(self): + return DefaultStates + class PreprintRequestableMixin(MachineableMixin): @@ -849,7 +849,13 @@ class PreprintRequestableMixin(MachineableMixin): class Meta: abstract = True - MachineClass = PreprintRequestMachine + @property + def MachineClass(self): + return PreprintRequestMachine + + @property + def States(self): + return DefaultStates class GuardianMixin(models.Model): diff --git a/osf/models/preprint.py b/osf/models/preprint.py index 3c4995d4ab5..e83ee015d21 100644 --- a/osf/models/preprint.py +++ b/osf/models/preprint.py @@ -45,7 +45,7 @@ from .base import BaseModel, GuidMixin, GuidMixinQuerySet from .identifiers import IdentifierMixin, Identifier -from .mixins import TaxonomizableMixin, ContributorMixin, SpamOverrideMixin, TitleMixin, DescriptionMixin +from .mixins import TaxonomizableMixin, ContributorMixin, SpamOverrideMixin, TitleMixin, DescriptionMixin, MachineableMixin from addons.osfstorage.models import OsfStorageFolder, Region, BaseFileNode, OsfStorageFile from framework.sentry import log_exception @@ -110,17 +110,18 @@ def can_view(self, base_queryset=None, user=None, allow_contribs=True, public_on return ret.distinct('id', 'created') if include_non_public else ret -class Preprint(DirtyFieldsMixin, GuidMixin, IdentifierMixin, BaseModel, TitleMixin, DescriptionMixin, +class Preprint(DirtyFieldsMixin, GuidMixin, MachineableMixin, IdentifierMixin, BaseModel, TitleMixin, DescriptionMixin, Loggable, Taggable, ContributorMixin, GuardianMixin, SpamOverrideMixin, TaxonomizableMixin): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + @property + def MachineClass(self): from osf.utils.machines import PreprintStateMachine - self.state_machine = PreprintStateMachine( - model=self, - active_state=self.state, - state_property_name='state' - ) + raise PreprintStateMachine + + @property + def States(self): + from osf.utils.workflows import PreprintStates + return PreprintStates machine_state = models.CharField( max_length=15, @@ -128,7 +129,6 @@ def __init__(self, *args, **kwargs): choices=PreprintStates.char_field_choices(), default=PreprintStates.INITIAL.db_name ) - date_last_transitioned = models.DateTimeField(null=True, blank=True, db_index=True) def _validate_state(self, ev): from django.core.exceptions import ValidationError diff --git a/osf/utils/workflows.py b/osf/utils/workflows.py index aca763b5848..56b97823e8e 100644 --- a/osf/utils/workflows.py +++ b/osf/utils/workflows.py @@ -268,32 +268,32 @@ class DefaultTriggers(ModerationEnum): DEFAULT_TRANSITIONS = [ { - 'trigger': 'submit', + 'trigger': 'run_submit', 'source': [DefaultStates.INITIAL.db_name], 'dest': DefaultStates.PENDING.db_name, 'after': ['save_action', 'update_last_transitioned', 'save_changes', 'notify_submit'], }, { - 'trigger': 'submit', + 'trigger': 'run_submit', 'source': [DefaultStates.PENDING.db_name, DefaultStates.REJECTED.db_name], 'conditions': 'resubmission_allowed', 'dest': DefaultStates.PENDING.db_name, 'after': ['save_action', 'update_last_transitioned', 'save_changes', 'notify_resubmit'], }, { - 'trigger': 'accept', + 'trigger': 'run_accept', 'source': [DefaultStates.PENDING.db_name, DefaultStates.REJECTED.db_name], 'dest': DefaultStates.ACCEPTED.db_name, 'after': ['save_action', 'update_last_transitioned', 'save_changes', 'notify_accept_reject'], }, { - 'trigger': 'reject', + 'trigger': 'run_reject', 'source': [DefaultStates.PENDING.db_name, DefaultStates.ACCEPTED.db_name], 'dest': DefaultStates.REJECTED.db_name, 'after': ['save_action', 'update_last_transitioned', 'save_changes', 'notify_accept_reject'], }, { - 'trigger': 'edit_comment', + 'trigger': 'run_edit_comment', 'source': [DefaultStates.PENDING.db_name, DefaultStates.REJECTED.db_name, DefaultStates.ACCEPTED.db_name], 'dest': '=', 'after': ['save_action', 'save_changes', 'notify_edit_comment'],