# Fichier: python_cheats/cheatsheets/streamlit.txt
# Cheatsheet Streamlit - Guide TRÈS DÉTAILLÉ pour Grands Débutants


[OK] STREAMLIT: C'EST QUOI EXACTEMENT ?

# COMPRENDRE LE CONCEPT DE BASE:
# ================================

# Imaginez que vous avez un script Python simple:
print("Bienvenue")
print("1 + 1 =", 1 + 1)

# Normalement, ce script s'exécute dans le terminal une seule fois, puis s'arrête.

# Avec Streamlit, c'est DIFFÉRENT:
# -> Streamlit transforme votre script Python en APPLICATION WEB INTERACTIVE
# -> L'utilisateur accède à votre app dans un NAVIGATEUR (Chrome, Firefox, etc)
# -> L'app a des BOUTONS, INPUTS, GRAPHIQUES... que l'utilisateur peut manipuler
# -> Tout cela SANS connaître HTML, CSS ou JavaScript!

# EXEMPLE CONCRET:
# Au lieu d'écrire:
# html = "<button>Cliquez</button>"   <- Vous ne faites PAS ça!

# Vous écrivez simplement:
# if st.button("Cliquez"):            <- Beaucoup plus simple!
#     st.write("Bouton cliqué!")

# Streamlit crée automatiquement le HTML pour vous!


# COMPARAISON AVEC D'AUTRES APPROCHES:
# ====================================

# Sans Streamlit (approche traditionnelle):
# 1. Écrire HTML (structure): <button>, <input>, etc
# 2. Écrire CSS (style): couleurs, tailles, etc
# 3. Écrire JavaScript (interactivité): quand on clique, faire quoi
# 4. Écrire Python (backend): traiter les données
# -> Très compliqué pour un débutant!

# Avec Streamlit (approche simple):
# 1. Écrire du Python (tout)
# -> Streamlit gère le reste automatiquement!


# À QUOI ÇA SERT ?
# ================

# Streamlit est PARFAIT pour:
# [OK] Créer rapidement des dashboards (tableaux de bord)
# [OK] Montrer vos analyses de données
# [OK] Créer des outils de visualisation
# [OK] Présenter des modèles de Machine Learning
# [OK] Faire des prototypes rapidement
# [OK] Créer des apps de data science

# Streamlit n'est PAS adapté pour:
# [X] Sites web complexes avec beaucoup de pages
# [X] Apps nécessitant beaucoup de contrôle sur le design
# [X] Vraies applications professionnelles web


[OK] COMMENT FONCTIONNE STREAMLIT ? (TRÈS IMPORTANT!)

# === LE CONCEPT "RÉEXÉCUTION COMPLÈTE" ===

# VOICI LE POINT LE PLUS IMPORTANT À COMPRENDRE:

# STREAMLIT RÉEXÉCUTE VOTRE SCRIPT ENTIER À CHAQUE INTERACTION!

# Exemple:
import streamlit as st

# La première fois que quelqu'un ouvre votre app:
st.write("Bonjour!")                    # <- Exécuté
x = 5                                   # <- Exécuté
st.write(f"x = {x}")                    # <- Exécuté

# Puis, si l'utilisateur clique sur un bouton:
if st.button("Augmenter x"):
    st.write("Bouton cliqué!")

# <- LE SCRIPT ENTIER S'EXÉCUTE ENCORE UNE FOIS DE HAUT EN BAS!
# <- "Bonjour!" s'affiche ENCORE
# <- x est recalculé à 5 ENCORE
# <- x = 5 s'affiche ENCORE

# C'est pourquoi:
counter = 0
if st.button("Incrémenter"):
    counter += 1
    st.write(counter)  # Affiche TOUJOURS 1, jamais 2, 3, 4...

# Pourquoi? Parce que counter recommence à 0 à chaque exécution!


# === LA SOLUTION: STATE (ÉTAT) ===

# Pour conserver des valeurs entre les exécutions:
# -> Utiliser st.session_state

# Comprendre session_state:
# session_state = un dictionnaire qui persiste entre exécutions
# C'est comme une "mémoire" que Streamlit garde

# Exemple CORRECT:

# Vérifier si c'est la première exécution
if "counter" not in st.session_state:
    st.session_state.counter = 0  # Initialiser à 0 une fois

# Maintenant, on peut augmenter le compteur
if st.button("Incrémenter"):
    st.session_state.counter += 1  # Augmenter la valeur CONSERVÉE

st.write(f"Compteur: {st.session_state.counter}")

# Résultat: Chaque clic augmente le compteur (1, 2, 3, 4...)
# Parce que session_state.counter persiste entre les exécutions!


# === ORDRE D'EXÉCUTION ===

# Streamlit exécute TOUJOURS le script du HAUT VERS LE BAS:

import streamlit as st

st.write("1. Ceci s'affiche en premier")           # Ligne 1
st.write("2. Ceci s'affiche en deuxième")          # Ligne 2

if st.button("Cliquez"):                           # Ligne 3
    st.write("3. Ceci s'affiche quand on clique")  # Ligne 4 (si bouton cliqué)

st.write("4. Ceci s'affiche en dernier")           # Ligne 5

# Ordre d'affichage:
# 1. -> "1. Ceci s'affiche en premier"
# 2. -> "2. Ceci s'affiche en deuxième"
# 3. -> Bouton (pas du texte, c'est une zone interactive)
# 4. -> "4. Ceci s'affiche en dernier"

# Si on clique le bouton, TOUT se réexécute, donc:
# 1. -> "1. Ceci s'affiche en premier"
# 2. -> "2. Ceci s'affiche en deuxième"
# 3. -> Bouton
# 4. -> "3. Ceci s'affiche quand on clique" (NOUVEAU, parce qu'on a cliqué)
# 5. -> "4. Ceci s'affiche en dernier"


