Пример #1
0
    def feature_behavior_from_behavior_template(self):
        """
            The method to create a FeatureBehavior from the template_feature_behavior.
            This might be a subclass instance of FeatureBehavior and/or preconfigured values
            Override FeatureBehavior.set_defaults to set preconfigured values

        """
        template_feature_behavior = self.subclassed_template_feature_behavior
        # Clone it, making a new intersection instance by removing the id
        # If the FeatureBehavior has no Intersection get it from the Behavior
        # Some behaviors, like background_imagery and result also have no Intersection
        intersection = template_feature_behavior.intersection_subclassed or self.intersection_subclassed
        if not intersection:
            raise Exception(
                "No intersection for the template FeatureBehavior resolved")
        intersection.id = 0
        logger.debug(
            "Intersection from template is %s of subclass %s" %
            (model_dict(intersection, include_primary_key=True) if intersection
             else 'None', intersection.__class__ if intersection else 'None'))
        feature_behavior = template_feature_behavior.__class__(**merge(
            model_dict(template_feature_behavior),
            dict(behavior=self, is_template=False, intersection=intersection)))
        # Set any defaults defined by the base or subclass
        feature_behavior.set_defaults()
        return feature_behavior
Пример #2
0
def update_or_create_layer_style(layer_style_configuration,
                                 style_key,
                                 existing_layer_style=None):
    """
        Creates a LayerStyle the StyleAttribute for cartocss styling.
        A Template is not specific to a Layer instance, rather specific to underlying Feature clss of the Layer
    :param layer_style_configuration: A dict() containing values specific to the model (i.e. the Feature subclass).
    This will be merged with layer_style that matches the subclass and 0 or more attributes of the Feature subclass
    that the Layer represents.
    :param style_key: The unique key of the template. Use the same key to share the template among instances
    :param existing_layer_style. The optional layer style class upon which the template key is named. If this is omitted,
    a generic template is created that doesn't load any predefined style info from the system.
        TODO There is no particular purpose for a Template based on only a db_entity_key at this point.
        The one based on a styled_class (Feature class) can load the template matching the class and attributes to
        provide default styling for the layer. We might have a case for having various generic default styling for a
        layer that is not based on a feature_class.
    :return:
    """
    logger.info("existing_layer_style %s" % model_dict(existing_layer_style))

    layer_style, created, updated = LayerStyle.objects.update_or_create(
        key=style_key,
        defaults=merge(
            # look first for whether the layer style exists and update otherwise create it
            model_dict(existing_layer_style)
            if existing_layer_style else dict(),
            dict(
                name=style_key,
                content_type=ContentTypeKey.CSS,
                # This represents the master version of the LayerStyle and will not change
                # unless the backend configuration is updated
                geometry_type=layer_style_configuration.get('geometry_type'),
                html_class=style_key)))

    # iterate over the configured style attribtues and update or create new instances and cartocss
    for style_attribute_config in layer_style_configuration.get(
            'style_attributes') or []:

        style_attribute, created, updated = StyleAttribute.objects.update_or_create(
            key=style_attribute_config.get('key')
            if style_attribute_config.get('key') else style_key + "__default",
            defaults=dict(name=style_attribute_config.get('name')
                          if style_attribute_config.get('name') else 'Default',
                          attribute=style_attribute_config.get('attribute'),
                          style_type=style_attribute_config.get('style_type'),
                          opacity=style_attribute_config.get('opacity') or 1,
                          style_value_contexts=style_attribute_config.get(
                              'style_value_contexts')))
        layer_style.style_attributes.add(style_attribute)
        layer_style.save()

    return layer_style
