Spaces:
Sleeping
Sleeping
File size: 9,058 Bytes
950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b 950504b 7c67a2b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
import streamlit as st
import requests
import pandas as pd
import plotly.express as px
from bs4 import BeautifulSoup
import random
import time
# Configuration de l'interface Streamlit
st.set_page_config(page_title="Wine prices", layout="wide")
# 🟢 Initialisation du session state
if "selected_wines" not in st.session_state:
st.session_state.selected_wines = []
dark_mode_css = """
<style>
body {
background-color: #0e1117;
color: white;
}
.stApp {
background-color: #0e1117;
}
.stSidebar {
background-color: #161a25;
}
.stTextInput, .stSelectbox, .stButton, .stDataFrame, .stTable {
background-color: #21262d;
color: white;
}
.stPlotlyChart {
background-color: transparent;
}
</style>
"""
st.markdown(dark_mode_css, unsafe_allow_html=True)
# Charger le fichier Excel contenant la correspondance LWIN7 -> Nom du vin
@st.cache_data
def load_wine_data():
return pd.read_excel("LWINdatabase.xlsx") # Assure-toi d'avoir ce fichier avec LWIN7, DISPLAY_NAME
df_wines = load_wine_data()
# Sélection du vin par l'utilisateur
def select_wine():
return st.sidebar.selectbox("Sélectionnez un vin :", df_wines["DISPLAY_NAME"].unique())
# Génération du LWIN11
def generate_lwin11(lwin7, vintage):
return str(lwin7) + str(vintage)
# Récupération du taux de change GBP/EUR depuis Boursorama
def get_exchange_rate():
url = "https://www.boursorama.com/bourse/devises/taux-de-change-livresterling-euro-GBP-EUR/"
response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"})
if response.status_code == 200:
soup = BeautifulSoup(response.text, "html.parser")
rate_tag = soup.find("span", class_="c-instrument c-instrument--last")
if rate_tag:
rate = rate_tag.text.replace(",", ".") # Convertir en format float
return float(rate)
st.error("Impossible de récupérer le taux de change GBP/EUR.")
return 1.2 # Valeur par défaut
cookies_list = [
{'_vinous_session' : "BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWQ4MDUyOTllZGNkZDE5YTUyZWE3OTU0NzZjMTRjMDk1BjsAVEkiGXdhcmRlbi51c2VyLnVzZXIua2V5BjsAVFsHWwZpAq5mSSIiJDJhJDEwJFdTaU9uamkydDBNN3RqMG01YXRJVS4GOwBUSSIdd2FyZGVuLnVzZXIudXNlci5zZXNzaW9uBjsAVHsGSSIUbGFzdF9yZXF1ZXN0X2F0BjsAVGwrB5mlAGg%3D--7777dea1a4299409509b5af6636777dd9f84f0b7"}, # via Max
]
def get_vinous_data(lwin11, display_name):
headers = {"User-Agent": "Mozilla/5.0", "Accept": "application/json"}
current_cookie = random.choice(cookies_list)
url_prices = f"https://vinous.com/api/v2/wine/{lwin11}/livex_price_history"
response_prices = requests.get(url_prices, headers=headers, cookies=current_cookie)
time.sleep(0.5)
df_prices = pd.DataFrame()
if response_prices.status_code == 200:
try:
data_prices = response_prices.json().get("livex_price_history", [])
if not data_prices:
st.warning(f"🚨 Malheureusement, le vin **{display_name} ({lwin11})** n'est pas disponible.")
return pd.DataFrame()
df_prices = pd.DataFrame(data_prices)
except Exception as e:
st.error(f"⚠️ Erreur lors du traitement des données pour **{display_name} ({lwin11})**.")
return pd.DataFrame()
else:
st.warning(f"🚨 Impossible de récupérer les prix pour **{display_name} ({lwin11})**. Code erreur: {response_prices.status_code}")
return pd.DataFrame()
return df_prices
# Convertir les prix en EUR et diviser par 12 pour obtenir le prix par bouteille
def convert_price_per_bottle(df_prices, exchange_rate):
df_prices["date_string"] = pd.to_datetime(df_prices["date_string"], format="%Y-%m-%d")
df_prices["value"] = df_prices["value"].astype(float)
df_prices["price_per_bottle"] = (df_prices["value"] * exchange_rate) / 12
return df_prices
def check_wine_availability(lwin11):
"""Teste la disponibilité d'un vin/millésime via un appel API."""
headers = {"User-Agent": "Mozilla/5.0", "Accept": "application/json"}
current_cookie = random.choice(cookies_list)
url = f"https://vinous.com/api/v2/wine/{lwin11}/livex_price_history"
response = requests.get(url, headers=headers, cookies=current_cookie)
if response.status_code == 200:
try:
data = response.json()
if "error" in data:
return False # Vin non disponible
return True # Vin disponible
except:
return False # Erreur JSON
return False # Code erreur autre que 200
# Sélection du vin par l'utilisateur
st.sidebar.title("Filtres")
# 🟢 Sélection de la région
selected_region = st.sidebar.selectbox("Sélectionnez une région :", df_wines["REGION"].dropna().unique())
# 🟢 Filtrer les vins selon la région sélectionnée
filtered_wines = df_wines[df_wines["REGION"] == selected_region]
# 🟢 Sélection **multiple** de vins parmi les vins de la région
selected_wines = st.sidebar.multiselect("Sélectionnez un ou plusieurs vins :", filtered_wines["DISPLAY_NAME"].unique())
# 🟢 Filtrer les millésimes selon les vins sélectionnés
if selected_wines:
filtered_vintages = sorted(range(1925, 2026), reverse=True)
selected_vintages = st.sidebar.multiselect("Sélectionnez un ou plusieurs millésimes :", filtered_vintages)
else:
selected_vintages = []
# 🟢 Générer les LWIN11 pour chaque combinaison vin-millésime
wine_selections = []
for selected_wine in selected_wines:
wine_data = filtered_wines[filtered_wines["DISPLAY_NAME"] == selected_wine].iloc[0]
lwin7 = wine_data["LWIN"]
for vintage in selected_vintages:
lwin11 = f"{lwin7}{vintage}"
wine_selections.append((selected_wine, vintage, lwin11))
# 🟢 Vérifier la disponibilité des vins/millésimes avant ajout au graphique
# 🟢 Vérifier la disponibilité des vins/millésimes avant ajout au graphique
available_wines = []
for wine_name, vintage, lwin11 in wine_selections:
if check_wine_availability(lwin11): # Vérifie si le vin/millésime est dispo via API
available_wines.append((wine_name, vintage, lwin11))
else:
st.sidebar.warning(f"⚠️ {wine_name} ({vintage}) n'est pas disponible.")
# 🟢 Bouton pour ajouter au graphique les vins/millésimes disponibles (évite les doublons)
if available_wines and st.sidebar.button("Ajouter au graphique"):
for wine in available_wines:
if wine not in st.session_state.selected_wines: # Ajout uniquement si non présent
st.session_state.selected_wines.append(wine)
# 🟢 Bouton pour réinitialiser le graphique
if st.sidebar.button("Reset graphique"):
st.session_state.selected_wines = []
# Récupération et affichage des prix
if st.session_state.selected_wines:
fig = px.line(
title="Évolution des Prix",
color_discrete_sequence=px.colors.qualitative.Safe
)
# Récupérer le taux de change une seule fois
exchange_rate = get_exchange_rate()
all_dates = [] # Stocke toutes les dates pour fixer l'axe X
for wine_name, vintage, lwin11 in st.session_state.selected_wines:
df_prices = get_vinous_data(lwin11, wine_name)
if not df_prices.empty:
df_prices = convert_price_per_bottle(df_prices, exchange_rate)
all_dates.extend(df_prices["date_string"]) # Ajoute toutes les dates au pool global
df_prices["tooltip"] = df_prices.apply(
lambda row: f"<b>Prix:</b> {row['price_per_bottle']:.2f} EUR<br><b>Date:</b> {row['date_string']}",
axis=1
)
last_price = df_prices["price_per_bottle"].iloc[-1]
fig.add_scatter(
x=df_prices["date_string"],
y=df_prices["price_per_bottle"],
mode="lines+markers",
name=f"{wine_name} ({vintage}) - {last_price:.2f} EUR",
text=df_prices["tooltip"],
hoverinfo="text"
)
# Récupérer la plage complète des dates pour fixer l'axe X
if all_dates:
min_date = min(all_dates)
max_date = max(all_dates)
fig.update_layout(
height=600,
legend=dict(
orientation="h",
yanchor="bottom", y=-0.3,
xanchor="center", x=0.5
),
xaxis=dict(
tickangle=45,
tickmode="array",
tickvals=pd.date_range(
start=min_date,
end=max_date,
freq='Q'
).strftime('%Y-%m').tolist(),
tickformat="%Y-%m",
range=[min_date, max_date] # Fixe la plage de l'axe X
),
margin=dict(l=20, r=20, t=50, b=100),
)
st.plotly_chart(fig, use_container_width=True) |