[OK] INSTALLATION & PREMIÈRE APP (ÉTAPE PAR ÉTAPE)

# === ÉTAPE 1: INSTALLER PYTHON ===
# Vous devez avoir Python installé sur votre ordinateur
# Vérifier:
python --version
# Vous devez voir: Python 3.9+ (ou plus récent)


# === ÉTAPE 2: CRÉER UN DOSSIER POUR VOTRE APP ===
# Sur votre ordinateur, créer un dossier (n'importe où):
# C:\Users\VotreNom\Mes Documents\mon_app_streamlit

# Puis ouvrir un terminal ET ALLER DANS CE DOSSIER:
cd C:\Users\VotreNom\Mes Documents\mon_app_streamlit


# === ÉTAPE 3: CRÉER UN ENVIRONNEMENT VIRTUEL ===
# (Voir le cheatsheet virtualenv.txt pour plus de détails)
# C'est important pour isoler les packages!

python -m venv .venv

# === ÉTAPE 4: ACTIVER L'ENVIRONNEMENT ===
# Sur Windows:
.venv\Scripts\activate

# Sur Mac/Linux:
source .venv/bin/activate

# Vous devez voir "(.venv)" au début de votre ligne de terminal!


# === ÉTAPE 5: INSTALLER STREAMLIT ===
# Tapez cette commande:
pip install streamlit

# Cela télécharge et installe Streamlit (peut prendre 1-2 minutes)
# Vérifier:
streamlit --version

# Vous devez voir: Streamlit, version X.X.X


# === ÉTAPE 6: CRÉER VOTRE PREMIÈRE APP ===
# Créer un fichier nommé "app.py" avec ce contenu:

import streamlit as st

st.title("Ma Première App Streamlit")
st.write("Bienvenue dans Streamlit!")
st.write("C'est votre première app!")


# === ÉTAPE 7: LANCER L'APP ===
# Dans le terminal (dans votre dossier), tapez:
streamlit run app.py

# Attendez quelques secondes...
# Votre navigateur s'ouvre automatiquement!
# Vous voyez votre app à l'adresse: http://localhost:8501


# === ÉTAPE 8: MODIFIER L'APP ===
# Changez le contenu de "app.py":

import streamlit as st

st.title("Ma Première App - MODIFIÉE!")
st.write("J'ai modifié l'app!")

# Sauvegardez le fichier (Ctrl+S)
# Streamlit DÉTECTE automatiquement le changement
# La page web se RÉACTUALISE automatiquement!

# C'est INCROYABLE: vous n'avez pas besoin de redémarrer!


# === ARRÊTER L'APP ===
# Dans le terminal, appuyez sur: Ctrl+C
# L'app s'arrête, le navigateur reste ouvert mais ne fonctionne plus


[OK] AFFICHAGE DE TEXTE - EXPLICATIONS DÉTAILLÉES

# === st.title() - TITRE PRINCIPAL ===

import streamlit as st

st.title("Mon Application")
# Affiche: "Mon Application" en TRÈS GROS, en haut de la page
# Utilisé pour le titre principal uniquement (une fois par app)


# === st.header() - SOUS-TITRE ===

st.header("Section Importante")
# Affiche: "Section Importante" en gros
# Utilisé pour organiser votre app en sections


# === st.subheader() - SOUS-SOUS-TITRE ===

st.subheader("Détail important")
# Affiche: "Détail important" en moins gros


# === st.write() - LE COUTEAU SUISSE ===

st.write("Texte simple")              # Texte normal
st.write(42)                           # Nombre
st.write(3.14)                         # Nombre décimal
st.write([1, 2, 3, 4, 5])             # Liste
st.write({"nom": "Alice", "age": 30}) # Dictionnaire
st.write("**Gras** et *italique*")    # Markdown

# st.write() devine automatiquement le type et l'affiche correctement!
# C'est le plus utilisé dans Streamlit


# === st.text() - TEXTE MONOSPACE ===

st.text("Ceci ressemble à du code")
st.text("Les espaces sont conservés    comme    ça")
# Utilisé pour afficher du code ou du texte "brut"


# === st.markdown() - MARKDOWN COMPLET ===

st.markdown("# Titre en Markdown")
st.markdown("**Gras** et *italique*")
st.markdown("- Item 1\n- Item 2\n- Item 3")

# Le markdown est un langage simple pour formater du texte
# # = titre
# ** = gras
# * = italique
# - = liste


# === st.code() - AFFICHER DU CODE ===

# Montrer un petit code:
st.code("print('Bonjour')", language="python")

# Montrer du code plus long:
code = """
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

result = fibonacci(10)
print(result)
"""
st.code(code, language="python")

# Le paramètre language peut être: python, javascript, html, sql, etc


# === COULEURS (Streamlit 1.16+) ===

st.write(":blue[Texte bleu]")
st.write(":red[Texte rouge]")
st.write(":green[Texte vert]")
st.write(":orange[Texte orange]")

# Les couleurs disponibles: blue, green, orange, red, violet, gray


# === BOÎTES D'INFORMATION ===

st.info("ℹ Ceci est une information")
# Affiche une boîte bleue avec "ℹ"
# Utilisé pour dire quelque chose d'utile à l'utilisateur

st.success("[OK] Opération réussie!")
# Affiche une boîte verte avec "[OK]"
# Utilisé quand quelque chose fonctionne

st.warning("[ATTENTION] Attention!")
# Affiche une boîte orange avec "[ATTENTION]"
# Utilisé pour avertir l'utilisateur

st.error("[X] Erreur!")
# Affiche une boîte rouge avec "[X]"
# Utilisé pour montrer une erreur

# Ces boîtes sont utiles pour donner du feedback à l'utilisateur


# === DIVIDER (SÉPARATEUR) ===

