예제 #1
0
    def sync_db_table_entities(self, config_entity):
        """
            Syncs the db_entities representing tables with the tables in the this instance's schema. This should only
            be used when tables are added or removed from the system outside of the UrbanFootprint application.
            Normally the db_entries will stay synced automatically. No DbEntry instances representing queries, views,
            or other table-based representations are added or deleted here.
        """
        # Load the db_entities that represent tables
        table_entities = config_entity.db_entities.filter(table__isnull=False,
                                                          query__isnull=True)
        table_entity_names = map(lambda table_entity: table_entity.table,
                                 table_entities)
        # Load the physical tables in the schema
        table_names = unique(
            map(
                lambda information_schema: information_schema.table_name,
                InformationSchema.objects.filter(
                    table_schema=config_entity.schema())))
        # Compare table names to find new tables for which to create entries
        owner_interest = Interest.objects.get(key=Keys.INTEREST_OWNER)
        for new_table_name in set(table_names) - set(table_entity_names):
            # Create the DbEntity and join it to the ConfigEntity with an owner DbEntityInterest
            table_entity = DbEntity.objects.create(
                name=new_table_name,
                schema=config_entity.schema(),
                table=new_table_name)
            self.create(config_entity=config_entity,
                        db_entity=table_entity,
                        interest=owner_interest)

        # Compare table names to find deleted tables for which to delete entries
        for table_entity in table_entities:
            if not table_entity.table in table_names:
                table_entity.delete()
예제 #2
0
    def save(self, force_insert=False, force_update=False, using=None):
        """
            Overrides the default save to merge the self.config_entities properties after doing an initial save
        :param force_insert:
        :param force_update:
        :param using:
        :return:
        """

        # First save to create a pk
        super(CombinedConfigEntity, self).save(force_insert, force_update,
                                               using)
        # Add unique instances to each collection from the config_entities. References to the parent_config_entity's
        # instances will automatically be adopted first.
        for method in ConfigEntity.INHERITABLE_COLLECTIONS:
            # get the add_method or add_method_through method name
            getattr(self, '_add_{0}'.format(method))(*unique(
                flat_map(
                    lambda config_entity: getattr(
                        config_entity, 'computed_{0}'.format(method))(),
                    self.config_entities), lambda instance: instance.pk))

        # Combine the bounds of the config_entities to make this instance's bounds
        self.bounds = MultiPolygon(
            map(lambda config_entity: config_entity.bounds.cascaded_union,
                self.config_entities))
예제 #3
0
    def save(self, force_insert=False, force_update=False, using=None):
        """
            Overrides the default save to merge the self.config_entities properties after doing an initial save
        :param force_insert:
        :param force_update:
        :param using:
        :return:
        """

        # First save to create a pk
        super(CombinedConfigEntity, self).save(force_insert, force_update, using)
        # Add unique instances to each collection from the config_entities. References to the parent_config_entity's
        # instances will automatically be adopted first.
        for method in ConfigEntity.INHERITABLE_COLLECTIONS:
            # get the add_method or add_method_through method name
            getattr(self, "_add_{0}".format(method))(
                *unique(
                    flat_map(
                        lambda config_entity: getattr(config_entity, "computed_{0}".format(method))(),
                        self.config_entities,
                    ),
                    lambda instance: instance.pk,
                )
            )

        # Combine the bounds of the config_entities to make this instance's bounds
        self.bounds = MultiPolygon(map(lambda config_entity: config_entity.bounds.cascaded_union, self.config_entities))
