Compare commits

...

37 Commits

Author SHA1 Message Date
19672e6106 fix deployment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-03-12 12:57:45 +01:00
97e9d3e4e5 load route files correctly
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-03-12 12:01:07 +01:00
0914a91fa0 debug start script and separate routes into separate files
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-03-12 11:57:37 +01:00
a972916704 switch to python 3.12
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-19 13:11:55 +01:00
1774bb11aa change public name
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-13 11:37:13 +01:00
d19ac55dea time reqs, 2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-13 11:09:18 +01:00
4db989022e time reqs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-13 10:59:17 +01:00
af76406afa packets, 3
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 19:01:58 +01:00
d85c32247e packets, 2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 18:48:37 +01:00
ba7b86e527 packets
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 18:42:27 +01:00
c93ae4067e more graphs, 5
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 13:38:26 +01:00
1bbfdf65fb more graphs, 3
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 13:28:29 +01:00
315ad9998b more graphs, 2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 13:18:41 +01:00
047a3a6c08 more graphs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 13:14:01 +01:00
5573024fd9 ticksuffix, 2
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 12:54:33 +01:00
a58f914c40 ticksuffix
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 12:45:52 +01:00
4ec9690981 from ntpserver_added
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-12 12:38:41 +01:00
6af509c2f6 drop kaleido 2025-02-12 12:35:07 +01:00
22eba69526 merged
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-02-12 12:32:55 +01:00
a2855edd47 use tag
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-02-12 12:20:04 +01:00
a2f720855d sbom scanning
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-02-12 12:15:42 +01:00
cbb9ff7a23 kaleido, 3
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-02-11 22:13:57 +01:00
84b5bbe325 python 3.11
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-11 21:59:14 +01:00
a42254cf95 import kaleido, 2
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-11 21:21:14 +01:00
3d05ea5d28 import kaleido
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-11 19:58:08 +01:00
52217c5251 still kaleido
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-11 19:53:48 +01:00
022d7a8926 merged
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-02-11 19:52:27 +01:00
698f926376 Merge branch 'main' of gitea.hottis.de:wn/pv-stats 2025-02-11 19:52:10 +01:00
f74ad50b94 kaleido 2025-02-11 19:52:04 +01:00
0c03d9f94e add kaleido, 3
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-02-11 19:49:49 +01:00
eca5affd53 add kaleido, 2
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-02-11 19:48:09 +01:00
6236673d28 add kaleido
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-02-11 19:47:07 +01:00
4213dc7329 ntp as png
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-11 19:42:19 +01:00
2d3eab0db8 disable trivy for the moment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-02-11 16:59:58 +01:00
73b55b05c4 ntp server numbers
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-02-11 16:52:51 +01:00
aa74c02498 timing
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-01-31 10:29:42 +01:00
6fd2bd0863 timing
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-01-31 10:19:03 +01:00
9 changed files with 364 additions and 150 deletions

View File

@ -13,12 +13,42 @@ steps:
dockerfile: Dockerfile dockerfile: Dockerfile
when: when:
- event: [push, tag] - event: [push, tag]
scan_image:
image: aquasec/trivy scan:
image: quay.io/wollud1969/woodpecker-helper:0.5.1
environment:
TRIVY_TOKEN:
from_secret: trivy_token
TRIVY_URL:
from_secret: trivy_url
DTRACK_API_KEY:
from_secret: dtrack_api_key
DTRACK_API_URL:
from_secret: dtrack_api_url
commands: commands:
- TRIVY_DISABLE_VEX_NOTICE=1 trivy image $FORGE_NAME/$CI_REPO:$CI_COMMIT_SHA --quiet --exit-code 1 - HOME=/home/`id -nu`
- TAG="${CI_COMMIT_TAG:-$CI_COMMIT_SHA}"
- |
trivy image \
--server $TRIVY_URL \
--token $TRIVY_TOKEN \
--format cyclonedx \
--scanners license \
--output /tmp/sbom.xml \
$FORGE_NAME/$CI_REPO:$TAG
- cat /tmp/sbom.xml
- |
curl -X "POST" \
-H "Content-Type: multipart/form-data" \
-H "X-Api-Key: $DTRACK_API_KEY" \
-F "autoCreate=true" \
-F "projectName=$CI_REPO" \
-F "projectVersion=$TAG" \
-F "bom=@/tmp/sbom.xml"\
"$DTRACK_API_URL/api/v1/bom"
when: when:
- event: [push, tag] - event: [push, tag]
deploy: deploy:
image: quay.io/wollud1969/k8s-admin-helper:0.2.1 image: quay.io/wollud1969/k8s-admin-helper:0.2.1
environment: environment:

