Пример #1
0
    def _set_attr_name_map(self):
        """ build a map for attributes names and display names

    Dict containing all display_name to attr_name mappings
    for all objects used in the current query
    Example:
        { Program: {"Program URL": "url", "Code": "slug", ...} ...}
    """
        self.attr_name_map = {}
        for object_query in self.query:
            object_name = object_query["object_name"]
            object_class = self.object_map[object_name]
            aliases = AttributeInfo.gather_aliases(object_class)
            self.attr_name_map[object_class] = {}
            for key, value in aliases.items():
                filter_by = None
                if isinstance(value, dict):
                    filter_name = value.get("filter_by", None)
                    if filter_name is not None:
                        filter_by = getattr(object_class, filter_name, None)
                    value = value["display_name"]
                if value:
                    self.attr_name_map[object_class][value.lower()] = (
                        key.lower(), filter_by)
            if not self.ca_disabled:
                custom_attrs = AttributeInfo.get_custom_attr_definitions(
                    object_class)
            else:
                custom_attrs = {}

            for key, definition in custom_attrs.items():
                if not key.startswith("__custom__:") or \
                   "display_name" not in definition:
                    continue
                try:
                    # Global custom attribute definition can only have a single id on
                    # their name, so it is safe for that. Currently the filters do not
                    # work with object level custom attributes.
                    attr_id = definition["definition_ids"][0]
                except KeyError:
                    continue
                filter_by = CustomAttributeValue.mk_filter_by_custom(
                    object_class, attr_id)
                name = definition["display_name"].lower()
                self.attr_name_map[object_class][name] = (name, filter_by)
Пример #2
0
    def _add_ca_value_dicts(self, values):
        """Add CA dict representations to _custom_attributes_values property.

    This adds or updates the _custom_attribute_values with the values in the
    custom attribute values serialized dictionary.

    Args:
      values: List of dictionaries that represent custom attribute values.
    """
        from ggrc.models.custom_attribute_value import CustomAttributeValue

        for value in values:
            if not value.get("attribute_object_id"):
                # value.get("attribute_object", {}).get("id") won't help because
                # value["attribute_object"] can be None
                value["attribute_object_id"] = (
                    value["attribute_object"].get("id")
                    if value.get("attribute_object") else None)

            attr = self._values_map.get(value.get("custom_attribute_id"))
            if attr:
                attr.attributable = self
                attr.attribute_value = value.get("attribute_value")
                attr.attribute_object_id = value.get("attribute_object_id")
            elif "custom_attribute_id" in value:
                # this is automatically appended to self._custom_attribute_values
                # on attributable=self
                CustomAttributeValue(
                    attributable=self,
                    custom_attribute_id=value.get("custom_attribute_id"),
                    attribute_value=value.get("attribute_value"),
                    attribute_object_id=value.get("attribute_object_id"),
                )
            elif "href" in value:
                # Ignore setting of custom attribute stubs. Getting here means that the
                # front-end is not using the API correctly and needs to be updated.
                logger.info("Ignoring post/put of custom attribute stubs.")
            else:
                raise BadRequest("Bad custom attribute value inserted")
