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 = "
| Datum | Zahlen (5) | Super 1 | Super 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 = "| Datum | Zahlen (6) | Superzahl | Super 6 | Spiel 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}
+
+ """.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()