CoolFace
Apppublic

kingame21/BioTech.ai

sourceHugging Facemitupdated 7mo agoView on Hugging Face
0likes
home.py275 linesDownload Raw Back to views
1import flet as ft2import threading3from groq import Groq4 5from state import AppState6from config import (7    CYAN, LIME, TEXT_MAIN, TEXT_GREY, BG_CARD, BG_RESALT, BORDER, GROQ_API_KEY8)9from views.allenamenti import show_scheda, show_feedback10 11 12def show_home(13    page: ft.Page,14    state: AppState,15    content_area: ft.Container,16    *,17    start_checkin_cb=None,      # callback: start_checkin(...)18    show_allenamenti_cb=None,   # callback: show_allenamenti(...)19):20    ultima = state.storico[-1] if state.storico else None21    n_sett = len(state.storico)22 23    # ---- PRIMA VOLTA ----24    if not ultima:25        content_area.content = ft.Column(26            [27                ft.Container(height=40),28                ft.Icon(ft.Icons.BOLT_ROUNDED, color=CYAN, size=60),29                ft.Text("BIOMECH AI", size=32, weight="bold", color=TEXT_MAIN),30                ft.Text("IL TUO COACH BIOMECCANICO", size=12, color=TEXT_GREY),31                ft.Container(height=40),32                ft.FilledButton(33                    "INIZIA IL TUO PERCORSO",34                    on_click=(lambda _: start_checkin_cb()) if callable(start_checkin_cb) else None,35                    style=ft.ButtonStyle(36                        bgcolor=CYAN,37                        color="white",38                        shape=ft.RoundedRectangleBorder(radius=15),39                    ),40                    width=280,41                    height=55,42                ),43            ],44            horizontal_alignment=ft.CrossAxisAlignment.CENTER,45        )46        page.update()47        return48 49    # ---- DATI SETTIMANA ----50    completati = ultima.get("completati", [])51    giorni_totali = int(ultima["profilo"].get("giorni", 3))52    fatti = sum(completati) if completati else 053    prossimo_idx = next((i for i, c in enumerate(completati) if not c), None)54 55    scheda_text = ultima.get("scheda", "")56    parti = scheda_text.split("### GIORNO ")57    nomi_sessioni = []58    testo_giorni = []59 60    for p in parti[1:]:61        testo_giorni.append("### GIORNO " + p)62        righe = p.strip().split("\n")63        nome = ""64        for r in righe[1:3]:65            r = r.strip().replace("#", "").replace("*", "").strip()66            if r and not r.startswith("["):67                nome = r68                break69        nomi_sessioni.append(nome)70 71    # ---- CARD RECUPERO (come nel tuo originale) ----72    recupero_ref = [5]73    risposta_ai = ft.Text("", size=13, color=TEXT_GREY, italic=True, text_align=ft.TextAlign.CENTER)74    ring_recupero = ft.ProgressRing(visible=False, color=CYAN, width=20, height=20)75 76    def get_label(val: int):77        if val <= 2: return "Distrutto"78        if val <= 4: return "Stanco"79        if val <= 6: return "Nella norma"80        if val <= 8: return "Bene"81        return "Al top!"82 83    slider_label = ft.Text("5 - Nella norma", size=16, weight="bold", color=CYAN)84 85    def on_slider_change(e):86        val = int(e.control.value)87        recupero_ref[0] = val88        slider_label.value = f"{val} - {get_label(val)}"89        risposta_ai.value = ""90        page.update()91 92    def chiedi_ai_recupero(e=None):93        val = recupero_ref[0]94        ring_recupero.visible = True95        risposta_ai.value = ""96        page.update()97 98        def _call():99            try:100                client = Groq(api_key=GROQ_API_KEY)101                obiettivo = ultima["profilo"].get("obiettivo", "")102                prossimo_nome = (103                    nomi_sessioni[prossimo_idx]104                    if prossimo_idx is not None and prossimo_idx < len(nomi_sessioni)105                    else "allenamento"106                )107                msg = (108                    f"L'atleta si sente {val}/10 ({get_label(val)}). "109                    f"Obiettivo: {obiettivo}. Prossimo workout: {prossimo_nome}. "110                    "Consiglio breve (2-3 frasi): allenarsi o riposare?"111                )112                comp = client.chat.completions.create(113                    model="llama-3.3-70b-versatile",114                    messages=[{"role": "user", "content": msg}],115                    temperature=0.4,116                    max_tokens=150,117                )118                risposta_ai.value = comp.choices[0].message.content119            except Exception as ex:120                risposta_ai.value = f"Errore: {ex}"121            ring_recupero.visible = False122            page.update()123 124        threading.Thread(target=_call, daemon=True).start()125 126    # ---- BOTTONE “PROSSIMA SESSIONE” ----127    def apri_prossima():128        if prossimo_idx is None:129            return130        testo = (131            testo_giorni[prossimo_idx]132            if prossimo_idx < len(testo_giorni)133            else scheda_text134        )135        show_scheda(136            page,137            state,138            content_area,139            testo,140            prossimo_idx + 1,141            da_home=True,142            idx_giorno=prossimo_idx,143            torna_home=lambda: show_home(144                page, state, content_area,145                start_checkin_cb=start_checkin_cb,146                show_allenamenti_cb=show_allenamenti_cb147            ),148            torna_allenamenti=(lambda: show_allenamenti_cb()) if callable(show_allenamenti_cb) else None,149            apri_feedback=lambda idx, torna_a_home: show_feedback(150                page, state, content_area, idx,151                torna_a_home=torna_a_home,152                torna_home=lambda: show_home(153                    page, state, content_area,154                    start_checkin_cb=start_checkin_cb,155                    show_allenamenti_cb=show_allenamenti_cb156                ),157                torna_allenamenti=(lambda: show_allenamenti_cb()) if callable(show_allenamenti_cb) else None,158            ),159        )160 161    if prossimo_idx is not None:162        nome = nomi_sessioni[prossimo_idx] if prossimo_idx < len(nomi_sessioni) else ""163        btn_prossimo = ft.Container(164            content=ft.Column(165                [166                    ft.Text("PROSSIMA SESSIONE", size=10, weight="bold", color="white"),167                    ft.Text(f"Giorno {prossimo_idx + 1}", size=22, weight="bold", color="white"),168                    ft.Text(nome.upper() if nome else "WORKOUT", size=14, color="white"),169                ],170                spacing=2,171            ),172            padding=25,173            bgcolor=CYAN,174            border_radius=25,175            on_click=lambda _: apri_prossima(),176            ink=True,177        )178    else:179        btn_prossimo = ft.Container(180            content=ft.Row(181                [182                    ft.Icon(ft.Icons.CHECK_CIRCLE_OUTLINE, color="white"),183                    ft.Text("SETTIMANA COMPLETATA!", size=16, weight="bold", color="white"),184                ],185                alignment=ft.MainAxisAlignment.CENTER,186            ),187            padding=25,188            bgcolor=LIME,189            border_radius=25,190            on_click=(lambda _: start_checkin_cb()) if callable(start_checkin_cb) else None,191            ink=True,192        )193 194    # ---- STATS ----195    perc = fatti / giorni_totali if giorni_totali > 0 else 0196    stelle = ("* " * fatti + "o " * (giorni_totali - fatti)).strip()197 198    card_stats = ft.Container(199        content=ft.Column(200            [201                ft.Row(202                    [203                        ft.Column(204                            [205                                ft.Text("SETTIMANA", size=10, color=TEXT_GREY),206                                ft.Text(str(n_sett), size=20, weight="bold", color=TEXT_MAIN),207                            ],208                            expand=1,209                        ),210                        ft.Column(211                            [212                                ft.Text("PROGRESSO", size=10, color=TEXT_GREY),213                                ft.Text(f"{fatti}/{giorni_totali}", size=20, weight="bold", color=TEXT_MAIN),214                            ],215                            expand=1,216                            horizontal_alignment=ft.CrossAxisAlignment.CENTER,217                        ),218                        ft.Column(219                            [220                                ft.Text("PESO", size=10, color=TEXT_GREY),221                                ft.Text(f"{ultima['profilo'].get('peso')}kg", size=20, weight="bold", color=TEXT_MAIN),222                            ],223                            expand=1,224                            horizontal_alignment=ft.CrossAxisAlignment.END,225                        ),226                    ]227                ),228                ft.ProgressBar(value=perc, color=CYAN, bgcolor=BG_RESALT, height=8, border_radius=10),229                ft.Text(stelle, size=18, color=CYAN, text_align=ft.TextAlign.CENTER),230            ],231            spacing=15,232        ),233        padding=20,234        bgcolor=BG_CARD,235        border_radius=20,236        border=ft.Border.all(1, BORDER),237    )238 239    card_recupero = ft.Container(240        content=ft.Column(241            [242                ft.Text("STATUS RECUPERO", size=10, weight="bold", color=TEXT_GREY),243                ft.Row([slider_label, ft.Container(expand=True), ring_recupero]),244                ft.Slider(min=1, max=10, divisions=9, value=5, on_change=on_slider_change, active_color=CYAN),245                ft.FilledButton(246                    "Chiedi consiglio all'AI",247                    on_click=chiedi_ai_recupero,248                    style=ft.ButtonStyle(249                        bgcolor=BG_RESALT,250                        color=CYAN,251                        shape=ft.RoundedRectangleBorder(radius=10),252                    ),253                    height=40,254                ),255                risposta_ai,256            ],257            spacing=10,258        ),259        padding=20,260        bgcolor=BG_CARD,261        border_radius=20,262        border=ft.Border.all(1, BORDER),263    )264 265    content_area.content = ft.Column(266        [267            ft.Text("DASHBOARD", size=24, weight="bold", color=TEXT_MAIN),268            btn_prossimo,269            card_stats,270            card_recupero,271        ],272        spacing=20,273        scroll=ft.ScrollMode.ADAPTIVE,274    )275    page.update()