Skip to content

Commit

Permalink
fix: production daily html + doc
Browse files Browse the repository at this point in the history
  • Loading branch information
m4dm4rtig4n committed Feb 13, 2024
1 parent 4851a1a commit 96598e6
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 51 deletions.
35 changes: 23 additions & 12 deletions src/models/ajax.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,18 +667,29 @@ def datatable_daily(self, all_data, start_index, end_index, measurement_directio
hp = hp / 1000
hc_kw = f'<div id="{measurement_direction}_hc_{target}_{date_text}" class="">{hc}</div>'
hp_kw = f'<div id="{measurement_direction}_hp_{target}_{date_text}" class="">{hp}</div>'
day_data = [
date_text,
conso_w,
conso_kw,
hc_kw,
hp_kw,
temp_color,
fail_count,
cache_state,
self.datatable_button(measurement_direction, db_data)["cache"],
self.datatable_button(measurement_direction, db_data)["blacklist"],
]
if measurement_direction == "consumption":
day_data = [
date_text,
conso_w,
conso_kw,
hc_kw,
hp_kw,
temp_color,
fail_count,
cache_state,
self.datatable_button(measurement_direction, db_data)["cache"],
self.datatable_button(measurement_direction, db_data)["blacklist"],
]
else:
day_data = [
date_text,
conso_w,
conso_kw,
fail_count,
cache_state,
self.datatable_button(measurement_direction, db_data)["cache"],
self.datatable_button(measurement_direction, db_data)["blacklist"],
]
result.append(day_data)
index = index + 1
return result
Expand Down
14 changes: 9 additions & 5 deletions src/models/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __init__(self, config, path=APPLICATION_PATH_DATA):

self.engine = create_engine(
self.uri,
echo=False,
echo=True,
query_cache_size=0,
isolation_level="READ UNCOMMITTED",
poolclass=NullPool,
Expand Down Expand Up @@ -1056,14 +1056,18 @@ def insert_daily(

def reset_daily(self, usage_point_id, date=None, mesure_type="consumption"):
data = self.get_daily_date(usage_point_id, date, mesure_type)
if mesure_type == "consumption":
table = ConsumptionDaily
else:
table = ProductionDaily
if data is not None:
values = {
ConsumptionDaily.value: 0,
ConsumptionDaily.blacklist: 0,
ConsumptionDaily.fail_count: 0,
table.value: 0,
table.blacklist: 0,
table.fail_count: 0,
}
unique_id = hashlib.md5(f"{usage_point_id}/{date}".encode("utf-8")).hexdigest()
self.session.execute(update(ConsumptionDaily, values=values).where(ConsumptionDaily.id == unique_id))
self.session.execute(update(table, values=values).where(table.id == unique_id))
self.session.flush()
return True
else:
Expand Down
70 changes: 60 additions & 10 deletions src/models/export_home_assistant_ws.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Import data in statistique recorder of Home Assistant."""

import json
import logging
import ssl
from datetime import datetime, timedelta
from pprint import pprint

import pytz
import websocket
Expand All @@ -15,7 +16,14 @@


class HomeAssistantWs:
"""Class to interact with Home Assistant WebSocket API."""

def __init__(self, usage_point_id):
"""Initialize the class with the usage point id.
Args:
usage_point_id (str): The usage point id
"""
self.ws = None
self.usage_point_id = usage_point_id
self.usage_point_id_config = DB.get_usage_point(self.usage_point_id)
Expand All @@ -37,6 +45,11 @@ def __init__(self, usage_point_id):
self.ws.close()

def load_config(self):
"""Load the Home Assistant WebSocket configuration from the configuration file.
Returns:
bool: True if the configuration is loaded, False otherwise
"""
self.config = CONFIG.home_assistant_ws()
if self.config is not None:
if "url" in self.config:
Expand All @@ -59,6 +72,11 @@ def load_config(self):
return True

def connect(self):
"""Connect to the Home Assistant WebSocket server.
Returns:
bool: True if the connection is successful, False otherwise
"""
try:
check_ssl = CONFIG.get("ssl")
sslopt = None
Expand All @@ -75,16 +93,21 @@ def connect(self):
logging.info("Authentification requise")
return self.authentificate()
return True
except Exception as e:
except Exception as _e:
self.ws.close()
logging.error(e)
logging.error(_e)
logging.critical("Connexion impossible vers Home Assistant")
logging.warning(
f" => ATTENTION, le WebSocket est également soumis au ban en cas de plusieurs échec d'authentification."
" => ATTENTION, le WebSocket est également soumis au ban en cas de plusieurs échec d'authentification."
)
logging.warning(f" => ex: 403: Forbidden")
logging.warning(" => ex: 403: Forbidden")

def authentificate(self):
"""Authenticate with the Home Assistant WebSocket server.
Returns:
bool: True if the authentication is successful, False otherwise
"""
data = {"type": "auth", "access_token": self.token}
auth_output = self.send(data)
if auth_output["type"] == "auth_ok":
Expand All @@ -95,6 +118,13 @@ def authentificate(self):
return False

def send(self, data):
"""Send data to the Home Assistant WebSocket server.
Args:
data (dict): The data to send
Returns:
dict: The output from the server
"""
self.ws.send(json.dumps(data))
self.id = self.id + 1
output = json.loads(self.ws.recv())
Expand All @@ -105,6 +135,11 @@ def send(self, data):
return output

def list_data(self):
"""List the data already cached in Home Assistant.
Returns:
dict: The list of data
"""
logging.info("Liste les données déjà en cache.")
import_statistics = {
"id": self.id,
Expand All @@ -118,6 +153,13 @@ def list_data(self):
return current_stats

def clear_data(self, statistic_ids):
"""Clear the data imported into Energy.
Args:
statistic_ids (list): The list of statistic ids
Returns:
dict: The output from clearing the data
"""
logging.info("Effacement des données importées dans Energy.")
for key in statistic_ids:
logging.info(f" - {key}")
Expand All @@ -132,6 +174,15 @@ def clear_data(self, statistic_ids):
return clear_stat

def get_data(self, statistic_ids, begin, end):
"""Get the data for a given period.
Args:
statistic_ids (list): The list of statistic ids
begin (datetime): The start of the period
end (datetime): The end of the period
Returns:
dict: The data for the period
"""
statistics_during_period = {
"id": self.id,
"type": "recorder/statistics_during_period",
Expand All @@ -144,14 +195,15 @@ def get_data(self, statistic_ids, begin, end):
return stat_period

def import_data(self):
"""Import the data for the usage point into Home Assistant."""
logging.info(f"Importation des données du point de livraison : {self.usage_point_id}")
try:
plan = self.usage_point_id_config.plan.upper()
if self.usage_point_id_config.consumption_detail:
logging.info("Consommation")
measurement_direction = "consumption"
if "max_date" in self.config:
logging.warn(f"WARNING : Max date détecter {self.config['max_date']}")
logging.warning("WARNING : Max date détecter %s", self.config["max_date"])
begin = datetime.strptime(self.config["max_date"], "%Y-%m-%d")
detail = DB.get_detail_all(begin=begin, usage_point_id=self.usage_point_id, order_dir="desc")
else:
Expand Down Expand Up @@ -267,7 +319,6 @@ def import_data(self):
CONFIG.set("purge", False)

for statistic_id, data in stats_kwh.items():
# self.clear_data(statistic_id)
metadata = {
"has_mean": False,
"has_sum": True,
Expand All @@ -285,7 +336,6 @@ def import_data(self):
self.send(import_statistics)

for statistic_id, data in stats_euro.items():
# self.clear_data(statistic_id)
metadata = {
"has_mean": False,
"has_sum": True,
Expand All @@ -305,7 +355,7 @@ def import_data(self):
if self.usage_point_id_config.production_detail:
logging.info("Production")
logging.error("L'import de la production n'est pas fonctionnel pour l'instant.")
except Exception as e:
except Exception as _e:
self.ws.close()
logging.error(e)
logging.error(_e)
logging.critical("Erreur lors de l'export des données vers Home Assistant")
74 changes: 74 additions & 0 deletions src/models/query_daily.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,57 @@ def daterange(start_date, end_date):


class Daily:
"""
The 'Daily' class represents a daily data retrieval and manipulation process for a specific usage point. It provides methods for fetching, resetting, deleting, and blacklisting daily data.
Attributes:
config (dict): The configuration settings.
db (object): The database object.
url (str): The base URL for API requests.
max_daily (int): The maximum number of days to retrieve data for.
date_format (str): The format of dates.
date_detail_format (str): The format of detailed dates.
headers (dict): The headers for API requests.
usage_point_id (str): The ID of the usage point.
usage_point_config (object): The configuration settings for the usage point.
contract (object): The contract associated with the usage point.
daily_max_days (int): The maximum number of days for daily data.
max_days_date (datetime): The maximum date for retrieving data.
activation_date (datetime): The activation date for retrieving data.
measure_type (str): The type of measurement (consumption or production).
base_price (float): The base price for the measurement type.
Methods:
run(begin, end):
Retrieves and stores daily data for a specified date range.
get():
Retrieves and returns all available daily data for the usage point.
reset(date=None):
Resets the daily data for the usage point, optionally for a specific date.
delete(date=None):
Deletes the daily data for the usage point, optionally for a specific date.
fetch(date):
Fetches and returns the daily data for a specific date.
blacklist(date, action):
Adds or removes a date from the blacklist for the usage point.
Note:
The 'Daily' class relies on the 'Query' class for making API requests and the 'Stat' class for retrieving additional statistics.
Example usage:
headers = {"Authorization": "Bearer token"}
usage_point_id = "1234567890"
daily = Daily(headers, usage_point_id)
data = daily.get()
for item in data:
print(item)
"""

def __init__(self, headers, usage_point_id, measure_type="consumption"):
self.config = CONFIG
self.db = DB
Expand Down Expand Up @@ -147,6 +198,29 @@ def run(self, begin, end):
logging.error(e)

def get(self):
"""Generate a range of dates between a start date and an end date.
Parameters:
start_date (datetime.date): The start date of the range.
end_date (datetime.date): The end date of the range.
Yields:
datetime.date: The next date in the range.
Example:
>>> start_date = datetime.date(2021, 1, 1)
>>> end_date = datetime.date(2021, 1, 5)
>>> for date in daterange(start_date, end_date):
... print(date)
...
2021-01-01
2021-01-02
2021-01-03
2021-01-04
Note:
The end date is exclusive, meaning it is not included in the range.
"""
end = datetime.combine((datetime.now() + timedelta(days=2)), datetime.max.time())
begin = datetime.combine(end - relativedelta(days=self.max_daily), datetime.min.time())
finish = True
Expand Down
32 changes: 32 additions & 0 deletions src/routers/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,55 @@

@ROUTER.get("/favicon.ico")
async def favicon():
"""
This function handles the endpoint '/favicon.ico' and returns the favicon.ico file as a response.
Returns:
- FileResponse: The favicon.ico file as a response.
"""
return FileResponse(f"{APPLICATION_PATH}/static/favicon.ico")


@ROUTER.get("/", response_class=HTMLResponse)
def main():
"""This function handles the root endpoint '/' and returns the HTML response generated by the 'display' method of the 'Index' class.
Returns:
- HTMLResponse: The HTML response generated by the 'display' method of the 'Index' class.
"""
return Index(CONFIG, DB).display()


@ROUTER.get("/usage_point_id/{usage_point_id}", response_class=HTMLResponse)
@ROUTER.get("/usage_point_id/{usage_point_id}/", response_class=HTMLResponse)
def usage_point_id(usage_point_id):
"""This function handles the endpoint '/usage_point_id/{usage_point_id}' and '/usage_point_id/{usage_point_id}/' and returns the HTML response generated by the 'display' method of the 'UsagePoint' class.
Parameters:
- usage_point_id (str): The ID of the usage point.
Returns:
- HTMLResponse: The HTML response generated by the 'display' method of the 'UsagePoint' class.
"""
return UsagePoint(usage_point_id).display()


@ROUTER.get("/datatable/{usage_point_id}/{measurement_direction}")
@ROUTER.get("/datatable/{usage_point_id}/{measurement_direction}/")
def datatable(request: Request, usage_point_id, measurement_direction):
"""Get datatable for a specific usage point and measurement direction.
Parameters:
- request (Request): The FastAPI request object.
- usage_point_id (str): The ID of the usage point.
- measurement_direction (str): The measurement direction (e.g., consumption, production).
Returns:
- The datatable for the specified usage point and measurement direction.
Example:
datatable(request, "usage_point_id", "measurement_direction")
"""
return Ajax(usage_point_id).datatable(measurement_direction, request)


Expand Down
Loading

0 comments on commit 96598e6

Please sign in to comment.