st.divider()
# Affiche une ligne pour séparer les sections
# Utile pour l'organisation visuelle


[OK] ENTRÉES UTILISATEUR (WIDGETS) - EXPLICATIONS TRÈS DÉTAILLÉES

# === CONCEPT GÉNÉRAL ===

# Un "widget" en Streamlit = un élément interactif avec lequel l'utilisateur peut agir:
# - Boutons qu'on clique
# - Champs texte où on type
# - Sliders qu'on déplace
# - Menus déroulants
# - Cases à cocher
# etc

# IMPORTANT: Chaque widget retourne une VALEUR que vous pouvez utiliser

# Exemple simple:
name = st.text_input("Quel est votre nom?")
# name = la valeur que l'utilisateur a tapée
# Si l'utilisateur tape "Alice", alors: name = "Alice"

st.write(f"Vous vous appelez: {name}")
# Affiche: "Vous vous appelez: Alice"


# === BOUTON (st.button) ===

# Un bouton que l'utilisateur peut cliquer:
if st.button("Cliquez-moi"):
    st.write("Merci d'avoir cliqué!")

# Comment ça marche:
# 1. Le bouton apparaît
# 2. Si l'utilisateur clique -> st.button() retourne True
# 3. On rentre dans le if et on affiche "Merci..."
# 4. Si l'utilisateur ne clique pas -> st.button() retourne False
# 5. On n'entre pas dans le if


# === CHAMP TEXTE (st.text_input) ===

# L'utilisateur tape du texte:
name = st.text_input("Quel est votre nom?")

# La valeur dans "name" est ce que l'utilisateur a tapé
# Si rien n'a été tapé, name = ""

# Afficher le résultat:
if name:  # Si name n'est pas vide
    st.write(f"Bonjour {name}!")

# PARAMÈTRES UTILES:
name = st.text_input("Nom:", value="Alice")
# "value" = valeur initiale par défaut

email = st.text_input("Email:", placeholder="exemple@mail.com")
# "placeholder" = texte gris qui disparaît quand on tape

password = st.text_input("Mot de passe:", type="password")
# type="password" = le texte s'affiche avec des points (sécurité)


# === ZONE TEXTE MULTILIGNE (st.text_area) ===

# L'utilisateur peut écrire plusieurs lignes:
comment = st.text_area("Vos commentaires:")

st.write("Vous avez écrit:")
st.write(comment)

# PARAMÈTRES:
comment = st.text_area("Commentaires:", height=150)
# height = nombre de lignes visibles


# === NOMBRE (st.number_input) ===

# L'utilisateur choisit un nombre:
age = st.number_input("Quel âge avez-vous?")
st.write(f"Vous avez {age} ans")

# PARAMÈTRES UTILES:
age = st.number_input("Âge:", min_value=0, max_value=150, value=25)
# min_value = minimum autorisé
# max_value = maximum autorisé
# value = valeur par défaut

# Avec décimales:
price = st.number_input("Prix (€):", min_value=0.0, step=0.01)
# step = l'incrément (augmente par 0.01 chaque clic sur la flèche)


# === CURSEUR (st.slider) ===

# L'utilisateur déplace un curseur:
temperature = st.slider("Température (°C)", -50, 50, 20)
# -50 = minimum
# 50 = maximum
# 20 = valeur par défaut
st.write(f"Température: {temperature}°C")

# Curseur avec plage (min et max):
min_price, max_price = st.slider("Prix de recherche (€)", 0, 1000, (100, 500))
# (100, 500) = valeurs par défaut (min=100, max=500)
st.write(f"Vous cherchez entre {min_price}€ et {max_price}€")

# Curseur avec étapes:
speed = st.slider("Vitesse", 0, 100, 50, step=5)
# step=5 = augmente par 5 (0, 5, 10, 15, 20...)


# === SÉLECTION DÉROULANTE (st.selectbox) ===

# L'utilisateur choisit une option parmi une liste:
fruit = st.selectbox("Quel est votre fruit préféré?", 
                     ["Pomme", "Banane", "Cerise", "Datte"])
# L'utilisateur voit un menu déroulant
# Quand il clique, il voit les 4 options

st.write(f"Vous avez choisi: {fruit}")

# Index (qui a été sélectionné):
fruit_index = st.selectbox("Fruit:", ["Pomme", "Banane", "Cerise"], index=1)
# index=1 = "Banane" est sélectionné par défaut (0=Pomme, 1=Banane, 2=Cerise)


# === SÉLECTION MULTIPLE (st.multiselect) ===

# L'utilisateur choisit plusieurs options:
languages = st.multiselect("Quels langages savez-vous?", 
                           ["Python", "JavaScript", "Java", "C++", "Go"])
# L'utilisateur peut choisir 0, 1, 2, 3, 4 ou 5 langages

st.write(f"Vous savez: {languages}")

# Si l'utilisateur choisit Python et JavaScript:
# languages = ["Python", "JavaScript"]


# === BOUTONS RADIO (st.radio) ===

# L'utilisateur choisit UNE option parmi plusieurs (visuellement, ce sont des boutons):
genre = st.radio("Quel est votre genre?", 
                 ["Masculin", "Féminin", "Autre", "Préfère ne pas dire"])

st.write(f"Genre: {genre}")

# Contrairement à selectbox, les options sont visibles d'emblée


# === CASE À COCHER (st.checkbox) ===

# L'utilisateur peut cocher/décocher:
agree = st.checkbox("J'accepte les conditions d'utilisation")

if agree:
    st.write("[OK] Vous avez accepté!")
else:
    st.write("[X] Vous n'avez pas accepté")

# Plusieurs checkboxes:
st.write("Quels intérêts vous concernent?")
python = st.checkbox("Python")
data = st.checkbox("Data Science")
web = st.checkbox("Web Development")

if python:
    st.write("  -> Vous aimez Python")
