comparison graph.py @ 15:c04c75360a3b

Fix timezone screw up. Remove cut & paste code from the AGL script
author Daniel O'Connor <darius@dons.net.au>
date Fri, 23 Feb 2018 14:57:45 +1030
parents aa18210c2703
children 29a61ec4755b
comparison
equal deleted inserted replaced
14:aa18210c2703 15:c04c75360a3b
112 112
113 if args.start.tzinfo == None: 113 if args.start.tzinfo == None:
114 args.start = args.start.replace(tzinfo = lt) 114 args.start = args.start.replace(tzinfo = lt)
115 if args.end.tzinfo == None: 115 if args.end.tzinfo == None:
116 args.end = args.end.replace(tzinfo = lt) 116 args.end = args.end.replace(tzinfo = lt)
117 startlt = args.start
118 endlt = args.end
119 args.start = args.start.astimezone(utc)
120 args.end = args.end.astimezone(utc)
121 graph(args.graphfn, cur, cols, args.start, args.end, lt, utc) 117 graph(args.graphfn, cur, cols, args.start, args.end, lt, utc)
122 118
123 def graph(fname, cur, _cols, start, end, lt, utc): 119 def graph(fname, cur, _cols, start, end, lt, utc):
124 import numpy 120 import numpy
125 import matplotlib 121 import matplotlib
161 cur.execute('SELECT tstamp, ' + c.rowname + ' FROM ' + c.table + ' WHERE tstamp > ? AND tstamp < ? ORDER BY tstamp', 157 cur.execute('SELECT tstamp, ' + c.rowname + ' FROM ' + c.table + ' WHERE tstamp > ? AND tstamp < ? ORDER BY tstamp',
162 (startepoch, endepoch)) 158 (startepoch, endepoch))
163 ary = numpy.array(cur.fetchall()) 159 ary = numpy.array(cur.fetchall())
164 if ary.shape[0] == 0: 160 if ary.shape[0] == 0:
165 print('No data for ' + c.rowname) 161 print('No data for ' + c.rowname)
166 return 162 continue
167 163
168 # Create TZ naive from POSIX stamp, then convert to TZ aware UTC then adjust to local time 164 # Convert epoch stamp to datetime with UTC timezone
169 c.xdata = map(lambda f: datetime.datetime.fromtimestamp(f).replace(tzinfo = utc).astimezone(lt), ary[:,0]) 165 c.xdata = map(lambda f: datetime.datetime.utcfromtimestamp(f).replace(tzinfo = utc), ary[:,0])
170 c.ydata = ary[:,1] 166 c.ydata = ary[:,1]
171 if c.conv != None: 167 if c.conv != None:
172 c.ydata = map(c.conv, c.ydata) 168 c.ydata = map(c.conv, c.ydata)
173 169
174 scale_min, scale_max = c.limits 170 scale_min, scale_max = c.limits
185 else: 181 else:
186 ax = ax2lines 182 ax = ax2lines
187 c.colour = colourlist[colouridx] 183 c.colour = colourlist[colouridx]
188 colouridx += 1 184 colouridx += 1
189 ax.append(c) 185 ax.append(c)
186
187 if len(ax1lines) == 0 and len(ax2lines) == 0:
188 print('Nothing to plot')
189 return
190 190
191 # Load the right backend for display or save 191 # Load the right backend for display or save
192 if fname == None: 192 if fname == None:
193 import matplotlib.pylab 193 import matplotlib.pylab
194 fig = matplotlib.pylab.figure() 194 fig = matplotlib.pylab.figure()
205 ax1.plot(line.xdata, line.ydata, label = line.title, color = line.colour) 205 ax1.plot(line.xdata, line.ydata, label = line.title, color = line.colour)
206 if line.limits[0] != None or line.limits[1] != None: 206 if line.limits[0] != None or line.limits[1] != None:
207 ax1.set_ylim(line.limits[0], line.limits[1]) 207 ax1.set_ylim(line.limits[0], line.limits[1])
208 if line.annotation != None: 208 if line.annotation != None:
209 annotations.append(line.annotation) 209 annotations.append(line.annotation)
210 ax1.legend(loc = 'upper left') 210 ax1.legend(loc = 'center left')
211 211
212 if len(ax2lines) > 0: 212 if len(ax2lines) > 0:
213 ax2 = ax1.twinx() 213 ax2 = ax1.twinx()
214 ax2.set_ylabel(yaxisunits2) 214 ax2.set_ylabel(yaxisunits2)
215 215
218 if line.limits[0] != None or line.limits[1] != None: 218 if line.limits[0] != None or line.limits[1] != None:
219 ax2.set_ylim(line.limits[0], line.limits[1]) 219 ax2.set_ylim(line.limits[0], line.limits[1])
220 if line.annotation != None: 220 if line.annotation != None:
221 annotations.append(line.annotation) 221 annotations.append(line.annotation)
222 222
223 ax2.legend(loc = 'upper right') 223 ax2.legend(loc = 'center right')
224 224
225 if len(annotations) > 0: 225 if len(annotations) > 0:
226 ax1.text(0.02, 0.5, reduce(lambda a, b: a + '\n' + b, annotations), 226 ax1.text(0.02, 0.3, reduce(lambda a, b: a + '\n' + b, annotations),
227 transform = ax1.transAxes, bbox = dict(facecolor = 'blue', alpha = 0.5), 227 transform = ax1.transAxes, bbox = dict(facecolor = 'blue', alpha = 0.5),
228 ha = 'left', va = 'top') 228 ha = 'left', va = 'top')
229 ndays = int(max(1, round(((end - start).total_seconds()) / 86400))) 229 ndays = int(max(1, round(((end - start).total_seconds()) / 86400)))
230 once = False
230 for ax in fig.get_axes(): 231 for ax in fig.get_axes():
231 if ndays > 1: 232 if not once:
232 ax.set_title('%s to %s' % (start.strftime('%Y-%m-%d'), end.strftime('%Y-%m-%d'))) 233 once = True
233 else: 234 if ndays > 1:
234 ax.set_title('%s' % (start.strftime('%Y-%m-%d'))) 235 ax.set_title('%s to %s' % (start.astimezone(lt).strftime('%Y-%m-%d'), end.astimezone(lt).strftime('%Y-%m-%d')))
235 ax.set_xlim([start, end]) 236 else:
236 ax.format_xdata = lambda d: matplotlib.dates.num2date(d).strftime('%d %b %H:%M') 237 ax.set_title('%s' % (start.astimezone(lt).strftime('%Y-%m-%d')))
238 ax.set_xlim([start.astimezone(utc), end.astimezone(utc)])
237 ax.xaxis.grid(True) 239 ax.xaxis.grid(True)
240 ax.xaxis.axis_date(lt)
238 ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%d %b\n%H:%M')) 241 ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%d %b\n%H:%M'))
239 ax.xaxis.set_major_locator(matplotlib.dates.HourLocator(interval = 2 * ndays)) 242 ax.xaxis.set_major_locator(matplotlib.dates.HourLocator(interval = 2 * ndays))
240 ax.xaxis.set_minor_locator(matplotlib.dates.MinuteLocator(interval = 5 * ndays)) 243 ax.xaxis.set_minor_locator(matplotlib.dates.MinuteLocator(interval = 5 * ndays))
241 for label in ax.get_xticklabels(): 244 for label in ax.get_xticklabels():
242 label.set_ha('center') 245 label.set_ha('center')
248 matplotlib.pyplot.show() 251 matplotlib.pyplot.show()
249 else: 252 else:
250 canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig) # Sets canvas in fig too 253 canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig) # Sets canvas in fig too
251 fig.savefig(startdt.strftime(fname)) 254 fig.savefig(startdt.strftime(fname))
252 255
253 def updatedb(cur, data):
254 mkdb(cur)
255 for d in data['reads']['data']:
256 ts = datetime.datetime.strptime(d['t_stamp'], '%Y-%m-%dT%H:%M:%SZ')
257 # Note we rename *energy* to *power* here to match what it actually means
258 vals = [ts, d['battery_charge'], d['battery_energy'], d['energy_consumed'], d['energy_expected'], d['energy_exported'], d['energy_generated'],
259 d['energy_imported'], d['estimated_savings'], d['pv_forecast'], d['pv_generation']['battery_energy'],
260 d['pv_generation']['grid_energy'], d['pv_generation']['site_energy'], d['site_consumption']['battery_energy'],
261 d['site_consumption']['grid_energy'], d['site_consumption']['pv_energy']]
262 skip = True
263 for v in vals[1:]:
264 if v != None:
265 skip = False
266 break
267 if skip:
268 print('Skipping empty record at ' + str(ts))
269 continue
270 cur.execute('INSERT OR IGNORE INTO agl(t_stamp, battery_charge, battery_power, power_consumed, power_expected, power_exported, power_generated, power_imported, estimated_savings, pv_forecast, pv_gen_battery, pv_gen_grid, pv_gen_site, site_cons_battery, site_cons_grid, site_cons_pv) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', vals)
271
272 def gettoken(username, password):
273 authblob = json.encoder.JSONEncoder().encode({'email' : username, 'password' : password})
274 reply = requests.request('POST', loginurl, data = authblob, headers = {'Content-Type' : 'application/json'})
275 if reply.status_code != 200:
276 return None
277 return json.decoder.JSONDecoder().decode(reply.content)['access_token']
278
279 def getdata(token, startdate, enddate):
280 #print('getting ' + startdate.strftime('%Y-%m-%d'))
281 reply = requests.request('GET', dataurl, params = {
282 'startDate' : startdate.strftime('%Y-%m-%d'),
283 'endDate' : enddate.strftime('%Y-%m-%d'),
284 'granularity' : 'Minute',
285 'metrics' : 'read',
286 'units' : 'W',
287 }, headers = { 'Authorization' : 'Bearer ' + token})
288
289 if reply.status_code != 200:
290 return None
291
292 return json.decoder.JSONDecoder().decode(reply.content)
293
294 def logout(token):
295 reply = requests.request('GET', logouturl, headers = { 'Authorization' : 'Bearer ' + token})
296 return reply.status_code == 200
297
298 if __name__ == '__main__': 256 if __name__ == '__main__':
299 main() 257 main()