예제 #4
0
    def sync_db_table_entities(self, config_entity):
        """
            Syncs the db_entities representing tables with the tables in the this instance's schema. This should only
            be used when tables are added or removed from the system outside of the UrbanFootprint application.
            Normally the db_entries will stay synced automatically. No DbEntry instances representing queries, views,
            or other table-based representations are added or deleted here.
        """
        # Load the db_entities that represent tables
        table_entities = config_entity.db_entities.filter(table__isnull=False, query__isnull=True)
        table_entity_names = map(lambda table_entity: table_entity.table, table_entities)
        # Load the physical tables in the schema
        table_names = unique(
            map(
                lambda information_schema: information_schema.table_name,
                InformationSchema.objects.filter(table_schema=config_entity.schema())))
        # Compare table names to find new tables for which to create entries
        owner_interest = Interest.objects.get(key=Keys.INTEREST_OWNER)
        for new_table_name in set(table_names) - set(table_entity_names):
            # Create the DbEntity and join it to the ConfigEntity with an owner DbEntityInterest
            table_entity = DbEntity.objects.create(name=new_table_name, schema=config_entity.schema(), table=new_table_name)
            self.create(config_entity=config_entity, db_entity=table_entity, interest=owner_interest)

        # Compare table names to find deleted tables for which to delete entries
        for table_entity in table_entities:
            if not table_entity.table in table_names:
                table_entity.delete()
    def test_config_entity_api__permissions(self):
        """
            Make sure that users only get ConfigEntity's that match their permission settings
        :return:
        """

        permission_configuration = TestConfigEntityPermissions.config_entity_configuration()
        resource_name = 'config_entity'

        # Iterate through the test_configurstions and extract a user for each group_key
        # Make a dict with the user as the key and all the instances from the test_config that the
        # user corresponds to. This gives a lookup of a user to the config_entities that we expect
        # the user to be able to view
        # Create a user->instances dict
        # Combine our {user1:instances, user2:instances,...} dicts
        user_to_expected_instances = merge_dict_list_values(
            *map(
                lambda test_configuration:\
                    # Combine our [user, instance] pairs into {user1:instances, user2:instances,...}
                    # Remove null keys (owing to groups with no users)
                    compact_dict(map_to_dict_with_lists(
                        # Each test_configuration has several groups.
                        # For each group resolve a user and return [user, instance]
                        lambda group_key: [
                            get_first_value_or_none(Group.objects.get(name=group_key).user_set.all()),
                            test_configuration['instance']],
                        test_configuration['groups'].keys())),
                permission_configuration.test_configuration)
        )

        all_instances = set(unique(flatten(user_to_expected_instances.values())))
        for user, instances in user_to_expected_instances.items():
            other_instances = all_instances - set(instances)
            # Fetch all instances with this user and create a lookup so we can test
            # that the resulting instances are present or not present as expected according to
            # the permissions
            response = self.get(resource_name, user=user)
            result_instance_lookup = map_to_dict(
                lambda instance_dict: [int(instance_dict['id']), instance_dict],
                self.deserialize(response)['objects'])

            for instance in instances:
                matching_instance = result_instance_lookup.get(instance.id)
                assert_is_not_none(matching_instance,
                                   "User %s should have view permission to instance %s with id %s and key %s but does." % \
                                   (user.username,
                                    instance,
                                    instance.id,
                                    permission_configuration.key_class.Fab.remove(
                                        permission_configuration.instance_key_lambda(instance))))

            for instance in other_instances:
                assert_is_none(matching_instance,
                                   "User %s should not have view permission to instance %s with id %s and key %s but does." % \
                                   (user.username,
                                    instance,
                                    instance.id,
                                    permission_configuration.key_class.Fab.remove(
                                        permission_configuration.instance_key_lambda(instance))))
예제 #6
0
def model_ancestry(model):
    """
        Returns the model and its ancestors using a breadth-first-search ordering
    :param model:
    :return:
    """

    return unique([model] + _model_ancestry(model))
 def feature_class_configuration_from_geojson_introspection(self, data):
     """
         Creates a dynamic Feature class configuration by introspecting the db_entity's Feature table.
     :return: The Feature class configuration
     """
     properties = unique(flat_map(lambda feature: feature.properties.keys(), data.features))
     fields = map(lambda property: models.CharField(property), properties)
     return self.generate_configuration(fields)
예제 #8
0
def model_ancestry(model):
    """
        Returns the model and its ancestors using a breadth-first-search ordering
    :param model:
    :return:
    """

    return unique([model] + _model_ancestry(model))
예제 #9
0
def region_fixtures(**kwargs):
    """
        Convenience method to fetch all region features of a client. Normally there is only one
    """
    return unique(
        map(
            lambda schema: resolve_fixture("config_entity", "region",
                                           RegionFixture, schema, **kwargs),
            region_schemas_of_client()), lambda fixture: fixture.__class__)
