Changeset 31434 for branches/czw_branch/20110406/ippToPsps/jython/batch.py
- Timestamp:
- May 4, 2011, 3:20:38 PM (15 years ago)
- Location:
- branches/czw_branch/20110406
- Files:
-
- 2 edited
-
. (modified) (1 prop)
-
ippToPsps/jython/batch.py (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/czw_branch/20110406
- Property svn:mergeinfo changed
-
branches/czw_branch/20110406/ippToPsps/jython/batch.py
r31253 r31434 12 12 13 13 from datastore import Datastore 14 from scratchdb import ScratchDb 14 15 from gpc1db import Gpc1Db 15 16 from ipptopspsdb import IppToPspsDb … … 24 25 class Batch(object): 25 26 26 driverName="com.mysql.jdbc.Driver"27 28 27 ''' 29 28 Constructor … … 33 32 "../config/2/tables.vot" 34 33 ''' 35 def __init__(self, logger, batchType, inputFitsPath="", survey="" ):34 def __init__(self, logger, batchType, inputFitsPath="", survey="", useFullTables=False): 36 35 37 36 # set up logging 38 37 self.logger = logger 38 self.logger.info("-------------------------------------------------------------------------------") 39 39 self.logger.debug("Batch class constructor") 40 40 … … 44 44 self.inputFitsPath = inputFitsPath 45 45 self.survey = survey 46 self.useFullTables = useFullTables 47 48 # TODO 49 self.tablesToExport = [] 46 50 47 51 # open config 48 52 doc = ElementTree(file="config.xml") 49 53 50 # set up JDBC connection to local Db51 dbName = doc.find("localdatabase/name").text52 dbHost = doc.find("localdatabase/host").text53 dbUser = doc.find("localdatabase/user").text54 dbPass = doc.find("localdatabase/password").text55 self.localUrl = "jdbc:mysql://"+dbHost+"/"+dbName+"?user="+dbUser+"&password="+dbPass56 self.localCon = DriverManager.getConnection(self.localUrl)57 self.localStmt = self.localCon.createStatement()58 59 54 # create Gpc1Db object 60 55 self.gpc1Db = Gpc1Db(self.logger) 61 62 # get survey ID from init table 63 sql = "SELECT surveyID from Survey WHERE name = '" + survey + "'" 64 try: 65 rs = self.localStmt.executeQuery(sql) 66 rs.first() 67 self.surveyID = rs.getInt(1) 68 except: 69 self.logger.exception("No survey ID found for this survey: '" + survey + "'") 70 self.surveyID = -1; 56 self.ippToPspsDb = IppToPspsDb(logger) 57 self.scratchDb = ScratchDb(logger, self.useFullTables) 58 59 if self.survey != "": 60 self.surveyID = self.scratchDb.getSurveyID(self.survey) 71 61 72 # get dvo info from config 73 if survey != "": 74 dvoName = doc.find(survey+"dvo/name").text 75 dvoLocation = doc.find(survey+"dvo/location").text 62 # get dvo info from config 63 dvoName = doc.find("dvo_" + self.survey + "/name").text 64 self.dvoLocation = doc.find("dvo_" + self.survey + "/location").text 76 65 else: 77 66 dvoName = "" 78 dvoLocation = "" 67 self.dvoLocation = "" 68 self.surveyID = -1; 79 69 80 70 # get datastore info from config 81 71 self.datastore = Datastore(self.logger) 82 72 83 # create IppToPspsDb object and create a new batch 84 self.ippToPspsDb = IppToPspsDb(logger) 85 self.batchID = self.ippToPspsDb.createNewBatch(66, 86 survey, 73 # create a new batch 74 self.batchID = self.ippToPspsDb.createNewBatch( 87 75 self.getPspsBatchType(), 76 survey, 88 77 dvoName, 89 78 self.datastore.product) … … 95 84 if not os.path.exists(self.localOutPath): os.makedirs(self.localOutPath) 96 85 97 98 86 # store today's date 99 87 now = datetime.datetime.now(); 100 88 self.dateStr = now.strftime("%Y-%m-%d") 101 89 102 if self.inputFitsPath != "": self.parseFitsHeader() 103 104 # create DVO table 105 self.createDvoTable() 90 if self.inputFitsPath != "": 91 file = open(self.inputFitsPath) 92 self.header = self.parseFitsHeader(file) 93 self.logger.info("Read primary and found " + str(len(self.header)) + " header cards") 94 # TODO close file? 95 96 # create DVO tables if accessing DVO directly 97 if not self.useFullTables: self.scratchDb.createDvoTables() 106 98 107 99 ''' … … 111 103 112 104 self.logger.debug("Batch destructor") 113 self.localStmt.close() 114 self.localCon.close() 105 106 107 ''' 108 Returns the value from this dictinary or else NULL 109 ''' 110 def safeDictionaryAccess(self, header, key): 111 112 if key in header: return header[key] 113 else: return "NULL" 114 115 ''' 116 Finds and reads a header extension 117 ''' 118 def findAndReadFITSHeader(self, name, file): 119 120 found = False 121 122 while True: 123 124 index = file.tell() 125 126 record = file.read(80) 127 if not record: break; 128 129 # quit when we reach 'END' 130 if record.startswith("XTENSION= 'IMAGE"): 131 132 header = self.parseFitsHeader(file) 133 if header['EXTNAME'] == name: 134 found = True 135 file.seek(index + 2880, 0) 136 break 137 138 file.seek(index + 2880, 0) 139 140 if found != True: self.logger.error("...could not find extension '" + name + "'") 141 else: self.logger.info("...read header at '" + name + "' and found " + str(len(header)) + " header cards") 142 143 return header 144 115 145 116 146 ''' … … 127 157 root.attrib['name'] = self.batchName 128 158 root.attrib['type'] = self.getPspsBatchType() 129 root.attrib['survey'] = self.getPspsSurveyType()130 159 root.attrib['timestamp'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 131 root.attrib['minObjId'] = str(self.minObjID) 132 root.attrib['maxObjId'] = str(self.maxObjID) 160 if self.survey != "": 161 root.attrib['survey'] = self.getBatchFriendlySurveyType() 162 try: self.minObjID 163 except: pass 164 else: root.attrib['minObjId'] = str(self.minObjID) 165 try: self.maxObjID 166 except: pass 167 else: root.attrib['maxObjId'] = str(self.maxObjID) 133 168 134 169 # get md5sum … … 190 225 def publishToDatastore(self): 191 226 192 self.datastore.publish(self.batchName, self.subDir, self.tarballFile, "tgz")193 # TODO update ippToPsps Db here227 if self.datastore.publish(self.batchName, self.subDir, self.tarballFile, "tgz"): 228 self.ippToPspsDb.updateLoadedToDatastore(self.batchID, 1) 194 229 195 230 ''' 196 231 Gets PSPS-friendly survey type 197 232 ''' 198 def getPspsSurveyType(self): 199 200 if self.survey == "ThreePi": return "3PI" 201 elif self.survey == "MD04": return "MD04" 202 else: self.logger.error("Don't know this survey: " + self.survey) 233 def getBatchFriendlySurveyType(self): 234 235 return "SCR" # TODO 236 237 try: 238 self.survey 239 except: 240 return "NA" 241 242 if self.survey == "3PI": return "3PI" 243 elif self.survey == "MD04": return "MD4" 244 else: 245 self.logger.error("Don't know this survey: '" + self.survey + "'") 246 return "NA" 203 247 204 248 ''' … … 212 256 else: self.logger.error("Don't know this batch type: " + self.survey) 213 257 214 215 ''' 216 Prints a log message with the current time 217 ''' 218 def log(self, msg): 219 220 print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " | " + msg 221 222 ''' 223 Sets min and max obj ID using the provided table 224 ''' 225 def setMinMaxObjID(self, table): 226 227 sql = "SELECT MIN(objID), MAX(objID) FROM " + table 228 print 229 rs = self.localStmt.executeQuery(sql) 230 rs.first() 231 self.minObjID = rs.getLong(1) 232 self.maxObjID = rs.getLong(2) 233 234 print "MIN mAX = %d %d" % (self.minObjID, self.maxObjID) 235 236 ''' 237 Updates a table with surveyID 238 ''' 239 def updateSurveyID(self, table): 240 241 sql = "UPDATE " + table + " SET surveyID=%d" % self.surveyID 242 self.localStmt.execute(sql) 243 244 ''' 245 Updates a table with filterID grabbed from Filter init table 246 ''' 247 def updateFilterID(self, table): 248 249 sql = "UPDATE "+table+" AS a, Filter AS b SET a.filterID=b.filterID WHERE b.filterType = '" + self.filter + "'" 250 self.localStmt.execute(sql) 258 ''' 259 Sets min and max obj ID using the provided table, or list of tables 260 ''' 261 def setMinMaxObjID(self, tables): 262 263 first = True 264 for table in tables: 265 266 sql = "SELECT MIN(objID), MAX(objID) FROM " + table 267 rs = self.scratchDb.stmt.executeQuery(sql) 268 rs.first() 269 270 if first: 271 self.minObjID = rs.getLong(1) 272 self.maxObjID = rs.getLong(2) 273 else: 274 if rs.getLong(1) < self.minObjID: self.minObjID = rs.getLong(1) 275 if rs.getLong(2) > self.maxObjID: self.maxObjID = rs.getLong(2) 276 277 first = False 278 279 self.ippToPspsDb.updateMinMaxObjID(self.batchID, self.minObjID, self.maxObjID) 251 280 252 281 ''' 253 282 Reads FITS header and stores all fields in a dictionary object 254 283 ''' 255 def parseFitsHeader(self): 256 257 fitsFile = open(self.inputFitsPath) 258 259 self.header = {} 284 def parseFitsHeader(self, fitsFile): 285 286 header = {} 260 287 261 288 while (True): 289 262 290 record = fitsFile.read(80) 263 291 264 292 # quit when we reach 'END' 265 if record.startswith("END"): break 266 267 # ignore comments 268 if record.startswith("COMMENT"): continue 269 match = re.match('(.*)=(.*)', record) 293 if re.match('END\s+', record): break 294 295 # this regex will get param/value pairs for all header cards, ignoring comments and parsing out 'HIERACH' prefixes 296 match = re.match('^(HIERARCH )*([a-zA-Z0-9-_\.]+)\s*=\s+\'*([a-zA-Z0-9-_\.:\s@#]+)\'*\\/*', record) 270 297 if match: 271 298 272 # remove HIERARCH prefix 273 param = match.group(1).replace("HIERARCH", "") 274 param = param.strip() 275 276 value = match.group(2) 277 # remove trailing comment after / char, if there is one 278 index = value.find("/") 279 if index != -1: value = value[0:index] 280 281 # remove ' chars around content 282 value = value.replace("'", "") 283 284 # remove leading and trailing whitespace 285 value = value.strip() 286 287 # store in out dictionary object 288 self.header[param] = value 299 param = match.group(2) 300 value = match.group(3).strip() 301 if value == "NaN": value = "NULL" 302 header[param] = value 289 303 290 304 #print param + "|" + value + "|" 305 306 return header 291 307 292 308 ''' … … 298 314 for table in self.pspsTables: 299 315 self.logger.info("Creating PSPS table: " + table.name) 300 table.write(self.localUrl + '#' + table.name) 301 302 self.indexPspsTables() 316 table.write(self.scratchDb.url + '#' + table.name) 317 self.tablesToExport.append(table.name) 318 319 self.alterPspsTables(); 303 320 304 321 ''' … … 310 327 311 328 ''' 312 Adds an index to the supplied table and column 313 ''' 314 def createIndex(self, table, column): 315 316 self.logger.info("Creating index on column '"+column+"' for table '"+table+"'") 317 318 sql = "CREATE INDEX "+table+"_index ON "+table+" ("+column+")" 319 try: 320 self.localStmt.execute(sql) 321 except: 322 self.logger.warn("Index already in place on '" + column + "' for table '" + table + "'") 323 324 325 ''' 326 Subclass should implement this to index PSPS tables 329 Alter PSPS tables 327 330 ''' 328 def indexPspsTables(self):329 self.logger.warn(" indexPspsTables not implemented")331 def alterPspsTables(self): 332 self.logger.warn("alterPspsTables not implemented") 330 333 331 334 ''' 332 335 Imports IPP tables from FITS file 333 336 334 Accepts a regular expression filter so not all tabl s need to be imported337 Accepts a regular expression filter so not all tables need to be imported 335 338 ''' 336 339 def importIppTables(self, filter): 337 340 341 self.logger.info("Attempting to import tables from input FITS file") 338 342 tables = stilts.treads(self.inputFitsPath) 339 343 … … 343 347 match = re.match(filter, table.name) 344 348 if not match: continue 345 self.logger.info(" Creating IPP table " + table.name)349 self.logger.info(" Reading IPP table " + table.name + " from FITS file") 346 350 table = stilts.tpipe(table, cmd='explodeall') 351 352 # drop any previous tables before import 353 self.scratchDb.dropTable(table.name) 354 355 # IPP FITS files are littered with infinities, so remove these 356 self.logger.info(" Removing Infinity values from all columns") 357 table = stilts.tpipe(table, cmd='replaceval -Infinity null *') 358 table = stilts.tpipe(table, cmd='replaceval Infinity null *') 359 347 360 try: 348 table.write(self. localUrl + '#' + table.name)361 table.write(self.scratchDb.url + '#' + table.name) 349 362 except: 350 self.logger.exception("Problem writing table '" + table.name + "' to the database") 351 363 self.logger.exception(" Problem writing table '" + table.name + "' to the database") 352 364 count = count + 1 353 365 354 self.logger.info(" Imported %d tables from '%s' " % (count, self.inputFitsPath))366 self.logger.info("Done. Imported %d tables" % count) 355 367 356 368 self.indexIppTables() 357 369 358 370 ''' 359 Exports PSPS tables from the database to FITS format 371 Exports PSPS tables from the database to FITS format. Optional regex if you want to alter table names prior to export 360 372 ''' 361 def exportPspsTablesToFits(self ):362 363 self.logger.info(" Exporting all PSPS tables to FITS")373 def exportPspsTablesToFits(self, regex="(.*)"): 374 375 self.logger.info("Replacing NULLs with -999 then exporting all PSPS tables to FITS") 364 376 _tables = [] 365 377 366 378 self.logger.info(" Selecting database tables") 367 for table in self.pspsTables: 368 _table = stilts.tread(self.localUrl + '#SELECT * FROM ' + table.name) 369 _table = stilts.tpipe(_table, cmd='tablename ' + table.name) 379 for table in self.tablesToExport: 380 381 # check for an empty table 382 if self.scratchDb.getRowCount(table) < 1: continue 383 384 # get everything from table 385 _table = stilts.tread(self.scratchDb.url + '#SELECT * FROM ' + table) 386 387 # replace nulls and empty fields with weird PSPS -999 pseudo-null 388 _table = stilts.tpipe(_table, cmd='replaceval "" -999 *') 389 390 match = re.match(regex, table) 391 newTableName = match.group(1) 392 393 # change table names 394 _table = stilts.tpipe(_table, cmd='tablename ' + newTableName) 370 395 _tables.append(_table) 371 396 … … 373 398 stilts.twrites(_tables, self.outputFitsPath, fmt='fits') 374 399 self.logger.info(" ...done") 375 376 ''' 377 Returns a list of column names for this table 378 ''' 379 def getColumnNames(self, tableName): 380 381 sql = "SHOW COLUMNS FROM " + tableName 382 rs = self.localStmt.executeQuery(sql) 383 columns = [] 384 while (rs.next()): columns.append(rs.getString(1)) 385 rs.close() 386 387 return columns 388 389 ''' 390 Replaces all NULL values in the provided table with the prvoded substitute 391 ''' 392 def replaceNulls(self, tableName, sub): 393 394 # get list of columns 395 columns = self.getColumnNames(tableName) 396 397 # now loop through all columns and replace all NULLs with sub 398 for column in columns: 399 400 sql = "UPDATE " + tableName + " SET " + column + " = " + sub + " WHERE " + column + " IS NULL" 401 self.localStmt.execute(sql) 402 403 404 ''' 405 Searches a table and reports the columns that are either partially or completely populated with NULLs 406 ''' 407 def reportNulls(self, tableName, showPartials): 408 409 # first, count rows 410 sql = "SELECT COUNT(*) FROM " + tableName 411 rs = self.localStmt.executeQuery(sql) 412 rs.first() 413 numRows = rs.getInt(1) 414 415 # get list of columns 416 columns = self.getColumnNames(tableName) 417 418 print "+----------------------+---------------+" 419 print "| %25s |" % tableName 420 print "+----------------------+---------------+" 421 422 # now see which columns are full of NULLS, with are partially NULL 423 for column in columns: 424 425 sql = "SELECT COUNT(*) FROM " + tableName + " WHERE " + column + " IS NULL" 426 rs = self.localStmt.executeQuery(sql) 427 rs.first() 428 if rs.getInt(1) == numRows: 429 print "| %20s | all NULL |" % column 430 elif showPartials and rs.getInt(1) > 0: 431 print "| %20s | partial NULL |" % column 432 rs.close() 433 print "+----------------------+---------------+" 434 400 self.ippToPspsDb.updateProcessed(self.batchID, 1) 435 401 436 402 ''' … … 440 406 441 407 for table in self.pspsTables: 442 self. reportNulls(table.name, showPartials)408 self.scratchDb.reportNulls(table.name, showPartials) 443 409 444 410 ''' … … 449 415 self.logger.info("Replacing all NULL values in PSPS tables with '" + sub + "'...") 450 416 for table in self.pspsTables: 451 self. replaceNulls(table.name, sub)417 self.scratchDb.replaceNulls(table.name, sub) 452 418 self.logger.info("...done") 453 419 … … 459 425 460 426 ''' 461 Updates provided table with DVO IDs from DVOtable462 ''' 463 def updateDvoIDs(self, table):464 465 self.logger.info("Updating table '" + table + "' with DVO IDs...")466 sql = "UPDATE " + table + " AS a, dvo AS b SET \467 a.ippObjID = b.ippObjID, \468 a.objID = b.objID \469 WHERE a.ippDetectID = b.ippDetectID"470 self.localStmt.execute(sql)427 Calls DVO program to 'query' DVO database and populate results to local MySQL Db table 428 ''' 429 def getIDsFromDVO(self): 430 431 # TODO path to DVO prog hardcoded temporarily 432 cmd = "../src/dvograbber " + self.dvoLocation 433 self.logger.info("Running: '" + cmd + "'...") 434 p = Popen(cmd, shell=True, stdout=PIPE) 435 p.wait() 436 # out = p.stdout.read() 471 437 self.logger.info("...done") 472 438 473 474 ''' 475 Creates a table for for ID matching 476 ''' 477 def createDvoTable(self): 478 479 self.logger.info("Creating DVO table for ID matching") 480 sql = "DROP TABLE dvo" 481 self.localStmt.execute(sql) 482 sql = "CREATE TABLE dvo (ippDetectID BIGINT PRIMARY KEY, ippObjID BIGINT, objID BIGINT)" 483 self.localStmt.execute(sql) 484 439 if self.scratchDb.getRowCount("dvoDetection") < 1: 440 self.logger.error("No DVO IDs found") 441 return False 442 443 return True 444 445 ''' 446 Checks whether this batch has already been processed and published. To be implemented by all subclasses 447 ''' 448 def alreadyProcessed(self): 449 self.logger.info("Not implemented") 450 451 452
Note:
See TracChangeset
for help on using the changeset viewer.
