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)
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)
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
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)
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