if data:
    st.write("  -> Vous aimez la Data Science")


# === DATE (st.date_input) ===

# L'utilisateur choisit une date:
date = st.date_input("Choisissez votre date de naissance")
# Un calendrier apparaît quand on clique

st.write(f"Date: {date}")

# Avec plage:
start, end = st.date_input("Période:", [])
# L'utilisateur choisit une date de début ET de fin


# === HEURE (st.time_input) ===

# L'utilisateur choisit une heure:
time = st.time_input("À quelle heure?", value="12:30")
st.write(f"Heure: {time}")


# === UPLOAD DE FICHIER (st.file_uploader) ===

# L'utilisateur peut charger un fichier depuis son ordinateur:
uploaded_file = st.file_uploader("Choisissez un fichier")

if uploaded_file is not None:
    # L'utilisateur a chargé un fichier
    st.write(f"Fichier: {uploaded_file.name}")
    
    # Lire le contenu du fichier
    if uploaded_file.name.endswith(".csv"):
        import pandas as pd
        df = pd.read_csv(uploaded_file)
        st.dataframe(df)
    
    elif uploaded_file.name.endswith(".txt"):
        content = uploaded_file.read().decode("utf-8")
        st.write(content)

# PARAMÈTRES:
file = st.file_uploader("Fichier:", type=["csv", "xlsx", "txt"])
# type = les extensions autorisées


[OK] AFFICHAGE DE DONNÉES - TRÈS DÉTAILLÉ

# === st.write() POUR TOUS LES TYPES ===

st.write(42)                              # Nombre
st.write(3.14)                            # Décimal
st.write([1, 2, 3, 4])                   # Liste
st.write({"nom": "Alice", "âge": 30})    # Dict

# st.write() est le plus flexible!


# === st.dataframe() - TABLEAU INTERACTIF ===

import pandas as pd

# Créer un DataFrame (tableau de données):
df = pd.DataFrame({
    "Nom": ["Alice", "Bob", "Charlie"],
    "Âge": [30, 25, 35],
    "Ville": ["Paris", "Lyon", "Marseille"]
})

# Afficher le tableau:
st.dataframe(df)

# LE TABLEAU EST INTERACTIF:
# [OK] On peut trier les colonnes
# [OK] On peut ajuster la largeur des colonnes
# [OK] On peut chercher dans les données

# PARAMÈTRES:
st.dataframe(df, width=800, height=300)
# width = largeur du tableau
# height = hauteur du tableau

# Cacher l'index (numérotation):
st.dataframe(df.reset_index(drop=True))


# === st.table() - TABLEAU STATIQUE ===

st.table(df)

# Pareil qu'un dataframe, MAIS:
# [X] Pas interactif
# [OK] Plus simple visuellement
# Utilisé pour afficher une simple table


# === st.metric() - MONTRER DES CHIFFRES ===

# Afficher une métrique (KPI - Key Performance Indicator):
st.metric("Température", "25°C")
# "Température" = label
# "25°C" = valeur

# Avec delta (changement):
st.metric("Ventes", "1000€", "+5%")
# "+5%" = augmentation (vert)

st.metric("Visites", "500", "-10%")
# "-10%" = diminution (rouge)

# Sur plusieurs colonnes:
col1, col2, col3 = st.columns(3)

with col1:
    st.metric("Clients", "150", "+10")

with col2:
    st.metric("Revenu", "5000€", "+500€")

with col3:
    st.metric("Taux", "95%", "-2%")


# === st.json() - AFFICHER DU JSON ===

data = {
    "name": "Alice",
    "age": 30,
    "hobbies": ["lecture", "natation", "musique"]
}

st.json(data)
# Affiche le JSON de façon lisible avec coloration


[OK] ORGANISATION DE LA PAGE - LAYOUT

# === COLONNES (st.columns) ===

# Comprendre les colonnes:
# Une page Streamlit est normalement une colonne unique
# Vous pouvez la diviser en PLUSIEURS colonnes

# Créer 3 colonnes égales:
col1, col2, col3 = st.columns(3)

# Maintenant, tout ce que vous faites DANS col1 s'affiche dans la 1ère colonne
with col1:
    st.write("Colonne 1")
    st.button("Bouton 1")

with col2:
    st.write("Colonne 2")
    st.button("Bouton 2")

with col3:
    st.write("Colonne 3")
    st.button("Bouton 3")

# Résultat: 3 colonnes l'une à côté de l'autre

# Colonnes avec largeurs DIFFÉRENTES:
col1, col2 = st.columns([3, 1])
# [3, 1] = la 1ère colonne est 3 fois plus large que la 2ème

with col1:
    st.write("Grande colonne (75%)")

with col2:
    st.write("Petite (25%)")


# === ONGLETS (st.tabs) ===

# Créer 3 onglets:
tab1, tab2, tab3 = st.tabs(["Accueil", "Données", "Paramètres"])

# L'utilisateur voit 3 onglets en haut
# En cliquant sur un onglet, seul le contenu de cet onglet s'affiche

with tab1:
    st.write("Bienvenue sur la page d'accueil!")
    st.write("Ceci est le contenu de l'onglet 1")

with tab2:
    st.write("Vos données ici")
    st.dataframe({"A": [1, 2, 3]})

with tab3:
    st.write("Paramètres")
    option = st.selectbox("Choisir:", ["A", "B", "C"])


# === EXPANDER (SECTIONS DÉPLIABLES) ===

# Créer une section qu'on peut déplier:
with st.expander("Voir plus"):
    st.write("Ce texte est caché par défaut")
    st.write("Cliquez sur 'Voir plus' pour le voir")
    st.write("Cliquez à nouveau pour le cacher")

# Utilisé pour:
# [OK] Afficher des détails optionnels
# [OK] Ne pas surcharger la page
# [OK] Garder l'attention sur le contenu principal