Пример #3
0
def update_or_create_layer_style(layer_style_configuration, style_key, existing_layer_style=None):
    """
        Creates a LayerStyle the StyleAttribute for cartocss styling.
        A Template is not specific to a Layer instance, rather specific to underlying Feature clss of the Layer
    :param layer_style_configuration: A dict() containing values specific to the model (i.e. the Feature subclass).
    This will be merged with layer_style that matches the subclass and 0 or more attributes of the Feature subclass
    that the Layer represents.
    :param style_key: The unique key of the template. Use the same key to share the template among instances
    :param existing_layer_style. The optional layer style class upon which the template key is named. If this is omitted,
    a generic template is created that doesn't load any predefined style info from the system.
        TODO There is no particular purpose for a Template based on only a db_entity_key at this point.
        The one based on a styled_class (Feature class) can load the template matching the class and attributes to
        provide default styling for the layer. We might have a case for having various generic default styling for a
        layer that is not based on a feature_class.
    :return:
    """
    logger.info("existing_layer_style %s" % model_dict(existing_layer_style))

    layer_style, created, updated = LayerStyle.objects.update_or_create(
        key=style_key,
        defaults=merge(
            # look first for whether the layer style exists and update otherwise create it
            model_dict(existing_layer_style) if existing_layer_style else dict(),
            dict(
                name=style_key,
                content_type=ContentTypeKey.CSS,
                # This represents the master version of the LayerStyle and will not change
                # unless the backend configuration is updated
                geometry_type=layer_style_configuration.get('geometry_type'),
                html_class=style_key
            )
        )
    )

    # iterate over the configured style attribtues and update or create new instances and cartocss
    for style_attribute_config in layer_style_configuration.get('style_attributes') or []:

        style_attribute, created, updated = StyleAttribute.objects.update_or_create(
            key=style_attribute_config.get('key') if style_attribute_config.get('key') else style_key + "__default",
            defaults=dict(
                name=style_attribute_config.get('name') if style_attribute_config.get('name') else 'Default',
                attribute=style_attribute_config.get('attribute'),
                style_type=style_attribute_config.get('style_type'),
                opacity=style_attribute_config.get('opacity') or 1,
                style_value_contexts=style_attribute_config.get('style_value_contexts')
            )
        )
        layer_style.style_attributes.add(style_attribute)
        layer_style.save()

    return layer_style
Пример #4
0
    def update_or_create_associations(self, behavior):
        # Update/Create template_feature_behavior and set it
        # This is done after the update because the template references back to the Behavior
        self.template_feature_behavior = self.update_or_create_template_feature_behavior(behavior._template_feature_behavior)
        self.name = titleize(BehaviorKey.Fab.remove(self.key))
        self.save()

        # Handle the manys that were embedded in the instance constructor
        # _parents are always configured without the BehaviorKey prefix
        if behavior._parents:
            self.parents.clear()
            self.parents.add(*Behavior.objects.filter(key__in=map(lambda parent: BehaviorKey.Fab.ricate(parent), behavior._parents)))
        if behavior._tags:
            self.tags.clear()
            self.tags.add(*map(lambda tag: Tag.objects.update_or_create(tag=tag.tag)[0], behavior._tags))

        # Add the a default tag that matches the key of the Behavior
        tag_key = BehaviorKey.Fab.remove(self.key)
        if not self.tags.filter(tag=tag_key):
            self.tags.add(Tag.objects.update_or_create(tag=tag_key)[0])

        # Inherit the intersection from the first parent if we don't have one
        # Default to a polygon to polygon GeographicIntersection
        intersection = behavior._intersection or\
                       (self.parents.all()[0].intersection_subclassed if\
                            self.parents.count() == 1 else\
                            GeographicIntersection.polygon_to_polygon)
        if not intersection:
            raise Exception("All behaviors must have a default intersection")
        # Use all attributes to find/create the Intersection instance, including nulls
        # Behaviors can't have specific Intersection properties, as FeatureBehaviors can, so it's safe to
        # share Intersection instances among them. The Intersection of a Behavior is really just the template
        # for that of a FeatureBehavior
        self.intersection = intersection.__class__.objects.get_or_create(**model_dict(intersection))[0]
        self.save()
Пример #5
0
def update_or_create_attribute_group(attribute_group):
    """
        Update or create a behavior based on the given behavior
    """

    return AttributeGroup.objects.update_or_create(
        key=attribute_group.key,
        defaults=model_dict(attribute_group, omit_fields=['key'])
    )[0]
Пример #6
0
 def update_layer_of_scenario(scenario):
     logger.info("Updating layer of db_entity_key %s, Scenario %s" % (db_entity_key, scenario.name))
     db_entity_interest = DbEntityInterest.objects.get(config_entity=scenario, db_entity__key=db_entity_key)
     return Layer.objects.update_or_create(
         db_entity_interest=db_entity_interest,
         defaults=merge(
             remove_keys(model_dict(template_layer), ['db_entity_key']),
         )
     )[0]