Пример #3
0
    def custom_attributes(self, src):
        """Legacy setter for custom attribute values and definitions.

    This code should only be used for custom attribute definitions until
    setter for that is updated.
    """
        # pylint: disable=too-many-locals
        from ggrc.models.custom_attribute_value import CustomAttributeValue

        ca_values = src.get("custom_attribute_values")
        if ca_values and "attribute_value" in ca_values[0]:
            # This indicates that the new CA API is being used and the legacy API
            # should be ignored. If we need to use the legacy API the
            # custom_attribute_values property should contain stubs instead of entire
            # objects.
            return

        definitions = src.get("custom_attribute_definitions")
        if definitions is not None:
            self.process_definitions(definitions)

        attributes = src.get("custom_attributes")
        if not attributes:
            return

        old_values = collections.defaultdict(list)

        # attributes looks like this:
        #    [ {<id of attribute definition> : attribute value, ... }, ... ]

        # 1) Get all custom attribute values for the CustomAttributable instance
        attr_values = db.session.query(CustomAttributeValue).filter(
            and_(
                CustomAttributeValue.attributable_type ==
                self.__class__.__name__,
                CustomAttributeValue.attributable_id == self.id)).all()

        # Save previous value of custom attribute. This is a bit complicated by
        # the fact that imports can save multiple values at the time of writing.
        # old_values holds all previous values of attribute, last_values holds
        # chronologically last value.
        for value in attr_values:
            old_values[value.custom_attribute_id].append(
                (value.created_at, value.attribute_value))

        self._remove_existing_items(attr_values)

        # 4) Instantiate custom attribute values for each of the definitions
        #    passed in (keys)
        # pylint: disable=not-an-iterable
        # filter out attributes like Person:None
        attributes = {
            k: v
            for k, v in attributes.items() if v != "Person:None"
        }
        definitions = {
            d.id: d
            for d in self.get_custom_attribute_definitions()
        }
        for ad_id in attributes.keys():
            obj_type = self.__class__.__name__
            obj_id = self.id
            new_value = CustomAttributeValue(
                custom_attribute_id=int(ad_id),
                attributable=self,
                attribute_value=attributes[ad_id],
            )
            if definitions[int(ad_id)].attribute_type.startswith("Map:"):
                obj_type, obj_id = new_value.attribute_value.split(":")
                new_value.attribute_value = obj_type
                new_value.attribute_object_id = long(obj_id)
            elif definitions[int(ad_id)].attribute_type == "Checkbox":
                new_value.attribute_value = "1" if new_value.attribute_value else "0"
Пример #4
0
    def _add_ca_value_dicts(self, values):
        """Add CA dict representations to _custom_attributes_values property.

    This adds or updates the _custom_attribute_values with the values in the
    custom attribute values serialized dictionary.

    Args:
      values: List of dictionaries that represent custom attribute values.
    """
        from ggrc.utils import referenced_objects
        from ggrc.models.custom_attribute_value import CustomAttributeValue

        for value in values:
            # TODO: decompose to smaller methods
            # TODO: remove complicated nested conditions, better to use
            # instant exception raising
            if not value.get("attribute_object_id"):
                # value.get("attribute_object", {}).get("id") won't help because
                # value["attribute_object"] can be None
                value["attribute_object_id"] = (
                    value["attribute_object"].get("id")
                    if value.get("attribute_object") else None)

            attr = self._values_map.get(value.get("custom_attribute_id"))
            if attr:
                attr.attributable = self
                attr.attribute_value = value.get("attribute_value")
                attr.attribute_object_id = value.get("attribute_object_id")
            elif "custom_attribute_id" in value:
                # this is automatically appended to self._custom_attribute_values
                # on attributable=self
                custom_attribute_id = value.get("custom_attribute_id")
                custom_attribute = referenced_objects.get(
                    "CustomAttributeDefinition", custom_attribute_id)
                attribute_object = value.get("attribute_object")

                if attribute_object is None:
                    CustomAttributeValue(
                        attributable=self,
                        custom_attribute=custom_attribute,
                        custom_attribute_id=custom_attribute_id,
                        attribute_value=value.get("attribute_value"),
                        attribute_object_id=value.get("attribute_object_id"),
                    )
                elif isinstance(attribute_object, dict):
                    attribute_object_type = attribute_object.get("type")
                    attribute_object_id = attribute_object.get("id")

                    attribute_object = referenced_objects.get(
                        attribute_object_type, attribute_object_id)

                    cav = CustomAttributeValue(
                        attributable=self,
                        custom_attribute=custom_attribute,
                        custom_attribute_id=custom_attribute_id,
                        attribute_value=value.get("attribute_value"),
                        attribute_object_id=value.get("attribute_object_id"),
                    )
                    cav.attribute_object = attribute_object
                else:
                    raise BadRequest("Bad custom attribute value inserted")
            elif "href" in value:
                # Ignore setting of custom attribute stubs. Getting here means that the
                # front-end is not using the API correctly and needs to be updated.
                logger.info("Ignoring post/put of custom attribute stubs.")
            else:
                raise BadRequest("Bad custom attribute value inserted")