예제 #10
0
 def lineage(cls, discovered=[]):
     """
         Returns the hierarchy of parent classes of this class. Duplicates are ignored. Order is from this class up
         until GlobalConfig
     :param cls:
     :return:
     """
     return unique([cls] + flat_map(lambda parent_class: parent_class.lineage(discovered + cls.parent_classes()),
                                    filter(lambda parent_class: parent_class not in discovered,
                                           cls.parent_classes())))
예제 #11
0
 def lineage(cls, discovered=[]):
     """
         Returns the hierarchy of parent classes of this class. Duplicates are ignored. Order is from this class up
         until GlobalConfig
     :param cls:
     :return:
     """
     return unique([cls] + flat_map(lambda parent_class: parent_class.lineage(discovered + cls.parent_classes()),
                                    filter(lambda parent_class: parent_class not in discovered,
                                           cls.parent_classes())))
예제 #12
0
def create_query(attribute, config_entity, layer):

    db_entity = layer.db_entity_interest.db_entity
    feature_class = config_entity.db_entity_feature_class(db_entity.key)
    # Create a query that selects the wkb_geometry and the attribute we need
    # There's nothing to prevent styling multiple attributes in the future
    try:
        query = str(feature_class.objects.values(*unique(['wkb_geometry', attribute])).query)
    except Exception, e:
        raise Exception("Error creating the query for db_entity %s. Original exception: %s" % (db_entity.key, e.message))
예제 #13
0
def project_specific_scenario_fixtures(**kwargs):
    """
        Convenience method to find ScenarioFixture instances for all projects of the client.
        :kwargs: Optional arguments for result_fixture, such as the config_entity
    :return:
    """
    return unique(
        map(
            lambda schema: resolve_fixture("config_entity", "scenario",
                                           ScenarioFixture, schema, **kwargs),
            project_schemas_of_client()), lambda fixture: fixture.__class__)
예제 #14
0
def create_layer_selections(layers):
    """
        Create LayerSelection classes and instances for the given Scenario subclasses among the
        classes in limit_to_classes. Also filters by db_entity_key if they are specified
    :return:
    """
    for config_entity in unique(map(lambda layer: layer.config_entity, layers)):
        FeatureClassCreator(config_entity).ensure_dynamic_models()
    for selection_layer in layers:
        # Recreate
        get_or_create_layer_selection_class_for_layer(selection_layer)
    update_or_create_layer_selections(config_entity=None)
예제 #15
0
def create_layer_selections(layers):
    """
        Create LayerSelection classes and instances for the given Scenario subclasses among the
        classes in limit_to_classes. Also filters by db_entity_key if they are specified
    :return:
    """
    for config_entity in unique(map(lambda layer: layer.config_entity,
                                    layers)):
        FeatureClassCreator(config_entity).ensure_dynamic_models()
    for selection_layer in layers:
        # Recreate
        get_or_create_layer_selection_class_for_layer(selection_layer)
    update_or_create_layer_selections(config_entity=None)
 def geography_scopes(self, with_existing_tables_only=True):
     """
         Returns all of the geography scopes for this config_entity, beginning with itself and ascending
         up until but not including the GlobalConfig
     :param with_existing_tables_only: Default True. Only return geography scopes that have a representative
     geography table, meaning that a DbEntity exists at that scope that is the primary_geography. This
     is useful to prevent trying to join to geography tables at scopes where they don't exist.
     :return:
     """
     return unique(filter(
         lambda config_entity: not with_existing_tables_only or\
                               self.__class__(config_entity,  no_ensure=True).config_entity_has_primary_geography,
         self.geography_scope.ascendants()[:-1]
     ))
예제 #17
0
def add_categories(bundle, *submitted_categories):
    """
            When the user updates the values of one or more categories, we assume that they want to delete the current Category instances with the same keys and replace them with the selected Category value. For instance, if a scenario has the Category key:'category' value:'smart' and the user chooses 'dumb' for the new value, we want to delete the Category instance valued by 'smart' and insert the one valued by 'dumb'. But we don't want to mess with Category instances that have different keys
    """
    logger = logging.getLogger(__name__)
    try:
        submitted_categories_by_key = map_to_keyed_collections(lambda category: category.key, submitted_categories)
        existing_categories_by_key = map_to_keyed_collections(lambda category: category.key, bundle.obj.categories.all())
        categories_to_add_or_maintain = flat_map_values(lambda key, categories: unique(categories, lambda category: category.value),
                                                        merge(existing_categories_by_key, submitted_categories_by_key))
        bundle.obj.categories.clear()
        bundle.obj.categories.add(*categories_to_add_or_maintain)
    except Exception, e:
        logger.critical(e.message)
