IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Sep 19, 2014, 4:05:27 PM (12 years ago)
Author:
eugene
Message:

merge changes from trunk

Location:
branches/eam_branches/ps2-tc3-20130727
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/ps2-tc3-20130727

  • branches/eam_branches/ps2-tc3-20130727/ippToPsps/jython/stackbatch.py

    r36680 r37403  
    44import glob
    55import sys
     6import math
    67
    78import stilts
     
    1617from ipptopspsdb import IppToPspsDb
    1718from scratchdb import ScratchDb
     19from sqlUtility import sqlUtility
    1820
    1921import logging.config
     
    3739                 ippToPspsDb,
    3840                 scratchDb,
    39                  stackID,
    40                  batchID,
    41                  useFullTables):
     41                 skyID,
     42                 batchID):
    4243
    4344       super(StackBatch, self).__init__(
     
    4849               ippToPspsDb,
    4950               scratchDb,
    50                stackID,
     51               skyID,
    5152               batchID,
    5253               "ST",
    53                gpc1Db.getStackStageCmf(skychunk.dvoLabel, stackID),
    54                useFullTables)
    55 
    56        # self.printline = 0
    57        # # self.testprint()
     54               None)
    5855
    5956       self.stackType = "DEEP_STACK" # TODO
    60 
    61        # get stack meta data
    62        meta = self.gpc1Db.getStackStageMeta(self.id)
    63        if not meta:
    64            self.logger.errorPair("Could not get stack", "metadata")
     57       self.stackTypeID = self.getStackTypeID()
     58       
     59       self.filters = ['g', 'r', 'i', 'z', 'y']
     60       self.stackIDs = {}
     61       self.imageIDs = {}
     62       self.fits = {}
     63       self.headerSet = {}
     64
     65       # we have two sets of imageIDs here:
     66
     67       # stackID : this is the id from the gpc1 stack table and (should) match the
     68       # cmf header keyword 'IMAGEID', this is also called EXTERN_ID in dvoImagesTable
     69       
     70       # imageID : this is the DVO-internal image ID for the stack cmf, and is called
     71       # IMAGE_ID in dvoImagesTable
     72
     73       # we just need one valid filter to get the generic metadata
     74       validFilter = 'none'
     75
     76       # get a list of stackIDs, identify the CMFs, read the headers:
     77       for filter in self.filters:
     78           filterName = filter + ".00000"
     79
     80           self.logger.infoPair ("getting stackID for: ", filterName)
     81           stackID = gpc1Db.getStackIDFromSkyIDAndFilter(skychunk.dvoLabel, skyID, filterName)
     82           self.stackIDs[filter] = stackID
     83
     84           if (stackID <= 0):
     85               continue
     86               
     87           validFilter = filter
     88
     89           # from each stack, get fits name from getStackStageCmf
     90           self.logger.infoPair ("finding fits file for: ", filterName)
     91           self.fits[filter] = gpc1Db.getStackStageCmf(skychunk.dvoLabel, stackID)
     92           if not self.fits[filter]:
     93               self.logger.errorPair("could not find cmf file name for ", stackID)
     94               raise
     95
     96           header = self.fits[filter].getPrimaryHeader()
     97           if not header:
     98               self.logger.errorPair("could not read FITS PHU for ", stackID)
     99               raise
     100
     101           self.headerSet[filter] = header
     102
     103           self.logger.infoPair(filter + " stackID = ", stackID)       
     104
     105           if not (str(stackID) == header['IMAGEID']):
     106               self.logger.errorPair("inconsistency: header IMAGEID does not match stackID", str(stackID) + " vs " + header['IMAGEID'])
     107               raise
     108
     109           imageID = self.scratchDb.getImageIDFromExternID(stackID)
     110           self.logger.infoPair(filter + " imageID = ", imageID)       
     111           self.imageIDs[filter] = imageID
     112
     113       # did we find any valid stacks?
     114       if validFilter == 'none':
     115           self.logger.errorPair("can't find any valid stacks...?? ","??")
    65116           raise
    66 
    67        self.filter = meta[0];
    68        self.filter = self.filter[0:1]
    69        self.filterID = self.scratchDb.getFilterID(self.filter)
    70        self.skycell = meta[1];
    71 
    72        # self.testprint()
    73 
    74        # skycell is, eg "skycell.1133.081"
    75        # we need 1133.081 for skycell, but store as an int, so instead store 1133081
    76        # projection cell is just the 1133 bit
    77        # this is how it should be done
    78        #self.projectioncell = self.skycell[8:12]
    79        #self.skycell = self.skycell[8:]
    80        #self.skycell = self.skycell.replace(".", "")
    81        #self.projectioncell = self.skycell[8:12]
    82 
    83        # TODO this is the wrong way, but is consistent with SkyCell table in IN data
    84        self.skycell = self.skycell[8:12]
    85        self.projectioncell = self.skycell
    86 
    87        # self.testprint()
    88 
    89        # proposed new values. Need to coordinate with the SkyCell table
    90        # this fits in 32 bits for 0 < tessID < 214
    91        # self.skycell = str( (tessID * (10000 * 1000)) + (int(self.projectioncell) * 1000) + cellID )
    92 
    93        self.analysisVer = meta[2];
    94 
    95        #self.expTime = gpc1Db.getStackExpTime(self.id)
    96        self.expTime = self.header['EXPTIME']
     117   
     118       validStackID = self.stackIDs[validFilter]
     119
     120       # using the given stack ID, get the generic information: filter, skycell, etc.
     121       metadata = self.gpc1Db.getStackStageMeta(validStackID) 
     122
     123       if not metadata:
     124           self.logger.errorPair("Could not get stack metadata for", validStackID)
     125           raise
     126
     127       validHeader = self.headerSet[validFilter]
     128
     129       tessName = self.getKeyValue(header, 'TESS_ID')
     130       self.tessID = self.scratchDb.getTessID(tessName)
     131
     132       skycellName = self.getKeyValue(header, 'SKYCELL')
     133
     134       # XXX EAM 20140812 : I am hardwiring the parsing logic for RINGS vs LOCAL
     135       if tessName == 'RINGS.V3':
     136           # skycell is, eg "skycell.1133.081"
     137           #                 0123456789012345
     138           self.projectionID = skycellName[8:12]
     139           self.skycellID = skycellName[13:]
     140       else:
     141           # skycell is, eg "skycell.081"
     142           #                 01234567890
     143           self.projectionID = 0
     144           self.skycellID = skycellName[8:11]
     145       if self.skycellID == "":
     146           self.skycellID = -1
     147
     148       self.analysisVer = metadata[2];
    97149
    98150       # delete PSPS tables
    99        self.scratchDb.dropTable("StackMeta")
    100        self.scratchDb.dropTable("StackDetection")
    101        self.scratchDb.dropTable("StackModelFit")
    102        self.scratchDb.dropTable("StackApFlx")
    103        self.scratchDb.dropTable("StackToImage")
    104        self.scratchDb.dropTable("SkinnyObject")
    105        #self.scratchDb.dropTable("ObjectCalColor")
    106        self.scratchDb.dropTable("StackDetectionCalib")
    107 
    108        # self.testprint()
     151       for filter in self.filters:
     152           tableName = filter + "StackMeta"
     153           self.dropTableVerbose(tableName)
     154
     155       self.dropTableVerbose("StackObjectThin")
     156       self.dropTableVerbose("StackObjectRaw")
     157       
     158       self.dropTableVerbose("StackModelFitExtra")
     159       self.dropTableVerbose("StackModelFitDeV")
     160       self.dropTableVerbose("StackModelFitExp")
     161       self.dropTableVerbose("StackModelFitSer")
     162
     163       self.dropTableVerbose("StackApFlx")
     164       self.dropTableVerbose("StackApFlxFull")
     165       self.dropTableVerbose("StackApFlxFullC1")
     166       self.dropTableVerbose("StackApFlxFullC2")
     167
     168       self.dropTableVerbose("StackPetrosian")
     169
     170       self.dropTableVerbose("StackToImage")
    109171
    110172       # delete IPP tables
    111        self.scratchDb.dropTable("SkyChip_psf")
    112        self.scratchDb.dropTable("SkyChip_xrad")
    113        self.scratchDb.dropTable("SkyChip_xfit")
    114        self.scratchDb.dropTable("SkyChip_xsrc")
     173       if not self.config.retry:
     174           self.logger.info("dropping Stack cmf tables")
     175           for filter in self.filters:
     176               tableName = filter + "SkyChip_psf"
     177               self.scratchDb.dropTable(tableName)
     178               tableName = filter + "SkyChip_xrad"
     179               self.scratchDb.dropTable(tableName)
     180               tableName = filter + "SkyChip_xsrc"
     181               self.scratchDb.dropTable(tableName)
     182               tableName = filter + "SkyChip_xfit"
     183               self.scratchDb.dropTable(tableName)
    115184
    116185       # create an output filename, which is {stackID}.FITS
     186       self.logger.infoPair( "self.id", self.id)
     187       self.logger.infoPair ("self.localOutPath",self.localOutPath)
    117188       self.outputFitsFile = "%08d.FITS" % self.id
    118189       self.outputFitsPath = "%s/%s" % (self.localOutPath, self.outputFitsFile)
    119 
     190       self.logger.infoPair("self.outputFitsFile",self.outputFitsFile)
     191       
    120192       # set some constants
    121193       self.historyModNum = "0"
    122194
    123195       # insert what we know about this stack batch into the stack table
    124        self.ippToPspsDb.insertStackMeta(self.batchID, self.filter, self.stackType)
    125 
    126        # insert sourceID/imageID combo so DVO can look it up
    127        if not self.useFullTables:
    128            self.scratchDb.insertNewDvoExternID(self.header['SOURCEID'], self.header['IMAGEID'])
    129 
    130        # self.testprint()
     196       self.ippToPspsDb.insertStackMeta(self.batchID, "x" , self.stackType)
    131197
    132198       # dump stuff to log
    133199       self.logger.infoPair("Stack ID", "%d" % self.id)
    134200       self.logger.infoPair("Stack type", "%s" % self.stackType)
    135        self.logger.infoPair("Skycell", "%s" % self.skycell)
    136        self.logger.infoPair("Projectioncell", "%s" % self.projectioncell)
    137        self.logger.infoPair("Filter", "%s" % self.filter)
    138 
    139     '''
    140     Updates a table with stackMetaID
    141     '''
    142     def updateStackMetaID(self, table):
    143 
    144         sql = "UPDATE " + table + "  SET stackMetaID=" + str(self.id)
    145         self.scratchDb.execute(sql)
    146 
    147     '''
    148     Updates a table with stackTypeID grabbed from FitModel init table
    149     '''
    150     def updateStackTypeID(self, table):
    151 
    152         sql = "UPDATE "+table+" AS a, StackType AS b SET a.stackTypeID=b.stackTypeID WHERE b.name = '" + self.stackType + "'"
    153         self.scratchDb.execute(sql)
    154 
    155 
    156     def testprint(self):
    157       print "here ", self.printline
    158       self.printline += 1
     201       self.logger.infoPair("SkycellID", "%s" % self.skycellID)
     202       self.logger.infoPair("ProjectionID", "%s" % self.projectionID)
     203
     204    ### *********************** main populate functions ************************************
     205
     206    '''
     207    Populates the StackMeta table, mainly from header values found in stack cmf FITS header
     208    '''
     209    def populateStackMeta(self,filter):
     210
     211        header = self.headerSet[filter]
     212        tablename = filter + "StackMeta"
     213        stackID = self.stackIDs[filter]
     214        filterName = filter + ".00000"
     215
     216        self.logger.infoPair("Populating table", tablename)
     217
     218        fwhm_maj    = self.getKeyFloat(header, "%.8f", 'FWHM_MAJ')
     219        fwhm_maj_uq = self.getKeyFloat(header, "%.8f", 'FW_MJ_UQ')
     220        psfmodel    = self.getKeyValue(header, 'PSFMODEL')
     221
     222        # make a table
     223        filterID = self.scratchDb.getFilterID(filter)
     224        self.logger.infoPair("filterID is ",str(filterID))
     225
     226        photoCalID = str(self.scratchDb.getPhotoCalID(stackID))
     227
     228        # mysql is sensitive to values which are ambiugously float.  eg
     229        # a warning is raised if we try to insert '25.' into a float field.
     230        # use getKeyFloat(hdr, format, key) to avoid this problem
     231
     232        sql = "CREATE TABLE " + tablename + " LIKE StackMeta"
     233        try: self.scratchDb.execute(sql)
     234        except: pass
     235
     236        sqlLine = sqlUtility("INSERT INTO " + tablename + " (")
     237
     238        sqlLine.group("stackMetaID",   str(stackID))       
     239        sqlLine.group("batchID",       str(self.batchID))
     240        sqlLine.group("surveyID",      str(self.surveyID))
     241        sqlLine.group("filterID",      str(filterID))           
     242        sqlLine.group("stackTypeID",   str(self.stackTypeID))
     243        sqlLine.group("tessID",        str(self.tessID))           
     244        sqlLine.group("projectionID",  str(self.projectionID))           
     245        sqlLine.group("skyCellID",     str(self.skycellID))           
     246        sqlLine.group("photoCalID",    photoCalID)
     247        sqlLine.group("analysisVer",   str(self.analysisVer))
     248        sqlLine.group("expTime",       self.getKeyFloat(header, "%.5f", 'EXPTIME')) 
     249        sqlLine.group("psfModelID",    psfmodel)           
     250        sqlLine.group("psfFwhm_mean",  fwhm_maj)     
     251        sqlLine.group("psfFwhm_max",   fwhm_maj_uq)   
     252        sqlLine.group("photoZero",     self.getKeyFloat(header, "%.5f", 'FPA.ZP'))
     253        sqlLine.group("ctype1",        header['CTYPE1']) 
     254        sqlLine.group("ctype2",        header['CTYPE2']) 
     255        sqlLine.group("crval1",        self.getKeyFloat(header, "%.8f", 'CRVAL1'))   
     256        sqlLine.group("crval2",        self.getKeyFloat(header, "%.8f", 'CRVAL2'))   
     257        sqlLine.group("crpix1",        self.getKeyFloat(header, "%.8f", 'CRPIX1'))   
     258        sqlLine.group("crpix2",        self.getKeyFloat(header, "%.8f", 'CRPIX2'))   
     259        sqlLine.group("cdelt1",        self.getKeyFloat(header, "%.8e", 'CDELT1'))   
     260        sqlLine.group("cdelt2",        self.getKeyFloat(header, "%.8e", 'CDELT2'))   
     261        sqlLine.group("pc001001",      self.getKeyFloat(header, "%.8e", 'PC001001'))
     262        sqlLine.group("pc001002",      self.getKeyFloat(header, "%.8e", 'PC001002'))
     263        sqlLine.group("pc002001",      self.getKeyFloat(header, "%.8e", 'PC002001'))
     264        sqlLine.group("pc002002",      self.getKeyFloat(header, "%.8e", 'PC002002'))
     265        sqlLine.group("dataRelease",   str(self.skychunk.dataRelease))
     266
     267        sql = sqlLine.make(") VALUES ( ", ")")
     268
     269        try: self.scratchDb.execute(sql)
     270        except:
     271            self.logger.error("problem with SQL: " + sql)
     272            raise
     273
     274        self.tablesToExport.append(tablename)
     275
     276    '''
     277    Populates the StackObjectThin table
     278    '''
     279    def populateStackObjectThin(self):
     280
     281        self.logger.infoPair("Procesing table", "StackObjectThin")
     282
     283        # insert the per-object data (IDs, random stack ID, dataRelease, etc)
     284        self.selectDvoObjIDs()
     285
     286        # add indexes StackObjectThin
     287        self.scratchDb.createIndex("StackObjectThin", "objID")
     288
     289        self.logger.infoPair("inserting filter dependent items into", "StackObjectThin")
     290
     291        for filter in self.filters:
     292            self.logger.infoPair("filter", filter)
     293
     294            stackID = self.stackIDs[filter]
     295            self.logger.infoPair(filter + "stackID = ", stackID)
     296
     297            if stackID <= 0: 
     298                self.logger.infoPair("no stack data for filter" , filter)
     299                continue
     300
     301            imageID = self.imageIDs[filter]
     302            self.logger.infoPair("selecting imageID", imageID)
     303
     304            # at this point, the StackObjectThin table includes objID and ippObjID
     305
     306            # insert detection information for each filter based on the DVO contents
     307            sqlLine = sqlUtility("UPDATE StackObjectThin as a, " + self.scratchDb.dvoDetectionTable + " as b SET")
     308
     309            sqlLine.group("a."+filter+"ippDetectID",   "b.ippDetectID") # eg, gippDetectID (from dvo.measure.detID)
     310            sqlLine.group("a."+filter+"stackDetectID", "b.detectID")    # eg, gstackDetectID (from dvo.measure.extID)
     311            sqlLine.group("a."+filter+"stackMetaID",    str(stackID))   # eg, gstackMetaID (from cmf.IMAGEID)
     312
     313            sqlLine.group("a.dvoRegionID",             "b.catID")       # this is not filter dependent, but is re-written for each filter (if data exists)
     314            sqlLine.group("a.primaryDetection",        "((b.flags & 0x10000) >> 16)") # this is not filter dependent, but is re-written for each filter (if data exists)
     315
     316            sqlLine.group("a."+filter+"haveData",      "'1'")
     317
     318            sqlLine.group("a."+filter+"ra",            "b.ra")
     319            sqlLine.group("a."+filter+"dec",           "b.dec_")
     320
     321            sqlLine.group("a."+filter+"PSFMag",        "b.Mpsf")
     322            sqlLine.group("a."+filter+"PSFMagErr",     "b.dMpsf")
     323            sqlLine.group("a."+filter+"KronMag",       "b.Mkron")
     324            sqlLine.group("a."+filter+"KronMagErr",    "b.dMkron")
     325            sqlLine.group("a."+filter+"ApMag",         "b.Map")
     326            sqlLine.group("a."+filter+"ApMagErr",      "b.dMap")
     327            sqlLine.group("a."+filter+"telluricExt",   "b.telluricExt")
     328
     329            sqlLine.group("a."+filter+"infoFlag2",     "(b.flags << 13)")
     330            sql = sqlLine.makeEquals("WHERE a.objID = b.objID AND b.imageID = " + str(imageID))
     331
     332            try: self.scratchDb.execute(sql)
     333            except:
     334                self.logger.errorPair('failed sql: ', sql)
     335                raise
     336
     337            # insert values coming from the CMF, not carried by DVO
     338            header = self.headerSet[filter]
     339            exptime = self.getKeyFloat(header, "%.5f", "EXPTIME")
     340
     341            sqlLine = sqlUtility("UPDATE StackObjectThin AS a , " + filter + "SkyChip_psf AS b SET")
     342
     343            sqlLine.group("a."+filter+"raErr",         "b.X_PSF_SIG * b.PLTSCALE")
     344            sqlLine.group("a."+filter+"decErr",        "b.Y_PSF_SIG * b.PLTSCALE")
     345            sqlLine.group("a."+filter+"sky",           "b.SKY / " + str(exptime))
     346            sqlLine.group("a."+filter+"skyErr",        "b.SKY_SIGMA / " + str(exptime))
     347            sqlLine.group("a."+filter+"infoFlag",      "b.FLAGS")
     348            sqlLine.group("a."+filter+"infoFlag2",     "b.FLAGS2 | a."+filter+"infoFlag2")
     349            sqlLine.group("a."+filter+"nFrames",       "b.N_FRAMES")
     350
     351            sql = sqlLine.makeEquals("WHERE a." + filter + "ippDetectID=b.IPP_IDET")
     352
     353            # index the (f)ippDetectIDs for the next steps?
     354            # for filter in self.filters:
     355            #     IDname = filter + "ippDetectID"
     356            #     self.scratchDb.createIndex("StackObjectThin", IDname)
     357
     358            try:
     359                self.scratchDb.execute(sql)
     360            except:
     361                self.logger.errorPair('failed sql: ', sql)
     362                raise
     363
     364        self.tablesToExport.append("StackObjectThin")
     365
     366    '''
     367    Populates the StackObjectRaw table
     368    '''
     369    def populateStackObjectRaw(self):
     370
     371        tablename = "StackObjectRaw"
     372
     373        self.logger.infoPair("Procesing table", tablename)
     374        self.insertDvoIDs(tablename, "StackObjectThin")
     375
     376        # if we are going to add a key, do it here so it is useful
     377        self.scratchDb.createIndex(tablename, "objID")
     378
     379        self.logger.infoPair("inserting filter dependent cmf items into", tablename)
     380        for filter in self.filters:
     381            self.logger.infoPair("filter", filter)
     382
     383            stackID = self.stackIDs[filter]
     384            self.logger.infoPair(filter + "stackID = ", stackID)
     385
     386            if stackID <= 0: 
     387                self.logger.infoPair("no stack data for filter" , filter)
     388                continue
     389
     390            header = self.headerSet[filter]
     391            exptime = self.getKeyFloat(header, "%.5f", "EXPTIME")
     392
     393            # insert all the detections
     394            sqlLine = sqlUtility("UPDATE " + tablename + " AS a , " + filter + "SkyChip_psf AS b SET")
     395
     396            sqlLine.group("a."+filter+"haveData",      "'1'")
     397
     398            sqlLine.group("a."+filter+"xPos",          "b.X_PSF")
     399            sqlLine.group("a."+filter+"yPos",          "b.Y_PSF")
     400            sqlLine.group("a."+filter+"xPosErr",       "b.X_PSF_SIG")
     401            sqlLine.group("a."+filter+"yPosErr",       "b.Y_PSF_SIG")
     402            sqlLine.group("a."+filter+"psfMajorFWHM",  "b.PSF_FWHM_MAJ")
     403            sqlLine.group("a."+filter+"psfMinorFWHM",  "b.PSF_FWHM_MIN")
     404            sqlLine.group("a."+filter+"psfTheta",      "b.PSF_THETA")
     405            sqlLine.group("a."+filter+"psfCore",       "b.PSF_CORE")
     406            sqlLine.group("a."+filter+"psfLikelihood", "psfLikelihood(b.EXT_NSIGMA)")
     407            sqlLine.group("a."+filter+"psfQf",         "b.PSF_QF")
     408            sqlLine.group("a."+filter+"psfQfPerfect",  "b.PSF_QF_PERFECT")
     409            sqlLine.group("a."+filter+"psfChiSq",      "b.PSF_CHISQ / b.PSF_NDOF")
     410            sqlLine.group("a."+filter+"momentXX",      "b.MOMENTS_XX")
     411            sqlLine.group("a."+filter+"momentXY",      "b.MOMENTS_XY")
     412            sqlLine.group("a."+filter+"momentYY",      "b.MOMENTS_YY")
     413            sqlLine.group("a."+filter+"momentR1",      "b.MOMENTS_R1")
     414            sqlLine.group("a."+filter+"momentRH",      "b.MOMENTS_RH")
     415            sqlLine.group("a."+filter+"PSFFlux",       "b.PSF_INST_FLUX / " + str(exptime))
     416            sqlLine.group("a."+filter+"PSFFluxErr",    "b.PSF_INST_FLUX_SIG / " + str(exptime))
     417            sqlLine.group("a."+filter+"ApFlux",        "b.AP_FLUX / " + str(exptime))
     418            sqlLine.group("a."+filter+"ApFluxErr",     "b.AP_FLUX_SIG / " + str(exptime))
     419            sqlLine.group("a."+filter+"KronFlux",      "b.KRON_FLUX / " + str(exptime))
     420            sqlLine.group("a."+filter+"KronFluxErr",   "b.KRON_FLUX_ERR / " + str(exptime))
     421            sqlLine.group("a."+filter+"ApFillFac",     "b.AP_NPIX / (3.14159265359 * POW(b.AP_MAG_RADIUS - 0.5, 2))")
     422            sqlLine.group("a."+filter+"ApRadius",      "b.AP_MAG_RADIUS")
     423            sqlLine.group("a."+filter+"KronRad",       "b.MOMENTS_R1 * 2.5")
     424            sqlLine.group("a."+filter+"PlateScale",    "b.PLTSCALE")
     425
     426            sql = sqlLine.makeEquals("WHERE a." + filter + "ippDetectID = b.IPP_IDET")
     427
     428            try:
     429                self.scratchDb.execute(sql)
     430            except:
     431                self.logger.errorPair('failed sql',sql)
     432                return
     433
     434            # insert detection information for each filter based on the DVO contents
     435            sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + self.scratchDb.dvoDetectionTable + " as b SET")
     436            sqlLine.group("a."+filter+"expTime",       "b.expTime")
     437            sqlLine.group("a."+filter+"zp",            "b.zp")
     438
     439            # where should these go?
     440            # sqlLine.group("a."+filter+"airmass",       "b.airmass")
     441
     442            sql = sqlLine.makeEquals("WHERE a.objID = b.objID AND a." + filter + "ippDetectID = b.ippDetectID")
     443
     444            try:
     445                self.scratchDb.execute(sql)
     446            except:
     447                self.logger.errorPair('failed sql',sql)
     448                return
     449
     450
     451        self.deleteRowsWithNoStackData(tablename)
     452        self.tablesToExport.append(tablename)
     453
     454    '''
     455    Populates the StackModelFitExtra table
     456    '''
     457    def populateStackModelFitExtra(self):
     458
     459        self.logger.infoPair("Procesing table", "StackModelFitExtra")
     460        self.insertDvoIDs("StackModelFitExtra", "StackObjectThin")
     461
     462        # if we are going to add a key, do it here so it is useful
     463        # self.scratchDb.createIndex(tablename, "objID")
     464
     465        # currently, none of the fields defined by the table are generated
     466       
     467    '''
     468    Populates the StackModelFitExp table
     469    '''
     470    def populateStackModelFit(self, model, ippModelType):
     471
     472        tablename = "StackModelFit" + model
     473
     474        if self.config.camera == "simtest":
     475            self.logger.infoPair("Skipping table for simtest:", tablename)
     476            return
     477
     478        self.logger.infoPair("Procesing table", tablename)
     479        self.insertDvoIDs(tablename, "StackObjectThin")
     480
     481        # if we are going to add a key, do it here so it is useful
     482        # self.scratchDb.createIndex(table, "objID")
     483
     484        for filter in self.filters:
     485            self.populateStackModelFitFilter(model, ippModelType, filter)
     486
     487        self.deleteRowsWithNoStackData(tablename)
     488        self.tablesToExport.append(tablename)
     489
     490    '''
     491    Updates parameters for a particular model in the StackModelFit table
     492    '''
     493    def populateStackModelFitFilter(self, model, ippModelType, filter):
     494
     495        tablename = "StackModelFit" + model
     496
     497        if self.stackIDs[filter] <= 0:
     498            self.logger.infoPair("no stack data for filter" , filter)
     499            return True
     500
     501        header  = self.headerSet[filter]
     502
     503        # model calibrated magnitude = instrumental magnitude + 2.5*log10(exptime) + ZP (added below from dvoDetectionTable)
     504        magtime = "%.5f" % (2.5*math.log10(float(header['EXPTIME'])))
     505
     506        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + filter + "SkyChip_xfit AS b SET")
     507
     508        sqlLine.group("a."+filter+"haveData",              "'1'")
     509
     510        # model calibrated magnitude = instrumental magnitude + 2.5*log10(exptime) + ZP (added below from dvoDetectionTable)
     511        sqlLine.group("a." + filter + model + "Mag",       "b.EXT_INST_MAG + " + magtime) # need to modify by zero point and 2.5*log10(exptime)
     512        sqlLine.group("a." + filter + model + "MagErr",    "b.EXT_INST_MAG_SIG")
     513                     
     514        sqlLine.group("a." + filter + model + "Radius",    "b.EXT_WIDTH_MAJ")
     515        sqlLine.group("a." + filter + model + "Ab",        "b.EXT_WIDTH_MIN/b.EXT_WIDTH_MAJ")
     516        sqlLine.group("a." + filter + model + "Phi",       "DEGREES(b.EXT_THETA)")
     517
     518        sqlLine.group("a." + filter + model + "RadiusErr", "b.EXT_WIDTH_MAJ_ERR") # these are not correctly defined.
     519        sqlLine.group("a." + filter + model + "AbErr",     "b.EXT_WIDTH_MIN_ERR") # these are not correctly defined.
     520        sqlLine.group("a." + filter + model + "PhiErr",    "b.EXT_THETA_ERR") # these are not correctly defined.
     521
     522        sqlLine.group("a." + filter + model + "Chisq",     "b.EXT_CHISQ / b.EXT_NDOF")
     523
     524        # I have raExp = X_EXT
     525
     526        # This is tricky: RA_EXT is not defined for PS1_SV3 or PS1_V5, but X_EXT is
     527        # XXX EAM 20140812: for PV3, update cmf output to define RA_EXT, DEC_EXT
     528        sqlLine.group("a." + filter + model + "Ra",        "b.X_EXT")
     529        sqlLine.group("a." + filter + model + "Dec",       "b.Y_EXT")
     530        sqlLine.group("a." + filter + model + "RaErr",     "b.X_EXT_SIG") # need to modify by platescale
     531        sqlLine.group("a." + filter + model + "DecErr",    "b.Y_EXT_SIG")
     532
     533        # sersic fit has an extra parameter
     534        if (ippModelType == "PS_MODEL_SERSIC"):
     535            sqlLine.group("a." + filter + model + "Nu",    "b.EXT_PAR_07")
     536            sqlLine.group("a." + filter + model + "NuErr", "'0.1'") # this is not measured at the moment
     537
     538        # missing these two fields?
     539        # sqlLine.group("a." + filter + model + "CF",         "b.XX")
     540        # sqlLine.group("a." + filter + model + "Likelihood", "b.XX")
     541
     542        sql = sqlLine.makeEquals("WHERE a." + filter + "ippDetectID = b.IPP_IDET AND b.MODEL_TYPE = '" + ippModelType + "'")
     543
     544        try: self.scratchDb.execute(sql)
     545        except:
     546            self.logger.error("problem with SQL: " + sql)
     547            raise
     548
     549        # insert detection information for each filter based on the
     550        # DVO contents.  Since I already have extracted
     551        # StackObjectRaw, this is probably faster using that table:
     552        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + self.scratchDb.dvoDetectionTable + " as b SET")
     553        sqlLine.group("a." + filter + model + "Mag",       "a." + filter + model + "Mag + b.zp")
     554        sql = sqlLine.makeEquals("WHERE a.objID = b.objID AND a." + filter + "ippDetectID = b.ippDetectID")
     555
     556        try:
     557            self.scratchDb.execute(sql)
     558        except:
     559            self.logger.errorPair('failed sql',sql)
     560            return
     561
     562        # we have (modulo signs):
     563        # gRaExp  = gra  + (X_EXT - gxPos)*gPlateScale / cos(gdec)
     564        # gDecExp = gdec + (Y_EXT - gyPos)*gPlateScale
     565
     566        rawS  = "c." + filter + "PlateScale"
     567
     568        extR  = "a." + filter + model + "Ra"
     569        rawR  = "b." + filter + "ra"
     570        rawX  = "c." + filter + "xPos"
     571
     572        extD  = "a." + filter + model + "Dec"
     573        rawD  = "b." + filter + "dec"
     574        rawY  = "c." + filter + "yPos"
     575
     576        # dRA / dX = -PlateScale
     577        # b.+filter+ra  - (a.+filter+model+Ra - c.+filter+xPos)*c.+filter+PlateScale / cos(+filter+dec)
     578        # extR = rawR - (extR - rawX)*rawS / 3600.0 / cos(rawD)
     579
     580        # dDEC / dY = +PlateScale
     581        # b.+filter+dec + (a.+filter+model+Dec - c.+filter+xPos)*c.+filter+PlateScale / cos(+filter+dec)
     582        # extD = rawD + (extD - rawY)*rawS / 3600.0
     583
     584        # insert detection information for each filter based on the DVO contents
     585        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, StackObjectThin as b, StackObjectRaw as c SET ")
     586        sqlLine.group(extR, rawR + " - (" + extR + " - " + rawX + ")*" + rawS + " / 3600.0 / cos(radians(" + rawD + "))")
     587        sqlLine.group(extD, rawD + " + (" + extD + " - " + rawY + ")*" + rawS + " / 3600.0")
     588        field = "a." + filter + model + "RaErr"
     589        sqlLine.group(field, field + "*" + rawS)
     590        field = "a." + filter + model + "DecErr"
     591        sqlLine.group(field, field + "*" + rawS)
     592        sql = sqlLine.makeEquals("WHERE a.objID = b.objID AND a.objID = c.objID")
     593
     594        try:
     595            self.scratchDb.execute(sql)
     596        except:
     597            self.logger.errorPair('failed sql',sql)
     598            return
     599
     600    '''
     601    Populates the StackPetrosian table, mainly from dictionary values found in IPP FITS header
     602    '''
     603    def populateStackPetrosian(self):
     604
     605        tablename = "StackPetrosian"
     606
     607        if self.config.camera == "simtest":
     608            self.logger.infoPair("Skipping table for simtest:", tablename)
     609            return
     610
     611        self.logger.infoPair("Procesing table", tablename)
     612        self.insertDvoIDs(tablename, "StackObjectThin")
     613
     614        # if we are going to add a key, do it here so it is useful
     615        # self.scratchDb.createIndex(tablename, "objID")
     616
     617        for filter in self.filters:
     618            self.populateStackPetrosianFilter(filter)
     619
     620        self.deleteRowsWithNoStackData(tablename)
     621        self.tablesToExport.append(tablename)
     622
     623    '''
     624    Populates the StackPetrosian table, mainly from dictionary values found in IPP FITS header
     625    '''
     626    def populateStackPetrosianFilter(self, filter):
     627
     628        tablename = "StackPetrosian"
     629
     630        if self.stackIDs[filter] <= 0:
     631            self.logger.infoPair("no stack data for filter" , filter)
     632            return True
     633
     634        header  = self.headerSet[filter]
     635
     636        # PETRO_MAG is instrumental + 2.5log(exptime) + FPA.ZP
     637        # we want to apply the DVO zero point, so remove FPA.ZP:
     638
     639        hdrZP   = "%.5f" % float(header['FPA.ZP'])
     640        # magtime = "%.5f" % (2.5*math.log10(float(header['EXPTIME'])))
     641
     642        self.logger.infoPair("Adding", "petrosians for extended sources")
     643        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + filter + "SkyChip_xsrc AS b SET")
     644
     645        sqlLine.group("a."+filter+"haveData",         "'1'")
     646
     647        sqlLine.group("a." + filter + "petRadius",    "b.PETRO_RADIUS")
     648        sqlLine.group("a." + filter + "petRadiusErr", "b.PETRO_RADIUS_ERR")
     649        sqlLine.group("a." + filter + "petMag",       "b.PETRO_MAG - " + hdrZP) 
     650        sqlLine.group("a." + filter + "petMagErr",    "1.0 / b.PETRO_MAG_ERR") 
     651        sqlLine.group("a." + filter + "petR50",       "b.PETRO_RADIUS_50")
     652        sqlLine.group("a." + filter + "petR50Err",    "b.PETRO_RADIUS_50_ERR")
     653        sqlLine.group("a." + filter + "petR90",       "b.PETRO_RADIUS_90")
     654        sqlLine.group("a." + filter + "petR90Err",    "b.PETRO_RADIUS_90_ERR")
     655        sqlLine.group("a." + filter + "petCf",        "b.PETRO_FILL")
     656
     657        sql = sqlLine.makeEquals("WHERE a." + filter + "ippDetectID = b.IPP_IDET")
     658
     659        try: self.scratchDb.execute(sql)
     660        except:
     661            self.logger.error("problem with SQL: " + sql)
     662            raise
     663
     664        # modify petMag to apply the zero point
     665        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + self.scratchDb.dvoDetectionTable + " as b SET")
     666        sqlLine.group("a." + filter + "petMag",       "a." + filter + "petMag + b.zp")
     667        sql = sqlLine.makeEquals("WHERE a.objID = b.objID AND a." + filter + "ippDetectID = b.ippDetectID")
     668
     669        try: self.scratchDb.execute(sql)
     670        except:
     671            self.logger.errorPair('failed sql',sql)
     672            return
     673
     674    '''
     675    Populates the StackApFlx table
     676    '''
     677    def populateStackApFlx(self):
     678
     679        tablename = "StackApFlx"
     680
     681        if self.config.camera == "simtest":
     682            self.logger.infoPair("Skipping table for simtest:", tablename)
     683            return
     684
     685        self.logger.infoPair("Procesing table", tablename)
     686        self.insertDvoIDs(tablename, "StackObjectThin")
     687
     688        # if we are going to add a key, do it here so it is useful
     689        self.scratchDb.createIndex(tablename, "objID")
     690
     691        for filter in self.filters:
     692            self.populateStackApFlxFilter(tablename, "", filter)
     693            self.populateStackApFlxFilter(tablename, "C1", filter)
     694            self.populateStackApFlxFilter(tablename, "C2", filter)
     695
     696        self.deleteRowsWithNoStackData(tablename)
     697        self.tablesToExport.append(tablename)
     698
     699    '''
     700    Populates the StackApFlxFull table
     701    '''
     702    def populateStackApFlxFull(self, version):
     703
     704        tablename = "StackApFlxFull" + version
     705
     706        if self.config.camera == "simtest":
     707            self.logger.infoPair("Skipping table for simtest:", tablename)
     708            return
     709
     710        self.logger.infoPair("Procesing table", tablename)
     711        self.insertDvoIDs(tablename, "StackObjectThin")
     712
     713        # if we are going to add a key, do it here so it is useful
     714        self.scratchDb.createIndex(tablename, "objID")
     715
     716        for filter in self.filters:
     717            self.populateStackApFlxFilter(tablename, version, filter)
     718
     719        self.deleteRowsWithNoStackData(tablename)
     720        self.tablesToExport.append(tablename)
    159721
    160722    '''
    161723    Updates aperture fluxes for StackApFkx table
    162724    '''
    163     def updateApFlxs(self, prefix, psfCondition):
    164         #properly -999 these to start with:
    165 
    166         sql = "UPDATE StackApFlx AS a, SkyChip_xrad AS b SET \
    167         "+prefix+"flxR1=-999, \
    168         "+prefix+"flxR1Err=-999, \
    169         "+prefix+"flxR1Std=-999, \
    170         "+prefix+"flxR1Fill=-999, \
    171         "+prefix+"flxR2=-999, \
    172         "+prefix+"flxR2Err=-999, \
    173         "+prefix+"flxR2Std=-999, \
    174         "+prefix+"flxR2Fill=-999, \
    175         "+prefix+"flxR3=-999, \
    176         "+prefix+"flxR3Err=-999, \
    177         "+prefix+"flxR3Std=-999, \
    178         "+prefix+"flxR3Fill=-999, \
    179         "+prefix+"flxR4=-999, \
    180         "+prefix+"flxR4Err=-999, \
    181         "+prefix+"flxR4Std=-999, \
    182         "+prefix+"flxR4Fill=-999, \
    183         "+prefix+"flxR5=-999, \
    184         "+prefix+"flxR5Err=-999, \
    185         "+prefix+"flxR5Std=-999, \
    186         "+prefix+"flxR5Fill=-999, \
    187         "+prefix+"flxR6=-999, \
    188         "+prefix+"flxR6Err=-999, \
    189         "+prefix+"flxR6Std=-999, \
    190         "+prefix+"flxR6Fill=-999, \
    191         "+prefix+"flxR7=-999, \
    192         "+prefix+"flxR7Err=-999, \
    193         "+prefix+"flxR7Std=-999, \
    194         "+prefix+"flxR7Fill=-999, \
    195         "+prefix+"flxR8=-999, \
    196         "+prefix+"flxR8Err=-999, \
    197         "+prefix+"flxR8Std=-999, \
    198         "+prefix+"flxR8Fill=-999, \
    199         "+prefix+"flxR9=-999, \
    200         "+prefix+"flxR9Err=-999, \
    201         "+prefix+"flxR9Std=-999, \
    202         "+prefix+"flxR9Fill=-999, \
    203         "+prefix+"flxR10=-999, \
    204         "+prefix+"flxR10Err=-999, \
    205         "+prefix+"flxR10Std=-999, \
    206         "+prefix+"flxR10Fill=-999 \
    207         WHERE a.ippDetectID=b.IPP_IDET "
    208         #self.logger.infoPair("sql", sql)
    209         self.scratchDb.execute(sql)
    210 
    211 
    212         #we have variable numbers of these columns. find out which are in use
    213         sql = "DESCRIBE SkyChip_xrad 'aper_fill_%'"
     725    def populateStackApFlxFilter(self, tablename, version, filter):
     726
     727        prefix = "none"
     728
     729        # order of the 3 convolutions is fixed (raw, C1, C2) = (1, 2, 3)
     730        if version == "":   
     731            psfCondition = "(b.table_index % 3 = 1)"
     732            prefix = ""
     733        if version == "C1":
     734            psfCondition = "(b.table_index % 3 = 2)"
     735            prefix = "c1"
     736        if version == "C2":
     737            psfCondition = "(b.table_index % 3 = 0)"
     738            prefix = "c2"
     739
     740        if prefix == "none":
     741            self.logger.errorPair("invalid stack ap flux version", version)
     742            raise
     743
     744        if self.stackIDs[filter] <= 0:
     745            self.logger.infoPair("no stack data for filter" , filter)
     746            return True
     747
     748        minRadius = 1
     749        maxRadius = 10
     750        if tablename == 'StackApFlx':
     751            minRadius = 3
     752            maxRadius = 5
     753
     754        # properly -999 these to start with.  the default should take
     755        # care of this, but does not
     756
     757        sqlLine = sqlUtility("UPDATE " + tablename + " SET")
     758        for radius in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
     759            if radius < minRadius: continue
     760            if radius > maxRadius: continue
     761            sqlLine.group(filter + prefix + "flxR" + str(radius),          "-999")
     762            sqlLine.group(filter + prefix + "flxR" + str(radius) + "Err",  "-999")
     763            sqlLine.group(filter + prefix + "flxR" + str(radius) + "Std",  "-999")
     764            sqlLine.group(filter + prefix + "flxR" + str(radius) + "Fill", "-999")
     765
     766        sql = sqlLine.makeEquals("")
     767
     768        try: self.scratchDb.execute(sql)
     769        except:
     770            self.logger.errorPair('failed sql',sql)
     771            return
     772
     773        # set the flux values from the cmf file
     774        cmfTable = filter + "SkyChip_xrad"
     775
     776        # we have variable numbers of these columns. find out which are in use
     777        sql = "DESCRIBE " + cmfTable + " 'aper_fill_%'"
    214778        rs = self.scratchDb.executeQuery(sql)
    215779        rs.first()
     
    222786        rs.close()
    223787
    224         #use regex to leave just the numbers
    225 
     788        # use regex to select the aperture number (APER_FILL_N)
    226789        numbers = []
    227790        for column in columns:
    228791            number = re.sub("APER_FILL_","",column)
    229             if (int(number) >= 1 and int(number) <=10):
     792            if (int(number) >= minRadius and int(number) <= maxRadius):
    230793                numbers.append(number)
    231794                self.logger.infoPair("Aperture Numbers",number)
    232795
    233         #generate the sql to do the necessary ops on the columns   
    234         sql = "UPDATE StackApFlx AS a, SkyChip_xrad AS b SET "
    235         cnt =0
     796        # get the exposure time for aperture fluxes
     797        header = self.headerSet[filter]
     798        exptime = self.getKeyFloat(header, "%.5f", "EXPTIME")
     799
     800        # generate the sql to do the necessary ops on the columns   
     801        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + cmfTable + " AS b SET ")
     802
     803        sqlLine.group("a."+filter+"haveData", "'1'")
     804
    236805        for number in numbers:
    237             if (cnt>0):
    238                 sql=sql+" , "
    239             sql=sql+prefix+"flxR"+number+"=b.APER_FLUX_"+number +" / " + str(self.expTime) + ", "
    240             sql=sql+prefix+"flxR"+number+"Err=b.APER_FLUX_ERR_"+number +" / " + str(self.expTime) + ", "
    241             sql=sql+prefix+"flxR"+number+"Std=b.APER_FLUX_STDEV_"+number +" / " + str(self.expTime) + ", "
    242             sql=sql+prefix+"flxR"+number+"Fill=b.APER_FILL_"+number + " "
    243             cnt=cnt+1
     806            field = filter + prefix + "flxR" + number
     807            sqlLine.group(field,          "b.APER_FLUX_"       + number + " / " + exptime)
     808            sqlLine.group(field + "Err",  "b.APER_FLUX_ERR_"   + number + " / " + exptime)
     809            sqlLine.group(field + "Std",  "b.APER_FLUX_STDEV_" + number + " / " + exptime)
     810            sqlLine.group(field + "Fill", "b.APER_FILL_"       + number)
    244811           
    245         sql=sql+"WHERE a.ippDetectID=b.IPP_IDET AND  "+psfCondition     
     812        sql = sqlLine.makeEquals("WHERE a." + filter + "ippDetectID = b.IPP_IDET AND  " + psfCondition)
     813
    246814        self.scratchDb.execute(sql)
    247815
    248         #self.logger.infoPair("sql",sql)
    249 
    250         #sql = "UPDATE StackApFlx AS a, SkyChip_xrad AS b SET \
    251         #"+prefix+"flxR1=b.APER_FLUX_1 / " + str(self.expTime) + ", \
    252         #"+prefix+"flxR1Err=b.APER_FLUX_ERR_1 / " + str(self.expTime) + ", \
    253         #"+prefix+"flxR1Std=b.APER_FLUX_STDEV_1 / " + str(self.expTime) + ", \
    254         #"+prefix+"flxR1Fill=b.APER_FILL_1, \
    255         #"+prefix+"flxR2=b.APER_FLUX_2 / " + str(self.expTime) + ", \
    256         ##"+prefix+"flxR2Err=b.APER_FLUX_ERR_2 / " + str(self.expTime) + ", \
    257         #"+prefix+"flxR2Std=b.APER_FLUX_STDEV_2 / " + str(self.expTime) + ", \
    258         #"+prefix+"flxR2Fill=b.APER_FILL_2, \
    259         #"+prefix+"flxR3=b.APER_FLUX_3 / " + str(self.expTime) + ", \
    260         #"+prefix+"flxR3Err=b.APER_FLUX_ERR_3 / " + str(self.expTime) + ", \
    261         #"+prefix+"flxR3Std=b.APER_FLUX_STDEV_3 / " + str(self.expTime) + ", \
    262         #"+prefix+"flxR3Fill=b.APER_FILL_3, \
    263         #"+prefix+"flxR4=b.APER_FLUX_4 / " + str(self.expTime) + ", \
    264         #"+prefix+"flxR4Err=b.APER_FLUX_ERR_4 / " + str(self.expTime) + ", \
    265         #"+prefix+"flxR4Std=b.APER_FLUX_STDEV_4 / " + str(self.expTime) + ", \
    266         #"+prefix+"flxR4Fill=b.APER_FILL_4, \
    267         #"+prefix+"flxR5=b.APER_FLUX_5 / " + str(self.expTime) + ", \
    268         #"+prefix+"flxR5Err=b.APER_FLUX_ERR_5 / " + str(self.expTime) + ", \
    269         #"+prefix+"flxR5Std=b.APER_FLUX_STDEV_5 / " + str(self.expTime) + ", \
    270         #"+prefix+"flxR5Fill=b.APER_FILL_5, \
    271         #"+prefix+"flxR6=b.APER_FLUX_6 / " + str(self.expTime) + ", \
    272         #"+prefix+"flxR6Err=b.APER_FLUX_ERR_6 / " + str(self.expTime) + ", \
    273         #"+prefix+"flxR6Std=b.APER_FLUX_STDEV_6 / " + str(self.expTime) + ", \
    274         #"+prefix+"flxR6Fill=b.APER_FILL_6, \
    275         #"+prefix+"flxR7=b.APER_FLUX_7 / " + str(self.expTime) + ", \
    276         #"+prefix+"flxR7Err=b.APER_FLUX_ERR_7 / " + str(self.expTime) + ", \
    277         #"+prefix+"flxR7Std=b.APER_FLUX_STDEV_7 / " + str(self.expTime) + ", \
    278         #"+prefix+"flxR7Fill=b.APER_FILL_7, \
    279         #"+prefix+"flxR8=b.APER_FLUX_8 / " + str(self.expTime) + ", \
    280         #"+prefix+"flxR8Err=b.APER_FLUX_ERR_8 / " + str(self.expTime) + ", \
    281         #"+prefix+"flxR8Std=b.APER_FLUX_STDEV_8 / " + str(self.expTime) + ", \
    282         #"+prefix+"flxR8Fill=b.APER_FILL_8, \
    283         #"+prefix+"flxR9=b.APER_FLUX_9 / " + str(self.expTime) + ", \
    284         #"+prefix+"flxR9Err=b.APER_FLUX_ERR_9 / " + str(self.expTime) + ", \
    285         #"+prefix+"flxR9Std=b.APER_FLUX_STDEV_9 / " + str(self.expTime) + ", \
    286         #"+prefix+"flxR9Fill=b.APER_FILL_9, \
    287         #"+prefix+"flxR10=b.APER_FLUX_10 / " + str(self.expTime) + ", \
    288         #"+prefix+"flxR10Err=b.APER_FLUX_ERR_10 / " + str(self.expTime) + ", \
    289         #"+prefix+"flxR10Std=b.APER_FLUX_STDEV_10 / " + str(self.expTime) + ", \
    290         #"+prefix+"flxR10Fill=b.APER_FILL_10 \
    291         #WHERE a.ippDetectID=b.IPP_IDET AND b.PSF_FWHM "+psfCondition
    292 
    293         #self.scratchDb.execute(sql)
    294 
    295     '''
    296     Updates parameters for a particular model in the StackModelFit table
    297     '''
    298     def updateModelFit(self, model, ippModelType):
    299 
    300         if model == "ser":
    301             modelLong = "sersic"
    302         else:
    303             modelLong = model;
    304 
    305         #does it have the EXT_COVAR in there?
    306 
    307         sql = "DESCRIBE SkyChip_xfit 'EXT_COVAR_%'"
     816        # instrumental flux, AB mags, Jy:
     817        # flux_inst = cts / sec
     818        # mag_AB = -2.5*log(flux_inst) + ZP [with appropriate definition of zero point]
     819        # mag_AB = -2.5*log(flux_cgs) - 48.6 [by construction with ~Vega flux in V-band]
     820        # flux_cgs = ten(-0.4*(mag_AB + 48.6))
     821        # flux_cgs = ten(-0.4*((-2.5*log(flux_inst) + ZP) + 48.6))
     822        # flux_cgs = flux_inst * ten(-0.4*(ZP + 48.6))
     823        # flux_Jy  = flux_cgs * 10^23
     824        # flux_Jy  = flux_inst * ten(-0.4*(ZP + 48.6) + 23.0)  [23.0 - 0.4*48.6 = 3.56]
     825        # flux_Jy  = flux_inst * ten(-0.4*ZP + 3.56)
     826        # flux_Jy  = flux_inst * ten(-0.4*ZP) * 3630.78
     827
     828        # modify flux to apply the zero point
     829        sqlLine = sqlUtility("UPDATE " + tablename + " AS a, " + self.scratchDb.dvoDetectionTable + " as b SET")
     830        for number in numbers:
     831            field = filter + prefix + "flxR" + number
     832            sqlLine.group(field,               field         + " * POW(10, 3.56 - 0.4* b.zp)")
     833            sqlLine.group(field + "Err",       field + "err" + " * POW(10, 3.56 - 0.4* b.zp)")
     834            sqlLine.group(field + "Std",       field + "Std" + " * POW(10, 3.56 - 0.4* b.zp)")
     835        sql = sqlLine.makeEquals("WHERE a.objID = b.objID AND a." + filter + "ippDetectID = b.ippDetectID")
     836        self.scratchDb.execute(sql)
     837
     838    '''
     839    Populates the StackToImage table
     840    '''
     841    def populateStackToImage(self):
     842
     843        self.logger.infoPair("Procesing table", "StackToImage")
     844
     845        for filter in self.filters:
     846           
     847            stackmetaID = self.stackIDs[filter]
     848            if stackmetaID > 0:
     849   
     850                imageIDs = self.gpc1Db.getImageIDsForThisStackID(stackmetaID)
     851
     852                for imageID in imageIDs:
     853
     854                    sql = "INSERT INTO StackToImage (stackMetaID, imageID) \
     855                   VALUES (\
     856                   " + str(stackmetaID) + ", " + imageID + ")"
     857                    self.scratchDb.execute(sql)
     858
     859                    # now update StackMeta with correct number of inputs
     860                    sql = "UPDATE "+filter+"StackMeta SET nP2Images = (SELECT COUNT(distinct(floor(imageID/100))) FROM StackToImage where stackMetaID = "+str(stackmetaID)+")"
     861                    self.scratchDb.execute(sql)
     862
     863        self.tablesToExport.append("StackToImage")
     864
     865    ### *********************** Utility Functions ************************************
     866
     867    '''
     868    Drop a table and report the drop
     869    '''
     870    def dropTableVerbose(self, table):
     871        self.logger.infoPair("dropping table:",table)
     872        self.scratchDb.dropTable(table)
     873
     874    '''
     875    get stackTypeID corresponding to our stackType
     876    '''
     877    def getStackTypeID(self):
     878
     879        sql = "SELECT stackTypeID from StackType WHERE name = '" + self.stackType + "'"
    308880        rs = self.scratchDb.executeQuery(sql)
    309         rs.first()
    310         hasResult = rs.next()
    311         if hasResult:
    312            print "EXT_COVARs exist"
    313         else:
    314            print "No EXT_COVARS here"
    315 
    316         ext_covar_sql = ""
    317  
    318         if (hasResult):
    319             ext_covar_sql = ", "+modelLong+"Covar11=b.EXT_COVAR_00_00,  \
    320             "+modelLong+"Covar12=b.EXT_COVAR_00_01,  \
    321             "+modelLong+"Covar13=b.EXT_COVAR_00_02,  \
    322             "+modelLong+"Covar14=b.EXT_COVAR_00_03,  \
    323             "+modelLong+"Covar15=b.EXT_COVAR_00_04,  \
    324             "+modelLong+"Covar16=b.EXT_COVAR_00_05,  \
    325             "+modelLong+"Covar17=b.EXT_COVAR_00_06,  \
    326             "+modelLong+"Covar22=b.EXT_COVAR_01_01,  \
    327             "+modelLong+"Covar23=b.EXT_COVAR_01_02,  \
    328             "+modelLong+"Covar24=b.EXT_COVAR_01_03,  \
    329             "+modelLong+"Covar25=b.EXT_COVAR_01_04,  \
    330             "+modelLong+"Covar26=b.EXT_COVAR_01_05,  \
    331             "+modelLong+"Covar27=b.EXT_COVAR_01_06,  \
    332             "+modelLong+"Covar33=b.EXT_COVAR_02_02,  \
    333             "+modelLong+"Covar34=b.EXT_COVAR_02_03,  \
    334             "+modelLong+"Covar35=b.EXT_COVAR_02_04,  \
    335             "+modelLong+"Covar36=b.EXT_COVAR_02_05,  \
    336             "+modelLong+"Covar37=b.EXT_COVAR_02_06,  \
    337             "+modelLong+"Covar44=b.EXT_COVAR_03_03,  \
    338             "+modelLong+"Covar45=b.EXT_COVAR_03_04,  \
    339             "+modelLong+"Covar46=b.EXT_COVAR_03_05,  \
    340             "+modelLong+"Covar47=b.EXT_COVAR_03_06,  \
    341             "+modelLong+"Covar55=b.EXT_COVAR_04_04,  \
    342             "+modelLong+"Covar56=b.EXT_COVAR_04_05,  \
    343             "+modelLong+"Covar57=b.EXT_COVAR_04_06,  \
    344             "+modelLong+"Covar66=b.EXT_COVAR_05_05,  \
    345             "+modelLong+"Covar67=b.EXT_COVAR_05_06,  \
    346             "+modelLong+"Covar77=b.EXT_COVAR_06_06"
    347        
     881        if not rs.next():
     882            self.logger.errorPair("cannot get stack type ID from database for", self.stackType)
     883            raise
    348884       
    349 
    350 
    351         sql = "UPDATE StackModelFit AS a, SkyChip_xfit AS b SET \
    352         "+model+"Radius=b.EXT_WIDTH_MAJ,  \
    353         "+model+"Flux=POW(10, -0.4 * b.EXT_INST_MAG) / " + str(self.expTime) + " ,  \
    354         "+model+"FluxErr=ABS(b.EXT_INST_MAG_SIG) * POW(10, -0.4 * b.EXT_INST_MAG) / " + str(self.expTime) + " / 1.085736, \
    355         "+model+"Ab=b.EXT_WIDTH_MAJ/b.EXT_WIDTH_MIN, \
    356         "+model+"Phi=b.EXT_THETA  \
    357         "+ext_covar_sql+"  \
    358         WHERE a.ippDetectID=b.IPP_IDET AND b.MODEL_TYPE = '"+ippModelType+"'"
    359 
    360         print sql
     885        ID = rs.getInt(1)
     886        return ID
     887
     888    def deleteRowsWithNoStackData(self, tableName):
     889
     890        self.logger.infoPair("Deleting rows with no stack data from", tableName)
     891        sql = "DELETE FROM " + tableName + " WHERE "
     892
     893        for i in range(len(self.filters)):
     894            filter = self.filters[i]
     895            sql += filter + "haveData is NULL"
     896
     897            if (i < len(self.filters) - 1):
     898                sql += " AND "
     899
     900        self.logger.infoPair('sql',sql)   
    361901        self.scratchDb.execute(sql)
    362902
    363         # sersic fit has an extra parameter
    364         if (ippModelType == "PS_MODEL_SERSIC" and hasResult > 0):
    365             sql = "UPDATE StackModelFit AS a, SkyChip_xfit AS b SET \
    366             "+modelLong+"Covar18=b.EXT_COVAR_00_07,  \
    367             "+modelLong+"Covar28=b.EXT_COVAR_01_07,  \
    368             "+modelLong+"Covar38=b.EXT_COVAR_02_07,  \
    369             "+modelLong+"Covar48=b.EXT_COVAR_03_07,  \
    370             "+modelLong+"Covar58=b.EXT_COVAR_04_07,  \
    371             "+modelLong+"Covar68=b.EXT_COVAR_05_07,  \
    372             "+modelLong+"Covar78=b.EXT_COVAR_06_07,  \
    373             "+modelLong+"Covar88=b.EXT_COVAR_07_07,   \
    374             "+"serNu=b.EXT_PAR_07,   \
    375             "+"serNuErr=SQRT(EXT_COVAR_07_07)  \
    376             WHERE a.ippDetectID=b.IPP_IDET AND b.MODEL_TYPE = '"+ippModelType+"'"
    377 
    378             self.scratchDb.execute(sql)
    379 
    380 
    381     '''
    382     Populates the StackMeta table, mainly from dictionary values found in IPP FITS header
    383     '''
    384     def populateStackMeta(self):
    385 
    386         self.logger.infoPair("Procesing table", "StackMeta")
    387 
    388         self.fwhm_maj    = self.safeDictionaryAccess(self.header, 'FWHM_MAJ')
    389         self.fwhm_maj_uq = self.safeDictionaryAccess(self.header, 'FW_MJ_UQ')
    390         self.psfmodel    = self.safeDictionaryAccess(self.header, 'PSFMODEL')
    391         if (self.fwhm_maj    == "NULL"): self.fwhm_maj    = -999
    392         if (self.fwhm_maj_uq == "NULL"): self.fwhm_maj_uq = -999
    393 
    394         # print "fwhm_maj    = ", self.fwhm_maj
    395         # print "fwhm_maj_uq = ", self.fwhm_maj_uq
    396         # print "psfmodel    = ", self.psfmodel
    397 
    398         sql = "INSERT INTO StackMeta (\
    399         stackMetaID \
    400         ,skycellID \
    401         ,photoCalID \
    402         ,photoZero \
    403         ,expTime \
    404         ,psfModelID \
    405         ,psfFwhm_mean \
    406         ,psfFwhm_max \
    407         ,ctype1 \
    408         ,ctype2 \
    409         ,crval1 \
    410         ,crval2 \
    411         ,crpix1 \
    412         ,crpix2 \
    413         ,cdelt1 \
    414         ,cdelt2 \
    415         ,pc001001 \
    416         ,pc001002 \
    417         ,pc002001 \
    418         ,pc002002 \
    419          ) VALUES ( \
    420         " + str(self.id) + " \
    421         ," + self.skycell + " \
    422         ," + str(self.scratchDb.getPhotoCalID(self.header['IMAGEID'])) + " \
    423         ," + self.header['FPA.ZP'] + " \
    424         ," + self.expTime + " \
    425         ,'" + self.psfmodel   + "' \
    426         ," + str(self.fwhm_maj)    + " \
    427         ," + str(self.fwhm_maj_uq) + " \
    428         ,'" + self.header['CTYPE1'] + "' \
    429         ,'" + self.header['CTYPE2'] + "' \
    430         ," + self.header['CRVAL1'] + " \
    431         ," + self.header['CRVAL2'] + " \
    432         ," + self.header['CRPIX1'] + " \
    433         ," + self.header['CRPIX2'] + " \
    434         ," + self.header['CDELT1'] + " \
    435         ," + self.header['CDELT2'] + " \
    436         ," + self.header['PC001001'] + " \
    437         ," + self.header['PC001002'] + " \
    438         ," + self.header['PC002001'] + " \
    439         ," + self.header['PC002002'] + " \
    440         )"
    441         self.scratchDb.execute(sql)
    442         self.scratchDb.updateAllRows("StackMeta", "batchID", str(self.batchID))
    443         self.scratchDb.updateAllRows("StackMeta", "surveyID", str(self.surveyID))
    444         self.scratchDb.updateFilterID("StackMeta", self.filter)
    445         self.scratchDb.updateAllRows("StackMeta", "dataRelease", str(self.skychunk.dataRelease))
    446         self.updateStackTypeID("StackMeta")
    447 
    448     '''
    449     Populates the StackDetection table
    450     '''
    451     def populateStackDetection(self):
    452 
    453         self.logger.infoPair("Procesing table", "StackDetection")
    454 
    455         # insert all the detections
    456         sql = "INSERT INTO StackDetection (\
    457                ippDetectID \
    458                ,skyCellID \
    459                ,projectionCellID \
    460                ,xPos \
    461                ,yPos \
    462                ,xPosErr \
    463                ,yPosErr \
    464                ,psfFlux \
    465                ,psfFluxErr \
    466                ,peakFlux \
    467                ,sky \
    468                ,skyErr \
    469                ,sgSep \
    470                ,psfWidMajor \
    471                ,psfWidMinor \
    472                ,psfTheta \
    473                ,psfLikelihood \
    474                ,infoFlag \
    475                ,psfQf \
    476                ,psfQfPerfect \
    477                ,psfChiSq \
    478                ,momentXX \
    479                ,momentXY \
    480                ,momentYY \
    481                ,momentM3C \
    482                ,momentM3S \
    483                ,momentM4C \
    484                ,momentM4S \
    485                ,momentR1 \
    486                ,momentRH \
    487                ,apFlux \
    488                ,apFluxErr \
    489                ,kronFlux \
    490                ,kronFluxErr \
    491                ,kronRad \
    492                ,nFrames \
    493                ,assocDate \
    494                ,historyModNum \
    495                ) \
    496                SELECT \
    497                IPP_IDET \
    498                ," + self.skycell + " \
    499                ," + self.projectioncell + " \
    500                ,X_PSF \
    501                ,Y_PSF \
    502                ,X_PSF_SIG \
    503                ,Y_PSF_SIG \
    504                ,PSF_INST_FLUX / " + str(self.expTime) + " \
    505                ,PSF_INST_FLUX_SIG / " + str(self.expTime) + " \
    506                ,POW(10.0, (-0.4*PEAK_FLUX_AS_MAG)) / " + str(self.expTime) + " \
    507                ,SKY / " + str(self.expTime) + " \
    508                ,SKY_SIGMA / " + str(self.expTime) + " \
    509                ,EXT_NSIGMA \
    510                ,PSF_MAJOR \
    511                ,PSF_MINOR \
    512                ,PSF_THETA \
    513                ,psfLikelihood(EXT_NSIGMA) \
    514                , FLAGS2 << 32 | FLAGS \
    515                ,PSF_QF \
    516                ,PSF_QF_PERFECT \
    517                ,PSF_CHISQ / PSF_NDOF \
    518                ,MOMENTS_XX \
    519                ,MOMENTS_XY \
    520                ,MOMENTS_YY \
    521                ,MOMENTS_M3C \
    522                ,MOMENTS_M3S \
    523                ,MOMENTS_M4C \
    524                ,MOMENTS_M4S \
    525                ,MOMENTS_R1 \
    526                ,MOMENTS_RH \
    527                ,POW(10.0, -0.4*AP_MAG) / " + str(self.expTime) + " \
    528                ,NULL \
    529                ,KRON_FLUX / " + str(self.expTime) + " \
    530                ,KRON_FLUX_ERR / " + str(self.expTime) + " \
    531                ,MOMENTS_R1 * 2.5 \
    532                ,N_FRAMES \
    533                , '" + self.dateStr + "' \
    534                ," + self.historyModNum + " \
    535                FROM SkyChip_psf"
    536 
    537 # these were used above (left over from before PSF_INST_FLUX, PSF_INST_FLUX_SIG were available?
    538 #               ,POW(10.0, (-0.4*PSF_INST_MAG)) / " + str(self.expTime) + "
    539 #               ,ABS((PSF_INST_MAG_SIG*(POW(10.0, (-0.4*PSF_INST_MAG)) / " + str(self.expTime) + ")) / 1.085736)
    540 #               ,POW(10.0, -0.4*AP_MAG) / " + str(self.expTime) + "
    541 
    542 # I need a way to choose the sql above based on the cmf version: V3 (LAP.PV1) does not have AP_FLUX
    543 #               ,AP_FLUX / " + str(self.expTime) + "
    544 
    545         # print "sql: ", sql
    546         # response = raw_input("ready to insert stack det ")
    547 
    548         self.scratchDb.execute(sql)
    549 
    550         #it is possible to drop some detections from dvo (that are present in the cmf). when that happens we get a 0 for objid
    551         #we drop those...
    552         sql="DELETE FROM StackDetection where objID = 0"
    553         self.scratchDb.execute(sql)
    554         self.logger.infoPair("Deleting", "entries with StackDetection.objID = 0")
     903        for filter in self.filters:
     904            column = filter + "haveData"
     905            self.scratchDb.dropColumn(tableName, column)
     906
     907    '''
     908    Applies indexes to the PSPS tables
     909    '''
     910    def alterPspsTables(self):
     911
     912        self.logger.debug("Altering PSPS tables (currently does nothing in stackbatch)")
     913
     914        return True
     915
     916    '''
     917    Applies indexes to the IPP tables
     918    '''
     919    def indexIppTables(self):
     920
     921        for filter in self.filters:
     922            if self.stackIDs[filter] <= 0: 
     923                self.logger.infoPair("no files for filter" , filter)
     924                continue
     925
     926            self.logger.infoPair("Creating indexes on", "IPP tables")
     927            self.scratchDb.createIndex(filter + "SkyChip_psf",  "IPP_IDET")
     928            self.scratchDb.createIndex(filter + "SkyChip_xfit", "IPP_IDET")
     929            self.scratchDb.createIndex(filter + "SkyChip_xrad", "IPP_IDET")
     930            self.scratchDb.createIndex(filter + "SkyChip_xsrc", "IPP_IDET")
     931
     932        return True
     933
     934    '''
     935    select objID (psps object ID) from dvo restricted to this set of imageIDs. also insert
     936    ippObjID and several constants (for a skycell): surveyID, skyCellId, dataRelease
     937    '''
     938    def selectDvoObjIDs(self):
     939
     940        # we need a temporary column for each filter to identify empty rows
     941        for filter in self.filters:
     942            self.scratchDb.addColumn("StackObjectThin", filter + "haveData", "tinyint")
     943
     944        sqlLine = sqlUtility("INSERT INTO StackObjectThin (")
    555945       
    556         # XXX EAM : this seems quite inefficient : these commands use updates to set
    557         # fields which are constant across all rows.  Why not just include these in
    558         # the original insert above?
    559 
    560         self.scratchDb.updateAllRows("StackDetection", "surveyID", str(self.surveyID))
    561        
    562         self.scratchDb.updateFilterID("StackDetection", self.filter)
    563        
    564         self.scratchDb.updateAllRows("StackDetection", "dataRelease", str(self.skychunk.dataRelease))
    565         #set primary F to 0
    566         self.scratchDb.updateAllRows("StackDetection", "primaryF", "0")
    567        
    568         self.scratchDb.updateAllRows("StackDetection", "activeFlag", "0")
    569        
    570         self.updateStackMetaID("StackDetection")
    571        
    572         self.updateStackTypeID("StackDetection")
    573        
    574         self.updateDvoIDsAndFlags("StackDetection")
    575         #this now updates primary F as well
    576         # response = raw_input("updated dvo ")
    577        
    578         # XXX EAM : note that in mysql versions later than 5.1, this fails
    579         # unless the following is called first:
    580         # set session old_alter_table=1
    581         # follow the command with
    582         # set session old_alter_table=0
    583         # OF COURSE, this fails for mysql version < 5.5...
    584         if self.scratchDb.version > 5.1:
    585             self.scratchDb.execute("set session old_alter_table=1")
    586 
    587         sql = "ALTER IGNORE TABLE StackDetection ADD PRIMARY KEY (objID)"
    588        
    589         if self.scratchDb.version > 5.1:
    590             self.scratchDb.execute("set session old_alter_table=0")
    591 
    592         self.scratchDb.execute(sql)
    593         # response = raw_input("add primary key? ")
    594  
    595         ### XXX this should not be a DEEP_STACK only case...
    596         if False and self.stackType == "DEEP_STACK":
    597 
    598             #if deep stack and instFlux = null and err not null
    599             sql = "UPDATE StackDetection AS a, SkyChip_psf AS b \
    600                    SET psfFlux = 2*b.PSF_INST_FLUX_SIG / " + str(self.expTime) + " \
    601                    WHERE psfFlux IS NULL \
    602                    AND a.ippDetectID = b.IPP_IDET  \
    603                    AND b.PSF_INST_FLUX_SIG IS NOT NULL"
    604            
    605             self.scratchDb.execute(sql)
    606             # response = raw_input("add psf flux ")
    607            
    608         # NOTE : Flux limits : in the current PSPS schema, negative fluxes
    609         # cause problems for sql queries which work in mags as SQL cannot do
    610         # something like (f < 0.0) ? -999 : -2.5*log10(f)
    611         # as a result, the negative fluxes here result in floating point errors
    612         sql = "UPDATE StackDetection SET psfFlux = 1e20 WHERE psfFlux <= 0.0"
    613         self.scratchDb.execute(sql)
    614        
    615         sql = "UPDATE StackDetection SET apFlux = 1e20 WHERE apFlux <= 0.0"
    616         self.scratchDb.execute(sql)
    617 
    618         sql = "UPDATE StackDetection SET kronFlux = 1e20 WHERE kronFlux <= 0.0"
    619         self.scratchDb.execute(sql)
    620 
    621         #leave null instflux in
    622         #self.scratchDb.reportAndDeleteRowsWithNULLS("StackDetection", "instFlux")
    623        
    624         self.scratchDb.reportAndDeleteRowsWithNULLS("StackDetection", "objID")
    625         # response = raw_input("delete nulls ")
    626        
    627         sql="DELETE FROM StackDetection where objID = 0"
    628        
    629         self.scratchDb.execute(sql)
    630         self.logger.infoPair("Deleting", "entries with StackDetection.objID = 0")
    631         # response = raw_input("deleted objID is 0 ")
    632        
    633 
    634     '''
    635     Populates the StackApFlx table
    636     '''
    637     def populateStackApFlx(self):
    638         self.logger.infoPair("getting","imageID")
    639         imageID = self.scratchDb.getImageIDFromExternID(self.header['IMAGEID'])
    640         self.logger.infoPair("obtained","imageID")
    641 
    642         self.logger.infoPair("Procesing", "StackApFlx")
    643  
    644         sql = "INSERT INTO StackApFlx \
    645                (ippDetectID ) \
    646                SELECT \
    647                DISTINCT IPP_IDET \
    648                FROM SkyChip_xrad"
    649 
    650         try:
    651             self.scratchDb.execute(sql)
    652         except: return
    653 
    654         # TODO temporarily loading 1st convolved fluxes into unconvolved fields
    655 
    656         #This assumes that there are 3 different fluxs, the first is unconv, and the 2nd and 3rd are convolved. If there are more than 3 fluxes it will break.   
    657         # If there are more than 3 types of fluxes, we should do a count to find out how many there are and adjust this accordingly (make it more robust)
    658 
    659         self.logger.infoPair("Adding fluxes", "un-convolved")
    660         self.updateApFlxs("", "((b.table_index -1) % 3 = 0)")
    661         self.logger.infoPair("Adding fluxes", "1st convolved")
    662         self.updateApFlxs("c1", "((b.table_index -1) % 3 = 1)")
    663         self.logger.infoPair("Adding fluxes", "2nd convolved")
    664         self.updateApFlxs("c2", "((b.table_index -1) % 3 = 2)")
    665 
    666         self.logger.infoPair("Adding", "petrosians for extended sources")
    667         sql = "UPDATE StackApFlx AS a, SkyChip_xsrc AS b SET \
    668         petRadius=b.PETRO_RADIUS \
    669         ,petRadiusErr=b.PETRO_RADIUS_ERR \
    670         ,petFlux=POW(10.0, -0.4 * b.PETRO_MAG) /" + str(self.expTime) + " \
    671         ,petFluxErr=-999  \
    672         ,petR50=b.PETRO_RADIUS_50 \
    673         ,petR50Err=b.PETRO_RADIUS_50_ERR \
    674         ,petR90=b.PETRO_RADIUS_90 \
    675         ,petR90Err=b.PETRO_RADIUS_90_ERR \
    676         ,petCf=b.PETRO_FILL \
    677         WHERE a.ippDetectID=b.IPP_IDET"
    678         self.scratchDb.execute(sql)
    679 
    680         self.logger.infoPair("Adding", "petFluxErr for non nulls")
    681         sql = "UPDATE StackApFlx as a, SkyChip_xsrc AS b SET \
    682         petFluxErr=ABS(b.PETRO_MAG_ERR) * POW(10.0, (-0.4*b.PETRO_MAG)) / " + str(self.expTime) + " / 1.085736  \
    683         WHERE a.ippDetectID=b.IPP_IDET \
    684         AND (b.PETRO_MAG_ERR is not NULL AND b.PETRO_MAG is not NULL AND PETRO_MAG_ERR < 1000.0)"
    685         # added this because when the petromagerr is crazy large (10^29 it out of ranges the flux err calc)
    686         self.logger.infoPair("sql", sql)
    687         self.scratchDb.execute(sql)
    688 
    689 
    690 
    691         self.logger.infoPair("adding"," PrimaryF to stackApFlx")
    692         sql = "UPDATE StackApFlx AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
    693                a.primaryF = ((b.flags & 0x10000) >> 16 ) \
    694                WHERE a.ippDetectID = b.ippDetectID \
    695                AND b.imageID = " + str(imageID)
    696 
    697         print "update dvo sql: ", sql
    698         # response = raw_input("update dvo...")
    699         self.scratchDb.execute(sql)
    700 
    701        
    702 
    703         self.scratchDb.updateAllRows("StackApFlx", "surveyID", str(self.surveyID))
    704         self.scratchDb.updateFilterID("StackApFlx", self.filter)
    705         self.scratchDb.updateAllRows("StackApFlx", "dataRelease", str(self.skychunk.dataRelease))
    706         self.scratchDb.updateAllRows("StackApFlx", "activeFlag", "0")
    707         self.updateStackMetaID("StackApFlx")
    708         self.updateStackTypeID("StackApFlx")
    709         self.updateDvoIDs("StackApFlx")
    710         self.scratchDb.reportAndDeleteRowsWithNULLS("StackApFlx", "objID")
    711         self.deleteDetectionsNotInStackDetection("StackApFlx")
    712 
    713 
    714     '''
    715     Populates the StackModelFit table
    716     '''
    717     def populateStackModelFit(self):
    718         self.logger.infoPair("getting","imageID")
    719         imageID = self.scratchDb.getImageIDFromExternID(self.header['IMAGEID'])
    720         self.logger.infoPair("obtained","imageID")
    721         self.logger.infoPair("Procesing table", "StackModelFit")
    722 
    723 
    724         # insert all the detections
    725         sql = "INSERT INTO StackModelFit (ippDetectID) SELECT DISTINCT IPP_IDET from SkyChip_xfit"
    726         try:
    727             self.scratchDb.execute(sql)
    728         except:
    729             return
    730 
    731         # populate model parameters
    732         self.logger.infoPair("Adding model fit", "deVaucouleurs")
    733         self.updateModelFit("deV", "PS_MODEL_DEV")
    734         self.logger.infoPair("Adding model fit", "exponential")
    735         self.updateModelFit("exp", "PS_MODEL_EXP")
    736         self.logger.infoPair("Adding model fit", "sersic")
    737         self.updateModelFit("ser", "PS_MODEL_SERSIC")
    738 
    739         self.scratchDb.updateAllRows("StackModelFit", "surveyID", str(self.surveyID))
    740         self.scratchDb.updateFilterID("StackModelFit", self.filter)
    741         self.scratchDb.updateAllRows("StackModelFit", "dataRelease", str(self.skychunk.dataRelease))
    742 
    743         self.logger.infoPair("adding"," PrimaryF to stackApFlx")
    744         sql = "UPDATE StackModelFit AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
    745                a.primaryF = ((b.flags & 0x10000) >> 16 ) \
    746                WHERE a.ippDetectID = b.ippDetectID \
    747                AND b.imageID = " + str(imageID)
    748 
    749         print "update dvo sql: ", sql
    750         # response = raw_input("update dvo...")
    751         try:
    752             self.scratchDb.execute(sql)
    753         except:
    754             return
    755 
    756         self.scratchDb.updateAllRows("StackModelFit", "activeFlag", "0")
    757         self.updateStackMetaID("StackModelFit")
    758         self.updateStackTypeID("StackModelFit")
    759         self.updateDvoIDs("StackModelFit")
    760         self.scratchDb.reportAndDeleteRowsWithNULLS("StackModelFit", "objID")
    761         self.deleteDetectionsNotInStackDetection("StackModelFit")
    762 
    763     '''
    764     Reports and deletes detections in this table that are not in StackDetection
    765     '''
    766     def deleteDetectionsNotInStackDetection(self, table):
    767 
    768         sql = "SELECT COUNT(*) FROM " + table + " WHERE ippDetectID NOT IN (SELECT ippDetectID FROM StackDetection)"
    769         rs = self.scratchDb.executeQuery(sql)
    770         rs.first()
    771         nMissing = rs.getInt(1)
    772  
    773         if nMissing < 1: return
    774        
    775         sql = "DELETE FROM " + table + " WHERE ippDetectID NOT IN (SELECT ippDetectID FROM StackDetection)"
    776         self.scratchDb.execute(sql)
    777         self.logger.infoPair("Detections not in StackDetection", "%d deleted from %s" % (nMissing, table))
    778        
    779 
    780     '''
    781     Populates the StackToImage table
    782     '''
    783     def populateStackToImage(self):
    784 
    785         self.logger.infoPair("Procesing table", "StackToImage")
    786 
    787         imageIDs = self.gpc1Db.getImageIDsForThisStackID(self.id)
    788 
    789         for imageID in imageIDs:
    790             sql = "INSERT INTO StackToImage (stackMetaID, imageID) \
    791                    VALUES (\
    792                    " + str(self.id) + ", " + imageID + ")"
    793             self.scratchDb.execute(sql)
    794 
    795         # now update StackMeta with correct number of inputs
    796         sql = "UPDATE StackMeta SET nP2Images = (SELECT COUNT(distinct(floor(imageID/100))) FROM StackToImage)"
    797         self.scratchDb.execute(sql)
    798 
    799     '''
    800     Populates the SkinnyObject table
    801     '''
    802     def populateSkinnyObject(self):
    803 
    804         self.logger.infoPair("Procesing table", "SkinnyObject")
    805 
    806         sql = "INSERT INTO SkinnyObject (\
    807                objID \
    808                ,ippObjID \
    809                ,projectionCellID \
    810                ) \
    811                SELECT \
    812                DISTINCT objID \
    813                ,ippObjID \
    814                ,projectionCellID \
    815                FROM StackDetection"
    816         self.scratchDb.execute(sql)
    817 
    818         self.scratchDb.updateAllRows("SkinnyObject", "surveyID", str(self.surveyID))
    819         self.scratchDb.updateAllRows("SkinnyObject", "dataRelease", str(self.skychunk.dataRelease))
    820 
    821     '''   
    822     Populates the StackDetectionCalib table
    823     XXX this can probably get a big speed increase by using 'SELECT () INTO OUTFILE '/tmp/name' and then
    824     calling the load data infile '/tmp/name' into table;
    825     '''
    826     def populateStackDetectionCalibInsertUpdate(self):
    827         self.logger.infoPair("Processing table", "StackDetectionCalib")
    828         tableName = "StackDetectionCalib"
    829         # insert all detections into table
    830         sql = "INSERT INTO " + tableName + " ( \
    831                objID \
    832                ,stackDetectID \
    833                ,ippObjID \
    834                ,ippDetectID \
    835                ,filterID \
    836                ,surveyID \
    837                ) \
    838                SELECT \
    839                objID \
    840                ,stackDetectID \
    841                ,ippObjID \
    842                ,ippDetectID \
    843                ,filterID \
    844                ,surveyID \
    845                FROM StackDetection"
    846         self.scratchDb.execute(sql)
    847 
    848         imageID = self.scratchDb.getImageIDFromExternID(self.header['IMAGEID'])
    849         self.logger.infoPair("obtained","imageID")
    850 
    851         # insert calibration information from dvoDetections into the Table
    852         sql = "UPDATE " + tableName + " AS a, "  + self.scratchDb.dvoDetectionTable + " AS b \
    853             SET a.ra = b.ra, \
    854             a.dec = b.dec_, \
    855             a.raErr = b.raErr, \
    856             a.decErr = b.decErr, \
    857             a.zp = b.zp, \
    858             a.zpErr = b.zpErr, \
    859             a.expTime = b.expTime, \
    860             a.airMass = b.airMass   \
    861             WHERE a.stackDetectID = b.detectID \
    862             AND b.imageID = " + str(imageID)
    863         self.scratchDb.execute(sql)
    864         self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.skychunk.dataRelease))
    865        
    866     def populateStackDetectionCalib(self):
    867 
    868         tableName = "StackDetectionCalib"
    869         self.logger.infoPair("Processing table", tableName)
    870 
    871         imageID = self.scratchDb.getImageIDFromExternID(self.header['IMAGEID'])
    872         self.logger.infoPair("obtained","imageID")
    873 
    874         # check for & create output directory first
    875         datadumpDir = "/tmp/datadump"
    876         try:
    877             statinfo = os.stat(datadumpDir)
    878             # check on the stat results?
    879         except:
    880             print "making the data dump directory ", datadumpDir
    881             os.mkdir(datadumpDir)
    882             os.chmod(datadumpDir, 0777)
    883             statinfo = os.stat(datadumpDir)
    884 
    885         dumpFile = datadumpDir + "/genetest.xx.dat"
    886         files = glob.glob(dumpFile)
    887         if len(files) > 0:
    888             os.unlink(dumpFile)
    889 
    890         # insert all detections into table
    891         sql = "SELECT \
    892           a.objID,    \
    893           a.stackDetectID, \
    894           a.ippObjID,      \
    895           a.ippDetectID,   \
    896           a.filterID,      \
    897           a.surveyID,      \
    898           b.ra,            \
    899           b.dec_,          \
    900           b.raErr,         \
    901           b.decErr,        \
    902           b.zp,            \
    903           b.zpErr,         \
    904           b.expTime,       \
    905           b.airMass,       \
    906           " + str(self.skychunk.dataRelease) + " \
    907          FROM              \
    908            StackDetection as a \
    909          JOIN " + self.scratchDb.dvoDetectionTable + " as b \
    910          ON (a.stackDetectID = b.detectID) where b.imageID = " + str(imageID) + \
    911          " INTO OUTFILE '" + dumpFile + "'"
     946        sqlLine.group("objID",            "objID")
     947        sqlLine.group("ippObjID",         "ippObjID")
     948        sqlLine.group("surveyID",         "'" + str(self.surveyID) + "'")
     949        sqlLine.group("tessID",           "'" + str(self.tessID) + "'")
     950        sqlLine.group("projectionID",     "'" + str(self.projectionID) + "'")
     951        sqlLine.group("skyCellID",        "'" + str(self.skycellID) + "'")
     952        sqlLine.group("dataRelease",      "'" + str(self.skychunk.dataRelease) + "'")
     953        sqlLine.group("randomStackObjID", "FLOOR(RAND()*9223372036854775807)")
     954
     955        whereClause = " WHERE ("
     956        imageIDs = self.imageIDs.values()
     957        for i in range(len(imageIDs)):
     958            imageID = imageIDs[i]
     959            self.logger.infoPair("selecting imageID", imageID)
     960
     961            whereClause += "(" + self.scratchDb.dvoDetectionTable + ".imageID = " + str(imageID) + ")"
     962
     963            if (i < len(imageIDs) - 1):
     964                whereClause += " or "
     965
     966        whereClause += ")"
     967
     968        sql = sqlLine.makeRaw(") SELECT DISTINCT ", "FROM " + self.scratchDb.dvoDetectionTable + whereClause)
    912969
    913970        try: self.scratchDb.execute(sql)
    914971        except:
    915             self.logger.info("failed to select data for stackDetectionCalib")
    916             self.logger.infoPair("sql: ", sql)
    917             raise
    918 
    919         sql = "LOAD DATA INFILE '" + dumpFile + "' INTO TABLE " + tableName
     972            self.logger.errorPair('failed sql',sql)
     973            return
     974
     975    '''
     976    Insert objid (from dvo) into table from main table
     977    '''
     978    def insertDvoIDs(self, table, mainTable):
     979
     980        # we need a temporary column for each filter to identify empty rows
     981        for filter in self.filters:
     982            self.scratchDb.addColumn(table, filter + "haveData", "tinyint")
     983
     984        fields = "objID, ippObjID, randomStackObjID, primaryDetection, bestDetection, "
     985
     986        for i in range(len(self.filters)):
     987            filter = self.filters[i]
     988            fields += filter + "ippDetectID, "
     989            fields += filter + "stackDetectID, "
     990            fields += filter + "stackMetaID" # careful here: do not add comma to last entry
     991            if (i < len(self.filters) - 1):
     992                fields += ", "
     993       
     994        sql  = "INSERT INTO " + table + " (" + fields + ") "
     995        sql += "SELECT " + fields + " FROM " + mainTable
     996
    920997        try: self.scratchDb.execute(sql)
    921998        except:
    922             self.logger.info("failed to load data from infile for stackDetectionCalib")
    923             self.logger.infoPair("sql: ", sql)
    924             raise
    925 
    926         ## XXX write this with the select/insert
    927         # self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.skychunk.dataRelease))
    928 
    929     '''
    930     Populates the ObjectCalColor table
    931     '''
    932     def populateObjectCalColor(self):
    933 
    934         self.logger.infoPair("Procesing table", "ObjectCalColor table")
    935 
    936         sql = "INSERT INTO ObjectCalColor (\
    937                objID \
    938                ,ippObjID \
    939                ) \
    940                SELECT \
    941                DISTINCT objID \
    942                ,ippObjID \
    943                FROM StackDetection"
    944         self.scratchDb.execute(sql)
    945 
    946         self.scratchDb.updateFilterID("ObjectCalColor", self.filter)
    947         self.scratchDb.updateAllRows("ObjectCalColor", "dataRelease", str(self.skychunk.dataRelease))
    948 
    949 
    950     '''
    951     Applies indexes to the PSPS tables
    952     '''
    953     def alterPspsTables(self):
    954 
    955         self.logger.debug("Altering PSPS tables")
    956         # heather uncommented the unique line -- well that no worky
    957         #self.scratchDb.makeColumnUnique("StackDetection", "objID")
    958         self.scratchDb.createIndex("StackDetection", "ippDetectID")
    959         self.scratchDb.createIndex("StackDetection", "stackDetectID")
    960         self.scratchDb.createIndex("StackApFlx", "ippDetectID")
    961         self.scratchDb.createIndex("StackModelFit", "ippDetectID")
    962 
    963         # dec is reserved in MySQL, so STILTS replaces if with dec_, which is not the name of
    964         # the column in PSPS
    965         self.scratchDb.execute("ALTER TABLE StackDetectionCalib CHANGE dec_ `dec` double")
    966 
     999            self.logger.errorPair('failed sql',sql)
     1000            return
     1001   
     1002    '''
     1003    Does the processing, i.e. pulling stuff from IPP tables into PSPS tables
     1004    '''
     1005    def populatePspsTables(self):
     1006        self.logger.infoPair("starting","populatePspsTables");
     1007        self.skipBatch = False
     1008
     1009        # each of the "populate*" methods below add their tables to the list:
     1010        self.tablesToExport=[]   
     1011
     1012        # EAM 20140730 : I have revised the StackObjectXX construction
     1013        # process.  The challenge here is that we have dvo-generated
     1014        # objIDs and ippObjIDs for each stack detection in seperate
     1015        # rows, but we want to merge the 5 filter sets into a single
     1016        # set of rows.  The old code was relying on INSERT IGNORE,
     1017        # making it hard to catch errors, and probably not doing the
     1018        # insert very efficiently
     1019       
     1020        # Now we do a first pass with SELECT DISTINCT to get the
     1021        # complete set of objIDs which are associated with detections
     1022        # from one of the relevant images (ie, measure.imageID matches
     1023        # the set of image IDs). 
     1024
     1025        # once we have determined the complete set of desired objects,
     1026        # then we can use an update to set the per-field fields with
     1027        # data selected for each filter.  Once we have constructed
     1028        # this basic common information in StackObjectThin, then we
     1029        # can uses this table to seed the same rows in the other Stack
     1030        # tables
     1031
     1032        for filter in self.filters:
     1033            if self.stackIDs[filter] > 0:       
     1034                self.populateStackMeta(filter)
     1035
     1036        self.populateStackObjectThin()
     1037
     1038        self.populateStackObjectRaw()
     1039
     1040        if self.stackType != "NIGHTLY_STACK":
     1041
     1042            self.logger.infoPair("populating ","StackModelFitExtra")
     1043            self.populateStackModelFitExtra()
     1044
     1045            self.logger.infoPair("populating ","StackModelFit")
     1046            self.populateStackModelFit("Exp", "PS_MODEL_EXP")
     1047            self.populateStackModelFit("DeV", "PS_MODEL_DEV")
     1048            self.populateStackModelFit("Ser", "PS_MODEL_SERSIC")
     1049
     1050            self.populateStackPetrosian()
     1051
     1052            self.logger.infoPair("populating ","StackApFlx Tables")
     1053            self.populateStackApFlx()
     1054
     1055            self.populateStackApFlxFull("")
     1056            self.populateStackApFlxFull("C1")
     1057            self.populateStackApFlxFull("C2")
     1058
     1059        # delete rows with no content. for the other tables, this is done in
     1060        # the 'populate' functions, but StackObjectThin needs to retain all rows
     1061        # until those tables are done
     1062        self.deleteRowsWithNoStackData("StackObjectThin")
     1063
     1064        self.logger.infoPair("populating","StackToImage")
     1065        self.populateStackToImage()
     1066       
     1067        self.setMinMaxObjID(["StackObjectThin"])
     1068
     1069        self.logger.infoPair("finishing","populatePspsTables");
    9671070        return True
    9681071
    9691072    '''
    970     Applies indexes to the IPP tables
    971     '''
    972     def indexIppTables(self):
    973 
    974         self.logger.infoPair("Creating indexes on", "IPP tables")
    975         self.scratchDb.createIndex("SkyChip_psf", "IPP_IDET")
    976         self.scratchDb.createIndex("SkyChip_xfit", "IPP_IDET")
    977         self.scratchDb.createIndex("SkyChip_xrad", "IPP_IDET")
    978         self.scratchDb.createIndex("SkyChip_xsrc", "IPP_IDET")
    979 
     1073    overidding this method - use regex to trimoff letter before stack
     1074    '''
     1075    def exportPspsTablesToFits(self, regex="(.*)"):
     1076        return super(StackBatch, self).exportPspsTablesToFits("(Stack.*)")
     1077
     1078    '''
     1079    Imports IPP tables from FITS file
     1080    Accepts a regular expression to match the tables so not all tables need to be imported
     1081    (This regex feature is not currently used...)
     1082    '''
     1083    def importIppTables(self, columns="*", tableRE=""):
     1084       
     1085        if self.config.retry: return True
     1086
     1087        self.logger.infoPair("Importing ST tables with table match expression: ", tableRE)
     1088
     1089        count = 0
     1090        for filter in self.filters:
     1091            if self.stackIDs[filter] <= 0: 
     1092                self.logger.infoPair("no files for filter" , filter)
     1093                continue
     1094
     1095            fileName = self.fits[filter].getPath()
     1096           
     1097            self.logger.infoPair("using filename:",fileName)
     1098           
     1099            try:
     1100                tables = stilts.treads(fileName)
     1101            except:
     1102                self.logger.errorPair("STILTS could not import from", fileName)
     1103                return False
     1104           
     1105            # count = 0
     1106            for table in tables:
     1107             
     1108                match = re.match(tableRE, table.name)
     1109                if not match: continue
     1110
     1111                self.logger.infoPair("Reading IPP table", filter + table.name)
     1112                table = stilts.tpipe(table, cmd='addcol table_index $0')
     1113                table = stilts.tpipe(table, cmd='explodeall')
     1114                 
     1115                # drop any previous tables before import
     1116                self.scratchDb.dropTable(filter + table.name)
     1117                 
     1118                # IPP FITS files are littered with infinities, so remove these
     1119                self.logger.info("Removing Infinity values from all columns")
     1120                table = stilts.tpipe(table, cmd='keepcols "' + columns + '"')
     1121                table = stilts.tpipe(table, cmd='replaceval -Infinity null *')
     1122                table = stilts.tpipe(table, cmd='replaceval Infinity null *')
     1123                 
     1124                try:
     1125                    table.write(self.scratchDb.url + '#' + filter + table.name)
     1126                    count = count + 1
     1127                except:
     1128                    self.logger.exception("Problem writing table '" + filter + table.name + "' to the database")
     1129                     
     1130        self.logger.infoPair("Done. Imported", "%d tables" % count)
     1131        self.indexIppTables()
     1132                     
    9801133        return True
    981 
    982     '''
    983     Updates provided table with DVO IDs from DVO table
    984     '''
    985     def updateDvoIDs(self, table):
    986         self.logger.infoPair("getting","imageID")
    987         imageID = self.scratchDb.getImageIDFromExternID(self.header['IMAGEID'])
    988         self.logger.infoPair("obtained","imageID")
    989         self.logger.debug("Updating table '" + table + "' with DVO IDs...")
    990         sql = "UPDATE " + table + " AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
    991                a.ippObjID = b.ippObjID, \
    992                a.stackDetectID = b.detectID, \
    993                a.objID = b.objID \
    994                WHERE a.ippDetectID = b.ippDetectID \
    995                AND b.imageID = " + str(imageID)
    996         self.scratchDb.execute(sql)
    997    
    998     '''
    999     Updates Flags
    1000     '''
    1001     def updateDvoIDsAndFlags(self, table):
    1002         self.logger.infoPair("getting","imageID")
    1003         imageID = self.scratchDb.getImageIDFromExternID(self.header['IMAGEID'])
    1004         self.logger.infoPair("obtained","imageID")
    1005         self.logger.debug("Updating table '" + table + "' with DVO IDs...")
    1006         sql = "UPDATE " + table + " AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
    1007                a.ippObjID = b.ippObjID, \
    1008                a.stackDetectID = b.detectID, \
    1009                a.objID = b.objID, \
    1010                a.infoFlag = b.flags << 45 | a.infoFlag,  \
    1011                a.primaryF = ((b.flags & 0x10000) >> 16 ) \
    1012                WHERE a.ippDetectID = b.ippDetectID \
    1013                AND b.imageID = " + str(imageID)
    1014 
    1015         print "update dvo sql: ", sql
    1016         # response = raw_input("update dvo...")
    1017         self.scratchDb.execute(sql)
    1018    
    1019    
    1020     '''
    1021     Does the processing, i.e. pulling stuff from IPP tables into PSPS tables
    1022     '''
    1023     def populatePspsTables(self):
    1024 
    1025         self.skipBatch = False
    1026 
    1027         if not self.useFullTables:
    1028             if not self.getIDsFromDVO():
    1029                 return False
    1030         #self.logger.infoPair("populating","StackMeta")   
    1031         self.populateStackMeta()
    1032         #self.logger.infoPair("populating","StackDetection")
    1033         self.populateStackDetection()
    1034        
    1035         if self.stackType != "NIGHTLY_STACK":
    1036             #self.logger.infoPair("populating","StackModelFit")
    1037             self.populateStackModelFit()
    1038             #self.logger.infoPair("populating","StackApFlx")
    1039             self.populateStackApFlx()
    1040         #self.logger.infoPair("populating","StackToImage")
    1041         self.populateStackToImage()
    1042         #self.logger.infoPair("populating","SkinnyObject")
    1043         self.populateSkinnyObject()
    1044         #self.logger.infoPair("populating","ObjectCalColor")
    1045         #self.populateObjectCalColor()
    1046         #self.logger.infoPair("populating","StackDetectionCalib")
    1047         self.populateStackDetectionCalib()
    1048 
    1049         self.setMinMaxObjID(["StackDetection"])
    1050        
    1051         if self.totalDetections < 1:
    1052 
    1053             self.logger.error("No detections to publish")
    1054             return False
    1055 
    1056         return True
    1057 
Note: See TracChangeset for help on using the changeset viewer.