diff --git a/prijs_voorspelling_windows.png b/prijs_voorspelling_windows.png new file mode 100644 index 0000000..29cd6ac Binary files /dev/null and b/prijs_voorspelling_windows.png differ diff --git a/voorspel_advanced.py b/voorspel_advanced.py index acbc957..b1a4d28 100644 --- a/voorspel_advanced.py +++ b/voorspel_advanced.py @@ -4,13 +4,33 @@ import xgboost as xgb import mysql.connector from mysql.connector import Error 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 --- -# BELANGRIJK: Verwijs naar je NIEUWE model MODEL_FILE = 'price_forecast_model_v1_5.json' 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 = { 'host': '192.168.178.201', @@ -20,29 +40,26 @@ DB_CONFIG = { 'port': 3307 } -# NIEUW: Haal de lijst van features uit je model -# We hoeven de lijst niet meer handmatig te typen! +# --- Model laden --- try: print(f"Laden van model: {MODEL_FILE}...") model = xgb.XGBRegressor() 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).") except FileNotFoundError: 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() except Exception as e: print(f"āŒ Fout bij laden model: {e}") exit() -# Maak de feestdagen-checker klaar nl_holidays = holidays.Netherlands(years=[datetime.now().year, datetime.now().year + 1]) +# --- Functie: haal_data_uit_database (onveranderd) --- 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...") query_hist = """ @@ -52,7 +69,7 @@ def haal_data_uit_database(conn): p_avg.gemiddelde_prijs FROM amersfoort_weer_uurlijks AS w - LEFT JOIN -- <--- DE OPLOSSING + LEFT JOIN (SELECT datetime, AVG(price) AS gemiddelde_prijs FROM dynamic_price_data GROUP BY datetime) AS p_avg 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(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'].bfill() - # --- EINDE OPLOSSING --- - # Combineer nu de gevulde historie met de lege toekomst combined_df = pd.concat([hist_df, toekomst_df]) return combined_df.sort_index() @@ -97,52 +110,84 @@ def haal_data_uit_database(conn): print(f"āŒ Fout bij ophalen data: {e}") return None - +# --- Functie: maak_features_voor_uur (onveranderd) --- def maak_features_voor_uur(df, timestamp): - """ - MAAK FEATURES v1.5 - Deze functie is compleet VERNIEUWD - """ features = {} - - # Haal data op van het specifieke uur data_nu = df.loc[timestamp] - # 1. Tijd-features (simpel) features['maand'] = timestamp.month features['dag_van_het_jaar'] = timestamp.dayofyear - - # 2. Feestdag feature features['is_feestdag'] = 1 if timestamp in nl_holidays else 0 - # 3. Weer-features weer_cols = ['temperatuur', 'gevoelstemperatuur', 'neerslag', 'wind_richting', 'wind_snelheid', 'bewolking', 'luchtdruk', 'luchtvochtigheid'] for col in weer_cols: features[col] = data_nu[col] - # 4. Lag-features features['prijs_1u_geleden'] = df.loc[timestamp - timedelta(hours=1)]['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['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): 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): 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) +# --- 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 --- -# (Dit deel is weer 100% identiek aan je vorige script) + laagste_prijs = result_df['Voorspelde_Prijs'].min() + 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: conn = mysql.connector.connect(**DB_CONFIG) werk_df = haal_data_uit_database(conn) @@ -150,25 +195,57 @@ try: if werk_df is not None: te_voorspellen_tijden = werk_df[werk_df['gemiddelde_prijs'].isnull()].index - print(f"\n🧠 Start iteratieve voorspelling voor {len(te_voorspellen_tijden)} uur...") - voorspellingen = [] - - for timestamp in te_voorspellen_tijden: - features_nu = maak_features_voor_uur(werk_df, timestamp) - voorspelde_prijs = model.predict(features_nu)[0] - werk_df.loc[timestamp, 'gemiddelde_prijs'] = voorspelde_prijs - voorspellingen.append(voorspelde_prijs) + if len(te_voorspellen_tijden) == 0: + print("\nDatabase is al up-to-date. Geen voorspelling nodig.") + else: + print(f"\n🧠 Start iteratieve voorspelling voor {len(te_voorspellen_tijden)} uur...") + voorspellingen = [] - print("\n" + "="*70) - pd.set_option('display.max_rows', None) # Zorg dat we alles printen - print(f"--- VOORSPELDE PRIJZEN (komende {len(te_voorspellen_tijden)} uur) ---") - - resultaat_df = pd.DataFrame({ - 'Voorspelde_Prijs': voorspellingen - }, index=te_voorspellen_tijden) - - print(resultaat_df) - print("="*70) + for timestamp in te_voorspellen_tijden: + features_nu = maak_features_voor_uur(werk_df, timestamp) + voorspelde_prijs = model.predict(features_nu)[0] + werk_df.loc[timestamp, 'gemiddelde_prijs'] = voorspelde_prijs + voorspellingen.append(voorspelde_prijs) + + print("\n" + "="*70) + pd.set_option('display.max_rows', None) + print(f"--- VOORSPELDE PRIJZEN (komende {len(te_voorspellen_tijden)} uur) ---") + + 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: print(f"āŒ Fout met MySQL verbinding: {e}")