예제 #18
0
def create_query(attribute, config_entity, layer):

    db_entity = layer.db_entity_interest.db_entity
    feature_class = config_entity.db_entity_feature_class(db_entity.key)
    # Create a query that selects the wkb_geometry and the attribute we need
    # There's nothing to prevent styling multiple attributes in the future
    try:
        query = str(
            feature_class.objects.values(
                *unique(['wkb_geometry', attribute])).query)
    except Exception, e:
        raise Exception(
            "Error creating the query for db_entity %s. Original exception: %s"
            % (db_entity.key, e.message))
예제 #19
0
def add_categories(bundle, *submitted_categories):
    """
            When the user updates the values of one or more categories, we assume that they want to delete the current Category instances with the same keys and replace them with the selected Category value. For instance, if a scenario has the Category key:'category' value:'smart' and the user chooses 'dumb' for the new value, we want to delete the Category instance valued by 'smart' and insert the one valued by 'dumb'. But we don't want to mess with Category instances that have different keys
    """
    logger = logging.getLogger(__name__)
    try:
        submitted_categories_by_key = map_to_keyed_collections(lambda category: category.key, submitted_categories)
        existing_categories_by_key = map_to_keyed_collections(lambda category: category.key, bundle.obj.categories.all())
        categories_to_add_or_maintain = flat_map_values(lambda key, categories: unique(categories, lambda category: category.value),
                                                        merge(existing_categories_by_key, submitted_categories_by_key))
        bundle.obj.categories.clear()
        bundle.obj.categories.add(*categories_to_add_or_maintain)
    except Exception, e:
        logger.critical(e.message)
예제 #20
0
def match_subclasses(c, matching_lambda=lambda x: True, first_only=False):
    """
        Recursively find the subclasses matching lambda
    :param c: starting class
    :param matching_lambda: filter function that takes each subclass and returns true or false. Unmatched classes
    are still recursed beforehand. Defaults to returning all.
    :param first_only: Default False. Stop after the first match and return one result
    :return: A single result or None if first_only is True. Otherwise 0 or more results in an array
    """
    subclasses = c.__subclasses__()
    for d in list(subclasses):
        subclasses.extend(get_subclasses(d))
    unique_subclasses = unique(subclasses)
    return first(matching_lambda, unique_subclasses) if first_only else \
        filter(matching_lambda, unique_subclasses)
예제 #21
0
def match_subclasses(c, matching_lambda=lambda x: True, first_only=False):
    """
        Recursively find the subclasses matching lambda
    :param c: starting class
    :param matching_lambda: filter function that takes each subclass and returns true or false. Unmatched classes
    are still recursed beforehand. Defaults to returning all.
    :param first_only: Default False. Stop after the first match and return one result
    :return: A single result or None if first_only is True. Otherwise 0 or more results in an array
    """
    subclasses = c.__subclasses__()
    for d in list(subclasses):
        subclasses.extend(get_subclasses(d))
    unique_subclasses = unique(subclasses)
    return first(matching_lambda, unique_subclasses) if first_only else \
        filter(matching_lambda, unique_subclasses)
