From bf78690baafa3440d9864600942bdde9079523e1 Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 24 Jul 2025 16:31:58 +0200 Subject: [PATCH 01/15] =?UTF-8?q?Euro2SQL.py=20gel=C3=B6scht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Euro2SQL.py | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 Euro2SQL.py diff --git a/Euro2SQL.py b/Euro2SQL.py deleted file mode 100644 index 195af92..0000000 --- a/Euro2SQL.py +++ /dev/null @@ -1,42 +0,0 @@ -import requests -from bs4 import BeautifulSoup - -# URL der Zielseite (Eurojackpot bei ARD-Text) -url = "https://lotto.gmx.de/eurojackpot/zahlen-quoten" - -# HTTP-Request senden -response = requests.get(url) -response.raise_for_status() # Fehlerprüfung - -# HTML mit BeautifulSoup parsen -soup = BeautifulSoup(response.text, "html.parser") - -# Formatierten HTML-Code ausgeben -formatted_html = soup.prettify() - -# Ausgabe in der Konsole -#print(formatted_html) - -url = "https://lotto.gmx.de/eurojackpot/zahlen-quoten" -response = requests.get(url) -soup = BeautifulSoup(response.text, "html.parser") - -# Finde den ersten div mit einer bestimmten Klasse -html_code = soup.find("div", class_="std") -print(html_code) -# HTML parsen -soup = BeautifulSoup(response.text, "html.parser") - -# Robust: finde -Tag mit "Gewinnzahlen" (normalisiert auf Whitespace!) -b_tag = soup.find("b", string=lambda s: s and "gewinnzahlen" in s.lower()) - -if b_tag: - table = b_tag.find_parent().find_next_sibling("table") - if table: - gewinnzahlen = [int(td.get_text(strip=True)) for td in table.find_all("td")] - print("Gewinnzahlen:", gewinnzahlen) - else: - print("Tabelle nicht gefunden.") -else: - print("Gewinnzahlen nicht gefunden.") - From b58d4e6f9e74909c2c5568527943a2fc654696b0 Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 24 Jul 2025 16:33:32 +0200 Subject: [PATCH 02/15] aktueller Entwicklungsstand --- Euro2SQL.py | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 Euro2SQL.py diff --git a/Euro2SQL.py b/Euro2SQL.py deleted file mode 100644 index 195af92..0000000 --- a/Euro2SQL.py +++ /dev/null @@ -1,42 +0,0 @@ -import requests -from bs4 import BeautifulSoup - -# URL der Zielseite (Eurojackpot bei ARD-Text) -url = "https://lotto.gmx.de/eurojackpot/zahlen-quoten" - -# HTTP-Request senden -response = requests.get(url) -response.raise_for_status() # Fehlerprüfung - -# HTML mit BeautifulSoup parsen -soup = BeautifulSoup(response.text, "html.parser") - -# Formatierten HTML-Code ausgeben -formatted_html = soup.prettify() - -# Ausgabe in der Konsole -#print(formatted_html) - -url = "https://lotto.gmx.de/eurojackpot/zahlen-quoten" -response = requests.get(url) -soup = BeautifulSoup(response.text, "html.parser") - -# Finde den ersten div mit einer bestimmten Klasse -html_code = soup.find("div", class_="std") -print(html_code) -# HTML parsen -soup = BeautifulSoup(response.text, "html.parser") - -# Robust: finde -Tag mit "Gewinnzahlen" (normalisiert auf Whitespace!) -b_tag = soup.find("b", string=lambda s: s and "gewinnzahlen" in s.lower()) - -if b_tag: - table = b_tag.find_parent().find_next_sibling("table") - if table: - gewinnzahlen = [int(td.get_text(strip=True)) for td in table.find_all("td")] - print("Gewinnzahlen:", gewinnzahlen) - else: - print("Tabelle nicht gefunden.") -else: - print("Gewinnzahlen nicht gefunden.") - From 09197b43893a2daf359b26573bb6e3a0479bc8bb Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 24 Jul 2025 16:46:10 +0200 Subject: [PATCH 03/15] aktueller Entwicklungsstand --- lotto2py.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lotto2py.py b/lotto2py.py index e71b610..61bbca4 100644 --- a/lotto2py.py +++ b/lotto2py.py @@ -44,10 +44,12 @@ def notify_telegram(text): requests.post(url, params=params) def Euro(): url = 'https://www.ard-text.de/mobil/583' - response = requests.get(url) + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" + } + response = requests.get(url, headers=headers) response.raise_for_status() soup = BeautifulSoup(response.text, "html.parser") - ziffern = [] ZahlenEuro = { 'Freitag': {'Datum': '', 'Z1': '', 'Z2': '', 'Z3': '', 'Z4': '', 'Z5': '', 'Eurozahl1': '', 'Eurozahl2': ''}, 'Dienstag': {'Datum': '', 'Z1': '', 'Z2': '', 'Z3': '', 'Z4': '', 'Z5': '', 'Eurozahl1': '', 'Eurozahl2': ''}} @@ -74,14 +76,13 @@ def Euro(): ZahlenEuro[Tag]['Datum'] = b tabelle = b.find_parent().find_next_sibling("table") a = 1 - print(b) + #print(b) for n in tabelle.find_all("td"): c = (n.get_text(strip=True)) b = 'Z' + str(a) ZahlenEuro[Tag][b] = int(c) a = a + 1 eurozahlen_tags = soup.find_all("b", string=lambda s: s and "eurozahlen" in s.lower()) - aa = 1 if len(eurozahlen_tags) >= 2: eurozahlen_table_2 = eurozahlen_tags[1].find_next("table") @@ -205,7 +206,7 @@ def SQLdienstag(data): resp = cursor.execute(sql_q) if resp == 0: cursor.execute(sql) - print(resp) + #print(resp) connection.commit() cursor.close() connection.close() @@ -268,7 +269,7 @@ telegram_chat_id = passw['Telegram']['Chat_ID'] telegram_token = passw['Telegram']['TOKEN'] wochentag = datetime.today().weekday() -#wochentag = 2 +wochentag = 1 if wochentag == 2: Zahl = Normalziehung(582) From e30630ad9d504ac2f96b82c5552674ca0fc410dc Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 24 Jul 2025 21:22:52 +0200 Subject: [PATCH 04/15] aktueller Entwicklungsstand --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3ae246f..6f8ce01 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,5 @@ Todos: 2. [x] Ablage in SQL-Datenbank (MariaDB) 3. [ ] Webinterface +_Hinweis: +Ein Teil des Codes wurde durch Chat-GPT (GPT-4o) generiert. Dieser wurde durch den Inhaber des Repositorys modifiziert und angepastt._ \ No newline at end of file From f164cd286b9f95ce7f37f3a523f6d6a70ea8b013 Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 24 Jul 2025 21:23:54 +0200 Subject: [PATCH 05/15] regulatorischer Hinweis --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f8ce01..5b8024e 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,5 @@ Todos: 3. [ ] Webinterface _Hinweis: -Ein Teil des Codes wurde durch Chat-GPT (GPT-4o) generiert. Dieser wurde durch den Inhaber des Repositorys modifiziert und angepastt._ \ No newline at end of file +Ein Teil des Codes wurde durch Chat-GPT (GPT-4o) generiert. Dieser wurde durch den Inhaber des Repositorys modifiziert und angepastt._ +. From c9b4ee7af64877d88a411ff252cf58f88994c85b Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 24 Jul 2025 21:28:59 +0200 Subject: [PATCH 06/15] regulatorischer Hinweis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b8024e..791fe3f 100644 --- a/README.md +++ b/README.md @@ -16,5 +16,5 @@ Todos: 3. [ ] Webinterface _Hinweis: -Ein Teil des Codes wurde durch Chat-GPT (GPT-4o) generiert. Dieser wurde durch den Inhaber des Repositorys modifiziert und angepastt._ +Ein Teil des Codes wurde durch Chat-GPT (GPT-4o) generiert. Dieser wurde durch den Inhaber des Repositorys modifiziert und angepast._ . From 844967f57864ba4c469c6d4f8dbcc247773be6db Mon Sep 17 00:00:00 2001 From: hubobel Date: Sun, 5 Oct 2025 10:53:22 +0200 Subject: [PATCH 07/15] WebApp --- 6aus49APP/app.py | 303 +++++++++++++++++++++++++++++++++ 6aus49APP/requirements.txt | 15 ++ 6aus49APP/templates/index.html | 133 +++++++++++++++ lotto2py.py | 57 ++++++- 4 files changed, 501 insertions(+), 7 deletions(-) create mode 100644 6aus49APP/app.py create mode 100644 6aus49APP/requirements.txt create mode 100644 6aus49APP/templates/index.html diff --git a/6aus49APP/app.py b/6aus49APP/app.py new file mode 100644 index 0000000..c13a0b0 --- /dev/null +++ b/6aus49APP/app.py @@ -0,0 +1,303 @@ +# app.py +import os +from datetime import date +from typing import List, Optional + +from fastapi import FastAPI, Query, Request, HTTPException +from fastapi.responses import HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +from pydantic import BaseModel +from sqlalchemy import create_engine, text +from sqlalchemy.engine import Engine +from sqlalchemy.pool import NullPool +from dotenv import load_dotenv + +# ------------------------------------------------------ +# 0) Konfiguration laden +# ------------------------------------------------------ +load_dotenv() + +DATABASE_URL = os.getenv("DATABASE_URL") +if not DATABASE_URL: + raise RuntimeError("DATABASE_URL nicht gesetzt (.env prüfen)") + +PAGE_SIZE = int(os.getenv("PAGE_SIZE", "10")) # Standardlimit + +# ------------------------------------------------------ +# 1) App & DB initialisieren +# ------------------------------------------------------ +engine: Engine = create_engine(DATABASE_URL, poolclass=NullPool, future=True) + +app = FastAPI(title="Lotto-Auswertung (6aus49 / Eurojackpot)") +app.mount("/static", StaticFiles(directory="static"), name="static") +templates = Jinja2Templates(directory="templates") + +# ------------------------------------------------------ +# 2) Pydantic-Schemas +# ------------------------------------------------------ +class Draw(BaseModel): + datum: date + # gemeinsame Felder; bei Euro bleiben z6/sz/super6/spiel77 leer + z1: int; z2: int; z3: int; z4: int; z5: int + z6: Optional[int] = None + sz: Optional[int] = None + sz1: Optional[int] = None + sz2: Optional[int] = None + super6: Optional[str] = None + spiel77: Optional[str] = None + +class DrawList(BaseModel): + total: int + items: List[Draw] + +# ------------------------------------------------------ +# 3) Datumsparser (Template – Tabellename variabel) +# ------------------------------------------------------ +DATE_EXPR_TMPL = ( + "COALESCE(" + " (CASE WHEN {tbl}.datum REGEXP '^[0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}}$' THEN DATE({tbl}.datum) END)," + " STR_TO_DATE({tbl}.datum, '%d.%m.%Y')," + " STR_TO_DATE({tbl}.datum, '%d.%m.%y')," + " STR_TO_DATE(TRIM(SUBSTRING_INDEX({tbl}.datum, '/', -1)), '%d.%m.%Y')," + " STR_TO_DATE(TRIM(SUBSTRING_INDEX({tbl}.datum, '/', -1)), '%d.%m.%y')" + ")" +) + +# ------------------------------------------------------ +# 4) Helferfunktionen +# ------------------------------------------------------ +def _normalize_game(s: str) -> str: + return "euro" if (s or "").lower() == "euro" else "6aus49" + +def _to_date(s: Optional[str]): + if not s or s.strip() == "": + return None + try: + return date.fromisoformat(s.strip()) + except Exception: + return None + +def _rewrite_where_for_t_simple(where_parts): + if not where_parts: + return "" + return " WHERE " + " AND ".join(where_parts) + +def _build_base_select(game: str) -> dict: + """ + Konfiguriert Spalten & Layout je nach Spiel. + Liefert: + - tbl: Tabellenname (mit Backticks) + - dx: Datumsausdruck + - base_select: Subquery-SELECT (liefert t.*) + - headers: Tabellenkopf (HTML) + - row_tpl: Zeilen-Template (HTML) + - link_prefix: Pfadpräfix für Detail-Link + """ + if game == "euro": + tbl = "`euro`" + dx = DATE_EXPR_TMPL.format(tbl=tbl) + base_select = """ + SELECT + {dx} AS datum, + z1, z2, z3, z4, z5, + sz1, sz2 + FROM {tbl} + """.format(dx=dx, tbl=tbl) + # 👇 Hier neue Spaltennamen + headers = "DatumZahlen (5)Super 1Super 2" + row_tpl = ( + '' + '{datum}' + '' + '{z1} {z2} {z3} ' + '{z4} {z5}' + '' + '{sz1}' + '{sz2}' + '' + ) + link_prefix = "/api/draw/euro" + else: + tbl = "`6aus49`" + dx = DATE_EXPR_TMPL.format(tbl=tbl) + # super6/spiel77 als CHAR casten → Pydantic erwartet str + base_select = """ + SELECT + {dx} AS datum, + z1, z2, z3, z4, z5, z6, + sz, + CAST(super6 AS CHAR) AS super6, + CAST(spiel77 AS CHAR) AS spiel77 + FROM {tbl} + """.format(dx=dx, tbl=tbl) + headers = "DatumZahlen (6)SuperzahlSuper 6Spiel 77" + row_tpl = ( + '' + '{datum}' + '' + '{z1} {z2} {z3} ' + '{z4} {z5} {z6}' + '' + '{sz}' + '{super6}' + '{spiel77}' + '' + ) + link_prefix = "/api/draw/6aus49" + return { + "tbl": tbl, + "dx": dx, + "base_select": base_select, + "headers": headers, + "row_tpl": row_tpl, + "link_prefix": link_prefix, + } + +# ------------------------------------------------------ +# 5) Startseite +# ------------------------------------------------------ +@app.get("/", response_class=HTMLResponse) +async def index(request: Request): + return templates.TemplateResponse("index.html", {"request": request, "page_size": PAGE_SIZE}) + +# ------------------------------------------------------ +# 6) Gemeinsamer HTML-Endpoint (HTMX) +# ------------------------------------------------------ +@app.get("/ui/draws", response_class=HTMLResponse) +async def ui_draws( + request: Request, + game: str = Query("6aus49"), + date_from: Optional[str] = Query(None), + date_to: Optional[str] = Query(None), + limit: int = Query(10, ge=1, le=500), + offset: int = Query(0, ge=0), + order: str = Query("desc", pattern="^(asc|desc)$"), +): + game = _normalize_game(game) + cfg = _build_base_select(game) + + d_from = _to_date(date_from) + d_to = _to_date(date_to) + + where_parts = [] + params = {} + if d_from: + where_parts.append("t.datum >= :date_from") + params["date_from"] = d_from + if d_to: + where_parts.append("t.datum <= :date_to") + params["date_to"] = d_to + + where_sql = _rewrite_where_for_t_simple(where_parts) + if where_sql == "": + where_sql = " WHERE 1=1" + where_sql = where_sql + " AND t.datum IS NOT NULL" + + order_sql = " ORDER BY t.datum DESC" if order.lower() == "desc" else " ORDER BY t.datum ASC" + + count_sql = """ + SELECT COUNT(*) AS cnt + FROM ( + {base} + ) AS t + {where_sql} + """.format(base=cfg["base_select"], where_sql=where_sql) + + data_sql = """ + SELECT * + FROM ( + {base} + ) AS t + {where_sql} + {order_sql} + LIMIT :limit OFFSET :offset + """.format(base=cfg["base_select"], where_sql=where_sql, order_sql=order_sql) + + with engine.begin() as conn: + total = conn.execute(text(count_sql), params).scalar_one() + rows = conn.execute(text(data_sql), dict(params, limit=limit, offset=offset)).mappings() + + body_rows = [] + for r in rows: + d = dict(r) + d["link"] = cfg["link_prefix"] + body_rows.append(cfg["row_tpl"].format(**d)) + + html = """ +