Пример #7
0
def update_or_create_attribute_group(attribute_group):
    """
        Update or create a behavior based on the given behavior
    """

    return AttributeGroup.objects.update_or_create(key=attribute_group.key,
                                                   defaults=model_dict(
                                                       attribute_group,
                                                       omit_fields=['key']))[0]
Пример #8
0
 def update_layer_of_scenario(scenario):
     logger.info("Updating layer of db_entity_key %s, Scenario %s" %
                 (db_entity_key, scenario.name))
     db_entity_interest = DbEntityInterest.objects.get(
         config_entity=scenario, db_entity__key=db_entity_key)
     return Layer.objects.update_or_create(
         db_entity_interest=db_entity_interest,
         defaults=merge(
             remove_keys(model_dict(template_layer),
                         ['db_entity_key']), ))[0]
Пример #9
0
def update_or_create_behavior(behavior):
    """
        Update or create a behavior based on the given behavior
    """

    updated_behavior = Behavior.objects.update_or_create(
        key=behavior.key,
        defaults=model_dict(behavior, omit_fields=['key', 'template_feature_behavior'])
    )[0]
    updated_behavior.update_or_create_associations(behavior)
    return behavior
Пример #10
0
    def feature_behavior_from_behavior_template(self):
        """
            The method to create a FeatureBehavior from the template_feature_behavior.
            This might be a subclass instance of FeatureBehavior and/or preconfigured values
            Override FeatureBehavior.set_defaults to set preconfigured values

        """
        template_feature_behavior = self.subclassed_template_feature_behavior
        # Clone it, making a new intersection instance by removing the id
        # If the FeatureBehavior has no Intersection get it from the Behavior
        # Some behaviors, like background_imagery and result also have no Intersection
        intersection = template_feature_behavior.intersection_subclassed or self.intersection_subclassed
        if not intersection:
            raise Exception("No intersection for the template FeatureBehavior resolved")
        intersection.id = 0
        logger.debug("Intersection from template is %s of subclass %s" % (model_dict(intersection, include_primary_key=True) if intersection else 'None', intersection.__class__ if intersection else 'None'))
        feature_behavior = template_feature_behavior.__class__(
            **merge(model_dict(template_feature_behavior),
                    dict(behavior=self, is_template=False, intersection=intersection))
        )
        # Set any defaults defined by the base or subclass
        feature_behavior.set_defaults()
        return feature_behavior
Пример #11
0
    def update_or_create_template_feature_behavior(self, template_feature_behavior):
        """
            Saves an unsaved template FeatureBehavior, marking it as a template
        """

        if template_feature_behavior and template_feature_behavior.id:
            return template_feature_behavior
        from footprint.main.models.geospatial.feature_behavior import FeatureBehavior
        from footprint.main.models.geospatial.db_entity import DbEntity
        return (template_feature_behavior.__class__ if template_feature_behavior else FeatureBehavior).objects.update_or_create(
            behavior=self,
            db_entity=DbEntity.objects.get(key='template_feature_behavior'),  # Special DbEntity
            is_template=True,
            defaults=model_dict(template_feature_behavior) if template_feature_behavior else {}
        )[0]
Пример #12
0
def resolve_layer_configuration(config_entity, db_entity_key, layer):
    """
        Creates a LayerConfiguration for imported layers using the template
        LayerConfigurations designed in the LayerConfigurationFixture.import_layer_configurations
    """
    from footprint.client.configuration.fixture import LayerConfigurationFixture
    from footprint.client.configuration.utils import resolve_fixture

    # if a layer exists and it has a layer configuration update it
    if layer and layer.configuration:
        layer_configuration = LayerConfiguration(**merge(
            dict(
                db_entity_key=layer.db_entity_key,
                layer_library_key=layer.key,
                visible=layer.visible,
                # Convert the LayerStyle to a dict, which is what a LayerConfiguration expects, and wipe out the ids
                layer_style=model_dict(layer.medium)),
            dict(
                built_form_set_key=layer.configuration.
                get('built_form_key', None),
                sort_priority=layer.configuration.get('sort_priority', None),
                attribute_sort_priority=layer.configuration.
                get('attribute_sort_priority', None),
                column_alias_lookup=layer.configuration.
                get('column_alias_lookup', None),
            ) if layer.configuration else dict()))
    # if the the layer configuration doesn't exist, utilize the default layer configuration
    else:
        client_layer_configuration = resolve_fixture(
            "presentation",
            "layer",
            LayerConfigurationFixture,
            config_entity.schema(),
            config_entity=config_entity)

        geometry_type = config_entity.db_entity_by_key(
            db_entity_key).geometry_type
        layer_configuration = copy.copy(
            list(
                client_layer_configuration.import_layer_configurations(
                    geometry_type))[0])
        # Update the template LayerConfiguration to our db_entity_key
        layer_configuration.db_entity_key = db_entity_key

    return layer_configuration