Пример #5
0
    def custom_attributes(self, src):
        """Legacy setter for custom attribute values and definitions.

    This code should only be used for custom attribute definitions until
    setter for that is updated.
    """
        from ggrc.models.custom_attribute_value import CustomAttributeValue
        from ggrc.services import signals

        ca_values = src.get("custom_attribute_values")
        if ca_values and "attribute_value" in ca_values[0]:
            # This indicates that the new CA API is being used and the legacy API
            # should be ignored. If we need to use the legacy API the
            # custom_attribute_values property should contain stubs instead of entire
            # objects.
            return

        definitions = src.get("custom_attribute_definitions")
        if definitions:
            self.process_definitions(definitions)

        attributes = src.get("custom_attributes")
        if not attributes:
            return

        old_values = collections.defaultdict(list)
        last_values = dict()

        # attributes looks like this:
        #    [ {<id of attribute definition> : attribute value, ... }, ... ]

        # 1) Get all custom attribute values for the CustomAttributable instance
        attr_values = db.session.query(CustomAttributeValue).filter(
            and_(
                CustomAttributeValue.attributable_type ==
                self.__class__.__name__,
                CustomAttributeValue.attributable_id == self.id)).all()

        # Save previous value of custom attribute. This is a bit complicated by
        # the fact that imports can save multiple values at the time of writing.
        # old_values holds all previous values of attribute, last_values holds
        # chronologically last value.
        for value in attr_values:
            old_values[value.custom_attribute_id].append(
                (value.created_at, value.attribute_value))

        last_values = {
            str(key): max(old_vals, key=lambda (created_at, _): created_at)
            for key, old_vals in old_values.iteritems()
        }

        self._remove_existing_items(attr_values)

        # 4) Instantiate custom attribute values for each of the definitions
        #    passed in (keys)
        # pylint: disable=not-an-iterable
        # filter out attributes like Person:None
        attributes = {
            k: v
            for k, v in attributes.items() if v != "Person:None"
        }
        definitions = {
            d.id: d
            for d in self.get_custom_attribute_definitions()
        }
        for ad_id in attributes.keys():
            obj_type = self.__class__.__name__
            obj_id = self.id
            new_value = CustomAttributeValue(
                custom_attribute_id=int(ad_id),
                attributable=self,
                attribute_value=attributes[ad_id],
            )
            if definitions[int(ad_id)].attribute_type.startswith("Map:"):
                obj_type, obj_id = new_value.attribute_value.split(":")
                new_value.attribute_value = obj_type
                new_value.attribute_object_id = long(obj_id)
            elif definitions[int(ad_id)].attribute_type == "Checkbox":
                new_value.attribute_value = "1" if new_value.attribute_value else "0"

            # 5) Set the context_id for each custom attribute value to the context id
            #    of the custom attributable.
            # TODO: We are ignoring contexts for now
            # new_value.context_id = cls.context_id

            # new value is appended to self.custom_attribute_values by the ORM
            # self.custom_attribute_values.append(new_value)
            if ad_id in last_values:
                _, previous_value = last_values[ad_id]
                if previous_value != attributes[ad_id]:
                    signals.Signals.custom_attribute_changed.send(
                        self.__class__,
                        obj=self,
                        src={
                            "type": obj_type,
                            "id": obj_id,
                            "operation": "UPDATE",
                            "value": new_value,
                            "old": previous_value
                        },
                        service=self.__class__.__name__)
            else:
                signals.Signals.custom_attribute_changed.send(
                    self.__class__,
                    obj=self,
                    src={
                        "type": obj_type,
                        "id": obj_id,
                        "operation": "INSERT",
                        "value": new_value,
                    },
                    service=self.__class__.__name__)