Treffer: {total}

+ + {headers} + {rows} +
+ """.format(total=total, headers=cfg["headers"], rows="".join(body_rows)) + + return HTMLResponse(html) + +# ------------------------------------------------------ +# 7) Detail-Endpoint je Spiel (None-Felder ausblenden) +# ------------------------------------------------------ +@app.get( + "/api/draw/{game}/{draw_date}", + response_model=Draw, + response_model_exclude_none=True +) +async def get_draw_game(game: str, draw_date: date): + game = _normalize_game(game) + if game == "euro": + dx = DATE_EXPR_TMPL.format(tbl="`euro`") + sql = text(""" + SELECT t.datum, + t.z1, t.z2, t.z3, t.z4, t.z5, + NULL AS z6, + NULL AS sz, + t.sz1, t.sz2, + NULL AS super6, + NULL AS spiel77 + FROM ( + SELECT {dx} AS datum, z1, z2, z3, z4, z5, sz1, sz2 + FROM `euro` + ) AS t + WHERE t.datum = :d AND t.datum IS NOT NULL + """.format(dx=dx)) + else: + dx = DATE_EXPR_TMPL.format(tbl="`6aus49`") + sql = text(""" + SELECT t.datum, + t.z1, t.z2, t.z3, t.z4, t.z5, t.z6, + t.sz, + NULL AS sz1, NULL AS sz2, + t.super6, t.spiel77 + FROM ( + SELECT {dx} AS datum, z1, z2, z3, z4, z5, z6, sz, + CAST(super6 AS CHAR) AS super6, + CAST(spiel77 AS CHAR) AS spiel77 + FROM `6aus49` + ) AS t + WHERE t.datum = :d AND t.datum IS NOT NULL + """.format(dx=dx)) + + with engine.begin() as conn: + row = conn.execute(sql, {"d": draw_date}).mappings().first() + if not row: + raise HTTPException(status_code=404, detail="Ziehung nicht gefunden") + return Draw(**row) + +# ------------------------------------------------------ +# 8) Healthcheck +# ------------------------------------------------------ +@app.get("/health") +async def health(): + try: + with engine.begin() as conn: + conn.execute(text("SELECT 1")) + return {"status": "ok"} + except Exception as e: + return {"status": "error", "message": str(e)} + +# ------------------------------------------------------ +# 9) Lokaler Start +# ------------------------------------------------------ +if __name__ == "__main__": + import uvicorn + uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True) diff --git a/6aus49APP/requirements.txt b/6aus49APP/requirements.txt new file mode 100644 index 0000000..e6df49f --- /dev/null +++ b/6aus49APP/requirements.txt @@ -0,0 +1,15 @@ +fastapi==0.115.0 +uvicorn[standard]==0.30.0 +SQLAlchemy==2.0.34 +PyMySQL==1.1.1 +python-dotenv==1.0.1 +jinja2==3.1.4 +pydantic==2.9.2 +fastapi==0.115.0 +uvicorn[standard]==0.30.0 +SQLAlchemy==2.0.34 +psycopg2-binary==2.9.9 +python-dotenv==1.0.1 +jinja2==3.1.4 +pydantic==2.9.2 + diff --git a/6aus49APP/templates/index.html b/6aus49APP/templates/index.html new file mode 100644 index 0000000..9b8649f --- /dev/null +++ b/6aus49APP/templates/index.html @@ -0,0 +1,133 @@ + + + + + Lotto-Ziehungen + + + + + + +
+

Lotto-Ziehungen

+

Ein Service der Hintergasse – Angaben ohne Gewähr!

+ +
+ + + + + + + + + + + + + + + + +
+ +
+
+ + diff --git a/lotto2py.py b/lotto2py.py index 61bbca4..731f0db 100644 --- a/lotto2py.py +++ b/lotto2py.py @@ -71,6 +71,10 @@ def Euro(): bb = 'Eurozahl' + str(aa) ZahlenEuro[Tag][bb] = int(c) aa = aa + 1 + jahr = datetime.now().year + tag = datetime.now().day + monat = datetime.now().month + ZahlenEuro[Tag]['Datum'] = f"{jahr}-{monat}-{tag}" Tag = 'Dienstag' for b in soup.find("p", string=lambda s: s and "dienstag" in s.lower()): ZahlenEuro[Tag]['Datum'] = b @@ -91,10 +95,16 @@ def Euro(): bb = 'Eurozahl' + str(aa) ZahlenEuro[Tag][bb] = int(i) aa = aa + 1 + jahr = datetime.now().year + tag = datetime.now().day + monat = datetime.now().month + ZahlenEuro[Tag]['Datum'] = f"{jahr}-{monat}-{tag}" return ZahlenEuro def Normalziehung(a): wochentag = datetime.today().weekday() jahr = datetime.now().year + tag = f"{datetime.now().day:02d}" + monat = datetime.now().month url = "https://www.ard-text.de/mobil/" + str(a) @@ -125,7 +135,7 @@ def Normalziehung(a): datum_woche = line.strip() break # Regex: Hauptzahlen finden (z. B. Zeile enthält "11 20 28 30 35 41") - match_haupt = re.search(r"\s(\d{1,2}(?:\s+\d{1,2}){5})\s", text) + match_haupt = re.search(r"\s(\d{1,2}(?:\s+\d{1,2}){6})\s", text) if match_haupt: lottozahlen = [int(n) for n in match_haupt.group(1).split()] @@ -163,7 +173,7 @@ def Normalziehung(a): ef = str((ab + str(cd))) Lottozahlen[ef] = i cd = cd + 1 - Lottozahlen['Datum'] = str(jahr) + ' / ' + str(datum_woche) + Lottozahlen['Datum'] = f"{jahr}-{monat}-{tag}" Lottozahlen['Superzahl'] = superzahl Lottozahlen['Spiel77'] = int(game77) Lottozahlen['Super6'] = int(subber6) @@ -184,12 +194,18 @@ def SQLnorm(data): "','" + str(data['Super6']) + "','" + str(data['Spiel77']) + "')" sql_q = "SELECT * FROM 6aus49 WHERE datum like '%" + data['Datum'] + "%'" resp = cursor.execute(sql_q) + + + + if resp == 0: cursor.execute(sql) + notify_telegram(str(data)) connection.commit() cursor.close() connection.close() + def SQLdienstag(data): connection = pymysql.connect(db="hubobel", user="hubobel", @@ -206,6 +222,7 @@ def SQLdienstag(data): resp = cursor.execute(sql_q) if resp == 0: cursor.execute(sql) + notify_telegram(str(data)) #print(resp) connection.commit() cursor.close() @@ -225,6 +242,7 @@ def SQLfreitag(data): resp = cursor.execute(sql_q) if resp == 0: cursor.execute(sql) + notify_telegram(str(data)) connection.commit() cursor.close() @@ -264,30 +282,55 @@ def SQLmittwoch(data): cursor.close() connection.close() +def datum(): + sql = """UPDATE `6aus49` +SET datum = DATE_FORMAT( + COALESCE( + STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%Y'), + STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%y') + ), + '%Y-%m-%d' +) +WHERE + COALESCE( + STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%Y'), + STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%y') + ) IS NOT NULL;""" + connection = pymysql.connect(db="hubobel", + user="hubobel", + passwd="polier2003", + host='10.0.1.123', charset='utf8') + cursor = connection.cursor() + cursor.execute(sql) + connection.commit() + cursor.close() + connection.close() + + passw = conf() telegram_chat_id = passw['Telegram']['Chat_ID'] telegram_token = passw['Telegram']['TOKEN'] wochentag = datetime.today().weekday() -wochentag = 1 +wochentag = 5 if wochentag == 2: Zahl = Normalziehung(582) SQLnorm(Zahl) SQLmittwoch(Zahl) - notify_telegram(str(Zahl)) + #notify_telegram(str(Zahl)) elif wochentag == 5: Zahl = Normalziehung(581) SQLnorm(Zahl) SQLsamstag(Zahl) - notify_telegram(str(Zahl)) + #notify_telegram(str(Zahl)) elif wochentag == 1: Zahl = Euro() SQLdienstag(Zahl['Dienstag']) - notify_telegram(str(Zahl['Dienstag'])) + #notify_telegram(str(Zahl['Dienstag'])) elif wochentag == 4: Zahl = Euro() SQLfreitag(Zahl['Freitag']) - notify_telegram(str(Zahl['Freitag'])) + #notify_telegram(str(Zahl['Freitag'])) else: quit() From 9ebb2fc9abce48607680ab61af165c9df81d9557 Mon Sep 17 00:00:00 2001 From: hubobel Date: Sun, 5 Oct 2025 10:54:36 +0200 Subject: [PATCH 08/15] WebApp --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 791fe3f..4239bad 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ gescrapt. Todos: 1. [x] https://www.ard-text.de/mobil/583 (Eurojackpot) 2. [x] Ablage in SQL-Datenbank (MariaDB) -3. [ ] Webinterface +3. [x] Webinterface https://lotto.hintergasse.de _Hinweis: Ein Teil des Codes wurde durch Chat-GPT (GPT-4o) generiert. Dieser wurde durch den Inhaber des Repositorys modifiziert und angepast._ From 88eabe461344a01ced28cdde023173ae07e43119 Mon Sep 17 00:00:00 2001 From: hubobel Date: Sun, 5 Oct 2025 10:55:39 +0200 Subject: [PATCH 09/15] Fehler behoben: 6.Zahl bei 6aus49 wurde nicht gepaarst --- lotto2py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lotto2py.py b/lotto2py.py index 731f0db..e87357e 100644 --- a/lotto2py.py +++ b/lotto2py.py @@ -312,7 +312,7 @@ telegram_chat_id = passw['Telegram']['Chat_ID'] telegram_token = passw['Telegram']['TOKEN'] wochentag = datetime.today().weekday() -wochentag = 5 +#wochentag = 5 if wochentag == 2: Zahl = Normalziehung(582) From 2c85531360190c475c6eb4eaece353b8e89adebc Mon Sep 17 00:00:00 2001 From: hubobel Date: Thu, 16 Oct 2025 16:06:28 +0200 Subject: [PATCH 10/15] =?UTF-8?q?Fehler=20behoben:=20Es=20werden=20alle=20?= =?UTF-8?q?gezogenen=20Zahlen=20gepr=C3=BCft,=20ob=20Kombi=20bereits=20in?= =?UTF-8?q?=20db?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lotto2py.py | 103 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/lotto2py.py b/lotto2py.py index e87357e..3b6e312 100644 --- a/lotto2py.py +++ b/lotto2py.py @@ -205,7 +205,6 @@ def SQLnorm(data): connection.commit() cursor.close() connection.close() - def SQLdienstag(data): connection = pymysql.connect(db="hubobel", user="hubobel", @@ -220,7 +219,7 @@ def SQLdienstag(data): data['Eurozahl2']) + "')" sql_q = "SELECT * FROM euro WHERE datum like '%" + data['Datum'] + "%'" resp = cursor.execute(sql_q) - if resp == 0: + if test == 0: cursor.execute(sql) notify_telegram(str(data)) #print(resp) @@ -276,61 +275,101 @@ def SQLmittwoch(data): "','" + str(data['Super6']) + "','" + str(data['Spiel77']) + "')" sql_q = "SELECT * FROM samstag WHERE datum like '%" + data['Datum'] + "%'" resp = cursor.execute(sql_q) + if resp == 0: cursor.execute(sql) connection.commit() cursor.close() connection.close() +def SQLtesteuro(data): + print(data) + connection = pymysql.connect( + host='10.0.1.123', + user='hubobel', + password='polier2003', + database='hubobel', + charset='utf8mb4', + cursorclass=pymysql.cursors.Cursor + ) -def datum(): - sql = """UPDATE `6aus49` -SET datum = DATE_FORMAT( - COALESCE( - STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%Y'), - STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%y') - ), - '%Y-%m-%d' -) -WHERE - COALESCE( - STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%Y'), - STR_TO_DATE(TRIM(SUBSTRING_INDEX(datum, '/', -1)), '%d.%m.%y') - ) IS NOT NULL;""" - connection = pymysql.connect(db="hubobel", - user="hubobel", - passwd="polier2003", - host='10.0.1.123', charset='utf8') - cursor = connection.cursor() - cursor.execute(sql) - connection.commit() - cursor.close() - connection.close() + try: + with connection.cursor() as cursor: + # Beispielwerte + z1, z2, z3, z4, z5 = data['Z1'], data['Z2'],data['Z3'],data['Z4'],data['Z5'] + sql_check = """ + SELECT 1 FROM `euro` + WHERE z1 = %s AND z2 = %s AND z3 = %s AND z4 = %s AND z5 = %s + LIMIT 1 + """ + cursor.execute(sql_check, (z1, z2, z3, z4, z5)) + exists = cursor.fetchone() is not None + if exists: + print("✅ Kombination existiert bereits.") + a = 1 + else: + print("🆕 Kombination ist neu – kann eingefügt werden.") + a = 0 + + finally: + connection.close() + return a +def SQLtestsechs(data): + print(data) + connection = pymysql.connect( + host='10.0.1.123', + user='hubobel', + password='polier2003', + database='hubobel', + charset='utf8mb4', + cursorclass=pymysql.cursors.Cursor + ) + + try: + with connection.cursor() as cursor: + # Beispielwerte + z1, z2, z3, z4, z5, z6 = data['Z1'], data['Z2'],data['Z3'],data['Z4'],data['Z5'],data['Z6'] + sql_check = """ + SELECT 1 FROM `6aus49` + WHERE z1 = %s AND z2 = %s AND z3 = %s AND z4 = %s AND z5 = %s AND z6 = %s + LIMIT 1 + """ + cursor.execute(sql_check, (z1, z2, z3, z4, z5, z6)) + exists = cursor.fetchone() is not None + + if exists: + print("✅ Kombination existiert bereits.") + a = 1 + else: + print("🆕 Kombination ist neu – kann eingefügt werden.") + a = 0 + + finally: + connection.close() + return a passw = conf() telegram_chat_id = passw['Telegram']['Chat_ID'] telegram_token = passw['Telegram']['TOKEN'] wochentag = datetime.today().weekday() -#wochentag = 5 +#wochentag = 2 if wochentag == 2: Zahl = Normalziehung(582) + test = SQLtestsechs(Zahl) SQLnorm(Zahl) - SQLmittwoch(Zahl) - #notify_telegram(str(Zahl)) elif wochentag == 5: Zahl = Normalziehung(581) + test = SQLtestsechs(Zahl) SQLnorm(Zahl) - SQLsamstag(Zahl) - #notify_telegram(str(Zahl)) elif wochentag == 1: Zahl = Euro() + test = SQLtesteuro(Zahl['Dienstag']) SQLdienstag(Zahl['Dienstag']) - #notify_telegram(str(Zahl['Dienstag'])) elif wochentag == 4: Zahl = Euro() + test = SQLtesteuro(Zahl['Freitag']) SQLfreitag(Zahl['Freitag']) - #notify_telegram(str(Zahl['Freitag'])) else: quit() From e7d26abf2fc2e43dc922ae36e356a38f15b10b32 Mon Sep 17 00:00:00 2001 From: hubobel Date: Sun, 19 Oct 2025 17:29:26 +0200 Subject: [PATCH 11/15] diverse Anpassungen --- 6aus49APP/app.py | 480 ++++++++++++++++----------------- 6aus49APP/templates/index.html | 178 ++++++------ lotto2py.py | 10 +- 3 files changed, 333 insertions(+), 335 deletions(-) diff --git a/6aus49APP/app.py b/6aus49APP/app.py index c13a0b0..f5cb354 100644 --- a/6aus49APP/app.py +++ b/6aus49APP/app.py @@ -1,6 +1,6 @@ -# app.py +# /opt/lotto/app.py import os -from datetime import date +from datetime import date, datetime from typing import List, Optional from fastapi import FastAPI, Query, Request, HTTPException @@ -9,295 +9,283 @@ from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from pydantic import BaseModel from sqlalchemy import create_engine, text -from sqlalchemy.engine import Engine +from sqlalchemy.engine import Engine, Row from sqlalchemy.pool import NullPool from dotenv import load_dotenv -# ------------------------------------------------------ +# -------------------------------------------------------- # 0) Konfiguration laden -# ------------------------------------------------------ +# -------------------------------------------------------- load_dotenv() DATABASE_URL = os.getenv("DATABASE_URL") if not DATABASE_URL: raise RuntimeError("DATABASE_URL nicht gesetzt (.env prüfen)") -PAGE_SIZE = int(os.getenv("PAGE_SIZE", "10")) # Standardlimit +PAGE_SIZE = int(os.getenv("PAGE_SIZE", "10")) -# ------------------------------------------------------ -# 1) App & DB initialisieren -# ------------------------------------------------------ +# -------------------------------------------------------- +# 1) App und DB initialisieren +# -------------------------------------------------------- engine: Engine = create_engine(DATABASE_URL, poolclass=NullPool, future=True) -app = FastAPI(title="Lotto-Auswertung (6aus49 / Eurojackpot)") -app.mount("/static", StaticFiles(directory="static"), name="static") -templates = Jinja2Templates(directory="templates") +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +STATIC_DIR = os.path.join(BASE_DIR, "static") +TEMPLATE_DIR = os.path.join(BASE_DIR, "templates") -# ------------------------------------------------------ -# 2) Pydantic-Schemas -# ------------------------------------------------------ +app = FastAPI(title="Lotto Ziehungen API") +app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") +templates = Jinja2Templates(directory=TEMPLATE_DIR) + +# -------------------------------------------------------- +# 2) Modelle (Pydantic) +# -------------------------------------------------------- class Draw(BaseModel): - datum: date - # gemeinsame Felder; bei Euro bleiben z6/sz/super6/spiel77 leer - z1: int; z2: int; z3: int; z4: int; z5: int - z6: Optional[int] = None - sz: Optional[int] = None - sz1: Optional[int] = None - sz2: Optional[int] = None - super6: Optional[str] = None - spiel77: Optional[str] = None + datum: Optional[date] + z1: Optional[int] + z2: Optional[int] + z3: Optional[int] + z4: Optional[int] + z5: Optional[int] + z6: Optional[int] + sz: Optional[int] + sz1: Optional[int] + sz2: Optional[int] + super6: Optional[str] + spiel77: Optional[str] class DrawList(BaseModel): total: int items: List[Draw] -# ------------------------------------------------------ -# 3) Datumsparser (Template – Tabellename variabel) -# ------------------------------------------------------ -DATE_EXPR_TMPL = ( - "COALESCE(" - " (CASE WHEN {tbl}.datum REGEXP '^[0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}}$' THEN DATE({tbl}.datum) END)," - " STR_TO_DATE({tbl}.datum, '%d.%m.%Y')," - " STR_TO_DATE({tbl}.datum, '%d.%m.%y')," - " STR_TO_DATE(TRIM(SUBSTRING_INDEX({tbl}.datum, '/', -1)), '%d.%m.%Y')," - " STR_TO_DATE(TRIM(SUBSTRING_INDEX({tbl}.datum, '/', -1)), '%d.%m.%y')" - ")" -) +# -------------------------------------------------------- +# 3) Hilfsfunktionen +# -------------------------------------------------------- +def normalize_date_sql(column: str) -> str: + return ( + f"COALESCE(" + f" (CASE WHEN {column} REGEXP '^[0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}}$' THEN DATE({column}) END)," + f" STR_TO_DATE({column}, '%d.%m.%Y')," + f" STR_TO_DATE(SUBSTRING_INDEX({column}, '/', -1), '%d.%m.%y')" + f")" + ) -# ------------------------------------------------------ -# 4) Helferfunktionen -# ------------------------------------------------------ -def _normalize_game(s: str) -> str: - return "euro" if (s or "").lower() == "euro" else "6aus49" +def row_to_draw(row: Row) -> Draw: + return Draw(**row) -def _to_date(s: Optional[str]): - if not s or s.strip() == "": +def _to_date(s: Optional[str]) -> Optional[date]: + if not s: + return None + t = s.strip() + if not t: return None try: - return date.fromisoformat(s.strip()) - except Exception: - return None + return date.fromisoformat(t) + except ValueError: + pass + for fmt in ("%d.%m.%Y", "%d.%m.%y"): + try: + return datetime.strptime(t, fmt).date() + except ValueError: + continue + return None -def _rewrite_where_for_t_simple(where_parts): - if not where_parts: - return "" - return " WHERE " + " AND ".join(where_parts) - -def _build_base_select(game: str) -> dict: - """ - Konfiguriert Spalten & Layout je nach Spiel. - Liefert: - - tbl: Tabellenname (mit Backticks) - - dx: Datumsausdruck - - base_select: Subquery-SELECT (liefert t.*) - - headers: Tabellenkopf (HTML) - - row_tpl: Zeilen-Template (HTML) - - link_prefix: Pfadpräfix für Detail-Link - """ - if game == "euro": - tbl = "`euro`" - dx = DATE_EXPR_TMPL.format(tbl=tbl) - base_select = """ - SELECT - {dx} AS datum, - z1, z2, z3, z4, z5, - sz1, sz2 - FROM {tbl} - """.format(dx=dx, tbl=tbl) - # 👇 Hier neue Spaltennamen - headers = "DatumZahlen (5)Super 1Super 2" - row_tpl = ( - '' - '{datum}' - '' - '{z1} {z2} {z3} ' - '{z4} {z5}' - '' - '{sz1}' - '{sz2}' - '' - ) - link_prefix = "/api/draw/euro" - else: - tbl = "`6aus49`" - dx = DATE_EXPR_TMPL.format(tbl=tbl) - # super6/spiel77 als CHAR casten → Pydantic erwartet str - base_select = """ - SELECT - {dx} AS datum, - z1, z2, z3, z4, z5, z6, - sz, - CAST(super6 AS CHAR) AS super6, - CAST(spiel77 AS CHAR) AS spiel77 - FROM {tbl} - """.format(dx=dx, tbl=tbl) - headers = "DatumZahlen (6)SuperzahlSuper 6Spiel 77" - row_tpl = ( - '' - '{datum}' - '' - '{z1} {z2} {z3} ' - '{z4} {z5} {z6}' - '' - '{sz}' - '{super6}' - '{spiel77}' - '' - ) - link_prefix = "/api/draw/6aus49" - return { - "tbl": tbl, - "dx": dx, - "base_select": base_select, - "headers": headers, - "row_tpl": row_tpl, - "link_prefix": link_prefix, - } - -# ------------------------------------------------------ -# 5) Startseite -# ------------------------------------------------------ +# -------------------------------------------------------- +# 4) Routen +# -------------------------------------------------------- @app.get("/", response_class=HTMLResponse) async def index(request: Request): - return templates.TemplateResponse("index.html", {"request": request, "page_size": PAGE_SIZE}) + return templates.TemplateResponse( + "index.html", {"request": request, "page_size": PAGE_SIZE} + ) -# ------------------------------------------------------ -# 6) Gemeinsamer HTML-Endpoint (HTMX) -# ------------------------------------------------------ -@app.get("/ui/draws", response_class=HTMLResponse) -async def ui_draws( - request: Request, - game: str = Query("6aus49"), - date_from: Optional[str] = Query(None), - date_to: Optional[str] = Query(None), - limit: int = Query(10, ge=1, le=500), - offset: int = Query(0, ge=0), - order: str = Query("desc", pattern="^(asc|desc)$"), +# -------------------- API: Ziehungen (JSON) --------------------- +@app.get("/api/draws", response_model=DrawList) +async def list_draws( + game: str = Query("6aus49", pattern="^(6aus49|euro)$"), + date_from: Optional[date] = Query(None), + date_to: Optional[date] = Query(None), + limit: int = Query(PAGE_SIZE, ge=1, le=500), + offset: int = Query(0, ge=0), + order: str = Query("desc", pattern="^(asc|desc)$"), ): - game = _normalize_game(game) - cfg = _build_base_select(game) + tbl = "`6aus49`" if game == "6aus49" else "`euro`" + dx = normalize_date_sql(f"{tbl}.datum") - d_from = _to_date(date_from) - d_to = _to_date(date_to) + if game == "6aus49": + base_select = f""" + SELECT {dx} AS datum, + z1, z2, z3, z4, z5, z6, sz, + CAST(super6 AS CHAR) AS super6, + CAST(spiel77 AS CHAR) AS spiel77, + NULL AS sz1, NULL AS sz2 + FROM {tbl} + """ + else: + base_select = f""" + SELECT {dx} AS datum, + z1, z2, z3, z4, z5, + NULL AS z6, NULL AS sz, + NULL AS super6, NULL AS spiel77, + sz1, sz2 + FROM {tbl} + """ where_parts = [] params = {} - if d_from: - where_parts.append("t.datum >= :date_from") - params["date_from"] = d_from - if d_to: - where_parts.append("t.datum <= :date_to") - params["date_to"] = d_to + if date_from: + where_parts.append("datum >= :date_from") + params["date_from"] = date_from + if date_to: + where_parts.append("datum <= :date_to") + params["date_to"] = date_to + where_sql = "WHERE " + " AND ".join(where_parts) if where_parts else "" + order_sql = "ORDER BY datum DESC" if order == "desc" else "ORDER BY datum ASC" - where_sql = _rewrite_where_for_t_simple(where_parts) - if where_sql == "": - where_sql = " WHERE 1=1" - where_sql = where_sql + " AND t.datum IS NOT NULL" - - order_sql = " ORDER BY t.datum DESC" if order.lower() == "desc" else " ORDER BY t.datum ASC" - - count_sql = """ - SELECT COUNT(*) AS cnt - FROM ( - {base} - ) AS t - {where_sql} - """.format(base=cfg["base_select"], where_sql=where_sql) - - data_sql = """ - SELECT * - FROM ( - {base} - ) AS t - {where_sql} - {order_sql} - LIMIT :limit OFFSET :offset - """.format(base=cfg["base_select"], where_sql=where_sql, order_sql=order_sql) + count_sql = f"SELECT COUNT(*) AS cnt FROM ({base_select}) AS t {where_sql}" + data_sql = f"SELECT * FROM ({base_select}) AS t {where_sql} {order_sql} LIMIT :limit OFFSET :offset" with engine.begin() as conn: total = conn.execute(text(count_sql), params).scalar_one() - rows = conn.execute(text(data_sql), dict(params, limit=limit, offset=offset)).mappings() + rows = conn.execute(text(data_sql), {**params, "limit": limit, "offset": offset}).mappings().all() + items = [row_to_draw(r) for r in rows] - body_rows = [] - for r in rows: - d = dict(r) - d["link"] = cfg["link_prefix"] - body_rows.append(cfg["row_tpl"].format(**d)) + return DrawList(total=total, items=items) - html = """ -

