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)