예제 #22
0
def _update_or_create_config_entity_group(config_entity, global_group_name):
    # ConfigEntity group name is created by combining it's schema with the global
    # group's name. The exception is GlobalConfig, whose ConfigEntity Group is simply the global admin group
    if not config_entity.schema_prefix:
        # GlobalConfig case, nothing to do here
        return Group.objects.get(name=global_group_name)

    config_entity_group_name = '__'.join(
        compact([config_entity.schema_prefix, global_group_name]))

    # The superiors of this group are our global Group and
    # the parent ConfigEntity's groups whose global Group is equal or greater than ours
    # (e.g. foo__user's superior is user and and bar_user and bar_manager if bar is the parent ConfigEntity of foo)

    # Get the name of all groups of the parent ConfigEntity
    all_parent_config_entity_group_hierarchies = config_entity.parent_config_entity.group_hierarchies.all(
    )

    # Only accept Groups that are at least at our global Group level
    eligible_global_groups_names = UserGroupKey.GLOBAL[
        0:UserGroupKey.GLOBAL.index(global_group_name) + 1]

    # Given a minimum eligible permission level (e.g. above Manager),
    # find all of the parent ConfigEntity's Groups that have at least that permission level (e.g. Manager and Admin).
    # These logically deserve all the permissions of the child ConfigEntity Group.
    # It's unlikely that a parent ConfigEntity Group would be a lower permission level (e.g. User), so this will normally accept all parent ConfigEntity Groups
    config_entity_groups_matching_eligible_global_groups = []
    for group_hierarchy in all_parent_config_entity_group_hierarchies:
        if group_hierarchy.globalized_group(
        ).name in eligible_global_groups_names:
            config_entity_groups_matching_eligible_global_groups.append(
                group_hierarchy)

    # Sort by ascending permission. This probably doesn't matter--
    # it just means we process the lower permission first for consistent logging. (There is typically only one Group anyway)
    group_hierarchies = sorted(
        config_entity_groups_matching_eligible_global_groups,
        key=lambda group_hierarchy: eligible_global_groups_names.index(
            group_hierarchy.globalized_group().name))

    # Combine our global Group name with the parent ConfigEntity Groups
    superior_group_names = unique([global_group_name] + map(
        lambda group_hierarchy: group_hierarchy.group.name, group_hierarchies))

    # Update or create the Group
    return update_or_create_group(name=config_entity_group_name,
                                  config_entity=config_entity,
                                  superiors=superior_group_names)
예제 #23
0
 def all_superiors(self):
     """
         Recursively returns all unique superiors. For a ConfigEntity Group the superiors are the corresponding
         global Group (e.g. Foo_Manager Group of Project Foo's global superior is the global Manager) as well
         as all ConfigEntity Groups of the parent ConfigEntity. For example, if Project Foo has a Foo_Manager
         and a Foo_User, and it's child Scenario Bar has a Bar_User, then Foo_Manager and Foo_User are
         the superiors of Bar_User. If a Bar_Manager existed its superiors would also be Foo_Manager and Foo_User,
         although the case of Bar_Manager having a Foo_User superior should probably be avoided (We could
         prevent this with code if such a configuration were ever desired)
     :return:
     """
     superiors = self.superiors.all()
     return unique(
         flat_map(
             lambda superior: [superior] + superior.group_hierarchy.
             all_superiors(), superiors), lambda obj: obj.id)
예제 #24
0
 def all_superiors(self):
     """
         Recursively returns all unique superiors. For a ConfigEntity Group the superiors are the corresponding
         global Group (e.g. Foo_Manager Group of Project Foo's global superior is the global Manager) as well
         as all ConfigEntity Groups of the parent ConfigEntity. For example, if Project Foo has a Foo_Manager
         and a Foo_User, and it's child Scenario Bar has a Bar_User, then Foo_Manager and Foo_User are
         the superiors of Bar_User. If a Bar_Manager existed its superiors would also be Foo_Manager and Foo_User,
         although the case of Bar_Manager having a Foo_User superior should probably be avoided (We could
         prevent this with code if such a configuration were ever desired)
     :return:
     """
     superiors = self.superiors.all()
     return unique(
         flat_map(
             lambda superior: [superior]+superior.group_hierarchy.all_superiors(), superiors),
         lambda obj: obj.id)
예제 #25
0
def delete_layer_selections(layers):
    for config_entity in unique(map(lambda layer: layer.config_entity, layers)):
        FeatureClassCreator(config_entity).ensure_dynamic_models()
    for selection_layer in layers:
        try:
            # Drop the table
            layer_selection_class = get_or_create_layer_selection_class_for_layer(selection_layer, no_table_creation=True)

            if layer_selection_class:
                if hasattr(layer_selection_class.features, 'through'):
                    layer_selection_features_class = layer_selection_class.features.through
                    drop_layer_selection_table(layer_selection_features_class)
                drop_layer_selection_table(layer_selection_class)

        except DatabaseError, e:
            logger.warning(
                "Couldn't destroy LayerSelection tables. Maybe the public.layer table no longer exists: %s" % e.message)