Пример #6
0
  def custom_attributes(self, src):
    """Legacy setter for custom attribute values and definitions.

    This code should only be used for custom attribute definitions until
    setter for that is updated.
    """
    # pylint: disable=too-many-locals
    from ggrc.models.custom_attribute_value import CustomAttributeValue

    ca_values = src.get("custom_attribute_values")
    if ca_values and "attribute_value" in ca_values[0]:
      # This indicates that the new CA API is being used and the legacy API
      # should be ignored. If we need to use the legacy API the
      # custom_attribute_values property should contain stubs instead of entire
      # objects.
      return

    definitions = src.get("custom_attribute_definitions")
    if definitions is not None:
      self.process_definitions(definitions)

    attributes = src.get("custom_attributes")
    if not attributes:
      return

    old_values = collections.defaultdict(list)

    # attributes looks like this:
    #    [ {<id of attribute definition> : attribute value, ... }, ... ]

    # 1) Get all custom attribute values for the CustomAttributable instance
    attr_values = db.session.query(CustomAttributeValue).filter(and_(
        CustomAttributeValue.attributable_type == self.__class__.__name__,
        CustomAttributeValue.attributable_id == self.id)).all()

    # Save previous value of custom attribute. This is a bit complicated by
    # the fact that imports can save multiple values at the time of writing.
    # old_values holds all previous values of attribute, last_values holds
    # chronologically last value.
    for value in attr_values:
      old_values[value.custom_attribute_id].append(
          (value.created_at, value.attribute_value))

    self._remove_existing_items(attr_values)

    # 4) Instantiate custom attribute values for each of the definitions
    #    passed in (keys)
    # pylint: disable=not-an-iterable
    # filter out attributes like Person:None
    attributes = {k: v for k, v in attributes.items() if v != "Person:None"}
    definitions = {d.id: d for d in self.get_custom_attribute_definitions()}
    for ad_id in attributes.keys():
      obj_type = self.__class__.__name__
      obj_id = self.id
      new_value = CustomAttributeValue(
          custom_attribute_id=int(ad_id),
          attributable=self,
          attribute_value=attributes[ad_id],
      )
      if definitions[int(ad_id)].attribute_type.startswith("Map:"):
        obj_type, obj_id = new_value.attribute_value.split(":")
        new_value.attribute_value = obj_type
        new_value.attribute_object_id = long(obj_id)
      elif definitions[int(ad_id)].attribute_type == "Checkbox":
        new_value.attribute_value = "1" if new_value.attribute_value else "0"
Пример #7
0
  def _add_ca_value_dicts(self, values):
    """Add CA dict representations to _custom_attributes_values property.

    This adds or updates the _custom_attribute_values with the values in the
    custom attribute values serialized dictionary.

    Args:
      values: List of dictionaries that represent custom attribute values.
    """
    from ggrc.utils import referenced_objects
    from ggrc.models.custom_attribute_value import CustomAttributeValue

    for value in values:
      if not value.get("attribute_object_id"):
        # value.get("attribute_object", {}).get("id") won't help because
        # value["attribute_object"] can be None
        value["attribute_object_id"] = (value["attribute_object"].get("id") if
                                        value.get("attribute_object") else
                                        None)

      attr = self._values_map.get(value.get("custom_attribute_id"))
      if attr:
        attr.attributable = self
        attr.attribute_value = value.get("attribute_value")
        attr.attribute_object_id = value.get("attribute_object_id")
      elif "custom_attribute_id" in value:
        # this is automatically appended to self._custom_attribute_values
        # on attributable=self
        custom_attribute_id = value.get("custom_attribute_id")
        custom_attribute = referenced_objects.get(
            "CustomAttributeDefinition", custom_attribute_id
        )
        attribute_object = value.get("attribute_object")

        if attribute_object is None:
          CustomAttributeValue(
              attributable=self,
              custom_attribute=custom_attribute,
              custom_attribute_id=custom_attribute_id,
              attribute_value=value.get("attribute_value"),
              attribute_object_id=value.get("attribute_object_id"),
          )
        elif isinstance(attribute_object, dict):
          attribute_object_type = attribute_object.get("type")
          attribute_object_id = attribute_object.get("id")

          attribute_object = referenced_objects.get(
              attribute_object_type, attribute_object_id
          )

          cav = CustomAttributeValue(
              attributable=self,
              custom_attribute=custom_attribute,
              custom_attribute_id=custom_attribute_id,
              attribute_value=value.get("attribute_value")
          )
          cav.attribute_object = attribute_object
        else:
          raise BadRequest("Bad custom attribute value inserted")
      elif "href" in value:
        # Ignore setting of custom attribute stubs. Getting here means that the
        # front-end is not using the API correctly and needs to be updated.
        logger.info("Ignoring post/put of custom attribute stubs.")
      else:
        raise BadRequest("Bad custom attribute value inserted")