Treffer: {total}

- - {headers} - {rows} -
- """.format(total=total, headers=cfg["headers"], rows="".join(body_rows)) +# -------------------- API: Detail (JSON) ------------------------ +@app.get("/api/draw/{game}/{draw_date}", response_model=Draw) +async def get_draw(game: str, draw_date: date): + tbl = "`6aus49`" if game == "6aus49" else "`euro`" + dx = normalize_date_sql(f"{tbl}.datum") - return HTMLResponse(html) - -# ------------------------------------------------------ -# 7) Detail-Endpoint je Spiel (None-Felder ausblenden) -# ------------------------------------------------------ -@app.get( - "/api/draw/{game}/{draw_date}", - response_model=Draw, - response_model_exclude_none=True -) -async def get_draw_game(game: str, draw_date: date): - game = _normalize_game(game) - if game == "euro": - dx = DATE_EXPR_TMPL.format(tbl="`euro`") - sql = text(""" - SELECT t.datum, - t.z1, t.z2, t.z3, t.z4, t.z5, - NULL AS z6, - NULL AS sz, - t.sz1, t.sz2, - NULL AS super6, - NULL AS spiel77 - FROM ( - SELECT {dx} AS datum, z1, z2, z3, z4, z5, sz1, sz2 - FROM `euro` - ) AS t - WHERE t.datum = :d AND t.datum IS NOT NULL - """.format(dx=dx)) + if game == "6aus49": + sql = text(f""" + SELECT {dx} AS datum, + z1, z2, z3, z4, z5, z6, sz, + CAST(super6 AS CHAR) AS super6, + CAST(spiel77 AS CHAR) AS spiel77, + NULL AS sz1, NULL AS sz2 + FROM {tbl} + WHERE {dx} = :d + """) else: - dx = DATE_EXPR_TMPL.format(tbl="`6aus49`") - sql = text(""" - SELECT t.datum, - t.z1, t.z2, t.z3, t.z4, t.z5, t.z6, - t.sz, - NULL AS sz1, NULL AS sz2, - t.super6, t.spiel77 - FROM ( - SELECT {dx} AS datum, z1, z2, z3, z4, z5, z6, sz, - CAST(super6 AS CHAR) AS super6, - CAST(spiel77 AS CHAR) AS spiel77 - FROM `6aus49` - ) AS t - WHERE t.datum = :d AND t.datum IS NOT NULL - """.format(dx=dx)) + sql = text(f""" + SELECT {dx} AS datum, + z1, z2, z3, z4, z5, + NULL AS z6, NULL AS sz, + NULL AS super6, NULL AS spiel77, + sz1, sz2 + FROM {tbl} + WHERE {dx} = :d + """) with engine.begin() as conn: row = conn.execute(sql, {"d": draw_date}).mappings().first() if not row: raise HTTPException(status_code=404, detail="Ziehung nicht gefunden") - return Draw(**row) + return row_to_draw(row) -# ------------------------------------------------------ -# 8) Healthcheck -# ------------------------------------------------------ +# -------------------- UI / HTMX (HTML) ------------------------- +@app.get("/ui/draws", response_class=HTMLResponse) +async def ui_draws( + request: Request, + game: str = Query("6aus49", pattern="^(6aus49|euro)$"), + date_from: Optional[str] = Query(None), + date_to: Optional[str] = Query(None), + limit: int = Query(PAGE_SIZE, ge=1, le=500), + offset: int = Query(0, ge=0), + order: str = Query("desc", pattern="^(asc|desc)$"), +): + d_from = _to_date(date_from) + d_to = _to_date(date_to) + + result = await list_draws(game, d_from, d_to, limit, offset, order) # type: ignore + + rows_html = [] + if game == "6aus49": + header = ( + "DatumZahlenSuperzahlSuper 6Spiel 77" + ) + for r in result.items: + numbers = " ".join( + f"{getattr(r, f'z{i}')}" + for i in range(1, 7) + if getattr(r, f"z{i}") is not None + ) + sz_html = "" if r.sz is None else r.sz + rows_html.append( + f"{r.datum or ''}{numbers}" + f"{sz_html}{r.super6 or ''}{r.spiel77 or ''}" + ) + else: + header = "DatumZahlenSuper 1Super 2" + for r in result.items: + numbers = " ".join( + f"{getattr(r, f'z{i}')}" + for i in range(1, 6) + if getattr(r, f"z{i}") is not None + ) + sz1_html = "" if r.sz1 is None else r.sz1 + sz2_html = "" if r.sz2 is None else r.sz2 + rows_html.append( + f"{r.datum or ''}{numbers}" + f"{sz1_html}{sz2_html}" + ) + + html = ( + f"

Treffer: {result.total}

" + f"{header}{''.join(rows_html)}
" + ) + return HTMLResponse(html) + +# -------------------- UI: Header-Kugeln (inkl. Datum) ------------------------- +@app.get("/ui/header", response_class=HTMLResponse) +async def ui_header( + game: str = Query("6aus49", pattern="^(6aus49|euro)$"), + date_from: Optional[str] = Query(None), + date_to: Optional[str] = Query(None), + order: str = Query("desc", pattern="^(asc|desc)$"), +): + d_from = _to_date(date_from) + d_to = _to_date(date_to) + + result = await list_draws(game, d_from, d_to, limit=1, offset=0, order=order) # type: ignore + if not result.items: + return HTMLResponse("") + + r = result.items[0] + date_label = r.datum.strftime("%d.%m.%Y") if r.datum else "" + + if game == "6aus49": + nums = " ".join( + f"
{getattr(r, f'z{i}')}
" + for i in range(1, 7) + if getattr(r, f"z{i}") is not None + ) + sz_html = "" if r.sz is None else f"
{r.sz}
" + html = f""" +
+ {nums}{sz_html} + Stand: {date_label} +
+ """ + else: + nums = " ".join( + f"
{getattr(r, f'z{i}')}
" + for i in range(1, 6) + if getattr(r, f"z{i}") is not None + ) + s1 = "" if r.sz1 is None else f"
{r.sz1}
" + s2 = "" if r.sz2 is None else f"
{r.sz2}
" + html = f""" +
+ {nums}{s1}{s2} + Stand: {date_label} +
+ """ + return HTMLResponse(html) + +# -------------------- Healthcheck -------------------------- @app.get("/health") -async def health(): - try: - with engine.begin() as conn: - conn.execute(text("SELECT 1")) - return {"status": "ok"} - except Exception as e: - return {"status": "error", "message": str(e)} - -# ------------------------------------------------------ -# 9) Lokaler Start -# ------------------------------------------------------ -if __name__ == "__main__": - import uvicorn - uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True) +def health(): + return {"status": "ok"} \ No newline at end of file diff --git a/6aus49APP/templates/index.html b/6aus49APP/templates/index.html index 9b8649f..c8c1bf0 100644 --- a/6aus49APP/templates/index.html +++ b/6aus49APP/templates/index.html @@ -5,104 +5,108 @@ Lotto-Ziehungen - +
-

Lotto-Ziehungen

+ +
+
+ Hintergasse Logo +

Lotto-Ziehungen

+
+
+
+

Ein Service der Hintergasse – Angaben ohne Gewähr!

-
+ + + + + + +
+ + +
+
+ + +
+ +
+ + diff --git a/datum.py b/datum.py new file mode 100644 index 0000000..6154808 --- /dev/null +++ b/datum.py @@ -0,0 +1,33 @@ +import pymysql + +# --- Verbindung herstellen --- +connection = pymysql.connect( + host='10.0.1.123', + user='hubobel', + password='polier2003', + database='hubobel', + charset='utf8mb4', + cursorclass=pymysql.cursors.Cursor +) + +try: + with connection.cursor() as cursor: + # Beispielwerte + z1, z2, z3, z4, z5 = 10, 22, 38, 42, 48 + + # --- Prüfabfrage: existiert diese Kombination? --- + sql_check = """ + SELECT 1 FROM `euro` + WHERE z1 = %s AND z2 = %s AND z3 = %s AND z4 = %s AND z5 = %s + LIMIT 1 + """ + cursor.execute(sql_check, (z1, z2, z3, z4, z5)) + exists = cursor.fetchone() is not None + + if exists: + print("✅ Kombination existiert bereits.") + else: + print("🆕 Kombination ist neu – kann eingefügt werden.") + +finally: + connection.close() \ No newline at end of file diff --git a/lotto2py.conf b/lotto2py.conf new file mode 100644 index 0000000..6ddbcae --- /dev/null +++ b/lotto2py.conf @@ -0,0 +1,12 @@ +{ + "Telegram": { + "Chat_ID": "322673713", + "TOKEN": "680737840:AAEaa7Vxl_kZz_LWS1_S-lH6Eda7HXqu6Y4" + }, + "mail": { + "mail_folder": "", + "mail_host": "", + "mail_pass": "", + "mail_user": "" + } +} \ No newline at end of file From b3b17f458da9b5a89ef6cb1d13b71c6800048af9 Mon Sep 17 00:00:00 2001 From: hubobel Date: Sun, 19 Oct 2025 17:52:09 +0200 Subject: [PATCH 15/15] Aktualisierung --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ae2dee..c99945d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Der Neustart eines längst aufgegeben Projektes: Es werden die aktuell gezogenen Lottozahlen per Cronjob (00 21 * * 3,6 python3 /home/scripts/lotto2py.py) -von den beiden Seiten +von den drei Seiten * https://www.ard-text.de/mobil/581 (Samstagsziehung) * https://www.ard-text.de/mobil/582 (Mittwochsziehung)