예제 #26
0
def _update_or_create_config_entity_group(config_entity, global_group_name):
    # ConfigEntity group name is created by combining it's schema with the global
    # group's name. The exception is GlobalConfig, whose ConfigEntity Group is simply the global admin group
    if not config_entity.schema_prefix:
        # GlobalConfig case, nothing to do here
        return Group.objects.get(name=global_group_name)

    config_entity_group_name = '__'.join(compact([config_entity.schema_prefix, global_group_name]))

    # The superiors of this group are our global Group and
    # the parent ConfigEntity's groups whose global Group is equal or greater than ours
    # (e.g. foo__user's superior is user and and bar_user and bar_manager if bar is the parent ConfigEntity of foo)

    # Get the name of all groups of the parent ConfigEntity
    all_parent_config_entity_group_hierarchies = config_entity.parent_config_entity.group_hierarchies.all()

    # Only accept Groups that are at least at our global Group level
    eligible_global_groups_names = UserGroupKey.GLOBAL[0:UserGroupKey.GLOBAL.index(global_group_name)+1]

    # Given a minimum eligible permission level (e.g. above Manager),
    # find all of the parent ConfigEntity's Groups that have at least that permission level (e.g. Manager and Admin).
    # These logically deserve all the permissions of the child ConfigEntity Group.
    # It's unlikely that a parent ConfigEntity Group would be a lower permission level (e.g. User), so this will normally accept all parent ConfigEntity Groups
    config_entity_groups_matching_eligible_global_groups = []
    for group_hierarchy in all_parent_config_entity_group_hierarchies:
        if group_hierarchy.globalized_group().name in eligible_global_groups_names:
            config_entity_groups_matching_eligible_global_groups.append(group_hierarchy)

    # Sort by ascending permission. This probably doesn't matter--
    # it just means we process the lower permission first for consistent logging. (There is typically only one Group anyway)
    group_hierarchies = sorted(
        config_entity_groups_matching_eligible_global_groups,
        key=lambda group_hierarchy: eligible_global_groups_names.index(group_hierarchy.globalized_group().name))

    # Combine our global Group name with the parent ConfigEntity Groups
    superior_group_names = unique(
        [global_group_name] +
        map(lambda group_hierarchy: group_hierarchy.group.name, group_hierarchies)
    )

    # Update or create the Group
    return update_or_create_group(
        name=config_entity_group_name,
        config_entity=config_entity,
        superiors=superior_group_names)
예제 #27
0
def best_matching_subclass(cls,
                           matching_lambda=lambda x: True,
                           limited_subclasses=None,
                           return_base_unless_match=False,
                           ascend_cls_until=None):
    """
        Find the best matching subclass according to the matching_lambda.
        This works by breadth-first. It searches immediate sublasses,
        and upon finding a match recurses on that subclass. If no match
        is found at a certain level it returns the match at the level above
    :param cls:
    :param matching_lambda: filter function that takes each subclass and returns true or false.
    :param limited_subclasses: If specified limits the search to the given subclasses.
    These subclasses are used recursively as well
    :param return_base_unless_match: Default false. Return the c if no match is found.
    :param ascend_cls_until: Default None. If set to a class, if no match is found for cls,
    recurse on cls.__base__ until a match or cls.__base__ equals the class specified here.
    Example. If cls is GlobalConfig and ascend_cls_until=FootprintModel, the method looks
    for a subclass with cls==GlobalConfig. If that fails as cls==ConfigEntity. If that fails
    then cls==FootprintModel so we give up and return None or if return_base_unless_match is True
    then we return ConfigEntity (which is probably useless)
    :return: The first match or None (or c if return_base_unless_match is True)
    """
    subclasses = filter(lambda limited_subclass: issubclass(limited_subclass, cls), limited_subclasses) if \
        limited_subclasses else \
        cls.__subclasses__()
    match = first(matching_lambda, unique(subclasses))
    if match:
        # If we have a match recurse on the match to try to find an even better match
        # specify return_base_unless_match since we already succeed at matching for this iteration
        return best_matching_subclass(match,
                                      matching_lambda,
                                      limited_subclasses,
                                      return_base_unless_match=True)
    elif ascend_cls_until and cls.__base__ != ascend_cls_until:
        # No match but recurse on cls.__base__ to find a more general match unless we reach the 'until' cls
        return best_matching_subclass(cls.__base__, matching_lambda,
                                      limited_subclasses,
                                      return_base_unless_match,
                                      ascend_cls_until)
    # Give up and return the current c or None
    return (cls if return_base_unless_match else None)
