/search.css" rel="stylesheet" type="text/css"/> /search.js">
00001 """ 00002 PMT HV scraping specialization 00003 """ 00004 import os, logging 00005 log = logging.getLogger(__name__) 00006 00007 from datetime import datetime, timedelta 00008 00009 from base import Regime, Scraper, Faker, DCS, SourceContext 00010 from dcs import LCR 00011 00012 00013 00014 class PmtHv(Regime): 00015 """ 00016 Regime frontend class with simple prescribed interface, 00017 takes the cfg argument into this dict and no args in call. 00018 This allows the frontend to be entirely generic. 00019 """ 00020 srcs = property( lambda self:PmtHvSource( DCS(self['source']) )) 00021 scraper = property( lambda self:PmtHvScraper(self.srcs, self.target, self )) 00022 faker = property( lambda self:PmtHvFaker(self.srcs,self )) 00023 00024 00025 00026 class PmtHvSource(list): 00027 def __init__(self, srcdb): 00028 """ 00029 :param srcdb: source DB instance of :py:class:`Scraper.base.DCS` 00030 00031 List of source SA classes that map tables/joins in srcdb 00032 Accommodates a table naming irregularity HVPw rather than HV_Pw 00033 """ 00034 dtn = SourceContext(table="DBNS_AD1_HV_Vmon:DBNS_AD1_HV_Pw",ksite="kDayaBay",ksubsite="kAD1", attjoin='id', pfxjoin='P_' ) 00035 self.append( srcdb.kls(dtn) ) 00036 dtn = SourceContext(table="DBNS_AD2_HV_Vmon:DBNS_AD1_HV_Pw",ksite="kDayaBay",ksubsite="kAD2", attjoin='id', pfxjoin='P_' ) 00037 self.append( srcdb.kls(dtn) ) 00038 dtn = SourceContext(table="LANS_AD3_HV_Vmon:LANS_AD3_HV_Pw",ksite="kLingAo",ksubsite="kAD1", attjoin='id', pfxjoin='P_' ) 00039 self.append( srcdb.kls(dtn) ) 00040 dtn = SourceContext(table="FARS_AD4_HV_Vmon:FARS_AD4_HV_Pw",ksite="kFar",ksubsite="kAD1", attjoin='id', pfxjoin='P_' ) 00041 self.append( srcdb.kls(dtn) ) 00042 dtn = SourceContext(table="FARS_AD5_HV_Vmon:FARS_AD5_HV_Pw",ksite="kFar",ksubsite="kAD2", attjoin='id', pfxjoin='P_' ) 00043 self.append( srcdb.kls(dtn) ) 00044 dtn = SourceContext(table="FARS_AD6_HV_Vmon:FARS_AD6_HV_Pw",ksite="kFar",ksubsite="kAD3", attjoin='id', pfxjoin='P_' ) 00045 self.append( srcdb.kls(dtn) ) 00046 00047 00048 class PmtHvScraper(Scraper): 00049 lcr_matcher = LCR() 00050 00051 def changed(self, sv ): 00052 """ 00053 :param sv: source vector instance :py:class:`Scraper.base.sourcevector.SourceVector` 00054 00055 Decide if sufficient change to propagate based on differences between the first and last elements of `SourceVector` instance argument 00056 """ 00057 p = self._lcrdict( sv[0] ) 00058 c = self._lcrdict( sv[-1] ) 00059 for lcr in sorted(p.keys()): 00060 pv = p[lcr] 00061 cv = c[lcr] 00062 if abs(pv['pw']-cv['pw']) > 0: 00063 return True 00064 if abs(pv['voltage']-cv['voltage']) > self.threshold: 00065 return True 00066 return False 00067 00068 def propagate(self, sv ): 00069 """ 00070 :param sv: source vector instance :py:class:`Scraper.base.sourcevector.SourceVector` 00071 00072 Yield write ready DybDbi target dicts to base class, note that a single source vector 00073 instance is yielding multiple target dicts. The keys of the target dict must match 00074 the specified attributes of the ``DybDbi`` target class. 00075 00076 Here the output is based entirely on the last element of the source vector. A smarter 00077 implementation might average the first and last to smooth variations. 00078 The python ``yield`` command makes it possible to iterate over a what is returned by 00079 a function/method. 00080 00081 """ 00082 dd = self._lcrdict(sv[-1]) 00083 n = 0 00084 for (l,c,r),v in sorted(dd.items()): 00085 d = dict(Ladder=l,Column=c,Ring=r,Voltage=float(v['voltage']),Pw=v['pw']) 00086 n += 1 00087 yield d 00088 log.debug("yielded %s target dicts for %s " % ( n, sv )) 00089 00090 00091 def seed(self, sc): 00092 """ 00093 Used for seeding target DB when testing into empty tables 00094 00095 :param sc: source class, potentially different seeds will be needed 00096 for each source that feeds into a single target 00097 """ 00098 return dict(Ladder=1,Column=1,Ring=1,Voltage=0,Pw=0) 00099 00100 00101 def _lcrdict(self, inst): 00102 """ 00103 :param inst: SQLAlchemy source instance 00104 00105 Examines source instance, extracting ladder,col,ring and values and collecting into a dict. 00106 Note the source is an instance of an SQLAlchmemy dynamic class that maps to the join of 00107 two tables. 00108 00109 This method is not not fulfilling any particular interface, it is just a helper to the 00110 other methods, and thus avoids duplication. 00111 """ 00112 dd = {} 00113 for k,v in inst.asdict.items(): 00114 if k in 'P_id id date_time P_date_time'.split(): 00115 continue 00116 qty,kk = ('pw',k[2:]) if k.startswith('P_') else ('voltage',k) 00117 lcr = self.lcr_matcher(kk) 00118 if dd.has_key(lcr): 00119 dd[lcr].update({qty:v}) 00120 else: 00121 dd[lcr] = {qty:v} 00122 return dd 00123 00124 00125 class PmtHvFaker(Faker): 00126 """ 00127 Creates fake instances and inserts them into sourcedb 00128 """ 00129 lcr_matcher = LCR() 00130 def fake(self, inst, id , dt ): 00131 """ 00132 Invoked from base class call method, 00133 set attributes of source instance to form a fake 00134 00135 :param inst: source instance 00136 :param id: id to assign to the instance instance 00137 """ 00138 fakefn=lambda (l,c,r),qty:l*100 + c*10 + r if qty == "voltage" else 1 00139 for k,v in inst.asdict.items(): 00140 if k in 'id P_id'.split(): 00141 pass 00142 #setattr( inst, k, id ) ## not setting the id leads to AUTO_INCREMENT kicking in which can avoid integrity errors from duplicate keys 00143 elif k in 'date_time P_date_time'.split(): 00144 setattr( inst, k, dt ) 00145 else: 00146 qty,kk = ('pw',k[2:]) if k.startswith('P_') else ('voltage',k) 00147 lcr = self.lcr_matcher(kk) 00148 setattr( inst , k, fakefn( lcr, qty) ) 00149 00150 00151 if __name__ == '__main__': 00152 pass 00153