Пример #13
0
def resolve_layer_configuration(config_entity, db_entity_key, layer):
    """
        Creates a LayerConfiguration for imported layers using the template
        LayerConfigurations designed in the LayerConfigurationFixture.import_layer_configurations
    """
    from footprint.client.configuration.fixture import LayerConfigurationFixture
    from footprint.client.configuration.utils import resolve_fixture

    # if a layer exists and it has a layer configuration update it
    if layer and layer.configuration:
        layer_configuration = LayerConfiguration(
            **merge(
                dict(
                    db_entity_key=layer.db_entity_key,
                    layer_library_key=layer.key,
                    visible=layer.visible,
                    # Convert the LayerStyle to a dict, which is what a LayerConfiguration expects, and wipe out the ids
                    layer_style=model_dict(layer.medium)
                ),
                dict(
                    built_form_set_key=layer.configuration.get('built_form_key', None),
                    sort_priority=layer.configuration.get('sort_priority', None),
                    attribute_sort_priority=layer.configuration.get('attribute_sort_priority', None),
                    column_alias_lookup=layer.configuration.get('column_alias_lookup', None),
                ) if layer.configuration else dict()
            )
        )
    # if the the layer configuration doesn't exist, utilize the default layer configuration
    else:
        client_layer_configuration = resolve_fixture(
            "presentation",
            "layer",
            LayerConfigurationFixture,
            config_entity.schema(),
            config_entity=config_entity)

        geometry_type = config_entity.db_entity_by_key(db_entity_key).geometry_type
        layer_configuration = copy.copy(list(client_layer_configuration.import_layer_configurations(geometry_type))[0])
        # Update the template LayerConfiguration to our db_entity_key
        layer_configuration.db_entity_key = db_entity_key

    return layer_configuration
Пример #14
0
def update_or_create_db_entity_and_interest(config_entity, config_db_entity):
    """
        Sync a single db_entity_configuration or db_entity and its db_entity_interest
        :return A tuple of the DbEntityInterest and the created flag
    """
    unique_key_combo = ['key', 'schema']

    db_entity, created, updated = DbEntity.objects.update_or_create(
        # key and schema uniquely identify the DbEntity
        key=config_db_entity.key,
        schema=config_db_entity.schema,
        defaults=remove_keys(model_dict(config_db_entity), unique_key_combo))

    db_entity.feature_behavior = config_db_entity.feature_behavior
    db_entity.save()

    logger.info("ConfigEntity/DbEntity Publishing. DbEntity: %s" %
                db_entity.full_name)

    # Create the DbEntityInterest through class instance which associates the ConfigEntity instance
    # to the DbEntity instance. For now the interest attribute is hard-coded to OWNER. This might
    # be used in the future to indicate other levels of interest
    interest = Interest.objects.get(key=Keys.INTEREST_OWNER)
    db_entity_interest, created, updated = DbEntityInterest.objects.update_or_create(
        config_entity=config_entity, db_entity=db_entity, interest=interest)

    #update the geography scope after the db_entity_interest saves as this is required to find 'owned' db_entites in a config entity
    if not db_entity.no_feature_class_configuration:
        feature_class_creator = FeatureClassCreator(config_entity,
                                                    db_entity,
                                                    no_ensure=True)
        db_entity.feature_class_configuration.geography_scope = config_entity.id if db_entity.feature_class_configuration.primary_geography \
            else feature_class_creator.resolved_geography_scope.id

        db_entity.save()

    return db_entity_interest, created
