Ejemplo n.º 1
0
class BaseNotification(base.ContextRBAC, Base, db.Model):
    """Base notifications and notifications history model."""
    __abstract__ = True

    object_id = db.Column(db.Integer, nullable=False)
    object_type = db.Column(db.String, nullable=False)
    send_on = db.Column(db.DateTime, nullable=False)
    sent_at = db.Column(db.DateTime, nullable=True)
    custom_message = db.Column(db.Text, nullable=False, default=u"")
    force_notifications = db.Column(db.Boolean, default=False, nullable=False)
    repeating = db.Column(db.Boolean, nullable=False, default=False)
    object = utils.PolymorphicRelationship("object_id", "object_type",
                                           "{}_notifiable")

    @declared_attr
    def notification_type_id(cls):  # pylint: disable=no-self-argument
        return db.Column(db.Integer,
                         db.ForeignKey('notification_types.id'),
                         nullable=False)

    @declared_attr
    def notification_type(cls):  # pylint: disable=no-self-argument
        return db.relationship('NotificationType',
                               foreign_keys='{}.notification_type_id'.format(
                                   cls.__name__))
Ejemplo n.º 2
0
class Notification(Base, db.Model):
  __tablename__ = 'notifications'

  object_id = db.Column(db.Integer, nullable=False)
  object_type = db.Column(db.String, nullable=False)
  send_on = db.Column(db.DateTime, nullable=False)
  sent_at = db.Column(db.DateTime, nullable=True)
  custom_message = db.Column(db.Text, nullable=True)
  force_notifications = db.Column(db.Boolean, default=False, nullable=False)
  notification_type_id = db.Column(
      db.Integer, db.ForeignKey('notification_types.id'), nullable=False)
  notification_type = db.relationship(
      'NotificationType', foreign_keys='Notification.notification_type_id')

  object = utils.PolymorphicRelationship("object_id", "object_type",
                                         "{}_notifiable")
Ejemplo n.º 3
0
class ObjectLabel(base.ContextRBAC, Base, db.Model):
  """ObjectLabel Model."""
  __tablename__ = 'object_labels'

  label_id = db.Column(db.Integer, db.ForeignKey(Label.id),
                       nullable=False, primary_key=True)
  object_id = db.Column(db.Integer, nullable=False)
  object_type = db.Column(db.String, nullable=False)

  label = db.relationship(
      'Label',
      primaryjoin='remote(Label.id) == ObjectLabel.label_id',
  )
  labeled_object = utils.PolymorphicRelationship("object_id", "object_type",
                                                 "{}_labeled")

  _extra_table_args = [
      db.UniqueConstraint('label_id', 'object_id', 'object_type'),
  ]