Пример #8
0
  def custom_attributes(self, src):
    """Legacy setter for custom attribute values and definitions.

    This code should only be used for custom attribute definitions until
    setter for that is updated.
    """
    # pylint: disable=too-many-locals
    from ggrc.models.custom_attribute_value import CustomAttributeValue
    from ggrc.services import signals

    ca_values = src.get("custom_attribute_values")
    if ca_values and "attribute_value" in ca_values[0]:
      # This indicates that the new CA API is being used and the legacy API
      # should be ignored. If we need to use the legacy API the
      # custom_attribute_values property should contain stubs instead of entire
      # objects.
      return

    definitions = src.get("custom_attribute_definitions")
    if definitions is not None:
      self.process_definitions(definitions)

    attributes = src.get("custom_attributes")
    if not attributes:
      return

    old_values = collections.defaultdict(list)
    last_values = dict()

    # attributes looks like this:
    #    [ {<id of attribute definition> : attribute value, ... }, ... ]

    # 1) Get all custom attribute values for the CustomAttributable instance
    attr_values = db.session.query(CustomAttributeValue).filter(and_(
        CustomAttributeValue.attributable_type == self.__class__.__name__,
        CustomAttributeValue.attributable_id == self.id)).all()

    # Save previous value of custom attribute. This is a bit complicated by
    # the fact that imports can save multiple values at the time of writing.
    # old_values holds all previous values of attribute, last_values holds
    # chronologically last value.
    for value in attr_values:
      old_values[value.custom_attribute_id].append(
          (value.created_at, value.attribute_value))

    last_values = {str(key): max(old_vals,
                                 key=lambda (created_at, _): created_at)
                   for key, old_vals in old_values.iteritems()}

    self._remove_existing_items(attr_values)

    # 4) Instantiate custom attribute values for each of the definitions
    #    passed in (keys)
    # pylint: disable=not-an-iterable
    # filter out attributes like Person:None
    attributes = {k: v for k, v in attributes.items() if v != "Person:None"}
    definitions = {d.id: d for d in self.get_custom_attribute_definitions()}
    for ad_id in attributes.keys():
      obj_type = self.__class__.__name__
      obj_id = self.id
      new_value = CustomAttributeValue(
          custom_attribute_id=int(ad_id),
          attributable=self,
          attribute_value=attributes[ad_id],
      )
      if definitions[int(ad_id)].attribute_type.startswith("Map:"):
        obj_type, obj_id = new_value.attribute_value.split(":")
        new_value.attribute_value = obj_type
        new_value.attribute_object_id = long(obj_id)
      elif definitions[int(ad_id)].attribute_type == "Checkbox":
        new_value.attribute_value = "1" if new_value.attribute_value else "0"

      # 5) Set the context_id for each custom attribute value to the context id
      #    of the custom attributable.
      # TODO: We are ignoring contexts for now
      # new_value.context_id = cls.context_id

      # new value is appended to self.custom_attribute_values by the ORM
      # self.custom_attribute_values.append(new_value)
      if ad_id in last_values:
        _, previous_value = last_values[ad_id]
        if previous_value != attributes[ad_id]:
          signals.Signals.custom_attribute_changed.send(
              self.__class__,
              obj=self,
              src={
                  "type": obj_type,
                  "id": obj_id,
                  "operation": "UPDATE",
                  "value": new_value,
                  "old": previous_value
              }, service=self.__class__.__name__)
      else:
        signals.Signals.custom_attribute_changed.send(
            self.__class__,
            obj=self,
            src={
                "type": obj_type,
                "id": obj_id,
                "operation": "INSERT",
                "value": new_value,
            }, service=self.__class__.__name__)
