317 lines
12 KiB
Python
317 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import bisect
|
|
import datetime
|
|
import json
|
|
import numpy
|
|
import redis
|
|
|
|
from libcitizenwatt import database
|
|
from libcitizenwatt import tools
|
|
from sqlalchemy import asc, desc
|
|
from libcitizenwatt.config import Config
|
|
|
|
|
|
config = Config()
|
|
|
|
|
|
def do_cache_ids(sensor, watt_euros, id1, id2, db, force_refresh=False):
|
|
"""
|
|
Computes the cache (if needed) for the API call
|
|
/api/<sensor:int>/get/<watt_euros:re:watts|kwatthours|euros>/by_id/<id1:int>/<id2:int>
|
|
|
|
Returns the stored (or computed) data or None if parameters are invalid.
|
|
"""
|
|
r = redis.Redis(decode_responses=True)
|
|
if not force_refresh:
|
|
data = r.get(watt_euros + "_" + str(sensor) + "_" + "by_id" + "_" +
|
|
str(id1) + "_" + str(id2))
|
|
if data:
|
|
# If found in cache, return it
|
|
return json.loads(data)
|
|
|
|
if id1 >= 0 and id2 >= 0 and id2 >= id1:
|
|
data = (db.query(database.Measures)
|
|
.filter(database.Measures.sensor_id == sensor,
|
|
database.Measures.id >= id1,
|
|
database.Measures.id < id2)
|
|
.order_by(asc(database.Measures.timestamp))
|
|
.all())
|
|
elif id1 <= 0 and id2 <= 0 and id2 >= id1:
|
|
data = (db.query(database.Measures)
|
|
.filter_by(sensor_id=sensor)
|
|
.order_by(desc(database.Measures.timestamp))
|
|
.slice(-id2, -id1)
|
|
.all())
|
|
data.reverse()
|
|
else:
|
|
return None
|
|
|
|
if not data:
|
|
data = None
|
|
else:
|
|
time1 = data[0].timestamp
|
|
time2 = data[-1].timestamp
|
|
if watt_euros == 'kwatthours' or watt_euros == 'euros':
|
|
data = tools.energy(data)
|
|
if watt_euros == 'euros':
|
|
if data["night_rate"] != 0:
|
|
night_rate = tools.watt_euros(0,
|
|
'night',
|
|
data['night_rate'],
|
|
db)
|
|
else:
|
|
night_rate = 0
|
|
if data["day_rate"] != 0:
|
|
day_rate = tools.watt_euros(0,
|
|
'day',
|
|
data['day_rate'],
|
|
db)
|
|
else:
|
|
day_rate = 0
|
|
data = {"value": night_rate + day_rate}
|
|
else:
|
|
data = tools.to_dict(data)
|
|
|
|
# Store in cache
|
|
r.set(watt_euros + "_" + str(sensor) + "_" + "by_id" + "_" +
|
|
str(id1) + "_" + str(id2),
|
|
json.dumps(data),
|
|
time2 - time1)
|
|
|
|
return data
|
|
|
|
|
|
def do_cache_group_id(sensor, watt_euros, id1, id2, step, db,
|
|
timestep=config.get("default_timestep"),
|
|
force_refresh=False):
|
|
"""
|
|
Computes the cache (if needed) for the API call
|
|
/api/<sensor:int>/get/<watt_euros:re:watts|kwatthours|euros>/by_id/<id1:int>/<id2:int>/<step:int>
|
|
|
|
Returns the stored (or computed) data.
|
|
"""
|
|
r = redis.Redis(decode_responses=True)
|
|
if not force_refresh:
|
|
data = r.get(watt_euros + "_" + str(sensor) + "_" + "by_id" + "_" +
|
|
str(id1) + "_" + str(id2) + "_" +
|
|
str(step) + "_" + str(timestep))
|
|
if data:
|
|
# If found in cache, return it
|
|
return json.loads(data)
|
|
|
|
steps = [i for i in range(id1, id2, step)]
|
|
steps.append(id2)
|
|
|
|
if id1 >= 0 and id2 >= 0 and id2 >= id1:
|
|
data = (db.query(database.Measures)
|
|
.filter(database.Measures.sensor_id == sensor,
|
|
database.Measures.id >= id1,
|
|
database.Measures.id < id2)
|
|
.order_by(asc(database.Measures.timestamp))
|
|
.all())
|
|
elif id1 <= 0 and id2 <= 0 and id2 >= id1:
|
|
data = (db.query(database.Measures)
|
|
.filter_by(sensor_id=sensor)
|
|
.order_by(desc(database.Measures.timestamp))
|
|
.slice(-id2, -id1)
|
|
.all())
|
|
data.reverse()
|
|
else:
|
|
raise ValueError
|
|
|
|
time2 = None
|
|
if not data:
|
|
data = [None for i in range(len(steps) - 1)]
|
|
else:
|
|
time1 = data[0].timestamp
|
|
time2 = data[-1].timestamp
|
|
data_dict = tools.to_dict(data)
|
|
tmp = [[] for i in range(len(steps) - 1)]
|
|
for i in data_dict:
|
|
tmp[bisect.bisect_left(steps, i["id"]) - 1].append(i)
|
|
|
|
data = []
|
|
for i in tmp:
|
|
if len(i) == 0:
|
|
data.append(None)
|
|
continue
|
|
|
|
energy = tools.energy(i)
|
|
if watt_euros == "watts":
|
|
tmp_data = {"value": energy["value"] / (step * timestep) * 1000 * 3600,
|
|
"day_rate": energy["day_rate"] / (step * timestep) * 1000 * 3600,
|
|
"night_rate": energy["night_rate"] / (step * timestep) * 1000 * 3600}
|
|
elif watt_euros == 'kwatthours':
|
|
tmp_data = energy
|
|
elif watt_euros == 'euros':
|
|
if energy["night_rate"] != 0:
|
|
night_rate = tools.watt_euros(0,
|
|
'night',
|
|
energy['night_rate'],
|
|
db)
|
|
else:
|
|
night_rate = 0
|
|
if energy["day_rate"] != 0:
|
|
day_rate = tools.watt_euros(0,
|
|
'day',
|
|
energy['day_rate'],
|
|
db)
|
|
else:
|
|
day_rate = 0
|
|
tmp_data = {"value": night_rate + day_rate}
|
|
data.append(tmp_data)
|
|
if len(data) == 0:
|
|
data = None
|
|
if time2 is not None:
|
|
# Store in cache
|
|
if time2 < datetime.datetime.now().timestamp():
|
|
# If new measures are to come, short lifetime (basically timestep)
|
|
r.set(watt_euros + "_" + str(sensor) + "_" + "by_id" + "_" +
|
|
str(id1) + "_" + str(id2) + "_" +
|
|
str(step) + "_" + str(timestep),
|
|
json.dumps(data),
|
|
timestep)
|
|
else:
|
|
# Else, store for a greater lifetime (basically time2 - time1)
|
|
r.set(watt_euros + "_" + str(sensor) + "_" + "by_id" + "_" +
|
|
str(id1) + "_" + str(id2) + "_" +
|
|
str(step) + "_" + str(timestep),
|
|
json.dumps(data),
|
|
time2 - time1)
|
|
|
|
return data
|
|
|
|
|
|
def do_cache_times(sensor, watt_euros, time1, time2, db, force_refresh=False):
|
|
"""
|
|
Computes the cache (if needed) for the API call
|
|
/api/<sensor:int>/get/<watt_euros:re:watts|kwatthours|euros>/by_time/<time1:float>/<time2:float>
|
|
Returns the stored (or computed) data.
|
|
"""
|
|
r = redis.Redis(decode_responses=True)
|
|
if not force_refresh:
|
|
data = r.get(watt_euros + "_" + str(sensor) + "_" + "by_time" + "_" +
|
|
str(time1) + "_" + str(time2))
|
|
if data:
|
|
# If found in cache, return it
|
|
return json.loads(data)
|
|
|
|
data = (db.query(database.Measures)
|
|
.filter(database.Measures.sensor_id == sensor,
|
|
database.Measures.timestamp >= time1,
|
|
database.Measures.timestamp < time2)
|
|
.order_by(asc(database.Measures.timestamp))
|
|
.all())
|
|
|
|
if not data:
|
|
data = None
|
|
else:
|
|
if watt_euros == "kwatthours" or watt_euros == "euros":
|
|
data = tools.energy(data)
|
|
if watt_euros == "euros":
|
|
data = {"value": (tools.watt_euros(0,
|
|
'night',
|
|
data['night_rate'],
|
|
db) +
|
|
tools.watt_euros(0,
|
|
'day',
|
|
data['day_rate'],
|
|
db))}
|
|
|
|
else:
|
|
data = tools.to_dict(data)
|
|
|
|
# Store in cache
|
|
r.set(watt_euros + "_" + str(sensor) + "_" + "by_id" + "_" +
|
|
str(time1) + "_" + str(time2),
|
|
json.dumps(data),
|
|
int(time2) - int(time1))
|
|
|
|
return data
|
|
|
|
|
|
def do_cache_group_timestamp(sensor, watt_euros, time1, time2, step, db,
|
|
force_refresh=True):
|
|
"""
|
|
Computes the cache (if needed) for the API call
|
|
/api/<sensor:int>/get/<watt_euros:re:watts|kwatthours|euros>/by_time/<time1:float>/<time2:float>/<step:float>
|
|
|
|
Returns the stored (or computed) data.
|
|
"""
|
|
r = redis.Redis(decode_responses=True)
|
|
if not force_refresh:
|
|
data = r.get(watt_euros + "_" + str(sensor) + "_" + "by_time" + "_" +
|
|
str(time1) + "_" + str(time2) + "_" + str(step))
|
|
if data:
|
|
# If found in cache, return it
|
|
return json.loads(data)
|
|
|
|
steps = [i for i in numpy.arange(time1, time2, step)]
|
|
steps.append(time2)
|
|
|
|
data = (db.query(database.Measures)
|
|
.filter(database.Measures.sensor_id == sensor,
|
|
database.Measures.timestamp
|
|
.between(time1, time2))
|
|
.order_by(asc(database.Measures.timestamp))
|
|
.all())
|
|
|
|
if not data:
|
|
data = [None for i in range(len(steps) - 1)]
|
|
else:
|
|
tmp = [[] for i in range(len(steps) - 1)]
|
|
for i in data:
|
|
index = bisect.bisect_left(steps, i.timestamp)
|
|
if index > 0:
|
|
index -= 1
|
|
tmp[index].append(i)
|
|
|
|
data = []
|
|
for i in tmp:
|
|
if len(i) == 0:
|
|
data.append(None)
|
|
continue
|
|
|
|
energy = tools.energy(i)
|
|
if watt_euros == "watts":
|
|
tmp_data = {"value": energy["value"] / step * 1000 * 3600,
|
|
"day_rate": energy["day_rate"] / step * 1000 * 3600,
|
|
"night_rate": energy["night_rate"] / step * 1000 * 3600}
|
|
elif watt_euros == 'kwatthours':
|
|
tmp_data = energy
|
|
elif watt_euros == 'euros':
|
|
if energy["night_rate"] != 0:
|
|
night_rate = tools.watt_euros(0,
|
|
'night',
|
|
energy['night_rate'],
|
|
db)
|
|
else:
|
|
night_rate = 0
|
|
if energy["day_rate"] != 0:
|
|
day_rate = tools.watt_euros(0,
|
|
'day',
|
|
energy['day_rate'],
|
|
db)
|
|
else:
|
|
day_rate = 0
|
|
tmp_data = {"value": night_rate + day_rate}
|
|
data.append(tmp_data)
|
|
if len(data) == 0:
|
|
data = None
|
|
# Store in cache
|
|
if time2 < datetime.datetime.now().timestamp():
|
|
# If new measures are to come, short lifetime (basically timestep)
|
|
r.setex(watt_euros + "_" + str(sensor) + "_" + "by_time" + "_" +
|
|
str(time1) + "_" + str(time2) + "_" + str(step),
|
|
json.dumps(data),
|
|
int(step))
|
|
else:
|
|
# Else, store for a greater lifetime (basically time2 - time1)
|
|
r.setex(watt_euros + "_" + str(sensor) + "_" + "by_time" + "_" +
|
|
str(time1) + "_" + str(time2) + "_" + str(step),
|
|
json.dumps(data),
|
|
int(time2 - time1))
|
|
|
|
return data
|