# === SIDEBAR (BARRE LATÉRALE) ===

# La sidebar est une zone sur la GAUCHE de l'écran
# Utilisée pour le menu, les filtres, les paramètres

st.sidebar.title("Menu Principal")

# Les éléments dans la sidebar:
page = st.sidebar.radio("Sélectionnez une page:", 
                        ["Accueil", "Analyse", "Paramètres"])

st.sidebar.write("---")

# Filtres:
min_val = st.sidebar.slider("Valeur min:", 0, 100, 50)
max_val = st.sidebar.slider("Valeur max:", 0, 100, 80)

st.sidebar.info("Utilisez les filtres ci-dessus")

# Dans la page principale:
st.title(f"Page: {page}")
st.write(f"Filtres: {min_val} à {max_val}")


# === CONTAINER ===

# Un container est simplement un zone où organiser le contenu
container1 = st.container()
container2 = st.container()

with container1:
    st.write("Zone 1")

with container2:
    st.write("Zone 2")

# Avec border:
with st.container(border=True):
    st.write("Conteneur avec bordure")


# === EMPTY (CONTENEUR VIDE) ===

# Créer une zone vide qu'on peut remplir plus tard:
placeholder = st.empty()

# On peut remplir cette zone plus tard:
placeholder.write("Texte initial")

import time
time.sleep(2)

# Remplacer le contenu:
placeholder.write("Texte remplacé!")

# Utilisé pour:
# [OK] Mettre à jour en temps réel
# [OK] Animations
# [OK] Affichage progressif


[OK] GRAPHIQUES ET VISUALISATIONS

# === GRAPHIQUES STREAMLIT SIMPLES ===

import streamlit as st
import pandas as pd

# Créer des données:
df = pd.DataFrame({
    "Mois": ["Jan", "Fév", "Mar", "Avr", "Mai"],
    "Ventes": [100, 150, 120, 180, 200]
})

# Graphique en ligne:
st.line_chart(df.set_index("Mois"))

# Graphique en barres:
st.bar_chart(df.set_index("Mois"))

# Graphique en aire:
st.area_chart(df.set_index("Mois"))

# Ces graphiques sont:
# [OK] Simples à utiliser
# [OK] Interactifs (on peut zoomer)
# [OK] Automatiquement formatés


# === PLOTLY (GRAPHIQUES AVANCÉS) ===

import plotly.express as px

# Plotly crée des graphiques BEAUCOUP plus beaux et interactifs

df = pd.DataFrame({
    "Mois": ["Jan", "Fév", "Mar", "Avr", "Mai"],
    "Ventes": [100, 150, 120, 180, 200]
})

# Graphique en ligne (Plotly):
fig = px.line(df, x="Mois", y="Ventes", title="Ventes mensuelles")
st.plotly_chart(fig)

# Graphique en barres (Plotly):
fig = px.bar(df, x="Mois", y="Ventes", title="Ventes par mois")
st.plotly_chart(fig)

# Graphique en points (scatter):
fig = px.scatter(df, x="Mois", y="Ventes")
st.plotly_chart(fig)

# Graphique en camembert (pie):
fig = px.pie(df, names="Mois", values="Ventes")
st.plotly_chart(fig)

# Boîte à moustaches (box plot):
fig = px.box(df, y="Ventes")
st.plotly_chart(fig)

# === MATPLOTLIB (GRAPHIQUES SCIENTIFIQUES) ===

import matplotlib.pyplot as plt
import numpy as np

# Matplotlib est une libraire très populaire pour les graphiques Python

# Créer un graphique:
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [1, 4, 2, 3], marker="o")
ax.set_title("Mon Graphique")
ax.set_xlabel("X")
ax.set_ylabel("Y")

# Afficher dans Streamlit:
st.pyplot(fig)

# Graphique plus complexe:
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot([1, 2, 3, 4], [1, 4, 2, 3], label="Série 1", marker="o")
ax.plot([1, 2, 3, 4], [2, 3, 1, 4], label="Série 2", marker="s")
ax.legend()
ax.set_title("Deux séries")
st.pyplot(fig)


# === ALTAIR (GRAPHIQUES ÉLÉGANTS) ===

import altair as alt

# Créer un graphique avec Altair:
chart = alt.Chart(df).mark_line().encode(
    x="Mois",
    y="Ventes"
).properties(title="Ventes par mois")

st.altair_chart(chart, use_container_width=True)

# Graphique interactif:
selection = alt.selection_single(fields=["Mois"], bind="legend")

chart = alt.Chart(df).mark_line().encode(
    x="Mois",
    y="Ventes",
    color=alt.condition(selection, "Mois:N", alt.value("lightgray")),
    opacity=alt.condition(selection, alt.value(1), alt.value(0.2))
).add_selection(selection)

st.altair_chart(chart, use_container_width=True)


[OK] ÉTAT ET INTERACTIVITÉ - TRÈS IMPORTANT!

# === LE PROBLÈME DU STATE ===

# Sans session_state, ce code NE FONCTIONNE PAS:
counter = 0
if st.button("Incrémenter"):
    counter += 1
    st.write(counter)

# Chaque clic fait réexécuter le script
# counter recommence toujours à 0
# st.write affiche toujours 1


# === LA SOLUTION: st.session_state ===

# session_state = une mémoire que Streamlit conserve
# Même si le script réexécute, les valeurs dans session_state restent

# Initialiser une variable:
if "counter" not in st.session_state:
    st.session_state.counter = 0

# Utiliser la variable:
st.write(f"Compteur: {st.session_state.counter}")

if st.button("Incrémenter"):
    st.session_state.counter += 1
    st.rerun()  # Forcer la réexécution du script

# Ou sans st.rerun() (réexécution automatique):
if st.button("Incrémenter"):
    st.session_state.counter += 1