Пример #9
0
  def custom_attributes(self, src):
    """Legacy setter for custom attribute values and definitions.

    This code should only be used for custom attribute definitions until
    setter for that is updated.
    """
    from ggrc.fulltext.mysql import MysqlRecordProperty
    from ggrc.models.custom_attribute_value import CustomAttributeValue
    from ggrc.services import signals

    ca_values = src.get("custom_attribute_values")
    if ca_values and "attribute_value" in ca_values[0]:
      # This indicates that the new CA API is being used and the legacy API
      # should be ignored. If we need to use the legacy API the
      # custom_attribute_values property should contain stubs instead of entire
      # objects.
      return

    definitions = src.get("custom_attribute_definitions")
    if definitions:
      self.process_definitions(definitions)

    attributes = src.get("custom_attributes")
    if not attributes:
      return

    old_values = collections.defaultdict(list)
    last_values = dict()

    # attributes looks like this:
    #    [ {<id of attribute definition> : attribute value, ... }, ... ]

    # 1) Get all custom attribute values for the CustomAttributable instance
    attr_values = db.session.query(CustomAttributeValue).filter(and_(
        CustomAttributeValue.attributable_type == self.__class__.__name__,
        CustomAttributeValue.attributable_id == self.id)).all()

    attr_value_ids = [value.id for value in attr_values]
    ftrp_properties = [
        "attribute_value_{id}".format(id=_id) for _id in attr_value_ids]

    # Save previous value of custom attribute. This is a bit complicated by
    # the fact that imports can save multiple values at the time of writing.
    # old_values holds all previous values of attribute, last_values holds
    # chronologically last value.
    for value in attr_values:
      old_values[value.custom_attribute_id].append(
          (value.created_at, value.attribute_value))

    last_values = {str(key): max(old_vals,
                                 key=lambda (created_at, _): created_at)
                   for key, old_vals in old_values.iteritems()}

    # 2) Delete all fulltext_record_properties for the list of values
    if len(attr_value_ids) > 0:
      db.session.query(MysqlRecordProperty)\
          .filter(
              and_(
                  MysqlRecordProperty.type == self.__class__.__name__,
                  MysqlRecordProperty.property.in_(ftrp_properties)))\
          .delete(synchronize_session='fetch')

      # 3) Delete the list of custom attribute values
      db.session.query(CustomAttributeValue)\
          .filter(CustomAttributeValue.id.in_(attr_value_ids))\
          .delete(synchronize_session='fetch')

      db.session.commit()

    # 4) Instantiate custom attribute values for each of the definitions
    #    passed in (keys)
    # pylint: disable=not-an-iterable
    definitions = {d.id: d for d in self.get_custom_attribute_definitions()}
    for ad_id in attributes.keys():
      obj_type = self.__class__.__name__
      obj_id = self.id
      new_value = CustomAttributeValue(
          custom_attribute_id=ad_id,
          attributable=self,
          attribute_value=attributes[ad_id],
      )
      if definitions[int(ad_id)].attribute_type.startswith("Map:"):
        obj_type, obj_id = new_value.attribute_value.split(":")
        new_value.attribute_value = obj_type
        new_value.attribute_object_id = long(obj_id)
      # 5) Set the context_id for each custom attribute value to the context id
      #    of the custom attributable.
      # TODO: We are ignoring contexts for now
      # new_value.context_id = cls.context_id
      self.custom_attribute_values.append(new_value)
      if ad_id in last_values:
        _, previous_value = last_values[ad_id]
        if previous_value != attributes[ad_id]:
          signals.Signals.custom_attribute_changed.send(
              self.__class__,
              obj=self,
              src={
                  "type": obj_type,
                  "id": obj_id,
                  "operation": "UPDATE",
                  "value": new_value,
                  "old": previous_value
              }, service=self.__class__.__name__)
      else:
        signals.Signals.custom_attribute_changed.send(
            self.__class__,
            obj=self,
            src={
                "type": obj_type,
                "id": obj_id,
                "operation": "INSERT",
                "value": new_value,
            }, service=self.__class__.__name__)