# HG changeset patch # User Daniel O'Connor # Date 1519360065 -37800 # Node ID c04c75360a3b1e692af966a54b3ab27a4fb107f0 # Parent aa18210c2703a84fbd979fe6dc6b0f1c84b79e40 Fix timezone screw up. Remove cut & paste code from the AGL script diff -r aa18210c2703 -r c04c75360a3b graph.py --- a/graph.py Thu Jan 11 08:43:40 2018 +1030 +++ b/graph.py Fri Feb 23 14:57:45 2018 +1030 @@ -114,10 +114,6 @@ args.start = args.start.replace(tzinfo = lt) if args.end.tzinfo == None: args.end = args.end.replace(tzinfo = lt) - startlt = args.start - endlt = args.end - args.start = args.start.astimezone(utc) - args.end = args.end.astimezone(utc) graph(args.graphfn, cur, cols, args.start, args.end, lt, utc) def graph(fname, cur, _cols, start, end, lt, utc): @@ -163,10 +159,10 @@ ary = numpy.array(cur.fetchall()) if ary.shape[0] == 0: print('No data for ' + c.rowname) - return + continue - # Create TZ naive from POSIX stamp, then convert to TZ aware UTC then adjust to local time - c.xdata = map(lambda f: datetime.datetime.fromtimestamp(f).replace(tzinfo = utc).astimezone(lt), ary[:,0]) + # Convert epoch stamp to datetime with UTC timezone + c.xdata = map(lambda f: datetime.datetime.utcfromtimestamp(f).replace(tzinfo = utc), ary[:,0]) c.ydata = ary[:,1] if c.conv != None: c.ydata = map(c.conv, c.ydata) @@ -188,6 +184,10 @@ colouridx += 1 ax.append(c) + if len(ax1lines) == 0 and len(ax2lines) == 0: + print('Nothing to plot') + return + # Load the right backend for display or save if fname == None: import matplotlib.pylab @@ -207,7 +207,7 @@ ax1.set_ylim(line.limits[0], line.limits[1]) if line.annotation != None: annotations.append(line.annotation) - ax1.legend(loc = 'upper left') + ax1.legend(loc = 'center left') if len(ax2lines) > 0: ax2 = ax1.twinx() @@ -220,21 +220,24 @@ if line.annotation != None: annotations.append(line.annotation) - ax2.legend(loc = 'upper right') + ax2.legend(loc = 'center right') if len(annotations) > 0: - ax1.text(0.02, 0.5, reduce(lambda a, b: a + '\n' + b, annotations), + ax1.text(0.02, 0.3, reduce(lambda a, b: a + '\n' + b, annotations), transform = ax1.transAxes, bbox = dict(facecolor = 'blue', alpha = 0.5), ha = 'left', va = 'top') ndays = int(max(1, round(((end - start).total_seconds()) / 86400))) + once = False for ax in fig.get_axes(): - if ndays > 1: - ax.set_title('%s to %s' % (start.strftime('%Y-%m-%d'), end.strftime('%Y-%m-%d'))) - else: - ax.set_title('%s' % (start.strftime('%Y-%m-%d'))) - ax.set_xlim([start, end]) - ax.format_xdata = lambda d: matplotlib.dates.num2date(d).strftime('%d %b %H:%M') + if not once: + once = True + if ndays > 1: + ax.set_title('%s to %s' % (start.astimezone(lt).strftime('%Y-%m-%d'), end.astimezone(lt).strftime('%Y-%m-%d'))) + else: + ax.set_title('%s' % (start.astimezone(lt).strftime('%Y-%m-%d'))) + ax.set_xlim([start.astimezone(utc), end.astimezone(utc)]) ax.xaxis.grid(True) + ax.xaxis.axis_date(lt) ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%d %b\n%H:%M')) ax.xaxis.set_major_locator(matplotlib.dates.HourLocator(interval = 2 * ndays)) ax.xaxis.set_minor_locator(matplotlib.dates.MinuteLocator(interval = 5 * ndays)) @@ -250,50 +253,5 @@ canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig) # Sets canvas in fig too fig.savefig(startdt.strftime(fname)) -def updatedb(cur, data): - mkdb(cur) - for d in data['reads']['data']: - ts = datetime.datetime.strptime(d['t_stamp'], '%Y-%m-%dT%H:%M:%SZ') - # Note we rename *energy* to *power* here to match what it actually means - vals = [ts, d['battery_charge'], d['battery_energy'], d['energy_consumed'], d['energy_expected'], d['energy_exported'], d['energy_generated'], - d['energy_imported'], d['estimated_savings'], d['pv_forecast'], d['pv_generation']['battery_energy'], - d['pv_generation']['grid_energy'], d['pv_generation']['site_energy'], d['site_consumption']['battery_energy'], - d['site_consumption']['grid_energy'], d['site_consumption']['pv_energy']] - skip = True - for v in vals[1:]: - if v != None: - skip = False - break - if skip: - print('Skipping empty record at ' + str(ts)) - continue - 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) - -def gettoken(username, password): - authblob = json.encoder.JSONEncoder().encode({'email' : username, 'password' : password}) - reply = requests.request('POST', loginurl, data = authblob, headers = {'Content-Type' : 'application/json'}) - if reply.status_code != 200: - return None - return json.decoder.JSONDecoder().decode(reply.content)['access_token'] - -def getdata(token, startdate, enddate): - #print('getting ' + startdate.strftime('%Y-%m-%d')) - reply = requests.request('GET', dataurl, params = { - 'startDate' : startdate.strftime('%Y-%m-%d'), - 'endDate' : enddate.strftime('%Y-%m-%d'), - 'granularity' : 'Minute', - 'metrics' : 'read', - 'units' : 'W', - }, headers = { 'Authorization' : 'Bearer ' + token}) - - if reply.status_code != 200: - return None - - return json.decoder.JSONDecoder().decode(reply.content) - -def logout(token): - reply = requests.request('GET', logouturl, headers = { 'Authorization' : 'Bearer ' + token}) - return reply.status_code == 200 - if __name__ == '__main__': main()