Ejemplo n.º 1
0
    def importer(self, config_entity, db_entity, **kwargs):
        """
            Creates various GeojsonFeature classes by importing geojson and saving it to the database via a dynamic subclass of GeojsonFeature
        :schema: The optional schema to use for the dynamic subclass's meta db_table attribute, which will allow the class's table to be saved in the specified schema. Defaults to public
        :data: Optional python dict data to use instead of loading from the db_entity.url
        :return: a list of lists. Each list is a list of features of distinct subclass of GeoJsonFeature that is created dynamically. To persist these features, you must first create the subclass's table in the database using create_table_for_dynamic_class(). You should also register the table as a DbEntity.
        """
        if self.seed_data:
            data = geojson.loads(jsonify(self.seed_data), object_hook=geojson.GeoJSON.to_instance)
        else:
            fp = open(db_entity.url.replace('file://', ''))
            data = geojson.load(fp, object_hook=geojson.GeoJSON.to_instance)
        feature_class_creator = FeatureClassCreator(config_entity, db_entity)
        # find all unique properties
        feature_class_configuration = feature_class_creator.feature_class_configuration_from_geojson_introspection(data)
        feature_class_creator.update_db_entity(feature_class_configuration)
        feature_class = feature_class_creator.dynamic_model_class(base_only=True)
        # Create our base table. Normally this is done by the import, but we're just importing into memory
        create_tables_for_dynamic_classes(feature_class)
        # Now write each feature to our newly created table
        for feature in map(lambda feature: self.instantiate_sub_class(feature_class, feature), data.features):
            feature.save()
        # Create the rel table too
        rel_feature_class = feature_class_creator.dynamic_model_class()
        create_tables_for_dynamic_classes(rel_feature_class)

        # PostGIS 2 handles this for us now
        # if InformationSchema.objects.table_exists(db_entity.schema, db_entity.table):
        #     # Tell PostGIS about the new geometry column or the table
        #     sync_geometry_columns(db_entity.schema, db_entity.table)

        # Create association classes and tables and populate them with data
        create_and_populate_relations(config_entity, db_entity)
Ejemplo n.º 2
0
    def create_empty_source_table(self, clazz, source_table_name, source_db_connection, extra_fields={}):
        project = Project(key='sutter_county', id=0)
        db_entity = DbEntity(key=Keys.DB_ABSTRACT_BASE_AGRICULTURE_FEATURE, feature_class_configuration=dict(
            abstract_class=full_module_path(clazz)
        ))
        SourceClass = FeatureClassCreator(project, db_entity, no_ensure=True).dynamic_model_class(base_only=True, schema='public', table=source_table_name)
        # class SourceClass(clazz):
        #     class Meta(clazz.Meta):
        #         db_table = source_table_name
        create_tables_for_dynamic_classes(SourceClass)
        for field in SourceClass._meta.fields[1:]:
            setattr(field, 'null', True)

        drop_table = "DROP TABLE IF EXISTS {final_table} CASCADE;".format(final_table=source_table_name)

        sql, refs = source_db_connection.creation.sql_create_model(SourceClass, color_style())
        add_geometry_fields = '''
            ALTER TABLE {final_table} ADD COLUMN geography_id VARCHAR;
            ALTER TABLE {final_table} ADD COLUMN wkb_geometry GEOMETRY;'''.format(final_table=source_table_name)

        sql = drop_table + sql[0] + add_geometry_fields
        for dbfield, fieldtype in extra_fields.items():
            sql += 'ALTER TABLE {final_table} ADD COLUMN {field} {type}'.format(
                final_table=source_table_name, field=dbfield, type=fieldtype)
        source_db_connection.cursor().execute(sql)
Ejemplo n.º 3
0
def get_or_create_layer_selection_class_for_layer(layer, config_entity=None, no_table_creation=False):
    """
        Generate a subclass of LayerSelection specific to the layer and use it to create a table
    :param layer
    :param config_entity: Defaults to the ConfigEntity that owns the DbEntity of the Layer. This
    should be set explicitly if the LayerSelection is in the context of a lower ConfigEntity, namely
    the active Scenario from the user interface. Setting the ConfigEntity is important if the LayerSelection
    contains a query that joins Feature classes that belong to that lower ConfigEntity
    :param no_table_creation for debugging, don't create the underlying table
    :return:
    """

    config_entity = config_entity or layer.config_entity
    # Name the class based on the optional passed in config_entity so that they are cached as separated classes
    dynamic_class_name = "LayerSelectionL{0}C{1}".format(layer.id, config_entity.id)
    try:
        feature_class = config_entity.db_entity_feature_class(layer.db_entity_key)
    except:
        logging.exception("no feature class")
        # For non feature db_entities, like google maps
        return None

    dynamic_through_class = dynamic_model_class(
        LayerSelectionFeature,
        layer.config_entity.schema(),
        "lsf_%s_%s" % (layer.id, layer.db_entity_key),
        class_name="{0}{1}".format(dynamic_class_name, "Feature"),
        fields=dict(
            layer_selection=models.ForeignKey(dynamic_class_name, null=False),
            feature=models.ForeignKey(feature_class, null=False),
        ),
    )

    # Table is layer specific. Use ls instead of layer_selection to avoid growing the schema.table over 64 characters
    table_name = "ls_%s_%s" % (layer.id, layer.db_entity_key)
    dynamic_class = dynamic_model_class(
        LayerSelection,
        # Schema is that of the config_entity
        layer.config_entity.schema(),
        table_name,
        class_name=dynamic_class_name,
        # The config_entity instance is a class attribute
        # This config_entity can be a child of the layer.config_entity if we need the
        # dynamic class to be in the context of a lower ConfigEntity in order to access lower join tables in its query
        class_attrs=dict(config_entity__id=config_entity.id, layer__id=layer.id, override_db=config_entity.db),
        related_class_lookup=dict(
            config_entity="footprint.main.models.config.config_entity.ConfigEntity",
            layer="footprint.main.models.presentation.layer.layer.Layer",
        ),
        fields=dict(
            features=models.ManyToManyField(feature_class, through=dynamic_through_class, related_name=table_name)
        ),
    )

    # Make sure the tables exist
    if not no_table_creation:
        create_tables_for_dynamic_classes(dynamic_class, dynamic_through_class)

    return dynamic_class