st.write(f"Compteur final: {st.session_state.counter}")


# === EXEMPLE COMPLET: FORMULAIRE AVEC STATE ===

# Initialiser
if "form_data" not in st.session_state:
    st.session_state.form_data = {
        "name": "",
        "age": 0,
        "email": ""
    }

st.title("Formulaire")

# Champs
name = st.text_input("Nom:", value=st.session_state.form_data["name"])
age = st.number_input("Âge:", value=st.session_state.form_data["age"])
email = st.text_input("Email:", value=st.session_state.form_data["email"])

# Bouton Soumettre
if st.button("Soumettre"):
    st.session_state.form_data["name"] = name
    st.session_state.form_data["age"] = age
    st.session_state.form_data["email"] = email
    st.success("Formulaire soumis!")

# Afficher les données
st.write("Données actuelles:")
st.write(st.session_state.form_data)


# === CALLBACKS (FONCTIONS DE RAPPEL) ===

# Un callback = une fonction appelée quand quelque chose change

def on_name_change():
    # Cette fonction s'exécute quand le text_input change
    st.session_state.name_upper = st.session_state.name.upper()

if "name_upper" not in st.session_state:
    st.session_state.name_upper = ""

# text_input avec callback:
st.text_input(
    "Entrez votre nom:",
    key="name",  # Lier à session_state.name
    on_change=on_name_change
)

st.write(f"En MAJUSCULES: {st.session_state.name_upper}")


[OK] CACHING - OPTIMISATION IMPORTANTE

# === LE PROBLÈME ===

# Streamlit réexécute TOUT à chaque interaction
# Si vous chargez un gros fichier, ça se réexécute à chaque fois
# C'est LENT!

# Exemple lent:
import pandas as pd
import time

df = pd.read_csv("huge_file.csv")  # Prend 10 secondes!
st.dataframe(df)

# Chaque clic réexécute ça - TRÈS LENT!


# === LA SOLUTION: @st.cache_data ===

# Décorateur qui cache le résultat:
@st.cache_data
def load_data():
    time.sleep(10)
    return pd.read_csv("huge_file.csv")

df = load_data()  # Première fois: 10 secondes
st.dataframe(df)  # Les fois suivantes: instantané!

# @st.cache_data cache le résultat ET LA CHANGE DE PARAMÈTRES
# Si les paramètres changent, le cache se recalcule


# === EXEMPLE AVEC PARAMÈTRES ===

@st.cache_data
def get_data(filename):
    # Le cache dépend de filename
    return pd.read_csv(filename)

file = st.selectbox("Fichier:", ["data1.csv", "data2.csv"])
df = get_data(file)  # Réexécute seulement si file change
st.dataframe(df)


# === TTLS (TIME TO LIVE) ===

# Invalider le cache après un certain temps:
@st.cache_data(ttl=3600)  # 1 heure
def get_weather():
    # Appel à une API
    return fetch_weather()

# Cache pendant 1 heure, puis se recalcule


# === @st.cache_resource ===

# Pour les ressources (connexions BD, modèles ML):
@st.cache_resource
def get_model():
    # Charger un modèle ML (coûteux)
    return load_ml_model()

model = get_model()  # Charge une fois seulement


[OK] EXEMPLE COMPLET: CALCULATRICE INTERACTIVE

import streamlit as st

st.set_page_config(page_title="Calculatrice", page_icon="[CALCUL]")

st.title("[CALCUL] Calculatrice Streamlit")
st.write("Une calculatrice simple et interactive!")

# Sidebar pour l'opération
st.sidebar.title("Opération")
operation = st.sidebar.radio(
    "Choisissez:",
    ["+", "-", "×", "÷"]
)

# Entrées principales
col1, col2 = st.columns(2)

with col1:
    a = st.number_input("Nombre 1:", value=0, step=1)

with col2:
    b = st.number_input("Nombre 2:", value=0, step=1)

# Calcul
if operation == "+":
    result = a + b
    symbol = "+"
elif operation == "-":
    result = a - b
    symbol = "-"
elif operation == "×":
    result = a * b
    symbol = "×"
else:  # ÷
    if b != 0:
        result = a / b
        symbol = "÷"
    else:
        result = "Erreur: division par zéro!"
        symbol = "÷"

# Affichage du résultat
st.divider()
st.subheader("Résultat")

if isinstance(result, str):
    st.error(result)
else:
    col1, col2, col3 = st.columns(3)
    with col1:
        st.metric("Opération", f"{a} {symbol} {b}")
    with col2:
        st.metric("Résultat", f"{result}")
    with col3:
        # Type de résultat
        if isinstance(result, float) and result != int(result):
            st.metric("Type", "Décimal")
        else:
            st.metric("Type", "Entier")

# Historique
if "history" not in st.session_state:
    st.session_state.history = []

if st.button("[NOTE] Ajouter à l'historique"):
    if isinstance(result, str):
        st.warning("Impossible: résultat invalide")
    else:
        entry = f"{a} {symbol} {b} = {result}"
        st.session_state.history.append(entry)
        st.success("Ajouté!")

# Afficher l'historique
if st.session_state.history:
    st.divider()
    st.subheader("[LISTE] Historique")
    for i, item in enumerate(st.session_state.history, 1):
        st.write(f"{i}. {item}")
    
    if st.button("[SUPPRIMER] Effacer l'historique"):
        st.session_state.history = []
        st.info("Historique effacé!")


[OK] EXEMPLE COMPLET: DASHBOARD DE DONNÉES

import streamlit as st
import pandas as pd
import plotly.express as px

# Configuration de la page
st.set_page_config(page_title="Dashboard Ventes", layout="wide")

st.title("[GRAPHIQUE] Dashboard - Ventes 2024")

# Créer des données d'exemple
import numpy as np
np.random.seed(42)

