graph and email in windows script added
This commit is contained in:
BIN
prijs_voorspelling_windows.png
Normal file
BIN
prijs_voorspelling_windows.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -4,13 +4,33 @@ import xgboost as xgb
|
|||||||
import mysql.connector
|
import mysql.connector
|
||||||
from mysql.connector import Error
|
from mysql.connector import Error
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import holidays # NIEUW: importeer holidays
|
import holidays
|
||||||
|
|
||||||
|
# --- NIEUW: Imports voor grafiek en e-mail ---
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import smtplib
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.image import MIMEImage
|
||||||
|
|
||||||
|
# --- NIEUW: Zorgt dat matplotlib werkt zonder GUI (bv. in terminal) ---
|
||||||
|
plt.switch_backend('Agg')
|
||||||
|
|
||||||
# --- CONFIGURATIE ---
|
# --- CONFIGURATIE ---
|
||||||
# BELANGRIJK: Verwijs naar je NIEUWE model
|
|
||||||
MODEL_FILE = 'price_forecast_model_v1_5.json'
|
MODEL_FILE = 'price_forecast_model_v1_5.json'
|
||||||
TARGET = 'gemiddelde_prijs'
|
TARGET = 'gemiddelde_prijs'
|
||||||
AANTAL_UUR_VOORSPELLEN = 72 # Hoeveel uur vooruit wil je kijken?
|
AANTAL_UUR_VOORSPELLEN = 72
|
||||||
|
GRAFIEK_BESTAND = 'prijs_voorspelling_windows.png' # Tijdelijk bestand
|
||||||
|
|
||||||
|
# --- NIEUW: E-mail Configuratie ---
|
||||||
|
# VUL DIT IN MET JE EIGEN GEGEVENS
|
||||||
|
EMAIL_CONFIG = {
|
||||||
|
'smtp_server': '192.168.178.201', # Bv. 'smtp.gmail.com'
|
||||||
|
'smtp_port': 587,
|
||||||
|
'sender': 'sftpuser@markkors.nl',
|
||||||
|
'password': 'Aae8-G9yFU5j', # Voeg hier het wachtwoord toe als dat nodig is
|
||||||
|
'receiver': 'mark@markkors.nl'
|
||||||
|
}
|
||||||
|
|
||||||
DB_CONFIG = {
|
DB_CONFIG = {
|
||||||
'host': '192.168.178.201',
|
'host': '192.168.178.201',
|
||||||
@@ -20,29 +40,26 @@ DB_CONFIG = {
|
|||||||
'port': 3307
|
'port': 3307
|
||||||
}
|
}
|
||||||
|
|
||||||
# NIEUW: Haal de lijst van features uit je model
|
# --- Model laden ---
|
||||||
# We hoeven de lijst niet meer handmatig te typen!
|
|
||||||
try:
|
try:
|
||||||
print(f"Laden van model: {MODEL_FILE}...")
|
print(f"Laden van model: {MODEL_FILE}...")
|
||||||
model = xgb.XGBRegressor()
|
model = xgb.XGBRegressor()
|
||||||
model.load_model(MODEL_FILE)
|
model.load_model(MODEL_FILE)
|
||||||
FEATURES = model.feature_names_in_ # Pakt automatisch alle feature-namen
|
FEATURES = model.feature_names_in_
|
||||||
print(f"✅ Model succesvol geladen (verwacht {len(FEATURES)} features).")
|
print(f"✅ Model succesvol geladen (verwacht {len(FEATURES)} features).")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"❌ Fout: Model bestand '{MODEL_FILE}' niet gevonden.")
|
print(f"❌ Fout: Model bestand '{MODEL_FILE}' niet gevonden.")
|
||||||
print("Heb je het 'v1_5' model al getraind en opgeslagen?")
|
print(" Heb je het 'v1_5' model al getraind en opgeslagen op Windows?")
|
||||||
exit()
|
exit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Fout bij laden model: {e}")
|
print(f"❌ Fout bij laden model: {e}")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
# Maak de feestdagen-checker klaar
|
|
||||||
nl_holidays = holidays.Netherlands(years=[datetime.now().year, datetime.now().year + 1])
|
nl_holidays = holidays.Netherlands(years=[datetime.now().year, datetime.now().year + 1])
|
||||||
|
|
||||||
|
|
||||||
|
# --- Functie: haal_data_uit_database (onveranderd) ---
|
||||||
def haal_data_uit_database(conn):
|
def haal_data_uit_database(conn):
|
||||||
# ... (Deze functie is 100% IDENTIEK aan je vorige script)
|
|
||||||
# ... (Kopieer de 'haal_data_uit_database' functie hier)
|
|
||||||
print("💾 Data ophalen uit MySQL...")
|
print("💾 Data ophalen uit MySQL...")
|
||||||
|
|
||||||
query_hist = """
|
query_hist = """
|
||||||
@@ -52,7 +69,7 @@ def haal_data_uit_database(conn):
|
|||||||
p_avg.gemiddelde_prijs
|
p_avg.gemiddelde_prijs
|
||||||
FROM
|
FROM
|
||||||
amersfoort_weer_uurlijks AS w
|
amersfoort_weer_uurlijks AS w
|
||||||
LEFT JOIN -- <--- DE OPLOSSING
|
LEFT JOIN
|
||||||
(SELECT datetime, AVG(price) AS gemiddelde_prijs
|
(SELECT datetime, AVG(price) AS gemiddelde_prijs
|
||||||
FROM dynamic_price_data GROUP BY datetime) AS p_avg
|
FROM dynamic_price_data GROUP BY datetime) AS p_avg
|
||||||
ON w.datum_tijd = p_avg.datetime
|
ON w.datum_tijd = p_avg.datetime
|
||||||
@@ -82,13 +99,9 @@ def haal_data_uit_database(conn):
|
|||||||
print(f"✅ {len(hist_df)} uur historie geladen.")
|
print(f"✅ {len(hist_df)} uur historie geladen.")
|
||||||
print(f"✅ {len(toekomst_df)} uur toekomstig weer geladen.")
|
print(f"✅ {len(toekomst_df)} uur toekomstig weer geladen.")
|
||||||
|
|
||||||
# --- OPLOSSING HIER ---
|
|
||||||
# Vul 'gaten' in de historische prijsdata ALLEEN op hist_df
|
|
||||||
hist_df['gemiddelde_prijs'] = hist_df['gemiddelde_prijs'].ffill()
|
hist_df['gemiddelde_prijs'] = hist_df['gemiddelde_prijs'].ffill()
|
||||||
hist_df['gemiddelde_prijs'] = hist_df['gemiddelde_prijs'].bfill()
|
hist_df['gemiddelde_prijs'] = hist_df['gemiddelde_prijs'].bfill()
|
||||||
# --- EINDE OPLOSSING ---
|
|
||||||
|
|
||||||
# Combineer nu de gevulde historie met de lege toekomst
|
|
||||||
combined_df = pd.concat([hist_df, toekomst_df])
|
combined_df = pd.concat([hist_df, toekomst_df])
|
||||||
|
|
||||||
return combined_df.sort_index()
|
return combined_df.sort_index()
|
||||||
@@ -97,52 +110,84 @@ def haal_data_uit_database(conn):
|
|||||||
print(f"❌ Fout bij ophalen data: {e}")
|
print(f"❌ Fout bij ophalen data: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# --- Functie: maak_features_voor_uur (onveranderd) ---
|
||||||
def maak_features_voor_uur(df, timestamp):
|
def maak_features_voor_uur(df, timestamp):
|
||||||
"""
|
|
||||||
MAAK FEATURES v1.5 - Deze functie is compleet VERNIEUWD
|
|
||||||
"""
|
|
||||||
features = {}
|
features = {}
|
||||||
|
|
||||||
# Haal data op van het specifieke uur
|
|
||||||
data_nu = df.loc[timestamp]
|
data_nu = df.loc[timestamp]
|
||||||
|
|
||||||
# 1. Tijd-features (simpel)
|
|
||||||
features['maand'] = timestamp.month
|
features['maand'] = timestamp.month
|
||||||
features['dag_van_het_jaar'] = timestamp.dayofyear
|
features['dag_van_het_jaar'] = timestamp.dayofyear
|
||||||
|
|
||||||
# 2. Feestdag feature
|
|
||||||
features['is_feestdag'] = 1 if timestamp in nl_holidays else 0
|
features['is_feestdag'] = 1 if timestamp in nl_holidays else 0
|
||||||
|
|
||||||
# 3. Weer-features
|
|
||||||
weer_cols = ['temperatuur', 'gevoelstemperatuur', 'neerslag', 'wind_richting',
|
weer_cols = ['temperatuur', 'gevoelstemperatuur', 'neerslag', 'wind_richting',
|
||||||
'wind_snelheid', 'bewolking', 'luchtdruk', 'luchtvochtigheid']
|
'wind_snelheid', 'bewolking', 'luchtdruk', 'luchtvochtigheid']
|
||||||
for col in weer_cols:
|
for col in weer_cols:
|
||||||
features[col] = data_nu[col]
|
features[col] = data_nu[col]
|
||||||
|
|
||||||
# 4. Lag-features
|
|
||||||
features['prijs_1u_geleden'] = df.loc[timestamp - timedelta(hours=1)]['gemiddelde_prijs']
|
features['prijs_1u_geleden'] = df.loc[timestamp - timedelta(hours=1)]['gemiddelde_prijs']
|
||||||
features['prijs_24u_geleden'] = df.loc[timestamp - timedelta(hours=24)]['gemiddelde_prijs']
|
features['prijs_24u_geleden'] = df.loc[timestamp - timedelta(hours=24)]['gemiddelde_prijs']
|
||||||
|
|
||||||
# 5. Rolling-features
|
|
||||||
features['temp_avg_3u'] = df.loc[timestamp - timedelta(hours=2) : timestamp]['temperatuur'].mean()
|
features['temp_avg_3u'] = df.loc[timestamp - timedelta(hours=2) : timestamp]['temperatuur'].mean()
|
||||||
features['prijs_avg_6u'] = df.loc[timestamp - timedelta(hours=5) : timestamp]['gemiddelde_prijs'].mean()
|
features['prijs_avg_6u'] = df.loc[timestamp - timedelta(hours=5) : timestamp]['gemiddelde_prijs'].mean()
|
||||||
|
|
||||||
# 6. ONE-HOT ENCODING (Handmatig)
|
|
||||||
# Voeg alle 7 'dag_' kolommen toe, en zet de juiste op 1
|
|
||||||
for dag in range(7):
|
for dag in range(7):
|
||||||
features[f'dag_{dag}'] = 1 if timestamp.dayofweek == dag else 0
|
features[f'dag_{dag}'] = 1 if timestamp.dayofweek == dag else 0
|
||||||
|
|
||||||
# Voeg alle 24 'uur_' kolommen toe, en zet de juiste op 1
|
|
||||||
for uur in range(24):
|
for uur in range(24):
|
||||||
features[f'uur_{uur}'] = 1 if timestamp.hour == uur else 0
|
features[f'uur_{uur}'] = 1 if timestamp.hour == uur else 0
|
||||||
|
|
||||||
# Converteer naar een DataFrame en gebruik de volgorde van het model
|
|
||||||
return pd.DataFrame([features], columns=FEATURES)
|
return pd.DataFrame([features], columns=FEATURES)
|
||||||
|
|
||||||
|
# --- NIEUW: Functie om de e-mail te bouwen en te versturen ---
|
||||||
|
def send_email_with_graph(image_path, result_df):
|
||||||
|
print("\n📬 E-mail opstellen...")
|
||||||
|
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg['Subject'] = f"Prijsvoorspelling (Windows Test) {datetime.now().strftime('%Y-%m-%d')}"
|
||||||
|
msg['From'] = EMAIL_CONFIG['sender']
|
||||||
|
msg['To'] = EMAIL_CONFIG['receiver']
|
||||||
|
|
||||||
# --- START VAN HET SCRIPT ---
|
laagste_prijs = result_df['Voorspelde_Prijs'].min()
|
||||||
# (Dit deel is weer 100% identiek aan je vorige script)
|
hoogste_prijs = result_df['Voorspelde_Prijs'].max()
|
||||||
|
|
||||||
|
body = f"""
|
||||||
|
Hallo,
|
||||||
|
|
||||||
|
Dit is een test van het Windows script.
|
||||||
|
Hier is de prijsvoorspelling voor de komende {len(result_df)} uur.
|
||||||
|
|
||||||
|
Laagste prijs: {laagste_prijs:.4f}
|
||||||
|
Hoogste prijs: {hoogste_prijs:.4f}
|
||||||
|
|
||||||
|
Volledige voorspelling:
|
||||||
|
{result_df.to_string()}
|
||||||
|
"""
|
||||||
|
msg.attach(MIMEText(body, 'plain'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(image_path, 'rb') as f:
|
||||||
|
img_attach = MIMEImage(f.read(), name='voorspelling.png')
|
||||||
|
msg.attach(img_attach)
|
||||||
|
print("✅ Grafiek bijgevoegd.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Kon grafiek-bestand niet lezen: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"Verbinding maken met SMTP server: {EMAIL_CONFIG['smtp_server']}...")
|
||||||
|
server = smtplib.SMTP(EMAIL_CONFIG['smtp_server'], EMAIL_CONFIG['smtp_port'])
|
||||||
|
server.starttls()
|
||||||
|
server.login(EMAIL_CONFIG['sender'], EMAIL_CONFIG['password'])
|
||||||
|
|
||||||
|
print("Inloggen succesvol. E-mail verzenden...")
|
||||||
|
server.sendmail(EMAIL_CONFIG['sender'], EMAIL_CONFIG['receiver'], msg.as_string())
|
||||||
|
server.quit()
|
||||||
|
print("✅ E-mail succesvol verzonden.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Fout bij verzenden e-mail: {e}")
|
||||||
|
print(" Controleer je EMAIL_CONFIG (server, poort, e-mail en App-wachtwoord)")
|
||||||
|
|
||||||
|
# --- START VAN HET SCRIPT (Aangepast) ---
|
||||||
try:
|
try:
|
||||||
conn = mysql.connector.connect(**DB_CONFIG)
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
werk_df = haal_data_uit_database(conn)
|
werk_df = haal_data_uit_database(conn)
|
||||||
@@ -150,25 +195,57 @@ try:
|
|||||||
if werk_df is not None:
|
if werk_df is not None:
|
||||||
te_voorspellen_tijden = werk_df[werk_df['gemiddelde_prijs'].isnull()].index
|
te_voorspellen_tijden = werk_df[werk_df['gemiddelde_prijs'].isnull()].index
|
||||||
|
|
||||||
print(f"\n🧠 Start iteratieve voorspelling voor {len(te_voorspellen_tijden)} uur...")
|
if len(te_voorspellen_tijden) == 0:
|
||||||
voorspellingen = []
|
print("\nDatabase is al up-to-date. Geen voorspelling nodig.")
|
||||||
|
else:
|
||||||
for timestamp in te_voorspellen_tijden:
|
print(f"\n🧠 Start iteratieve voorspelling voor {len(te_voorspellen_tijden)} uur...")
|
||||||
features_nu = maak_features_voor_uur(werk_df, timestamp)
|
voorspellingen = []
|
||||||
voorspelde_prijs = model.predict(features_nu)[0]
|
|
||||||
werk_df.loc[timestamp, 'gemiddelde_prijs'] = voorspelde_prijs
|
|
||||||
voorspellingen.append(voorspelde_prijs)
|
|
||||||
|
|
||||||
print("\n" + "="*70)
|
for timestamp in te_voorspellen_tijden:
|
||||||
pd.set_option('display.max_rows', None) # Zorg dat we alles printen
|
features_nu = maak_features_voor_uur(werk_df, timestamp)
|
||||||
print(f"--- VOORSPELDE PRIJZEN (komende {len(te_voorspellen_tijden)} uur) ---")
|
voorspelde_prijs = model.predict(features_nu)[0]
|
||||||
|
werk_df.loc[timestamp, 'gemiddelde_prijs'] = voorspelde_prijs
|
||||||
resultaat_df = pd.DataFrame({
|
voorspellingen.append(voorspelde_prijs)
|
||||||
'Voorspelde_Prijs': voorspellingen
|
|
||||||
}, index=te_voorspellen_tijden)
|
print("\n" + "="*70)
|
||||||
|
pd.set_option('display.max_rows', None)
|
||||||
print(resultaat_df)
|
print(f"--- VOORSPELDE PRIJZEN (komende {len(te_voorspellen_tijden)} uur) ---")
|
||||||
print("="*70)
|
|
||||||
|
resultaat_df = pd.DataFrame({
|
||||||
|
'Voorspelde_Prijs': voorspellingen
|
||||||
|
}, index=te_voorspellen_tijden)
|
||||||
|
|
||||||
|
print(resultaat_df)
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
# --- NIEUW: Grafiek maken en e-mailen ---
|
||||||
|
print("\n📊 Grafiek genereren...")
|
||||||
|
try:
|
||||||
|
fig, ax = plt.subplots(figsize=(15, 8))
|
||||||
|
resultaat_df.plot(
|
||||||
|
ax=ax,
|
||||||
|
title=f'Energieprijs Voorspelling ({len(resultaat_df)} uur vooruit)',
|
||||||
|
legend=False,
|
||||||
|
grid=True,
|
||||||
|
style='.-'
|
||||||
|
)
|
||||||
|
ax.set_ylabel('Voorspelde Prijs')
|
||||||
|
ax.set_xlabel('Datum en Tijd')
|
||||||
|
|
||||||
|
laagste_prijs = resultaat_df['Voorspelde_Prijs'].min()
|
||||||
|
hoogste_prijs = resultaat_df['Voorspelde_Prijs'].max()
|
||||||
|
ax.axhline(laagste_prijs, color='green', linestyle='--', linewidth=0.8)
|
||||||
|
ax.axhline(hoogste_prijs, color='red', linestyle='--', linewidth=0.8)
|
||||||
|
|
||||||
|
plt.savefig(GRAFIEK_BESTAND)
|
||||||
|
plt.close(fig) # Geheugen vrijgeven
|
||||||
|
print(f"✅ Grafiek opgeslagen als: {GRAFIEK_BESTAND}")
|
||||||
|
|
||||||
|
send_email_with_graph(GRAFIEK_BESTAND, resultaat_df)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Fout bij genereren van grafiek: {e}")
|
||||||
|
# --- EINDE NIEUWE BLOK ---
|
||||||
|
|
||||||
except Error as e:
|
except Error as e:
|
||||||
print(f"❌ Fout met MySQL verbinding: {e}")
|
print(f"❌ Fout met MySQL verbinding: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user