Ejemplo n.º 4
0
    def create_empty_source_table(self,
                                  clazz,
                                  source_table_name,
                                  source_db_connection,
                                  extra_fields={}):
        project = Project(key='sutter_county', id=0)
        db_entity = DbEntity(key=Keys.DB_ABSTRACT_BASE_AGRICULTURE_FEATURE,
                             feature_class_configuration=dict(
                                 abstract_class=full_module_path(clazz)))
        SourceClass = FeatureClassCreator(project, db_entity,
                                          no_ensure=True).dynamic_model_class(
                                              base_only=True,
                                              schema='public',
                                              table=source_table_name)
        # class SourceClass(clazz):
        #     class Meta(clazz.Meta):
        #         db_table = source_table_name
        create_tables_for_dynamic_classes(SourceClass)
        for field in SourceClass._meta.fields[1:]:
            setattr(field, 'null', True)

        drop_table = "DROP TABLE IF EXISTS {final_table} CASCADE;".format(
            final_table=source_table_name)

        sql, refs = source_db_connection.creation.sql_create_model(
            SourceClass, color_style())
        add_geometry_fields = '''
            ALTER TABLE {final_table} ADD COLUMN geography_id VARCHAR;
            ALTER TABLE {final_table} ADD COLUMN wkb_geometry GEOMETRY;'''.format(
            final_table=source_table_name)

        sql = drop_table + sql[0] + add_geometry_fields
        for dbfield, fieldtype in extra_fields.items():
            sql += 'ALTER TABLE {final_table} ADD COLUMN {field} {type}'.format(
                final_table=source_table_name, field=dbfield, type=fieldtype)
        source_db_connection.cursor().execute(sql)
Ejemplo n.º 5
0
def get_or_create_layer_selection_class_for_layer(layer, config_entity=None, no_table_creation=False):
    """
        Generate a subclass of LayerSelection specific to the layer and use it to create a table
    :param layer
    :param config_entity: Defaults to the ConfigEntity that owns the DbEntity of the Layer. This
    should be set explicitly if the LayerSelection is in the context of a lower ConfigEntity, namely
    the active Scenario from the user interface. Setting the ConfigEntity is important if the LayerSelection
    contains a query that joins Feature classes that belong to that lower ConfigEntity
    :param no_table_creation for debugging, don't create the underlying table
    :return:
    """

    config_entity = config_entity or layer.config_entity
    # Name the class based on the optional passed in config_entity so that they are cached as separated classes
    dynamic_class_name = 'LayerSelectionL{0}C{1}'.format(layer.id, config_entity.id)
    try:
        feature_class = config_entity.db_entity_feature_class(layer.db_entity_key)
    except:
        logging.exception('no feature class')
        # For non feature db_entities, like google maps
        return None

    dynamic_through_class = dynamic_model_class(
        LayerSelectionFeature,
        layer.config_entity.schema(),
        'lsf_%s_%s' % (layer.id, layer.db_entity_key),
        class_name='{0}{1}'.format(dynamic_class_name, 'Feature'),
        fields=dict(
            layer_selection=models.ForeignKey(dynamic_class_name, null=False),
            feature=models.ForeignKey(feature_class, null=False),
        )
    )

    # Table is layer specific. Use ls instead of layer_selection to avoid growing the schema.table over 64 characters
    table_name = 'ls_%s_%s' % (layer.id, layer.db_entity_key)
    dynamic_class = dynamic_model_class(
        LayerSelection,
        # Schema is that of the config_entity
        layer.config_entity.schema(),
        table_name,
        class_name=dynamic_class_name,
        # The config_entity instance is a class attribute
        # This config_entity can be a child of the layer.config_entity if we need the
        # dynamic class to be in the context of a lower ConfigEntity in order to access lower join tables in its query
        class_attrs=dict(
            config_entity__id=config_entity.id,
            layer__id=layer.id,
            override_db=config_entity.db
        ),
        related_class_lookup=dict(
            config_entity='footprint.main.models.config.config_entity.ConfigEntity',
            layer='footprint.main.models.presentation.layer.layer.Layer'
        ),
        fields=dict(
            features=models.ManyToManyField(feature_class, through=dynamic_through_class, related_name=table_name)
        )
    )

    # Make sure the tables exist
    if not no_table_creation:
        create_tables_for_dynamic_classes(dynamic_class, dynamic_through_class)

    return dynamic_class