dates = pd.date_range("2024-01-01", periods=100)
data = pd.DataFrame({
    "Date": dates,
    "Ventes": np.random.randint(100, 1000, 100),
    "Clients": np.random.randint(10, 50, 100),
    "Région": np.random.choice(["Nord", "Sud", "Est", "Ouest"], 100),
    "Produit": np.random.choice(["A", "B", "C"], 100)
})

# === SIDEBAR: FILTRES ===

st.sidebar.title("[CONFIG] Filtres")

# Filtre région
regions = st.sidebar.multiselect(
    "Régions:",
    data["Région"].unique(),
    default=data["Région"].unique()
)

# Filtre produit
products = st.sidebar.multiselect(
    "Produits:",
    data["Produit"].unique(),
    default=data["Produit"].unique()
)

# Filtre date
start_date, end_date = st.sidebar.date_input(
    "Période:",
    [data["Date"].min(), data["Date"].max()]
)

# Appliquer les filtres
filtered_data = data[
    (data["Région"].isin(regions)) &
    (data["Produit"].isin(products)) &
    (data["Date"].dt.date >= start_date) &
    (data["Date"].dt.date <= end_date)
]

# === MÉTRIQUES PRINCIPALES ===

st.subheader("[HAUSSE] Indicateurs Clés")

col1, col2, col3, col4 = st.columns(4)

with col1:
    st.metric(
        "Total Ventes",
        f"€{filtered_data['Ventes'].sum():,.0f}",
        f"+{filtered_data['Ventes'].sum() - 5000:,.0f}"
    )

with col2:
    st.metric(
        "Nombre Clients",
        f"{filtered_data['Clients'].sum():,.0f}",
        "+50"
    )

with col3:
    st.metric(
        "Vente Moyenne",
        f"€{filtered_data['Ventes'].mean():.0f}",
        "-5%"
    )

with col4:
    st.metric(
        "Nombre Transactions",
        f"{len(filtered_data):,.0f}",
        "+10%"
    )

st.divider()

# === GRAPHIQUES ===

col1, col2 = st.columns(2)

# Graphique 1: Ventes par jour
with col1:
    daily_sales = filtered_data.groupby("Date")["Ventes"].sum()
    fig = px.line(
        daily_sales,
        title="Ventes par jour",
        labels={"value": "Ventes (€)", "index": "Date"}
    )
    st.plotly_chart(fig, use_container_width=True)

# Graphique 2: Ventes par région
with col2:
    regional_sales = filtered_data.groupby("Région")["Ventes"].sum()
    fig = px.bar(
        regional_sales,
        title="Ventes par région",
        labels={"value": "Ventes (€)"}
    )
    st.plotly_chart(fig, use_container_width=True)

col1, col2 = st.columns(2)

# Graphique 3: Répartition par produit
with col1:
    product_sales = filtered_data.groupby("Produit")["Ventes"].sum()
    fig = px.pie(
        product_sales,
        names=product_sales.index,
        values=product_sales.values,
        title="Répartition par produit"
    )
    st.plotly_chart(fig, use_container_width=True)

# Graphique 4: Box plot
with col2:
    fig = px.box(
        filtered_data,
        x="Région",
        y="Ventes",
        title="Distribution des ventes par région"
    )
    st.plotly_chart(fig, use_container_width=True)

st.divider()

# === TABLEAU DE DONNÉES ===

st.subheader("[LISTE] Données détaillées")

# Afficher le tableau
st.dataframe(filtered_data, use_container_width=True)

# Télécharger les données
csv = filtered_data.to_csv(index=False)
st.download_button(
    label="[ENTREE] Télécharger les données (CSV)",
    data=csv,
    file_name="ventes_export.csv",
    mime="text/csv"
)


[OK] TÉLÉCHARGEMENT ET UPLOAD DE FICHIERS

# === TÉLÉCHARGER UN FICHIER (st.download_button) ===

import pandas as pd
import json

# Télécharger un CSV
df = pd.DataFrame({
    "Nom": ["Alice", "Bob"],
    "Âge": [30, 25]
})

csv_data = df.to_csv(index=False)

st.download_button(
    label="[ENTREE] Télécharger CSV",
    data=csv_data,
    file_name="data.csv",
    mime="text/csv"
)

# Télécharger du JSON
data = {"nom": "Alice", "âge": 30}
json_data = json.dumps(data, ensure_ascii=False, indent=2)

st.download_button(
    label="[ENTREE] Télécharger JSON",
    data=json_data,
    file_name="data.json",
    mime="application/json"
)

# Télécharger du texte
text = "Ceci est un fichier texte"

st.download_button(
    label="[ENTREE] Télécharger TXT",
    data=text,
    file_name="document.txt",
    mime="text/plain"
)


# === UPLOADER UN FICHIER (st.file_uploader) ===

uploaded_file = st.file_uploader("Choisissez un fichier:")

if uploaded_file is not None:
    # L'utilisateur a uploadé un fichier
    st.write(f"Fichier: {uploaded_file.name}")
    
    # Lire selon le type
    if uploaded_file.name.endswith(".csv"):
        df = pd.read_csv(uploaded_file)
        st.write("Aperçu:")
        st.dataframe(df.head())
    
    elif uploaded_file.name.endswith(".json"):
        data = json.load(uploaded_file)
        st.write("Contenu JSON:")
        st.json(data)
    
    elif uploaded_file.name.endswith(".txt"):
        text = uploaded_file.read().decode("utf-8")
        st.write("Contenu du fichier:")
        st.text(text)


[OK] DÉPLOIEMENT - METTRE VOTRE APP EN LIGNE

# === DÉPLOYER GRATUITEMENT SUR STREAMLIT CLOUD ===

# 1. Créer un compte GitHub (si vous n'en avez pas):
#    -> https://github.com/signup