40
debug-build-run.sh Executable file
View File

@ -0,0 +1,40 @@
#!/bin/bash
set -x
IMAGE_NAME=numberimage
docker build --progress=plain -t $IMAGE_NAME .
SECRETS=`mktemp`
gpg --decrypt --passphrase $GPG_PASSPHRASE --yes --batch --output $SECRETS ./deployment/secrets.asc
. $SECRETS
rm $SECRETS
DB_NAMESPACE=database1
DB_DEPLOYNAME=database
REDIS_NAMESPACE=redis
REDIS_SERVICE_NAME=redis
PGHOST=`kubectl get services $DB_DEPLOYNAME -n $DB_NAMESPACE -o jsonpath="{.status.loadBalancer.ingress[0].ip}"`
REDISHOST=`kubectl get services $REDIS_SERVICE_NAME -n $REDIS_NAMESPACE -o jsonpath="{.status.loadBalancer.ingress[0].ip}"`
REDIS_URL=redis://$REDISHOST:6379/4
docker run \
-it \
--rm \
-e "REDIS_URL=$REDIS_URL" \
-e "SECRET_KEY=$SECRET_KEY" \
-e "OIDC_CLIENT_SECRETS=$OIDC_CLIENT_SECRETS" \
-e "PGHOST=$PGHOST" \
-e "PGDATABASE=$PGDATABASE" \
-e "PGSSLMODE=$PGSSLMODE" \
-e "PGUSER=$PGUSER" \
-e "PGPASSWORD=$PGPASSWORD" \
-p 8080:8080 \
$IMAGE_NAME

View File

@ -48,10 +48,10 @@ metadata:
spec: spec:
tls: tls:
- hosts: - hosts:
- pv-stats.hottis.de - numbers.hottis.de
secretName: pv-stats-cert secretName: numbers-cert
rules: rules:
- host: pv-stats.hottis.de - host: numbers.hottis.de
http: http:
paths: paths:
- path: / - path: /

View File

@ -9,7 +9,7 @@ if [ "$GPG_PASSPHRASE" == "" ]; then
exit 1 exit 1
fi fi
IMAGE_NAME=gitea.hottis.de/wn/pv-stats IMAGE_NAME=gitea.hottis.de/wn/numbers
NAMESPACE=homea NAMESPACE=homea
DEPLOYMENT_DIR=$PWD/deployment DEPLOYMENT_DIR=$PWD/deployment

32
src/app.py Normal file
View File

@ -0,0 +1,32 @@
from flask import Flask, session, g, render_template_string
from flask_session import Session
from flask_oidc import OpenIDConnect
from werkzeug.middleware.proxy_fix import ProxyFix
from loguru import logger
import redis
import json
import os
try:
redis_url = os.environ['REDIS_URL']
oidc_client_secrets = os.environ['OIDC_CLIENT_SECRETS']
secret_key = os.environ['SECRET_KEY']
except KeyError as e:
logger.error(f"Required environment variable not set ({e})")
raise e
app = Flask(__name__)
app.config.update({
'SECRET_KEY': secret_key,
'SESSION_TYPE': 'redis',
'SESSION_REDIS': redis.from_url(redis_url),
'OIDC_CLIENT_SECRETS': json.loads(oidc_client_secrets),
'OIDC_SCOPES': 'openid email',
'OIDC_USER_INFO_ENABLED': True,
'SESSION_USE_SIGNER': True,
})
Session(app)
oidc = OpenIDConnect(app)

16
src/debug_routes.py Normal file
View File

@ -0,0 +1,16 @@
from loguru import logger
import json
from app import app
from app import oidc
@app.route('/token_debug', methods=['GET'])
@oidc.require_login
def token_debug():
# Access Token vom Identity Provider abrufen
access_token = oidc.get_access_token()
return json.dumps({
"access_token": access_token
})

