2025-01-21 17:29:58 +01:00
from flask import Flask , session , g , render_template_string
2025-01-20 14:25:53 +01:00
from flask_session import Session
2025-01-20 16:55:52 +01:00
from flask_oidc import OpenIDConnect
2025-01-27 18:35:04 +01:00
from werkzeug . middleware . proxy_fix import ProxyFix
2025-01-20 14:33:40 +01:00
from loguru import logger
2025-01-20 14:25:53 +01:00
import redis
2025-01-27 18:35:04 +01:00
import json
2025-01-20 14:33:40 +01:00
import os
2025-01-27 16:21:58 +01:00
import plotly . express as px
import pandas as pd
import psycopg
import sqlalchemy
2025-01-30 10:03:16 +01:00
import time
2025-01-20 14:33:40 +01:00
try :
redis_url = os . environ [ ' REDIS_URL ' ]
2025-01-27 18:35:04 +01:00
oidc_client_secrets = os . environ [ ' OIDC_CLIENT_SECRETS ' ]
2025-01-20 16:55:52 +01:00
secret_key = os . environ [ ' SECRET_KEY ' ]
2025-01-20 14:33:40 +01:00
except KeyError as e :
logger . error ( f " Required environment variable not set ( { e } ) " )
raise e
2025-01-20 14:25:53 +01:00
app = Flask ( __name__ )
2025-01-20 14:33:40 +01:00
2025-01-20 14:25:53 +01:00
app . config . update ( {
2025-01-20 16:55:52 +01:00
' SECRET_KEY ' : secret_key ,
2025-01-20 14:25:53 +01:00
' SESSION_TYPE ' : ' redis ' ,
2025-01-27 18:35:04 +01:00
' SESSION_REDIS ' : redis . from_url ( redis_url ) ,
' OIDC_CLIENT_SECRETS ' : json . loads ( oidc_client_secrets ) ,
2025-01-20 16:55:52 +01:00
' OIDC_SCOPES ' : ' openid email ' ,
' OIDC_USER_INFO_ENABLED ' : True ,
' SESSION_USE_SIGNER ' : True ,
2025-01-20 14:25:53 +01:00
} )
Session ( app )
2025-01-20 16:55:52 +01:00
oidc = OpenIDConnect ( app )
2025-01-20 14:25:53 +01:00
2025-01-28 21:49:27 +01:00
@app.route ( ' /token_debug ' , methods = [ ' GET ' ] )
@oidc.require_login
def token_debug ( ) :
# Access Token vom Identity Provider abrufen
access_token = oidc . get_access_token ( )
2025-01-28 21:54:13 +01:00
return json . dumps ( {
2025-01-28 21:57:23 +01:00
" access_token " : access_token
2025-01-28 21:54:13 +01:00
} )
2025-01-28 21:49:27 +01:00
2025-01-20 14:25:53 +01:00
@app.route ( ' / ' )
2025-01-20 16:55:52 +01:00
@oidc.require_login
2025-01-20 14:25:53 +01:00
def index ( ) :
2025-01-27 16:21:58 +01:00
try :
2025-01-30 13:30:45 +01:00
stepX_time = time . time ( )
2025-01-27 16:21:58 +01:00
dbh = psycopg . connect ( )
engine = sqlalchemy . create_engine ( " postgresql+psycopg:// " , creator = lambda : dbh )
2025-01-28 14:13:30 +01:00
2025-01-30 13:35:08 +01:00
step0_time = time . time ( )
2025-01-27 16:21:58 +01:00
df = pd . read_sql ( " SELECT month, cast(year AS varchar), current_energy AS value FROM pv_energy_by_month " , con = engine )
2025-01-30 10:19:07 +01:00
step1_time = time . time ( )
2025-01-30 13:30:45 +01:00
duration1 = step1_time - step0_time
2025-01-30 16:53:07 +01:00
logger . info ( f " { duration1 =} " )
2025-01-28 14:13:30 +01:00
fig_1 = px . bar ( df , x = ' month ' , y = ' value ' , color = ' year ' , barmode = ' group ' )
2025-01-30 10:19:07 +01:00
step2_time = time . time ( )
2025-01-30 10:31:36 +01:00
duration2 = step2_time - step1_time
2025-01-30 16:53:07 +01:00
logger . info ( f " { duration2 =} " )
2025-01-28 14:13:30 +01:00
fig_1 . update_layout (
2025-01-30 10:19:07 +01:00
title = f " Jahreswerte Exportierte Energie { duration1 : .3f } , { duration2 : .3f } " ,
2025-01-28 14:41:18 +01:00
xaxis_title = " " ,
2025-01-27 16:21:58 +01:00
yaxis_title = " " ,
legend_title = " Jahr " ,
xaxis = dict (
tickmode = " array " ,
tickvals = list ( range ( 1 , 13 ) ) , # Monate 1– 12
ticktext = [ " Jan " , " Feb " , " Mär " , " Apr " , " Mai " , " Jun " , " Jul " , " Aug " , " Sep " , " Okt " , " Nov " , " Dez " ]
) ,
yaxis = dict ( ticksuffix = " kWh " )
)
2025-01-30 14:16:01 +01:00
graph_html_1 = fig_1 . to_html ( full_html = False , default_height = ' 30 % ' )
2025-01-28 14:13:30 +01:00
2025-01-30 13:30:45 +01:00
step3_time = time . time ( )
2025-01-28 14:20:09 +01:00
df = pd . read_sql ( " SELECT time_bucket( ' 5 minutes ' , time) AS bucket, AVG(power) AS avg_power FROM pv_power_v WHERE time >= date_trunc( ' day ' , now()) - ' 1 day ' ::interval AND time < date_trunc( ' day ' , now()) GROUP BY bucket ORDER BY bucket " , con = engine )
2025-01-30 13:30:45 +01:00
step4_time = time . time ( )
2025-01-30 16:53:07 +01:00
duration3 = step4_time - step3_time
logger . info ( f " { duration3 =} " )
2025-01-28 14:13:30 +01:00
fig_2 = px . line ( df , x = ' bucket ' , y = ' avg_power ' )
2025-01-30 13:30:45 +01:00
step5_time = time . time ( )
2025-01-30 16:53:07 +01:00
duration4 = step5_time - step4_time
logger . info ( f " { duration4 =} " )
2025-01-28 14:13:30 +01:00
fig_2 . update_layout (
2025-01-28 14:41:18 +01:00
xaxis_title = " " ,
yaxis_title = " " ,
2025-01-30 16:53:07 +01:00
title = f " Export gestern { duration3 : .3f } , { duration4 : .3f } " ,
2025-01-28 14:41:18 +01:00
yaxis = dict ( ticksuffix = " W " )
2025-01-28 14:13:30 +01:00
)
2025-01-30 14:16:01 +01:00
graph_html_2 = fig_2 . to_html ( full_html = False , default_height = ' 30 % ' )
2025-01-27 16:21:58 +01:00
2025-01-30 13:30:45 +01:00
step6_time = time . time ( )
2025-01-28 14:28:24 +01:00
df = pd . read_sql ( " SELECT time_bucket( ' 5 minutes ' , time) AS bucket, AVG(power) AS avg_power FROM pv_power_v WHERE time >= date_trunc( ' day ' , now()) AND time < date_trunc( ' day ' , now()) + ' 1 day ' ::interval GROUP BY bucket ORDER BY bucket " , con = engine )
2025-01-30 13:30:45 +01:00
step7_time = time . time ( )
2025-01-30 16:53:07 +01:00
duration5 = step7_time - step6_time
logger . info ( f " { duration5 =} " )
2025-01-28 14:28:24 +01:00
fig_3 = px . line ( df , x = ' bucket ' , y = ' avg_power ' )
2025-01-30 13:30:45 +01:00
step8_time = time . time ( )
2025-01-30 16:53:07 +01:00
duration6 = step8_time - step7_time
logger . info ( f " { duration6 =} " )
2025-01-28 14:28:24 +01:00
fig_3 . update_layout (
2025-01-28 14:41:18 +01:00
xaxis_title = " " ,
yaxis_title = " " ,
2025-01-30 16:53:07 +01:00
title = f " Export heute { duration5 : .3f } , { duration6 : .3f } " ,
2025-01-28 14:41:18 +01:00
yaxis = dict ( ticksuffix = " W " )
2025-01-28 14:28:24 +01:00
)
2025-01-30 14:16:01 +01:00
graph_html_3 = fig_3 . to_html ( full_html = False , default_height = ' 30 % ' )
2025-01-30 13:30:45 +01:00
stepZ_time = time . time ( )
2025-01-30 16:53:07 +01:00
duration7 = stepZ_time - stepX_time
logger . info ( f " { duration7 =} " )
2025-01-28 14:28:24 +01:00
2025-01-27 16:21:58 +01:00
return render_template_string ( f """
< html >
< head >
< title > Jahreswerte PV - Energie < / title >
< / head >
< body >
2025-01-28 14:13:30 +01:00
{ graph_html_1 }
{ graph_html_2 }
2025-01-28 14:28:24 +01:00
{ graph_html_3 }
2025-01-30 16:59:36 +01:00
< div style = " height:9vh; background-color:lightgrey; font-family: Courier, Consolas, monospace; " >
2025-01-30 17:10:01 +01:00
< table style = " border-collapse: collapse; " >
2025-01-30 17:37:55 +01:00
< style > table td { { padding - right : 100 px } } < / style >
2025-01-30 16:53:07 +01:00
< tr >
2025-01-30 17:10:01 +01:00
< td > Query 1 : { duration1 : .3 f } s < / td > < td > Graph 1 : { duration2 : .3 f } s < / td >
2025-01-30 16:53:07 +01:00
< / tr > < tr >
2025-01-30 17:10:01 +01:00
< td > Query 2 : { duration3 : .3 f } s < / td > < td > Graph 2 : { duration4 : .3 f } s < / td >
2025-01-30 16:53:07 +01:00
< / tr > < tr >
2025-01-30 17:10:01 +01:00
< td > Query 3 : { duration5 : .3 f } s < / td > < td > Graph 3 : { duration6 : .3 f } s < / td >
2025-01-30 16:53:07 +01:00
< / tr > < tr >
2025-01-30 17:37:55 +01:00
< td > Total : { duration7 : .3 f } s < / td > < td > < / td >
2025-01-30 16:53:07 +01:00
< / tr >
< / table >
2025-01-30 14:16:01 +01:00
< / div >
2025-01-27 16:21:58 +01:00
< / body >
< / html >
""" )
except Exception as e :
raise Exception ( f " Error when querying energy export values: { e } " )
finally :
if dbh is not None :
dbh . close ( )
2025-01-21 17:29:58 +01:00
2025-01-20 14:25:53 +01:00
if __name__ == ' __main__ ' :
app . run ( port = 8080 )
2025-01-27 18:35:04 +01:00
else :
exposed_app = ProxyFix ( app , x_for = 1 , x_host = 1 )