Пример #15
0
def update_or_create_db_entity_and_interest(config_entity, config_db_entity):
    """
        Sync a single db_entity_configuration or db_entity and its db_entity_interest
        :return A tuple of the DbEntityInterest and the created flag
    """
    unique_key_combo = ['key', 'schema']

    db_entity, created, updated = DbEntity.objects.update_or_create(
        # key and schema uniquely identify the DbEntity
        key=config_db_entity.key,
        schema=config_db_entity.schema,
        defaults=remove_keys(model_dict(config_db_entity), unique_key_combo))

    db_entity.feature_behavior = config_db_entity.feature_behavior
    db_entity.save()

    logger.info("ConfigEntity/DbEntity Publishing. DbEntity: %s" % db_entity.full_name)

    # Create the DbEntityInterest through class instance which associates the ConfigEntity instance
    # to the DbEntity instance. For now the interest attribute is hard-coded to OWNER. This might
    # be used in the future to indicate other levels of interest
    interest = Interest.objects.get(key=Keys.INTEREST_OWNER)
    db_entity_interest, created, updated = DbEntityInterest.objects.update_or_create(
        config_entity=config_entity,
        db_entity=db_entity,
        interest=interest)

    #update the geography scope after the db_entity_interest saves as this is required to find 'owned' db_entites in a config entity
    if not db_entity.no_feature_class_configuration:
        feature_class_creator = FeatureClassCreator(config_entity, db_entity, no_ensure=True)
        db_entity.feature_class_configuration.geography_scope = config_entity.id if db_entity.feature_class_configuration.primary_geography \
            else feature_class_creator.resolved_geography_scope.id

        db_entity.save()

    return db_entity_interest, created
