class TaskGroup(WithContact, Timeboxed, Described, Titled, Slugged, Indexed, db.Model): """Workflow TaskGroup model.""" __tablename__ = 'task_groups' _title_uniqueness = False workflow_id = db.Column( db.Integer, db.ForeignKey('workflows.id', ondelete="CASCADE"), nullable=False, ) lock_task_order = db.Column(db.Boolean(), nullable=True) task_group_objects = db.relationship('TaskGroupObject', backref='task_group', cascade='all, delete-orphan') objects = association_proxy('task_group_objects', 'object', 'TaskGroupObject') task_group_tasks = db.relationship('TaskGroupTask', backref='task_group', cascade='all, delete-orphan') cycle_task_groups = db.relationship('CycleTaskGroup', backref='task_group') sort_index = db.Column(db.String(length=250), default="", nullable=False) _publish_attrs = [ 'workflow', 'task_group_objects', PublishOnly('objects'), 'task_group_tasks', 'lock_task_order', 'sort_index', # Intentionally do not include `cycle_task_groups` # 'cycle_task_groups', ] _aliases = { "title": "Summary", "description": "Details", "contact": { "display_name": "Assignee", "mandatory": True, }, "secondary_contact": None, "start_date": None, "end_date": None, "workflow": { "display_name": "Workflow", "mandatory": True, "filter_by": "_filter_by_workflow", }, "task_group_objects": { "display_name": "Objects", "type": AttributeInfo.Type.SPECIAL_MAPPING, "filter_by": "_filter_by_objects", }, } def copy(self, _other=None, **kwargs): columns = [ 'title', 'description', 'workflow', 'sort_index', 'modified_by', 'context' ] if kwargs.get('clone_people', False) and getattr(self, "contact"): columns.append("contact") else: kwargs["contact"] = get_current_user() target = self.copy_into(_other, columns, **kwargs) if kwargs.get('clone_objects', False): self.copy_objects(target, **kwargs) if kwargs.get('clone_tasks', False): self.copy_tasks(target, **kwargs) return target def copy_objects(self, target, **kwargs): # pylint: disable=unused-argument for task_group_object in self.task_group_objects: target.task_group_objects.append( task_group_object.copy( task_group=target, context=target.context, )) return target def copy_tasks(self, target, **kwargs): for task_group_task in self.task_group_tasks: target.task_group_tasks.append( task_group_task.copy( None, task_group=target, context=target.context, clone_people=kwargs.get("clone_people", False), )) return target @classmethod def _filter_by_workflow(cls, predicate): from ggrc_workflows.models import Workflow return Workflow.query.filter((Workflow.id == cls.workflow_id) & (predicate(Workflow.slug) | predicate(Workflow.title))).exists() @classmethod def _filter_by_objects(cls, predicate): parts = [] for model_name in all_models.__all__: model = getattr(all_models, model_name) query = getattr(model, "query", None) field = getattr(model, "slug", getattr(model, "email", None)) if query is None or field is None or not hasattr(model, "id"): continue parts.append( query.filter((TaskGroupObject.object_type == model_name) & (model.id == TaskGroupObject.object_id) & predicate(field)).exists()) return TaskGroupObject.query.filter( (TaskGroupObject.task_group_id == cls.id) & or_(*parts)).exists()
class TaskGroup(roleable.Roleable, relationship.Relatable, WithContact, Timeboxed, Described, Titled, base.ContextRBAC, Slugged, Indexed, db.Model): """Workflow TaskGroup model.""" __tablename__ = 'task_groups' _title_uniqueness = False workflow_id = db.Column( db.Integer, db.ForeignKey('workflows.id', ondelete="CASCADE"), nullable=False, ) lock_task_order = db.Column(db.Boolean(), nullable=True) task_group_objects = db.relationship('TaskGroupObject', backref='_task_group', cascade='all, delete-orphan') objects = association_proxy('task_group_objects', 'object', 'TaskGroupObject') task_group_tasks = db.relationship('TaskGroupTask', backref='_task_group', cascade='all, delete-orphan') cycle_task_groups = db.relationship('CycleTaskGroup', backref='task_group') sort_index = db.Column(db.String(length=250), default="", nullable=False) _api_attrs = reflection.ApiAttributes( 'workflow', 'task_group_objects', reflection.Attribute('objects', create=False, update=False), 'task_group_tasks', 'lock_task_order', 'sort_index', # Intentionally do not include `cycle_task_groups` # 'cycle_task_groups', ) _aliases = { "title": "Summary", "description": "Details", "contact": { "display_name": "Assignee", "mandatory": True, "description": ("One person could be added " "as a Task Group assignee") }, "secondary_contact": None, "start_date": None, "end_date": None, "workflow": { "display_name": "Workflow", "mandatory": True, "filter_by": "_filter_by_workflow", }, "task_group_objects": { "display_name": "Objects", "type": AttributeInfo.Type.SPECIAL_MAPPING, "filter_by": "_filter_by_objects", }, } # This parameter is overridden by workflow backref, but is here to ensure # pylint does not complain _workflow = None @hybrid.hybrid_property def workflow(self): """Getter for workflow foreign key.""" return self._workflow @workflow.setter def workflow(self, workflow): """Setter for workflow foreign key.""" if not self._workflow and workflow: all_models.Relationship(source=workflow, destination=self) self._workflow = workflow def ensure_assignee_is_workflow_member(self): # pylint: disable=invalid-name """Add Workflow Member role to user without role in scope of Workflow.""" people_with_role_ids = ( self.workflow.get_person_ids_for_rolename("Admin") + self.workflow.get_person_ids_for_rolename("Workflow Member")) if self.contact.id in people_with_role_ids: return self.workflow.add_person_with_role_name(self.contact, "Workflow Member") def copy(self, _other=None, **kwargs): columns = [ 'title', 'description', 'workflow', 'sort_index', 'modified_by', 'context' ] if kwargs.get('clone_people', False) and getattr(self, "contact"): columns.append("contact") else: kwargs["contact"] = get_current_user() target = self.copy_into(_other, columns, **kwargs) target.ensure_assignee_is_workflow_member() if kwargs.get('clone_objects', False): self.copy_objects(target, **kwargs) if kwargs.get('clone_tasks', False): self.copy_tasks(target, **kwargs) return target def copy_objects(self, target, **kwargs): # pylint: disable=unused-argument for task_group_object in self.task_group_objects: target.task_group_objects.append( task_group_object.copy( task_group=target, context=target.context, )) return target def copy_tasks(self, target, **kwargs): for task_group_task in self.task_group_tasks: target.task_group_tasks.append( task_group_task.copy( None, task_group=target, context=target.context, clone_people=kwargs.get("clone_people", False), )) return target @classmethod def eager_query(cls): query = super(TaskGroup, cls).eager_query() return query.options( orm.Load(cls).subqueryload('task_group_objects'), orm.Load(cls).subqueryload('task_group_tasks')) @classmethod def _filter_by_workflow(cls, predicate): from ggrc_workflows.models import Workflow return Workflow.query.filter((Workflow.id == cls.workflow_id) & (predicate(Workflow.slug) | predicate(Workflow.title))).exists() @classmethod def _filter_by_objects(cls, predicate): parts = [] for model_name in all_models.__all__: model = getattr(all_models, model_name) query = getattr(model, "query", None) field = getattr(model, "slug", getattr(model, "email", None)) if query is None or field is None or not hasattr(model, "id"): continue parts.append( query.filter((TaskGroupObject.object_type == model_name) & (model.id == TaskGroupObject.object_id) & predicate(field)).exists()) return TaskGroupObject.query.filter( (TaskGroupObject.task_group_id == cls.id) & or_(*parts)).exists()