00001 #!/usr/local/bin/python 00002 # 00003 # Copyright 2002 00004 # by 00005 # The Board of Trustees of the 00006 # Leland Stanford Junior University. 00007 # All rights reserved. 00008 # 00009 00010 __facility__ = "Online" 00011 __abstract__ = "Run report generator class for RunControl" 00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online" 00013 __date__ = "02/10/2003" 00014 __version__ = "$Revision: 2.13 $" 00015 __credits__ = "SLAC" 00016 00017 import LATTE.copyright_SLAC 00018 00019 import os 00020 import logging as log 00021 import time 00022 import calendar 00023 import cStringIO 00024 00025 from xml.sax.saxutils import XMLGenerator 00026 from xml.parsers.expat import ParserCreate 00027 00028 from qt import Qt, QVariant, QDate, QDateTime 00029 from qtsql import QSqlDatabase, QSqlCursor 00030 00031 UNITS_INT = 0 00032 UNITS_LONG = 1 00033 UNITS_FLOAT = 2 00034 UNITS_STRING = 3 00035 UNITS_LIST = 4 00036 UNITS_DICT = 5 00037 00038 UNIT_MAP = { 00039 'OperatorId' : UNITS_LONG, 00040 'EventCount' : UNITS_LONG, 00041 'BadEventCount' : UNITS_LONG, 00042 'ErrorEventCount' : UNITS_LONG, 00043 'PauseCount' : UNITS_INT, 00044 'ElapsedTime' : UNITS_FLOAT, 00045 'CompletionStatus': UNITS_INT, 00046 } 00047 00048 # Shift Dictionary 00049 SHIFT_DICT = { 0: ("T00:00:00", "T08:00:00", "Owl"), 00050 1: ("T08:00:00", "T16:00:00", "Day"), 00051 2: ("T16:00:00", "T00:00:00", "Swing") 00052 } 00053 00054 class rcReportGen(object): 00055 def __init__(self, fileName="rcReport.out", prefs=None): 00056 self.__fileName = fileName 00057 self.__prefs = prefs 00058 if prefs is not None: 00059 self.__elognbl = int(prefs["elognbl"]) 00060 else: 00061 self.__elognbl = 0 00062 00063 00064 def initialize(self, tstamp=None): 00065 self.__tstamp = tstamp 00066 self.__repFields = [] 00067 self.__repData = [] 00068 self.__repUnits = [] 00069 if self.__elognbl: 00070 self.__sqldriver = 'QMYSQL3' 00071 if 'ELOGBOOK_DB' in os.environ: 00072 dbname = os.environ['ELOGBOOK_DB'] 00073 else: 00074 dbname = 'elogbook' 00075 if QSqlDatabase.isDriverAvailable(self.__sqldriver): 00076 self.__db = QSqlDatabase.addDatabase(self.__sqldriver) 00077 if not self.connectDB(dbname): 00078 log.error("Error opening database %s" 00079 " disabling Elogbook integration" % dbname) 00080 log.error(str(self.__db.lastError().text())) 00081 self.__elognbl = 0 00082 else: 00083 log.debug("Database %s opened successfully" % dbname) 00084 else: 00085 log.error("The SQL driver %s is not available," 00086 " disabling Elogbook integration" % self.__sqldriver) 00087 self.__elognbl = 0 00088 00089 def connectDB(self, dbname): 00090 if 'ELOGBOOK_HOST' in os.environ: 00091 host = os.environ['ELOGBOOK_HOST'] 00092 else: 00093 host = 'localhost' 00094 self.__db.setHostName(host) 00095 self.__db.setUserName('elogbook') 00096 self.__db.setPassword('elogbook') 00097 self.__db.setDatabaseName(dbname) 00098 return self.__db.open() 00099 00100 def disconnectDB(self): 00101 return self.__db.close() 00102 00103 def addField(self, field, data, units=UNITS_STRING): 00104 if field == 'TimeStamp': 00105 self.__tstamp = data 00106 else: 00107 self.__repFields.append(field) 00108 self.__repData.append(data) 00109 self.__repUnits.append(units) 00110 00111 def writeReportCSV(self): 00112 if not os.path.exists(self.__fileName): 00113 f = file(self.__fileName, 'w+') 00114 i = 0 00115 for header in self.__repFields: 00116 f.write(header) 00117 i = i + 1 00118 if i < len(self.__repFields): 00119 f.write(',') 00120 f.write('\n') 00121 00122 f = file(self.__fileName, 'a+') 00123 i = 0 00124 for data in self.__repData: 00125 f.write('"'+data+'"') 00126 i = i + 1 00127 if i < len(self.__repData): 00128 f.write(',') 00129 f.write('\n') 00130 f.close() 00131 00132 def writeReportXML(self): 00133 if not os.path.exists(self.__fileName): 00134 f = file(self.__fileName, 'w+') 00135 newReportFile = 1 00136 else: 00137 f = file(self.__fileName, 'a+') 00138 newReportFile = 0 00139 00140 result = cStringIO.StringIO() 00141 gen = XMLGenerator(result) 00142 if newReportFile: 00143 gen.startDocument() 00144 if self.__tstamp is None: 00145 self.__tstamp = time.gmtime() 00146 self.__tstamp = str(self.__tstamp) 00147 gen.startElement("testReport", {'timestamp': self.__tstamp}) 00148 i = 0 00149 for header in self.__repFields: 00150 gen.startElement(header,{}) 00151 gen.characters(self.__repData[i]) 00152 gen.endElement(header) 00153 i = i + 1 00154 gen.endElement("testReport") 00155 if newReportFile: 00156 gen.endDocument() 00157 f.write(result.getvalue()+'\n') 00158 f.close() 00159 if self.__elognbl: 00160 if self.writeReportSQL() == 0: 00161 self.addShift(self.__tstamp) 00162 self.disconnectDB() 00163 00164 def writeReportSQL(self): 00165 fieldMap = {'RunId': 'RunID', 00166 'OperatorId': 'OperatorID' 00167 } 00168 00169 dateFields = ['TimeStamp', 'StartTime', 'EndTime'] 00170 00171 self.__repFields.append('TimeStamp') 00172 self.__repData.append(self.__tstamp) 00173 table = "elogreport" 00174 cur = QSqlCursor(table) 00175 fields = [f.name() for f in self.__db.recordInfo(table)] 00176 recBuffer = cur.primeInsert() 00177 additional = "" 00178 for i in range(len(self.__repFields)): 00179 field = self.__repFields[i] 00180 if field in fieldMap: 00181 field = fieldMap[field] 00182 if field in dateFields: 00183 t = eval(self.__repData[i]) 00184 value = "%04d-%02d-%02d %02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5]) 00185 else: 00186 value = self.__repData[i] 00187 if field in fields: 00188 recBuffer.setValue(field, QVariant(value)) 00189 else: 00190 # Tue Mar 15 14:05:15 2005: RiC debug log commented out. 00191 #log.debug("Can not find field %s in table %s. Adding it to the 'Additional Fields' column." 00192 # % (field, table)) 00193 additional = additional + field + "???" + value + "!!!" 00194 if additional != "": 00195 recBuffer.setValue("AdditionFields", QVariant(additional)) 00196 if cur.insert() != 1: 00197 log.error("Could not insert elogreport record, dumping field data:") 00198 for i in range(len(self.__repFields)): 00199 log.error("\t%s\t\t\t%s" % (self.__repFields[i], self.__repData[i])) 00200 log.error("SQL Error text: %s", str(cur.lastError().text())) 00201 return -1 00202 return 0 00203 00204 def readReportXML(self): 00205 # 3 handler functions 00206 def start_element(name, attrs): 00207 #print 'Start element:', str(name), attrs 00208 self.__data = '' 00209 if str(name) == 'testReport': 00210 self.__tstamp = str(attrs['timestamp']) 00211 self.__repFields.append('TimeStamp') 00212 self.__repData.append(self.__tstamp) 00213 self.__repUnits.append(UNITS_STRING) 00214 else: 00215 self.__repFields.append(str(name)) 00216 def end_element(name): 00217 #print 'End element:', str(name) 00218 name = str(name) 00219 if name != 'testReport': 00220 self.__repData.append(self.__data) 00221 if name in UNIT_MAP: 00222 self.__repUnits.append(UNIT_MAP[name]) 00223 else: 00224 self.__repUnits.append(UNITS_STRING) 00225 def char_data(data): 00226 #print 'Character data:', str(data) 00227 if self.__data is not None: 00228 self.__data += str(data) 00229 def external_entity_ref_handler(context,base,systemId,publicId): 00230 subParser = p.ExternalEntityParserCreate(context) 00231 subParser.StartElementHandler = start_element 00232 subParser.EndElementHandler = end_element 00233 subParser.CharacterDataHandler = char_data 00234 subParser.ParseFile(file(systemId)) 00235 return 1 00236 self.__data = None 00237 p = ParserCreate() 00238 p.ExternalEntityRefHandler = external_entity_ref_handler 00239 p.Parse("""<!DOCTYPE report [<!ENTITY reportfile SYSTEM \""""+self.__fileName+"""\"> 00240 ]><report>&reportfile;</report>""") 00241 00242 def getReportFields(self): 00243 return self.__repFields 00244 00245 def getReportData(self): 00246 return self.__repData 00247 00248 def getReportUnits(self): 00249 return self.__repUnits 00250 00251 def getTimestamp(self): 00252 return self.__tstamp 00253 00254 def addShift(self,tstamp): 00255 (shiftID, startLocal, endLocal, type) = self.getShiftInfo(eval(tstamp)) 00256 dateTime = QDateTime().currentDateTime() 00257 timeStamp = self.obtainGMTime(dateTime) 00258 # Shift Cursor 00259 shiftFilter = "ShiftID='" + str(shiftID) + "'" 00260 shiftCursor = self.obtainCursor("elogshiftsummary", shiftFilter) 00261 # If there are no records, create a new one 00262 if not shiftCursor.isValid(): 00263 cursorBuffer = shiftCursor.primeInsert() 00264 cursorBuffer.setValue("ShiftID", QVariant(shiftID)) 00265 cursorBuffer.setValue("ShiftType", QVariant(type)) 00266 cursorBuffer.setValue("StartTime", QVariant(startLocal)) 00267 cursorBuffer.setValue("EndTime", QVariant(endLocal)) 00268 cursorBuffer.setValue("LastUpdate", QVariant(timeStamp)) 00269 count = shiftCursor.insert() 00270 log.debug("rcReportGen: Added new shift id: %s, type: %s, startLocal: %s, endLocal: %s, timestamp: %s for run timestamp: %s" 00271 % (shiftID, type, startLocal, endLocal, timeStamp, tstamp)) 00272 log.debug("rcReportGen: Inserted record count: %d" % count) 00273 log.debug("rcReportGen: Last error: %s" % str(shiftCursor.lastError().text())) 00274 shiftCursor.update() 00275 #print "New Shift Type: ", type 00276 #print "New Shift Start: ", startLocal 00277 #print "New Shift End: ", endLocal 00278 #print "New Shift Last Update: ", timeStamp 00279 return True 00280 else: 00281 #print "Shift for run: ", tstamp, " already exists in the database." 00282 pass 00283 00284 00285 def getShiftInfo(self,t): 00286 #print "Timestamp=", t 00287 timeStampGMT = self.convertTimeTuple(t) 00288 timeStampLocal = self.obtainLocalTime(timeStampGMT) 00289 QTimeStampLocal = QDateTime.fromString(timeStampLocal, Qt.ISODate) 00290 #print "Run timestamp GMT: ", timeStampGMT, "Run timestamp Local: ", timeStampLocal 00291 timeStamp = timeStampLocal.split('T') 00292 date = timeStamp[0] 00293 time = timeStamp[1] 00294 #print "Date:", date, "Time:", time 00295 #Obtain End Date (Local) 00296 startQDate = QDate().fromString(date, Qt.ISODate) 00297 endQDate = startQDate.addDays(1) 00298 endDate = str(endQDate.toString(Qt.ISODate)) 00299 #print "End Date: ", endDate 00300 # Date tag for ShiftID 00301 dateList = date.split('-') 00302 dateTag = dateList[0] + dateList[1] + dateList[2] 00303 #print "Date Tag: ", dateTag 00304 # If run timeStamp is within shift obtain ShiftID 00305 for shift in SHIFT_DICT: 00306 (startT, endT, type) = SHIFT_DICT[shift] 00307 startLocal = date + startT 00308 if shift == 2: 00309 endLocal = endDate + endT 00310 else: 00311 endLocal = date + endT 00312 startQDateTime = QDateTime.fromString(startLocal, Qt.ISODate) 00313 endQDateTime = QDateTime.fromString(endLocal, Qt.ISODate) 00314 # The Core Code: 00315 # secsTo Returns the number of seconds from this datetime1 to (datetime2) 00316 # which is negative if dt is earlier than this datetime). 00317 # Example: datetime1.secsTo(datetime2) > 0 if datetime2 is later than datetime1 00318 secsToStartTime = startQDateTime.secsTo(QTimeStampLocal) 00319 secsToEndTime = endQDateTime.secsTo(QTimeStampLocal) 00320 #print "Shift: ", shift, "Start: ", startLocal, " End: ", endLocal 00321 #print "Shift: ", shift, "Secs To Start: ", secsToStartTime, "Secs To End: ", secsToEndTime 00322 if secsToStartTime >= 0 and secsToEndTime <= 0: 00323 shiftID = dateTag + str(shift) 00324 return (shiftID, startLocal, endLocal, type) 00325 00326 def obtainLocalTime(self, dateTime): 00327 dateTime = str(dateTime) 00328 splitTime = dateTime.split('T') 00329 #print dateTime, splitTime 00330 dateTime = splitTime[0] + splitTime[1] 00331 timeStamp = time.strptime(dateTime, "%Y-%m-%d%H:%M:%S") 00332 localTimeTuple = time.localtime(calendar.timegm(timeStamp)) 00333 localTime = self.convertTimeTuple(localTimeTuple) 00334 return localTime 00335 00336 def obtainGMTime(self, dateTime): 00337 dateTime = str(dateTime.toString(Qt.ISODate)) 00338 splitTime = dateTime.split('T') 00339 dateTime = splitTime[0] + splitTime[1] 00340 timeStamp = time.strptime(dateTime, "%Y-%m-%d%H:%M:%S") 00341 gmTimeTuple = time.gmtime(time.mktime(timeStamp)) 00342 gmTime = self.convertTimeTuple(gmTimeTuple) 00343 return gmTime 00344 00345 def convertTimeTuple(self, timeTuple): 00346 timeStamp = "%04d" % timeTuple[0] + "-" + \ 00347 "%02d" % timeTuple[1] + "-" + \ 00348 "%02d" % timeTuple[2] + "T" + \ 00349 "%02d" % timeTuple[3] + ":" + \ 00350 "%02d" % timeTuple[4] + ":" + \ 00351 "%02d" % timeTuple[5] 00352 return timeStamp 00353 00354 def obtainCursor(self, table, filter): 00355 shiftCursor = QSqlCursor(table, True, self.__db) 00356 shiftCursor.setFilter(filter) 00357 shiftCursor.select() 00358 shiftCursor.next() 00359 return shiftCursor