119
src/ntp_routes.py Normal file
View File

@ -0,0 +1,119 @@
from loguru import logger
import json
import plotly.express as px
import plotly.graph_objects as po
import pandas as pd
import psycopg
import sqlalchemy
import time
from app import app
from app import oidc
@app.route('/ntpserver')
def ntpserver():
try:
dbh = psycopg.connect()
engine = sqlalchemy.create_engine("postgresql+psycopg://", creator=lambda: dbh)
query = """
select time_bucket('5 minutes', time) as bucket,
device,
avg(cast(values->'rootdisp'->>'value' as float)) as rootdisp,
max(cast(values->'stratum'->>'value' as int)) as stratum
from measurements
where time >= date_trunc('day', now()) AND time < date_trunc('day', now()) + '1 day'::interval and
application = 'TSM' and attributes->>'Label' = 'david'
group by bucket, device
order by bucket, device
"""
df = pd.read_sql(query, con=engine)
fig = po.Figure()
fig.add_trace(po.Scatter(x=df['bucket'], y=df['rootdisp'], mode='lines', name='Root Dispersion', yaxis='y1', line=dict(color='red')))
fig.add_trace(po.Scatter(x=df['bucket'], y=df['stratum'], mode='lines', name='Stratum', yaxis='y2', line=dict(color='blue')))
fig.update_layout(
title='NTP Server Numbers',
# Linke Y-Achse
yaxis=dict(
title='Root Dispersion',
ticksuffix=' ms'
),
# Rechte Y-Achse
yaxis2=dict(
title='Stratum',
overlaying='y', # Legt die zweite Y-Achse über die erste
side='right', # Setzt sie auf die rechte Seite
tickmode='linear', # Stellt sicher, dass die Ticks in festen Intervallen sind
dtick=1, # Zeigt nur ganzzahlige Ticks
),
legend=dict(x=0.05, y=1) # Position der Legende
)
graph_html_1 = fig.to_html(full_html=False, default_height='30%')
query = """
select time_bucket('5 minutes', time) as bucket,
device,
avg(cast(values->'time-req-pkts'->>'value' as float)) as packets
from measurements
where time >= date_trunc('day', now()) AND time < date_trunc('day', now()) + '1 day'::interval and
application = 'SNMP' and attributes->>'Label' = 'david'
group by bucket, device
order by bucket, device
"""
df = pd.read_sql(query, con=engine)
fig_2 = px.line(df, x='bucket', y='packets')
fig_2.update_layout(
xaxis_title="",
yaxis_title="",
yaxis_ticksuffix="p/s",
title=f"Time Requests"
)
graph_html_2 = fig_2.to_html(full_html=False, default_height='30%')
query = """
select time_bucket('5 minutes', time) as bucket,
device,
avg(cast(values->'load1'->>'value' as float)) as loadaverage1min
from measurements
where time >= date_trunc('day', now()) AND time < date_trunc('day', now()) + '1 day'::interval and
application = 'SNMP' and attributes->>'Label' = 'david'
group by bucket, device
order by bucket, device
"""
df = pd.read_sql(query, con=engine)
fig_3 = px.line(df, x='bucket', y='loadaverage1min')
fig_3.update_layout(
xaxis_title="",
yaxis_title="",
title=f"CPU Load"
)
graph_html_3 = fig_3.to_html(full_html=False, default_height='30%')
return render_template_string(f"""
<html>
<head>
<title>NTP Server Numbers</title>
</head>
<body>
{graph_html_1}
{graph_html_2}
{graph_html_3}
</body>
</html>
""")
except Exception as e:
raise Exception(f"Error when querying NTP server values: {e}")
finally:
if dbh is not None:
dbh.close()

116
src/pv_routes.py Normal file
View File

