1#!/usr/local/bin/python3.8 2############################################################################ 3# 4# MODULE: v.db.reconnect.all 5# AUTHOR(S): Radim Blazek 6# Converted to Python by Glynn Clements 7# Update for GRASS 7 by Markus Metz 8# PURPOSE: Reconnect all vector maps from the current mapset 9# COPYRIGHT: (C) 2004, 2012 by the GRASS Development Team 10# 11# This program is free software under the GNU General 12# Public License (>=v2). Read the file COPYING that 13# comes with GRASS for details. 14# 15############################################################################# 16 17#%module 18#% description: Reconnects attribute tables for all vector maps from the current mapset to a new database. 19#% keyword: vector 20#% keyword: attribute table 21#% keyword: database 22#%end 23#%flag 24#% key: c 25#% description: Copy attribute tables to the target database if not exist 26#%end 27#%flag 28#% key: d 29#% description: Delete attribute tables from the source database 30#%end 31#%option G_OPT_DB_DATABASE 32#% key: old_database 33#% description: Name of source database 34#%end 35#%option G_OPT_DB_SCHEMA 36#% key: old_schema 37#% label: Name of source database schema 38#%end 39#%option 40#% key: new_driver 41#% description: Name for target driver 42#%end 43#%option G_OPT_DB_DATABASE 44#% key: new_database 45#% description: Name for target database 46#%end 47#%option G_OPT_DB_SCHEMA 48#% key: new_schema 49#% label: Name for target database schema 50#%end 51 52import sys 53import os 54import string 55 56import grass.script as gscript 57from grass.exceptions import CalledModuleError 58 59# substitute variables (gisdbase, location_name, mapset) 60 61 62def substitute_db(database): 63 gisenv = gscript.gisenv() 64 tmpl = string.Template(database) 65 66 return tmpl.substitute(GISDBASE=gisenv['GISDBASE'], 67 LOCATION_NAME=gisenv['LOCATION_NAME'], 68 MAPSET=gisenv['MAPSET']) 69 70# create database if doesn't exist 71 72 73def create_db(driver, database): 74 subst_database = substitute_db(database) 75 if driver == 'dbf': 76 path = subst_database 77 # check if destination directory exists 78 if not os.path.isdir(path): 79 # create dbf database 80 os.makedirs(path) 81 return True 82 return False 83 84 if driver == 'sqlite': 85 path = os.path.dirname(subst_database) 86 # check if destination directory exists 87 if not os.path.isdir(path): 88 os.makedirs(path) 89 90 if subst_database in gscript.read_command('db.databases', quiet=True, 91 driver=driver).splitlines(): 92 return False 93 94 gscript.info(_("Target database doesn't exist, " 95 "creating a new database using <%s> driver...") % driver) 96 try: 97 gscript.run_command('db.createdb', driver=driver, 98 database=subst_database) 99 except CalledModuleError: 100 gscript.fatal(_("Unable to create database <%s> by driver <%s>") % 101 (subst_database, driver)) 102 103 return False 104 105# copy tables if required (-c) 106 107 108def copy_tab(from_driver, from_database, from_table, 109 to_driver, to_database, to_table): 110 if to_table in gscript.read_command('db.tables', quiet=True, 111 driver=to_driver, 112 database=to_database, 113 stderr=nuldev).splitlines(): 114 return False 115 116 gscript.info("Copying table <%s> to target database..." % to_table) 117 try: 118 gscript.run_command('db.copy', from_driver=from_driver, 119 from_database=from_database, 120 from_table=from_table, to_driver=to_driver, 121 to_database=to_database, 122 to_table=to_table) 123 except CalledModuleError: 124 gscript.fatal(_("Unable to copy table <%s>") % from_table) 125 126 return True 127 128# drop tables if required (-d) 129 130 131def drop_tab(vector, layer, table, driver, database): 132 # disconnect 133 try: 134 gscript.run_command('v.db.connect', flags='d', quiet=True, map=vector, 135 layer=layer, table=table) 136 except CalledModuleError: 137 gscript.warning(_("Unable to disconnect table <%s> from vector <%s>") % 138 (table, vector)) 139 # drop table 140 try: 141 gscript.run_command('db.droptable', quiet=True, flags='f', 142 driver=driver, database=database, 143 table=table) 144 except CalledModuleError: 145 gscript.fatal(_("Unable to drop table <%s>") % table) 146 147# create index on key column 148 149 150def create_index(driver, database, table, index_name, key): 151 if driver == 'dbf': 152 return False 153 154 gscript.info(_("Creating index <%s>...") % index_name) 155 try: 156 gscript.run_command('db.execute', quiet=True, driver=driver, 157 database=database, 158 sql="create unique index %s on %s(%s)" % 159 (index_name, table, key)) 160 except CalledModuleError: 161 gscript.warning(_("Unable to create index <%s>") % index_name) 162 163 164def main(): 165 # old connection 166 old_database = options['old_database'] 167 old_schema = options['old_schema'] 168 # new connection 169 default_connection = gscript.db_connection() 170 if options['new_driver']: 171 new_driver = options['new_driver'] 172 else: 173 new_driver = default_connection['driver'] 174 if options['new_database']: 175 new_database = options['new_database'] 176 else: 177 new_database = default_connection['database'] 178 if options['new_schema']: 179 new_schema = options['new_schema'] 180 else: 181 new_schema = default_connection['schema'] 182 183 if old_database == '': 184 old_database = None 185 old_database_subst = None 186 if old_database is not None: 187 old_database_subst = substitute_db(old_database) 188 189 new_database_subst = substitute_db(new_database) 190 191 if old_database_subst == new_database_subst and old_schema == new_schema: 192 gscript.fatal(_("Old and new database connection is identical. " 193 "Nothing to do.")) 194 195 mapset = gscript.gisenv()['MAPSET'] 196 197 vectors = gscript.list_grouped('vect')[mapset] 198 num_vectors = len(vectors) 199 200 if flags['c']: 201 # create new database if not existing 202 create_db(new_driver, new_database) 203 204 i = 0 205 for vect in vectors: 206 vect = "%s@%s" % (vect, mapset) 207 i += 1 208 gscript.message(_("%s\nReconnecting vector map <%s> " 209 "(%d of %d)...\n%s") % 210 ('-' * 80, vect, i, num_vectors, '-' * 80)) 211 for f in gscript.vector_db(vect, stderr=nuldev).values(): 212 layer = f['layer'] 213 schema_table = f['table'] 214 key = f['key'] 215 database = f['database'] 216 driver = f['driver'] 217 218 # split schema.table 219 if '.' in schema_table: 220 schema, table = schema_table.split('.', 1) 221 else: 222 schema = '' 223 table = schema_table 224 225 if new_schema: 226 new_schema_table = "%s.%s" % (new_schema, table) 227 else: 228 new_schema_table = table 229 230 gscript.debug("DATABASE = '%s' SCHEMA = '%s' TABLE = '%s' ->\n" 231 " NEW_DATABASE = '%s' NEW_SCHEMA_TABLE = '%s'" % 232 (old_database, schema, table, new_database, 233 new_schema_table)) 234 235 do_reconnect = True 236 if old_database_subst is not None: 237 if database != old_database_subst: 238 do_reconnect = False 239 if database == new_database_subst: 240 do_reconnect = False 241 if schema != old_schema: 242 do_reconnect = False 243 244 if do_reconnect == True: 245 gscript.verbose(_("Reconnecting layer %d...") % layer) 246 247 if flags['c']: 248 # check if table exists in new database 249 copy_tab(driver, database, schema_table, 250 new_driver, new_database, new_schema_table) 251 252 # drop original table if required 253 if flags['d']: 254 drop_tab( 255 vect, 256 layer, 257 schema_table, 258 driver, 259 substitute_db(database)) 260 261 # reconnect tables (don't use substituted new_database) 262 # NOTE: v.db.connect creates an index on the key column 263 try: 264 gscript.run_command('v.db.connect', flags='o', quiet=True, 265 map=vect, layer=layer, 266 driver=new_driver, 267 database=new_database, 268 table=new_schema_table, key=key) 269 except CalledModuleError: 270 gscript.warning(_("Unable to connect table <%s> to vector " 271 "<%s> on layer <%s>") % 272 (table, vect, str(layer))) 273 274 else: 275 if database != new_database_subst: 276 gscript.warning(_("Layer <%d> will not be reconnected " 277 "because database or schema do not " 278 "match.") % layer) 279 return 0 280 281if __name__ == "__main__": 282 options, flags = gscript.parser() 283 nuldev = open(os.devnull, 'w') 284 sys.exit(main()) 285