# 2. Créer un nouveau repository sur GitHub:
#    -> Nommez-le "mon-app-streamlit"
#    -> Cochez "Add a README file"

# 3. Créer 2 fichiers dans votre repository:

# Fichier 1: app.py
# (votre code Streamlit)

# Fichier 2: requirements.txt
streamlit==1.28.0
pandas>=2.0
plotly>=5.0

# 4. Pousser votre code sur GitHub:
git add .
git commit -m "Initial commit"
git push

# 5. Aller sur Streamlit Cloud:
#    -> https://streamlit.io/cloud
#    -> Cliquez "New app"
#    -> Sélectionnez votre repository GitHub
#    -> Sélectionnez le fichier "app.py"
#    -> Cliquez "Deploy"

# Voilà! Votre app est en ligne!
# URL: https://[votrenom]-[nomapp].streamlit.app


# === STRUCTURE RECOMMANDÉE POUR GITHUB ===

# mon-app-streamlit/
# ├── app.py                 # Fichier principal
# ├── requirements.txt       # Dépendances
# ├── README.md              # Documentation
# ├── data/
# │   └── data.csv           # Fichiers de données
# └── pages/
#     ├── page1.py
#     └── page2.py


# === VERSIONNER AVEC REQUIREMENTS.TXT ===

# Générer requirements.txt (dans votre venv):
pip freeze > requirements.txt

# Contenu recommandé:
streamlit==1.28.0
pandas==2.0.3
plotly==5.14.0
numpy==1.24.0

# Versioning:
# ==1.2.3 = version exacte
# >=1.2.0 = version 1.2.0 ou plus récente
# >=1.2.0,<2.0.0 = entre 1.2.0 et 2.0.0


[OK] BONNES PRATIQUES

# === 1. ORGANISATION DU CODE ===

# Mauvais (tout dans un fichier):
# app.py (500 lignes)

# Bon (code organisé):
# app.py (main)
# utils/data.py (fonctions de données)
# utils/plots.py (graphiques)
# pages/home.py (page accueil)

# Exemple utils/data.py:
@st.cache_data
def load_data():
    return pd.read_csv("data.csv")


# === 2. UTILISER st.set_page_config ===

# Au début de votre app:
st.set_page_config(
    page_title="Ma Super App",
    page_icon="[RAPIDE]",
    layout="wide",  # "centered" ou "wide"
    initial_sidebar_state="expanded"  # "expanded" ou "collapsed"
)


# === 3. DOCUMENTER VOTRE CODE ===

def process_data(df):
    """
    Traite les données d'entrée.
    
    Args:
        df: DataFrame à traiter
    
    Returns:
        DataFrame traité
    """
    return df.dropna()


# === 4. GÉRER LES ERREURS ===

try:
    result = 10 / divisor
except ZeroDivisionError:
    st.error("[X] Division par zéro impossible!")
    result = None


# === 5. UTILISER LES SECRETS ===

# Créer .streamlit/secrets.toml:
# [database]
# password = "mon_mot_de_passe"

# Accéder:
password = st.secrets["database"]["password"]


# === 6. CACHER DU CONTENU SENSIBLE ===

# Ne JAMAIS mettre en dur:
# password = "mon_password"  <- MAUVAIS!

# Utiliser secrets:
# password = st.secrets["database"]["password"]  <- BON!


# === 7. OPTIMISER LES PERFORMANCES ===

# Utiliser le cache:
@st.cache_data
def load_data():
    return pd.read_csv("huge_file.csv")

# Éviter les boucles lentes:
# Au lieu de:
for i in range(1000):
    st.write(i)  # Très lent!

# Faire:
data = list(range(1000))
st.write(data)  # Rapide!


# === 8. TESTER LOCALEMENT AVANT DE DÉPLOYER ===

streamlit run app.py

# Puis visiter: http://localhost:8501


[OK] DÉPANNAGE COURANT

# === ERREUR: "Module streamlit not found" ===

# Solution:
# 1. Vérifier que vous êtes dans le bon venv
source .venv/bin/activate  # Linux/Mac
.venv\Scripts\activate     # Windows

# 2. Installer streamlit
pip install streamlit

# 3. Vérifier l'installation
streamlit --version


# === ERREUR: "Port 8501 already in use" ===

# Solution: Utiliser un autre port
streamlit run app.py --server.port 8502


# === LES VARIABLES DISPARAISSENT APRÈS UN CLIC ===

# CAUSE: Vous n'utilisez pas session_state

# Avant (MAUVAIS):
counter = 0
if st.button("Incrémenter"):
    counter += 1
    st.write(counter)  # Affiche toujours 1!

# Après (BON):
if "counter" not in st.session_state:
    st.session_state.counter = 0

if st.button("Incrémenter"):
    st.session_state.counter += 1

st.write(st.session_state.counter)  # Fonctionne!


# === L'APP EST LENTE ===

# CAUSE: Vous chargez des données à chaque exécution

# Avant (LENT):
df = pd.read_csv("huge_file.csv")  # 10 secondes à chaque fois!

# Après (RAPIDE):
@st.cache_data
def load_data():
    return pd.read_csv("huge_file.csv")

df = load_data()  # Instantané après la première fois!


# === BOÎTE BLANCHE VIDE ===

# Vérifier les logs:
streamlit run app.py --logger.level=debug

# Vérifier que app.py existe


[OK] RESSOURCES ET DOCUMENTATION

# Documentation officielle:
# https://docs.streamlit.io/

# Gallery (voir des exemples):
# https://streamlit.io/gallery

# Community Discord:
# https://discuss.streamlit.io/

# Cheat sheet officiel:
# https://docs.streamlit.io/library/cheatsheet

# Deploy docs:
# https://docs.streamlit.io/streamlit-cloud/deploy-your-app

# Tutoriels vidéo:
# https://www.youtube.com/c/StreamlitOfficial