@ -0,0 +1,116 @@
from loguru import logger
import json
import plotly.express as px
import plotly.graph_objects as po
import pandas as pd
import psycopg
import sqlalchemy
import time
from app import app
from app import oidc
@app.route('/')
@oidc.require_login
def index():
try:
stepX_time = time.time()
dbh = psycopg.connect()
engine = sqlalchemy.create_engine("postgresql+psycopg://", creator=lambda: dbh)
step0_time = time.time()
df = pd.read_sql("SELECT month, cast(year AS varchar), current_energy AS value FROM pv_energy_by_month", con=engine)
step1_time = time.time()
duration1 = step1_time - step0_time
logger.info(f"{duration1=}")
fig_1 = px.bar(df, x='month', y='value', color='year', barmode='group')
step2_time = time.time()
duration2 = step2_time - step1_time
logger.info(f"{duration2=}")
fig_1.update_layout(
title=f"Jahreswerte Exportierte Energie {duration1:.3f}, {duration2:.3f}",
xaxis_title="",
yaxis_title="",
legend_title="Jahr",
xaxis=dict(
tickmode="array",
tickvals=list(range(1, 13)), # Monate 112
ticktext=["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
),
yaxis=dict(ticksuffix=" kWh")
)
graph_html_1 = fig_1.to_html(full_html=False, default_height='30%')
step3_time = time.time()
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)
step4_time = time.time()
duration3 = step4_time - step3_time
logger.info(f"{duration3=}")
fig_2 = px.line(df, x='bucket', y='avg_power')
step5_time = time.time()
duration4 = step5_time - step4_time
logger.info(f"{duration4=}")
fig_2.update_layout(
xaxis_title="",
yaxis_title="",
title=f"Export gestern {duration3:.3f}, {duration4:.3f}",
yaxis=dict(ticksuffix=" W")
)
graph_html_2 = fig_2.to_html(full_html=False, default_height='30%')
step6_time = time.time()
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)
step7_time = time.time()
duration5 = step7_time - step6_time
logger.info(f"{duration5=}")
fig_3 = px.line(df, x='bucket', y='avg_power')
step8_time = time.time()
duration6 = step8_time - step7_time
logger.info(f"{duration6=}")
fig_3.update_layout(
xaxis_title="",
yaxis_title="",
title=f"Export heute {duration5:.3f}, {duration6:.3f}",
yaxis=dict(ticksuffix=" W")
)
graph_html_3 = fig_3.to_html(full_html=False, default_height='30%')
stepZ_time = time.time()
duration7 = stepZ_time - stepX_time
logger.info(f"{duration7=}")
return render_template_string(f"""
<html>
<head>
<title>Jahreswerte PV-Energie</title>
</head>
<body>
{graph_html_1}
{graph_html_2}
{graph_html_3}
<div style="height:9vh; background-color:lightgrey; font-family: Courier, Consolas, monospace;">
<table style="border-collapse: collapse;">
<style>
td.smallsep {{ padding-right: 10px }}
td.largesep {{ padding-right: 30px }}
</style>
<tr>
<td class="smallsep">Query 1:</td><td class="largesep"> {duration1:.3f} s</td><td class="smallsep">Graph 1:</td><td> {duration2:.3f} s</td>
</tr><tr>
<td class="smallsep">Query 2:</td><td class="largesep"> {duration3:.3f} s</td><td class="smallsep">Graph 2:</td><td> {duration4:.3f} s</td>
</tr><tr>
<td class="smallsep">Query 3:</td><td class="largesep"> {duration5:.3f} s</td><td class="smallsep">Graph 3:</td><td> {duration6:.3f} s</td>
</tr><tr>
<td class="smallsep">Total:</td><td> {duration7:.3f} s</td><td></td><td></td>
</tr>
</table>
</div>
</body>
</html>
""")
except Exception as e:
raise Exception(f"Error when querying energy export values: {e}")
finally:
if dbh is not None:
dbh.close()

View File