Ejemplo n.º 4
0
class Comment(Roleable, Relatable, Described, Notifiable, base.ContextRBAC,
              Base, Indexed, db.Model):
    """Basic comment model."""
    __tablename__ = "comments"

    assignee_type = db.Column(db.String, nullable=False, default=u"")
    revision_id = deferred(
        db.Column(
            db.Integer,
            db.ForeignKey('revisions.id', ondelete='SET NULL'),
            nullable=True,
        ), 'Comment')
    revision = db.relationship(
        'Revision',
        uselist=False,
    )
    custom_attribute_definition_id = deferred(
        db.Column(
            db.Integer,
            db.ForeignKey('custom_attribute_definitions.id',
                          ondelete='SET NULL'),
            nullable=True,
        ), 'Comment')
    custom_attribute_definition = db.relationship(
        'CustomAttributeDefinition',
        uselist=False,
    )

    initiator_instance_id = db.Column(db.Integer, nullable=True)
    initiator_instance_type = db.Column(db.String, nullable=True)
    INITIATOR_INSTANCE_TMPL = "{}_comment_initiated_by"

    initiator_instance = utils.PolymorphicRelationship(
        "initiator_instance_id", "initiator_instance_type",
        INITIATOR_INSTANCE_TMPL)

    # REST properties
    _api_attrs = reflection.ApiAttributes(
        "assignee_type",
        reflection.Attribute("custom_attribute_revision",
                             create=False,
                             update=False),
        reflection.Attribute("custom_attribute_revision_upd", read=False),
        reflection.Attribute("header_url_link", create=False, update=False),
    )

    _sanitize_html = [
        "description",
    ]

    def get_objects_to_reindex(self):
        """Return list required objects for reindex if comment C.U.D."""
        source_qs = db.session.query(
            Relationship.destination_type, Relationship.destination_id).filter(
                Relationship.source_type == self.__class__.__name__,
                Relationship.source_id == self.id)
        destination_qs = db.session.query(
            Relationship.source_type, Relationship.source_id).filter(
                Relationship.destination_type == self.__class__.__name__,
                Relationship.destination_id == self.id)
        result_qs = source_qs.union(destination_qs)
        klass_dict = defaultdict(set)
        for klass, object_id in result_qs:
            klass_dict[klass].add(object_id)

        queries = []
        for klass, object_ids in klass_dict.iteritems():
            model = inflector.get_model(klass)
            if not model:
                continue
            if issubclass(model, (Indexed, Commentable)):
                queries.append(
                    model.query.filter(model.id.in_(list(object_ids))))
        return list(itertools.chain(*queries))

    AUTO_REINDEX_RULES = [
        ReindexRule("Comment", lambda x: x.get_objects_to_reindex()),
        ReindexRule("Relationship", reindex_by_relationship),
    ]

    @builder.simple_property
    def header_url_link(self):
        """Return header url link to comment if that comment related to proposal
    and that proposal is only proposed."""
        if self.initiator_instance_type != "Proposal":
            return ""
        proposed_status = self.initiator_instance.STATES.PROPOSED
        if self.initiator_instance.status == proposed_status:
            return "proposal_link"
        return ""

    @classmethod
    def eager_query(cls):
        query = super(Comment, cls).eager_query()
        return query.options(
            orm.joinedload('revision'),
            orm.joinedload('custom_attribute_definition').undefer_group(
                'CustomAttributeDefinition_complete'),
        )

    def log_json(self):
        """Log custom attribute revisions."""
        res = super(Comment, self).log_json()
        res["custom_attribute_revision"] = self.custom_attribute_revision
        return res

    @builder.simple_property
    def custom_attribute_revision(self):
        """Get the historical value of the relevant CA value."""
        if not self.revision:
            return None
        revision = self.revision.content
        cav_stored_value = revision['attribute_value']
        cad = self.custom_attribute_definition
        return {
            'custom_attribute': {
                'id': cad.id if cad else None,
                'title': cad.title if cad else 'DELETED DEFINITION',
            },
            'custom_attribute_stored_value': cav_stored_value,
        }

    def custom_attribute_revision_upd(self, value):
        """Create a Comment-CA mapping with current CA value stored."""
        ca_revision_dict = value.get('custom_attribute_revision_upd')
        if not ca_revision_dict:
            return
        ca_val_dict = self._get_ca_value(ca_revision_dict)

        ca_val_id = ca_val_dict['id']
        ca_val_revision = Revision.query.filter_by(
            resource_type='CustomAttributeValue',
            resource_id=ca_val_id,
        ).order_by(Revision.created_at.desc(), ).limit(1).first()
        if not ca_val_revision:
            raise BadRequest(
                "No Revision found for CA value with id provided under "
                "'custom_attribute_value': {}".format(ca_val_dict))

        self.revision_id = ca_val_revision.id
        self.revision = ca_val_revision

        # Here *attribute*_id is assigned to *definition*_id, strange but,
        # as you can see in src/ggrc/models/custom_attribute_value.py
        # custom_attribute_id is link to custom_attribute_definitions.id
        # possible best way is use definition id from request:
        # ca_revision_dict["custom_attribute_definition"]["id"]
        # but needs to be checked that is always exist in request
        self.custom_attribute_definition_id = ca_val_revision.content.get(
            'custom_attribute_id', )

        self.custom_attribute_definition = CustomAttributeDefinition.query.get(
            self.custom_attribute_definition_id, )

    @staticmethod
    def _get_ca_value(ca_revision_dict):
        """Get CA value dict from json and do a basic validation."""
        ca_val_dict = ca_revision_dict.get('custom_attribute_value')
        if not ca_val_dict:
            raise ValueError(
                "CA value expected under "
                "'custom_attribute_value': {}".format(ca_revision_dict))
        if not ca_val_dict.get('id'):
            raise ValueError(
                "CA value id expected under 'id': {}".format(ca_val_dict))
        return ca_val_dict