Пример #16
0
                raise Exception("Attempt to change locked behavior on DbEntity %s from Behavior %s to Behavior %s" %
                                (self.full_name, self.feature_behavior.behavior.name, behavior.name))
            existing_feature_behavior.delete()
            existing_feature_behavior = None

        existing_intersection = None
        if existing_feature_behavior:
            # If we still have a valid existing feature_behavior have it conform to anything about the Behavior
            # configuration that might have changed.
            updated_existing_feature_behavior = existing_feature_behavior
            template_feature_behavior = behavior.feature_behavior_from_behavior_template()
            existing_intersection = existing_feature_behavior.intersection_subclassed
            # If cloning set the id to None
            if existing_intersection and not updated_existing_feature_behavior.id:
                existing_intersection.id = None
            logger.debug("Existing Intersection: %s" % model_dict(existing_intersection, include_primary_key=True))
            # TODO this seems problematic. The existing FeatureBehavior's tags should take precedence over the
            # Behavior's unless the former has no tags
            updated_existing_feature_behavior._tags = template_feature_behavior._tags
        else:
            # Get a new instance from the Behavior
            updated_existing_feature_behavior = behavior.feature_behavior_from_behavior_template()
        updated_existing_feature_behavior.set_defaults()

        # Intersection properties are defined on the Behavior and possibly extended or overridden on the FeatureBehavior
        # Every FeatureBehavior has its own Intersection instance so that we can customize the intersection for
        # the DbEntity. We always remove the is_template property that might have come the Behavior's Intersection
        intersection_dict = remove_keys(
            merge(
                model_dict(updated_existing_feature_behavior.intersection_subclassed),
                model_dict(configured_feature_behavior.intersection_subclassed),
Пример #17
0
def clone_or_update_db_entity_and_interest(config_entity, source_db_entity, db_entity, existing_db_entity_interest=None, override_on_update=False):
    """
        Clones or updates the source_db_entity modified with the given kwargs (including possibly the key) into this ConfigEntity.
        This is used for a duplicate clone from one ConfigEntity (same DbEntity key) to another and also for
        creating a modified DbEntity for a Result from a non-Result DbEntity (different DbEntity key). A third case for this
        method is cloning a DbEntity within a ConfigEntity, which is not yet implemented.

        If the kwargs['override_on_update'] is True, the kwargs should override the target DbEntity attribute values on update.
        This is useful for the Result clone case where we want to pick up updates to the source DbEntity. But in the straight
        clone case we want to make the target DbEntity independent of the source once it is created.
        Returns the DbEntityInterest

        :param: config_entity. The config_entity of the DbEntities
        :param: Optional source_db_entity. The source of the clone
        :param: db_entity. DbEntity of attributes matching the DbEntity that need to override those of the source_db_entity. This might be the
        key, schema, etc.
        Also for db_entity.feature_class_owner, default None, is passed in for Results that are copying
        a reference DbEntity's feature_class_configuration with the db_entity key of the onwer. We don't want the result version of the feature_class_configuration
        to be used to create the feature_class tables, because that would make foreign key references point to the
        wrong feature class parents (which are named by the DbEntity id). phwewww!
        :param: existing_db_entity_interest: Optional DbEntityInterest if it already exists for the clone
        :param: override_on_update: Default False, optional and is described above.
    """

    # Do a forced adoption of DbEntityInterests from the parent ConfigEntity. This makes sure that ConfigEntity has
    # the parent's DbEntityInterests before adding any of its own. Otherwise the parent's are never adopted and
    # are created from the db_entity_configurations instead, which is minimally less efficient
    # See _adopt_from_donor docs for an explanation.
    config_entity._adopt_from_donor('db_entities', True)

    # TODO Update to newer cleaner configuration style
    # key is always resolved by the db_entity or else the source DbEntity key
    key = db_entity.key or source_db_entity.key

    feature_behavior = db_entity._feature_behavior
    if existing_db_entity_interest:
        # Merge existing DbEntity data, including the id. Manually merge feature_behavior since it's not a real
        # property
        db_entity.__dict__.update(
            merge(
                model_dict(existing_db_entity_interest.db_entity, include_primary_key=True),
                dict(_feature_behavior=existing_db_entity_interest.db_entity.feature_behavior)
            )
        )
    if not db_entity._feature_behavior:
        from footprint.main.publishing.config_entity_initialization import get_behavior
        # If we don't already have a feature_behavior get it from the source DbEntity but 0 the id to make a copy
        # We'll also 0 out the Intersection of the FeatureBehavior and pass it to the FeatureBehavior creator
        # If we don't have a source_db_entity then create a default FeatureBehavior
        feature_behavior = source_db_entity.feature_behavior if \
            source_db_entity else  \
            FeatureBehavior(
                behavior=get_behavior('reference')
            )
        feature_behavior.id = None

    # Prefer the db_entity values over those of the source_db_entity
    db_entity.__dict__.update(merge(
        # Start with the source attributes
        model_dict(source_db_entity) if source_db_entity else {},
        # Copy the feature_behavior by assigning it to the temp property _feature_behavior
        # Prefer the FeatureBehavior of the new DbEntity (for the Result case)
        dict(_feature_behavior=feature_behavior),
        # Override with the initial clone overrides or existing clone values.
        model_dict(db_entity),
        # If update_on_override then overwrite the clone with the source values
        # This is for the case of updating the source configuration and wanting to mirror changes to the Result DbEntities
        remove_keys(model_dict(source_db_entity), ['key', 'schema', 'name']) if source_db_entity and override_on_update else {},
        # Set the clone source key if different than the target key
        dict(source_db_entity_key=source_db_entity.key) if source_db_entity and source_db_entity.key != key else {},
        dict(
             # Override feature_class_configuration keys if specified in the kwargs and create_or_update_on_override is True
             feature_class_configuration=FeatureClassConfiguration(**merge(
                # start with the source's attributes
                source_db_entity.feature_class_configuration.__dict__ if source_db_entity else {},
                # override with all those present on the clone if create_or_update_on_override is false, if true add only those unspecified on the source
                # The only exception is abstract_class_name, which must always come from the source
                remove_keys(db_entity.feature_class_configuration.__dict__,
                            source_db_entity.feature_class_configuration.__dict__.keys() if \
                                source_db_entity and override_on_update else \
                                ['abstract_class_name']
                            ),
                # feature_class_owner is set to the owning db_entity key for a Result DbEntity creation so that
                # the result feature_class_configuration doesn't create feature classes/tables
                # We either copy this value from the source feature_class_configuration (clone case) or get it from the kwargs (create Result DbEntity case)
                dict(
                    feature_class_owner=source_db_entity.feature_class_configuration.feature_class_owner if \
                        source_db_entity else \
                        db_entity.feature_class_configuration.feature_class_owner,
                )
             ))
        )
    ))
    # There must be a creator of a new DbEntity. The updater is that of the source DbEntity or simply the creator
    try:
        db_entity.creator = db_entity.creator
    except:
        db_entity.creator = source_db_entity.creator
    logger.debug(db_entity.creator.__class__)
    try:
        db_entity.updater = db_entity.updater
    except:
        db_entity.updater = db_entity.creator
    # Persist the feature_behavior
    FeatureBehavior._no_post_save_publishing = True
    db_entity.update_or_create_feature_behavior(db_entity._feature_behavior)
    # # save to persist the db_entity's knowledge of its behavior
    # db_entity.save()
    FeatureBehavior._no_post_save_publishing = False

    # This will trigger DbEntity post-save publishing, but the only thing that actually
    # runs is UserPublishing in order to give users permission to the DbEntity
    # The other publishers detect that the DbEntity has a source_db_entity_key and quit
    db_entity_interest = update_or_create_db_entity_and_interest(config_entity, db_entity)[0]

    # Copy the categories from the source if it exists
    if source_db_entity:
        db_entity_interest.db_entity.categories.add(*source_db_entity.categories.all())

    return db_entity_interest
Пример #18
0
            existing_feature_behavior = None

        existing_intersection = None
        if existing_feature_behavior:
            # If we still have a valid existing feature_behavior have it conform to anything about the Behavior
            # configuration that might have changed.
            updated_existing_feature_behavior = existing_feature_behavior
            template_feature_behavior = behavior.feature_behavior_from_behavior_template(
            )
            existing_intersection = existing_feature_behavior.intersection_subclassed
            # If cloning set the id to None
            if existing_intersection and not updated_existing_feature_behavior.id:
                existing_intersection.id = None
            logger.debug(
                "Existing Intersection: %s" %
                model_dict(existing_intersection, include_primary_key=True))
            # TODO this seems problematic. The existing FeatureBehavior's tags should take precedence over the
            # Behavior's unless the former has no tags
            updated_existing_feature_behavior._tags = template_feature_behavior._tags
        else:
            # Get a new instance from the Behavior
            updated_existing_feature_behavior = behavior.feature_behavior_from_behavior_template(
            )
        updated_existing_feature_behavior.set_defaults()

        # Intersection properties are defined on the Behavior and possibly extended or overridden on the FeatureBehavior
        # Every FeatureBehavior has its own Intersection instance so that we can customize the intersection for
        # the DbEntity. We always remove the is_template property that might have come the Behavior's Intersection
        intersection_dict = remove_keys(
            merge(
                model_dict(
Пример #19
0
def clone_or_update_db_entity_and_interest(config_entity,
                                           source_db_entity,
                                           db_entity,
                                           existing_db_entity_interest=None,
                                           override_on_update=False):
    """
        Clones or updates the source_db_entity modified with the given kwargs (including possibly the key) into this ConfigEntity.
        This is used for a duplicate clone from one ConfigEntity (same DbEntity key) to another and also for
        creating a modified DbEntity for a Result from a non-Result DbEntity (different DbEntity key). A third case for this
        method is cloning a DbEntity within a ConfigEntity, which is not yet implemented.

        If the kwargs['override_on_update'] is True, the kwargs should override the target DbEntity attribute values on update.
        This is useful for the Result clone case where we want to pick up updates to the source DbEntity. But in the straight
        clone case we want to make the target DbEntity independent of the source once it is created.
        Returns the DbEntityInterest

        :param: config_entity. The config_entity of the DbEntities
        :param: Optional source_db_entity. The source of the clone
        :param: db_entity. DbEntity of attributes matching the DbEntity that need to override those of the source_db_entity. This might be the
        key, schema, etc.
        Also for db_entity.feature_class_owner, default None, is passed in for Results that are copying
        a reference DbEntity's feature_class_configuration with the db_entity key of the onwer. We don't want the result version of the feature_class_configuration
        to be used to create the feature_class tables, because that would make foreign key references point to the
        wrong feature class parents (which are named by the DbEntity id). phwewww!
        :param: existing_db_entity_interest: Optional DbEntityInterest if it already exists for the clone
        :param: override_on_update: Default False, optional and is described above.
    """

    # Do a forced adoption of DbEntityInterests from the parent ConfigEntity. This makes sure that ConfigEntity has
    # the parent's DbEntityInterests before adding any of its own. Otherwise the parent's are never adopted and
    # are created from the db_entity_configurations instead, which is minimally less efficient
    # See _adopt_from_donor docs for an explanation.
    config_entity._adopt_from_donor('db_entities', True)

    # TODO Update to newer cleaner configuration style
    # key is always resolved by the db_entity or else the source DbEntity key
    key = db_entity.key or source_db_entity.key

    feature_behavior = db_entity._feature_behavior
    if existing_db_entity_interest:
        # Merge existing DbEntity data, including the id. Manually merge feature_behavior since it's not a real
        # property
        db_entity.__dict__.update(
            merge(
                model_dict(existing_db_entity_interest.db_entity,
                           include_primary_key=True),
                dict(_feature_behavior=existing_db_entity_interest.db_entity.
                     feature_behavior)))
    if not db_entity._feature_behavior:
        from footprint.main.publishing.config_entity_initialization import get_behavior
        # If we don't already have a feature_behavior get it from the source DbEntity but 0 the id to make a copy
        # We'll also 0 out the Intersection of the FeatureBehavior and pass it to the FeatureBehavior creator
        # If we don't have a source_db_entity then create a default FeatureBehavior
        feature_behavior = source_db_entity.feature_behavior if \
            source_db_entity else  \
            FeatureBehavior(
                behavior=get_behavior('reference')
            )
        feature_behavior.id = None

    # Prefer the db_entity values over those of the source_db_entity
    db_entity.__dict__.update(merge(
        # Start with the source attributes
        model_dict(source_db_entity) if source_db_entity else {},
        # Copy the feature_behavior by assigning it to the temp property _feature_behavior
        # Prefer the FeatureBehavior of the new DbEntity (for the Result case)
        dict(_feature_behavior=feature_behavior),
        # Override with the initial clone overrides or existing clone values.
        model_dict(db_entity),
        # If update_on_override then overwrite the clone with the source values
        # This is for the case of updating the source configuration and wanting to mirror changes to the Result DbEntities
        remove_keys(model_dict(source_db_entity), ['key', 'schema', 'name']) if source_db_entity and override_on_update else {},
        # Set the clone source key if different than the target key
        dict(source_db_entity_key=source_db_entity.key) if source_db_entity and source_db_entity.key != key else {},
        dict(
             # Override feature_class_configuration keys if specified in the kwargs and create_or_update_on_override is True
             feature_class_configuration=FeatureClassConfiguration(**merge(
                # start with the source's attributes
                source_db_entity.feature_class_configuration.__dict__ if source_db_entity else {},
                # override with all those present on the clone if create_or_update_on_override is false, if true add only those unspecified on the source
                # The only exception is abstract_class_name, which must always come from the source
                remove_keys(db_entity.feature_class_configuration.__dict__,
                            source_db_entity.feature_class_configuration.__dict__.keys() if \
                                source_db_entity and override_on_update else \
                                ['abstract_class_name']
                            ),
                # feature_class_owner is set to the owning db_entity key for a Result DbEntity creation so that
                # the result feature_class_configuration doesn't create feature classes/tables
                # We either copy this value from the source feature_class_configuration (clone case) or get it from the kwargs (create Result DbEntity case)
                dict(
                    feature_class_owner=source_db_entity.feature_class_configuration.feature_class_owner if \
                        source_db_entity else \
                        db_entity.feature_class_configuration.feature_class_owner,
                )
             ))
        )
    ))
    # There must be a creator of a new DbEntity. The updater is that of the source DbEntity or simply the creator
    try:
        db_entity.creator = db_entity.creator
    except:
        db_entity.creator = source_db_entity.creator
    logger.debug(db_entity.creator.__class__)
    try:
        db_entity.updater = db_entity.updater
    except:
        db_entity.updater = db_entity.creator
    # Persist the feature_behavior
    FeatureBehavior._no_post_save_publishing = True
    db_entity.update_or_create_feature_behavior(db_entity._feature_behavior)
    # # save to persist the db_entity's knowledge of its behavior
    # db_entity.save()
    FeatureBehavior._no_post_save_publishing = False

    # This will trigger DbEntity post-save publishing, but the only thing that actually
    # runs is UserPublishing in order to give users permission to the DbEntity
    # The other publishers detect that the DbEntity has a source_db_entity_key and quit
    db_entity_interest = update_or_create_db_entity_and_interest(
        config_entity, db_entity)[0]

    # Copy the categories from the source if it exists
    if source_db_entity:
        db_entity_interest.db_entity.categories.add(
            *source_db_entity.categories.all())

    return db_entity_interest