@ -1,150 +1,11 @@
from flask import Flask, session, g, render_template_string
from flask_session import Session
from flask_oidc import OpenIDConnect
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
from loguru import logger from loguru import logger
import redis
import json
import os
import plotly.express as px
import pandas as pd
import psycopg
import sqlalchemy
import time
try:
redis_url = os.environ['REDIS_URL']
oidc_client_secrets = os.environ['OIDC_CLIENT_SECRETS']
secret_key = os.environ['SECRET_KEY']
except KeyError as e:
logger.error(f"Required environment variable not set ({e})")
raise e
app = Flask(__name__)
app.config.update({
'SECRET_KEY': secret_key,
'SESSION_TYPE': 'redis',
'SESSION_REDIS': redis.from_url(redis_url),
'OIDC_CLIENT_SECRETS': json.loads(oidc_client_secrets),
'OIDC_SCOPES': 'openid email',
'OIDC_USER_INFO_ENABLED': True,
'SESSION_USE_SIGNER': True,
})
Session(app)
oidc = OpenIDConnect(app)
@app.route('/token_debug', methods=['GET'])
@oidc.require_login
def token_debug():
# Access Token vom Identity Provider abrufen
access_token = oidc.get_access_token()
return json.dumps({
"access_token": access_token
})
@app.route('/')
@oidc.require_login
def index():
try:
stepX_time = time.time()
dbh = psycopg.connect()
engine = sqlalchemy.create_engine("postgresql+psycopg://", creator=lambda: dbh)
step0_time = time.time()
df = pd.read_sql("SELECT month, cast(year AS varchar), current_energy AS value FROM pv_energy_by_month", con=engine)
step1_time = time.time()
duration1 = step1_time - step0_time
logger.info(f"{duration1=}")
fig_1 = px.bar(df, x='month', y='value', color='year', barmode='group')
step2_time = time.time()
duration2 = step2_time - step1_time
logger.info(f"{duration2=}")
fig_1.update_layout(
title=f"Jahreswerte Exportierte Energie {duration1:.3f}, {duration2:.3f}",
xaxis_title="",
yaxis_title="",
legend_title="Jahr",
xaxis=dict(
tickmode="array",
tickvals=list(range(1, 13)), # Monate 112
ticktext=["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
),
yaxis=dict(ticksuffix=" kWh")
)
graph_html_1 = fig_1.to_html(full_html=False, default_height='30%')
step3_time = time.time()
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)
step4_time = time.time()
duration3 = step4_time - step3_time
logger.info(f"{duration3=}")
fig_2 = px.line(df, x='bucket', y='avg_power')
step5_time = time.time()
duration4 = step5_time - step4_time
logger.info(f"{duration4=}")
fig_2.update_layout(
xaxis_title="",
yaxis_title="",
title=f"Export gestern {duration3:.3f}, {duration4:.3f}",
yaxis=dict(ticksuffix=" W")
)
graph_html_2 = fig_2.to_html(full_html=False, default_height='30%')
step6_time = time.time()
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)
step7_time = time.time()
duration5 = step7_time - step6_time
logger.info(f"{duration5=}")
fig_3 = px.line(df, x='bucket', y='avg_power')
step8_time = time.time()
duration6 = step8_time - step7_time
logger.info(f"{duration6=}")
fig_3.update_layout(
xaxis_title="",
yaxis_title="",
title=f"Export heute {duration5:.3f}, {duration6:.3f}",
yaxis=dict(ticksuffix=" W")
)
graph_html_3 = fig_3.to_html(full_html=False, default_height='30%')
stepZ_time = time.time()
duration7 = stepZ_time - stepX_time
logger.info(f"{duration7=}")
return render_template_string(f"""
<html>
<head>
<title>Jahreswerte PV-Energie</title>
</head>
<body>
{graph_html_1}
{graph_html_2}
{graph_html_3}
<div style="height:9vh; background-color:lightgrey; font-family: Courier, Consolas, monospace;">
<table style="border-collapse: collapse;">
<style>table td {{ padding-right: 100px }}</style>
<tr>
<td>Query 1: {duration1:.3f} s</td><td>Graph 1: {duration2:.3f} s</td>
</tr><tr>
<td>Query 2: {duration3:.3f} s</td><td>Graph 2: {duration4:.3f} s</td>
</tr><tr>
<td>Query 3: {duration5:.3f} s</td><td>Graph 3: {duration6:.3f} s</td>
</tr><tr>
<td>Total: {duration7:.3f} s</td><td></td>
</tr>
</table>
</div>
</body>
</html>
""")
except Exception as e:
raise Exception(f"Error when querying energy export values: {e}")
finally:
if dbh is not None:
dbh.close()
from app import app
import debug_routes
import pv_routes
import ntp_routes
if __name__ == '__main__': if __name__ == '__main__':