예제 #28
0
def delete_layer_selections(layers):
    for config_entity in unique(map(lambda layer: layer.config_entity,
                                    layers)):
        FeatureClassCreator(config_entity).ensure_dynamic_models()
    for selection_layer in layers:
        try:
            # Drop the table
            layer_selection_class = get_or_create_layer_selection_class_for_layer(
                selection_layer, no_table_creation=True)

            if layer_selection_class:
                if hasattr(layer_selection_class.features, 'through'):
                    layer_selection_features_class = layer_selection_class.features.through
                    drop_layer_selection_table(layer_selection_features_class)
                drop_layer_selection_table(layer_selection_class)

        except DatabaseError, e:
            logger.warning(
                "Couldn't destroy LayerSelection tables. Maybe the public.layer table no longer exists: %s"
                % e.message)
예제 #29
0
def best_matching_subclass(
        cls,
        matching_lambda=lambda x: True,
        limited_subclasses=None,
        return_base_unless_match=False,
        ascend_cls_until=None
    ):
    """
        Find the best matching subclass according to the matching_lambda.
        This works by breadth-first. It searches immediate sublasses,
        and upon finding a match recurses on that subclass. If no match
        is found at a certain level it returns the match at the level above
    :param cls:
    :param matching_lambda: filter function that takes each subclass and returns true or false.
    :param limited_subclasses: If specified limits the search to the given subclasses.
    These subclasses are used recursively as well
    :param return_base_unless_match: Default false. Return the c if no match is found.
    :param ascend_cls_until: Default None. If set to a class, if no match is found for cls,
    recurse on cls.__base__ until a match or cls.__base__ equals the class specified here.
    Example. If cls is GlobalConfig and ascend_cls_until=FootprintModel, the method looks
    for a subclass with cls==GlobalConfig. If that fails as cls==ConfigEntity. If that fails
    then cls==FootprintModel so we give up and return None or if return_base_unless_match is True
    then we return ConfigEntity (which is probably useless)
    :return: The first match or None (or c if return_base_unless_match is True)
    """
    subclasses = filter(lambda limited_subclass: issubclass(limited_subclass, cls), limited_subclasses) if \
        limited_subclasses else \
        cls.__subclasses__()
    match = first(matching_lambda, unique(subclasses))
    if match:
        # If we have a match recurse on the match to try to find an even better match
        # specify return_base_unless_match since we already succeed at matching for this iteration
        return best_matching_subclass(match, matching_lambda, limited_subclasses, return_base_unless_match=True)
    elif ascend_cls_until and cls.__base__ != ascend_cls_until:
        # No match but recurse on cls.__base__ to find a more general match unless we reach the 'until' cls
        return best_matching_subclass(cls.__base__, matching_lambda, limited_subclasses, return_base_unless_match, ascend_cls_until)
    # Give up and return the current c or None
    return (cls if return_base_unless_match else None)
예제 #30
0
            (configured_feature_behavior and configured_feature_behavior.intersection_subclassed and configured_feature_behavior.intersection_subclassed.__class__) or\
            (updated_existing_feature_behavior and updated_existing_feature_behavior.intersection and updated_existing_feature_behavior.intersection_subclassed.__class__)
        intersection = intersection_class()
        # Update to match the settings
        intersection.__dict__.update(
            intersection_dict
        )
        if intersection.__class__ == Intersection:
            raise Exception("Expected subclass: %s %s %s" % (intersection_dict, configured_feature_behavior.intersection_subclassed, updated_existing_feature_behavior.intersection_subclassed))
        # Now that we've updated everything, set the id if we already had an Intersection
        intersection.id = existing_intersection.id if existing_intersection else None
        intersection.save()

        # Merge the tags from existing and configurations
        tags = unique((list(updated_existing_feature_behavior.tags.all()) if updated_existing_feature_behavior.pk else []) + \
                      updated_existing_feature_behavior._tags + \
                      configured_feature_behavior._tags)
        configured_feature_behavior._tags = tags

        # Update or create and set to the attributes of the passed in instance
        updated_existing_feature_behavior = updated_existing_feature_behavior.__class__.objects.update_or_create(
            behavior=updated_existing_feature_behavior.behavior,
            db_entity=self,
            # Merge defaults for the template with passed in values
            # on both the Behavior and FeatureBehavior
            defaults=merge(
                model_dict(updated_existing_feature_behavior, omit_fields=['behavior', 'db_entity', 'intersection']),
                model_dict(configured_feature_behavior, omit_fields=['behavior', 'db_entity', 'intersection']),
                dict(intersection=intersection) if intersection else dict())
        )[0]
        if not updated_existing_feature_behavior.intersection:
