From 1606679001e46e8676acf368f6d1553b1ed6b817 Mon Sep 17 00:00:00 2001
From: Simon Majorczyk <simon.majorczyk.etu@univ-lille.fr>
Date: Mon, 17 Mar 2025 09:22:43 +0100
Subject: [PATCH] Changement CSS

---
 app_glob.py                                  |  93 ++++++++
 spotify-popularity-prediction-v2/app_glob.py |  94 ++++++++
 templates/index-glob.html                    | 225 +++++++++++++++++++
 3 files changed, 412 insertions(+)
 create mode 100644 app_glob.py
 create mode 100644 spotify-popularity-prediction-v2/app_glob.py
 create mode 100644 templates/index-glob.html

diff --git a/app_glob.py b/app_glob.py
new file mode 100644
index 0000000..62f8c72
--- /dev/null
+++ b/app_glob.py
@@ -0,0 +1,93 @@
+from flask import Flask, request, jsonify, render_template
+import pickle
+import pandas as pd
+from sklearn.preprocessing import StandardScaler
+import numpy as np
+
+app = Flask(__name__)
+
+# Charger le modèle
+with open('random_forest_model_binaire.pkl', 'rb') as model_file:
+    rf = pickle.load(model_file)
+
+# Charger le scaler entraîné
+with open('scaler_binaire.pkl', 'rb') as scaler_file:
+    scaler = pickle.load(scaler_file)
+
+@app.route('/')
+def home():
+    return render_template('index-glob.html')
+
+@app.route('/predict', methods=['POST'])
+def predict():
+    return process_prediction(request.form, '/predict')
+
+@app.route('/predict_sup0', methods=['POST'])
+def predict_sup0():
+    return process_prediction(request.form, '/predict_sup0')
+
+def process_prediction(form_data, endpoint):
+    data = form_data.to_dict()
+    
+    if 'name' in data:
+        data['nb_caracteres_sans_espaces'] = len(data['name'].replace(" ", ""))
+    if 'artists' in data:
+        data['nb_artistes'] = data['artists'].count(',') + 1
+        data['featuring'] = int(data['nb_artistes'] > 1)
+    if 'duration_ms' in data:
+        duration_ms = float(data['duration_ms'])
+        data['duree_minute'] = float(f"{int(duration_ms // 60000)}.{int((duration_ms % 60000) // 1000):02d}")
+    if 'year' in data:
+        year = int(data['year'])
+        data['categorie_annee'] = 3 if year < 1954 else 2 if year < 2002 else 1
+    if 'tempo' in data:
+        tempo = float(data['tempo'])
+        if 40 <= tempo < 60:
+            data['categorie_tempo'] = 1
+        elif 60 <= tempo < 66:
+            data['categorie_tempo'] = 2
+        elif 66 <= tempo < 76:
+            data['categorie_tempo'] = 3
+        elif 76 <= tempo < 108:
+            data['categorie_tempo'] = 4
+        elif 108 <= tempo < 120:
+            data['categorie_tempo'] = 5
+        elif 120 <= tempo < 163:
+            data['categorie_tempo'] = 6
+        elif 163 <= tempo < 200:
+            data['categorie_tempo'] = 7
+        elif 200 <= tempo <= 208:
+            data['categorie_tempo'] = 8
+        else:
+            data['categorie_tempo'] = 9
+    
+    # Supprimer les clés non utilisées directement
+    data.pop('name', None)
+    data.pop('artists', None)
+    data.pop('duration_ms', None)
+    
+    # Convertir les valeurs en float si possible
+    for key in data:
+        try:
+            data[key] = float(data[key])
+        except ValueError:
+            pass
+    
+    expected_features = ['year', 'acousticness', 'danceability', 'energy', 'explicit',
+                         'instrumentalness', 'key', 'liveness', 'loudness', 'mode',
+                         'speechiness', 'tempo', 'valence', 'nb_caracteres_sans_espaces',
+                         'nb_artistes', 'featuring', 'duree_minute', 'categorie_annee', 'categorie_tempo']
+    
+    input_data = pd.DataFrame([[data.get(key, 0) for key in expected_features]], columns=expected_features)
+    
+    missing_cols = [col for col in expected_features if col not in input_data.columns]
+    if missing_cols:
+        return jsonify({'error': f'Missing features: {missing_cols}'}), 400
+    
+    input_data_scaled = scaler.transform(input_data)
+    predictions = rf.predict(input_data_scaled)
+    
+    return jsonify({'predictions': int(predictions[0])})
+
+if __name__ == '__main__':
+    app.run(debug=True)
diff --git a/spotify-popularity-prediction-v2/app_glob.py b/spotify-popularity-prediction-v2/app_glob.py
new file mode 100644
index 0000000..8c9e52e
--- /dev/null
+++ b/spotify-popularity-prediction-v2/app_glob.py
@@ -0,0 +1,94 @@
+from flask import Flask, request, jsonify, render_template
+import pickle
+import pandas as pd
+from sklearn.preprocessing import StandardScaler
+import numpy as np
+
+app = Flask(__name__)
+
+# Charger le modèle
+with open('random_forest_model_binaire.pkl', 'rb') as model_file:
+    rf = pickle.load(model_file)
+
+# Charger le scaler entraîné
+with open('scaler_binaire.pkl', 'rb') as scaler_file:
+    scaler = pickle.load(scaler_file)
+
+@app.route('/')
+def home():
+    return render_template('index-glob.html')
+
+@app.route('/predict', methods=['POST'])
+def predict():
+    # Récupérer les données du formulaire
+    data = request.form.to_dict()
+    
+    # Calculer automatiquement les features
+    if 'name' in data:
+        data['nb_caracteres_sans_espaces'] = len(data['name'].replace(" ", ""))
+    if 'artists' in data:
+        data['nb_artistes'] = data['artists'].count(',') + 1
+        data['featuring'] = int(data['nb_artistes'] > 1)
+    if 'duration_ms' in data:
+        duration_ms = float(data['duration_ms'])
+        data['duree_minute'] = float(f"{int(duration_ms // 60000)}.{int((duration_ms % 60000) // 1000):02d}")
+    if 'year' in data:
+        year = int(data['year'])
+        data['categorie_annee'] = 3 if year < 1954 else 2 if year < 2002 else 1
+    if 'tempo' in data:
+        tempo = float(data['tempo'])
+        if 40 <= tempo < 60:
+            data['categorie_tempo'] = 1
+        elif 60 <= tempo < 66:
+            data['categorie_tempo'] = 2
+        elif 66 <= tempo < 76:
+            data['categorie_tempo'] = 3
+        elif 76 <= tempo < 108:
+            data['categorie_tempo'] = 4
+        elif 108 <= tempo < 120:
+            data['categorie_tempo'] = 5
+        elif 120 <= tempo < 163:
+            data['categorie_tempo'] = 6
+        elif 163 <= tempo < 200:
+            data['categorie_tempo'] = 7
+        elif 200 <= tempo <= 208:
+            data['categorie_tempo'] = 8
+        else:
+            data['categorie_tempo'] = 9
+    
+    # Supprimer les clés non utilisées directement
+    data.pop('name', None)
+    data.pop('artists', None)
+    data.pop('duration_ms', None)
+    
+    # Convertir les valeurs en float si possible
+    for key in data:
+        try:
+            data[key] = float(data[key])
+        except ValueError:
+            pass  # Garder les valeurs non convertibles (ex: texte)
+    
+    # Liste des features dans le bon ordre (comme lors de l'entraînement)
+    expected_features = ['year', 'acousticness', 'danceability', 'energy', 'explicit',
+                         'instrumentalness', 'key', 'liveness', 'loudness', 'mode',
+                         'speechiness', 'tempo', 'valence', 'nb_caracteres_sans_espaces',
+                         'nb_artistes', 'featuring', 'duree_minute', 'categorie_annee', 'categorie_tempo']
+    
+    # S'assurer que les colonnes du DataFrame correspondent à celles du modèle, dans le bon ordre
+    input_data = pd.DataFrame([[data.get(key, 0) for key in expected_features]], columns=expected_features)
+    
+    # Vérifier que toutes les colonnes attendues sont présentes
+    missing_cols = [col for col in expected_features if col not in input_data.columns]
+    if missing_cols:
+        return jsonify({'error': f'Missing features: {missing_cols}'}), 400
+    
+    # Normalisation des features
+    input_data_scaled = scaler.transform(input_data)
+    
+    # Prédiction
+    predictions = rf.predict(input_data_scaled)
+    
+    return jsonify({'predictions': int(predictions[0])})
+
+if __name__ == '__main__':
+    app.run(debug=True)
diff --git a/templates/index-glob.html b/templates/index-glob.html
new file mode 100644
index 0000000..cf39f73
--- /dev/null
+++ b/templates/index-glob.html
@@ -0,0 +1,225 @@
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Prédiction de Popularité</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            display: flex;
+        }
+        /* Sidebar */
+        .sidebar {
+            width: 200px;
+            background-color: #333;
+            color: white;
+            height: 100vh;
+            padding-top: 20px;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+        }
+        .sidebar button {
+            width: 80%;
+            padding: 10px;
+            margin: 10px 0;
+            border: none;
+            background: #444;
+            color: white;
+            cursor: pointer;
+            font-size: 16px;
+            text-align: center;
+        }
+        .sidebar button:hover {
+            background: #555;
+        }
+        .content {
+            flex-grow: 1;
+            padding: 20px;
+        }
+        /* Cacher les sections par défaut */
+        .tab-content {
+            display: none;
+        }
+        .active {
+            display: block;
+        }
+        form {
+            max-width: 600px;
+            margin: auto;
+        }
+        label {
+            display: block;
+            margin-top: 10px;
+            font-weight: bold;
+        }
+        input[type="text"], input[type="number"] {
+            width: 100%;
+            padding: 8px;
+            margin-top: 5px;
+            border: 1px solid #ccc;
+            border-radius: 4px;
+        }
+        button {
+            margin-top: 20px;
+            padding: 10px 20px;
+            background-color: #4CAF50;
+            color: white;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+        }
+        button:hover {
+            background-color: #45a049;
+        }
+        #result {
+            margin-top: 20px;
+            font-size: 1.2em;
+            color: #555;
+        }
+    </style>
+</head>
+<body>
+
+    <!-- Sidebar pour naviguer entre les onglets -->
+    <div class="sidebar">
+        <button onclick="showTab('tab1')">Prédiction Standard</button>
+        <button onclick="showTab('tab2')">Prédiction (>0)</button>
+    </div>
+
+    <!-- Contenu principal -->
+    <div class="content">
+        <h1>Prédire la Popularité</h1>
+
+        <!-- Formulaire 1 : Prédiction Standard -->
+        <div id="tab1" class="tab-content active">
+            <form id="predictionForm">
+                <label for="name">Titre de la chanson :</label>
+                <input type="text" id="name" name="name" required>
+        
+                <label for="year">Année (Sous format YYYY):</label>
+                <input type="number" step="0.0001" id="year" name="year" required>
+        
+                <label for="acousticness">Acousticness (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="acousticness" name="acousticness" required>
+        
+                <label for="danceability">Danceability (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="danceability" name="danceability" required>
+        
+                <label for="energy">Energy (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="energy" name="energy" required>
+        
+                <label for="explicit">Explicit (0 ou 1 en entier):</label>
+                <input type="number" id="explicit" name="explicit" required>
+        
+                <label for="instrumentalness">Instrumentalness (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="instrumentalness" name="instrumentalness" required>
+        
+                <label for="key">Key (0 à 11 en chiffre entier):</label>
+                <input type="number" id="key" name="key" required>
+        
+                <label for="liveness">Liveness (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="liveness" name="liveness" required>
+        
+                <label for="loudness">Loudness (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="loudness" name="loudness" required>
+        
+                <label for="mode">Mode (0 ou 1 en entier):</label>
+                <input type="number" id="mode" name="mode" required>
+        
+                <label for="speechiness">Speechiness (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="speechiness" name="speechiness" required>
+        
+                <label for="tempo">Tempo (Sous format d'un nombre décimal jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="tempo" name="tempo" required>
+        
+                <label for="valence">Valence (0 à 1 jusqu'à 4 chiffres après la virgule):</label>
+                <input type="number" step="0.0001" id="valence" name="valence" required>
+
+                <button type="button" onclick="sendPrediction('/predict')">Prédire</button>
+            </form>
+        </div>
+
+        <!-- Formulaire 2 : Prédiction (>0) -->
+        <div id="tab2" class="tab-content">
+            <form id="predictionFormSup0">
+                <label for="name">Titre de la chanson :</label>
+                <input type="text" id="name" name="name" required>
+        
+                <label for="year">Année :</label>
+                <input type="number" id="year" name="year" required>
+        
+                <label for="acousticness">Acousticness :</label>
+                <input type="number" step="0.0001" id="acousticness" name="acousticness" required>
+        
+                <label for="danceability">Danceability :</label>
+                <input type="number" step="0.0001" id="danceability" name="danceability" required>
+        
+                <label for="energy">Energy :</label>
+                <input type="number" step="0.0001" id="energy" name="energy" required>
+        
+                <label for="explicit">Explicit (0 ou 1) :</label>
+                <input type="number" id="explicit" name="explicit" required>
+        
+                <label for="instrumentalness">Instrumentalness :</label>
+                <input type="number" step="0.0001" id="instrumentalness" name="instrumentalness" required>
+        
+                <label for="key">Key :</label>
+                <input type="number" id="key" name="key" required>
+        
+                <label for="liveness">Liveness :</label>
+                <input type="number" step="0.0001" id="liveness" name="liveness" required>
+        
+                <label for="loudness">Loudness :</label>
+                <input type="number" step="0.0001" id="loudness" name="loudness" required>
+        
+                <label for="mode">Mode :</label>
+                <input type="number" id="mode" name="mode" required>
+        
+                <label for="speechiness">Speechiness :</label>
+                <input type="number" step="0.0001" id="speechiness" name="speechiness" required>
+        
+                <label for="tempo">Tempo :</label>
+                <input type="number" step="0.0001" id="tempo" name="tempo" required>
+        
+                <label for="valence">Valence :</label>
+                <input type="number" step="0.0001" id="valence" name="valence" required>
+
+                <button type="button" onclick="sendPrediction('/predict_sup0', 'predictionFormSup0')">Prédire (>0)</button>
+            </form>
+        </div>
+
+        <div id="result"></div>
+    </div>
+
+    <script>
+        // Fonction pour afficher un onglet et cacher l'autre
+        function showTab(tabId) {
+            document.querySelectorAll('.tab-content').forEach(tab => {
+                tab.style.display = "none";
+            });
+            document.getElementById(tabId).style.display = "block";
+        }
+
+        // Fonction pour envoyer la requête au bon endpoint
+        async function sendPrediction(endpoint, formId = "predictionForm") {
+            const form = document.getElementById(formId);
+            const formData = new FormData(form);
+
+            const response = await fetch(endpoint, {
+                method: "POST",
+                body: formData
+            });
+
+            const result = await response.json();
+            document.getElementById("result").innerText = `Prédiction: ${result.predictions}`;
+        }
+
+        // Afficher l'onglet par défaut
+        showTab('tab1');
+    </script>
+
+</body>
+</html>
-- 
GitLab