Ejemplo n.º 5
0
class IssuetrackerIssue(base.ContextRBAC, Base, db.Model):
    """Class representing IssuetrackerIssue."""

    __tablename__ = 'issuetracker_issues'

    object_id = db.Column(db.Integer, nullable=False)
    object_type = db.Column(db.String(250), nullable=False)
    enabled = db.Column(db.Boolean, nullable=False, default=False)

    title = db.Column(db.String(250), nullable=True)
    component_id = db.Column(db.String(50), nullable=True)
    hotlist_id = db.Column(db.String(50), nullable=True)
    issue_type = db.Column(db.String(50), nullable=True)
    issue_priority = db.Column(db.String(50), nullable=True)
    issue_severity = db.Column(db.String(50), nullable=True)
    assignee = db.Column(db.String(250), nullable=True)
    cc_list = db.Column(db.Text, nullable=False, default="")
    due_date = db.Column(db.Date, nullable=True)

    issue_id = db.Column(db.String(50), nullable=True)
    issue_url = db.Column(db.String(250), nullable=True)

    issue_tracked_obj = utils.PolymorphicRelationship("object_id",
                                                      "object_type",
                                                      "{}_issue_tracked")

    @classmethod
    def get_issue(cls, object_type, object_id):
        """Returns an issue object by given type and ID or None.

    Args:
      object_type: A string representing a model.
      object_id: An integer identifier of model's instance.

    Returns:
      An instance of IssuetrackerIssue or None.
    """
        return cls.query.filter(cls.object_type == object_type,
                                cls.object_id == object_id).first()

    def to_dict(self, include_issue=False, include_private=False):
        """Returns representation of object as a dict.

    Args:
      include_issue: A boolean whether to include issue related properties.
      include_private: A boolean whether to include private properties.

    Returns:
      A dict representing an instance of IssuetrackerIssue.
    """
        res = {
            'enabled': self.enabled,
            'component_id': self.component_id,
            'hotlist_id': self.hotlist_id,
            'issue_type': self.issue_type,
            'issue_priority': self.issue_priority,
            'issue_severity': self.issue_severity,
        }

        if include_issue:
            res['issue_id'] = self.issue_id
            res['issue_url'] = self.issue_url
            res['title'] = self.title

        if include_private:
            res['object_id'] = self.object_id
            res['object_type'] = self.object_type
            res['assignee'] = self.assignee
            res['cc_list'] = self.cc_list.split(',') if self.cc_list else []

        return res

    @classmethod
    def create_or_update_from_dict(cls, obj, info):
        """Creates or updates issue with given parameters.

    Args:
      obj: An object which is an IssueTracked instance.
      info: A dict with issue properties.

    Returns:
      An instance of IssuetrackerIssue.
    """
        if not info:
            raise ValueError('Issue tracker info cannot be empty.')

        issue_obj = cls.get_issue(obj.type, obj.id)

        info = dict(info, issue_tracked_obj=obj)
        if issue_obj is not None:
            issue_obj.update_from_dict(info)
        else:
            issue_obj = cls.create_from_dict(info)
            db.session.add(issue_obj)

        return issue_obj

    @classmethod
    def create_from_dict(cls, info):
        """Creates issue with given parameters.

    Args:
      info: A dict with issue properties.

    Returns:
      An instance of IssuetrackerIssue.
    """

        cc_list = info.get('cc_list')
        if cc_list is not None:
            cc_list = ','.join(cc_list)

        return cls(
            issue_tracked_obj=info['issue_tracked_obj'],
            enabled=bool(info.get('enabled')),
            title=info.get('title'),
            component_id=info.get('component_id'),
            hotlist_id=info.get('hotlist_id'),
            issue_type=info.get('issue_type'),
            issue_priority=info.get('issue_priority'),
            issue_severity=info.get('issue_severity'),
            assignee=info.get('assignee'),
            cc_list=cc_list,
            issue_id=info.get('issue_id'),
            issue_url=info.get('issue_url'),
        )

    def update_from_dict(self, info):
        """Updates issue with given parameters.

    Args:
      info: A dict with issue properties.

    Returns:
      An instance of IssuetrackerIssue.
    """
        cc_list = info.pop('cc_list', None)

        info = dict(self.to_dict(include_issue=True, include_private=True),
                    **info)

        if cc_list is not None:
            info['cc_list'] = cc_list

        if info['cc_list'] is not None:
            info['cc_list'] = ','.join(info['cc_list'])

        self.object_type = info['object_type']
        self.object_id = info['object_id']
        self.enabled = info['enabled']
        self.title = info['title']
        self.component_id = info['component_id']

        self.hotlist_id = info['hotlist_id']

        self.issue_type = info['issue_type']
        self.issue_priority = info['issue_priority']
        self.issue_severity = info['issue_severity']
        self.assignee = info['assignee']
        self.cc_list = info['cc_list']

        self.issue_id = info['issue_id']
        self.issue_url = info['issue_url']

        if info.get('due_date'):
            self.due_date = info.get('due_date')

    @staticmethod
    def get_issuetracker_issue_stub():
        """Returns dict with all Issue Tracker fields with empty values."""
        return {
            'enabled': False,
            'component_id': None,
            'hotlist_id': None,
            'issue_type': None,
            'issue_priority': None,
            'issue_severity': None,
            'title': None,
            'issue_id': None,
            'issue_url': None
        }