예제 #31
0
        intersection = intersection_class()
        # Update to match the settings
        intersection.__dict__.update(intersection_dict)
        if intersection.__class__ == Intersection:
            raise Exception(
                "Expected subclass: %s %s %s" %
                (intersection_dict,
                 configured_feature_behavior.intersection_subclassed,
                 updated_existing_feature_behavior.intersection_subclassed))
        # Now that we've updated everything, set the id if we already had an Intersection
        intersection.id = existing_intersection.id if existing_intersection else None
        intersection.save()

        # Merge the tags from existing and configurations
        tags = unique((list(updated_existing_feature_behavior.tags.all()) if updated_existing_feature_behavior.pk else []) + \
                      updated_existing_feature_behavior._tags + \
                      configured_feature_behavior._tags)
        configured_feature_behavior._tags = tags

        # Update or create and set to the attributes of the passed in instance
        updated_existing_feature_behavior = updated_existing_feature_behavior.__class__.objects.update_or_create(
            behavior=updated_existing_feature_behavior.behavior,
            db_entity=self,
            # Merge defaults for the template with passed in values
            # on both the Behavior and FeatureBehavior
            defaults=merge(
                model_dict(
                    updated_existing_feature_behavior,
                    omit_fields=['behavior', 'db_entity', 'intersection']),
                model_dict(
                    configured_feature_behavior,
예제 #32
0
    def test_config_entity_api__permissions(self):
        """
            Make sure that users only get ConfigEntity's that match their permission settings
        :return:
        """

        permission_configuration = TestConfigEntityPermissions.config_entity_configuration(
        )
        resource_name = 'config_entity'

        # Iterate through the test_configurstions and extract a user for each group_key
        # Make a dict with the user as the key and all the instances from the test_config that the
        # user corresponds to. This gives a lookup of a user to the config_entities that we expect
        # the user to be able to view
        # Create a user->instances dict
        # Combine our {user1:instances, user2:instances,...} dicts
        user_to_expected_instances = merge_dict_list_values(
            *map(
                lambda test_configuration:\
                    # Combine our [user, instance] pairs into {user1:instances, user2:instances,...}
                    # Remove null keys (owing to groups with no users)


                    compact_dict(map_to_dict_with_lists(
                        # Each test_configuration has several groups.
                        # For each group resolve a user and return [user, instance]
                        lambda group_key: [
                            get_first_value_or_none(Group.objects.get(name=group_key).user_set.all()),
                            test_configuration['instance']],
                        test_configuration['groups'].keys())),
                permission_configuration.test_configuration)
        )

        all_instances = set(
            unique(flatten(user_to_expected_instances.values())))
        for user, instances in user_to_expected_instances.items():
            other_instances = all_instances - set(instances)
            # Fetch all instances with this user and create a lookup so we can test
            # that the resulting instances are present or not present as expected according to
            # the permissions
            response = self.get(resource_name, user=user)
            result_instance_lookup = map_to_dict(
                lambda instance_dict:
                [int(instance_dict['id']), instance_dict],
                self.deserialize(response)['objects'])

            for instance in instances:
                matching_instance = result_instance_lookup.get(instance.id)
                assert_is_not_none(matching_instance,
                                   "User %s should have view permission to instance %s with id %s and key %s but does." % \
                                   (user.username,
                                    instance,
                                    instance.id,
                                    permission_configuration.key_class.Fab.remove(
                                        permission_configuration.instance_key_lambda(instance))))

            for instance in other_instances:
                assert_is_none(matching_instance,
                                   "User %s should not have view permission to instance %s with id %s and key %s but does." % \
                                   (user.username,
                                    instance,
                                    instance.id,
                                    permission_configuration.key_class.Fab.remove(
                                        permission_configuration.instance_key_lambda(instance))))