Menu 1 4 5 ok + bugs

This commit is contained in:
2026-07-24 14:38:35 +02:00
parent 67a0147412
commit b120c2bd72
8 changed files with 509 additions and 118 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ FROM python:3.11-slim
WORKDIR /app
RUN pip install --no-cache-dir flask flask-sqlalchemy pymysql cryptography
RUN pip install --no-cache-dir flask flask-sqlalchemy pymysql cryptography requests mysql-connector-python
EXPOSE 5000
+36 -43
View File
@@ -1,4 +1,7 @@
from flask import Blueprint, render_template, request, redirect, url_for, session, flash
import time
import requests
import mysql.connector
from werkzeug.security import check_password_hash, generate_password_hash
from sqlalchemy import text
from app import db
@@ -141,6 +144,7 @@ def gestion_utilisateurs():
return render_template('gestion_utilisateurs.html', users=users)
# Route pour la gestion des actions
@main.route('/gestion-actions', methods=['GET', 'POST'])
def gestion_actions():
@@ -149,67 +153,52 @@ def gestion_actions():
if request.method == 'POST':
action_type = request.form.get('action')
# On récupère aussi l'id caché s'il s'agit d'une modification
action_id = request.form.get('id')
isin = request.form.get('isin')
isin = request.form.get('isin') or None
ticker = request.form.get('ticker')
yahoo_code = request.form.get('yahoo_code')
company_name = request.form.get('company_name')
exchange = request.form.get('exchange')
pays = request.form.get('pays')
currency = request.form.get('currency')
if action_type == 'creer':
if isin:
try:
# Vérifier si l'ISIN existe déjà
check_query = text("SELECT COUNT(*) FROM actions WHERE isin = :isin")
exists = db.session.execute(check_query, {"isin": isin}).scalar()
if exists > 0:
flash("L'action existe déjà, pas de création.", "warning")
else:
# L'id s'incrémente tout seul, on ne l'inclus pas dans l'INSERT
query = text("""
INSERT INTO actions (isin, ticker, yahoo_code, company_name, updated_at)
VALUES (:isin, :ticker, :yahoo_code, :company_name, NOW())
""")
db.session.execute(query, {
"isin": isin,
"ticker": ticker,
"yahoo_code": yahoo_code,
"company_name": company_name
})
db.session.commit()
flash("Action créée", "success")
except Exception as e:
db.session.rollback()
flash("Erreur lors de la création de l'action.", "danger")
else:
flash("L'ISIN est obligatoire.", "danger")
try:
query = text("""
INSERT INTO actions (isin, ticker, company_name, exchange, pays, currency, updated_at)
VALUES (:isin, :ticker, :company_name, :exchange, :pays, :currency, NOW())
""")
db.session.execute(query, {
"isin": isin, "ticker": ticker, "company_name": company_name,
"exchange": exchange, "pays": pays, "currency": currency
})
db.session.commit()
flash("Action créée avec succès", "success")
except Exception as e:
db.session.rollback()
flash("Erreur lors de la création de l'action.", "danger")
elif action_type == 'modifier' and action_id:
try:
# On met à jour en se basant sur l'id technique unique
query = text("""
UPDATE actions
SET isin = :isin, ticker = :ticker, yahoo_code = :yahoo_code, company_name = :company_name, updated_at = NOW()
SET isin = :isin, ticker = :ticker, company_name = :company_name, exchange = :exchange,
pays = :pays, currency = :currency, updated_at = NOW()
WHERE id = :id
""")
db.session.execute(query, {
"id": action_id,
"isin": isin,
"ticker": ticker,
"yahoo_code": yahoo_code,
"company_name": company_name
"id": action_id, "isin": isin, "ticker": ticker, "company_name": company_name,
"exchange": exchange, "pays": pays, "currency": currency
})
db.session.commit()
flash("Modification effectuée", "success")
except Exception as e:
db.session.rollback()
flash("Erreur lors de la modification (Vérifiez si l'ISIN n'est pas déjà utilisé par une autre ligne).", "danger")
flash("Erreur lors de la modification.", "danger")
return redirect(url_for('main.gestion_actions'))
# On sélectionne l'id en plus dans la requête
actions_query = text("SELECT id, isin, ticker, yahoo_code, company_name, updated_at FROM actions ORDER BY company_name ASC")
# Récupération avec la nouvelle structure
actions_query = text("SELECT id, isin, ticker, company_name, exchange, pays, currency, updated_at FROM actions ORDER BY company_name ASC")
result = db.session.execute(actions_query)
actions_list = []
@@ -218,8 +207,10 @@ def gestion_actions():
'id': row.id,
'isin': row.isin if row.isin else '',
'ticker': row.ticker if row.ticker else '',
'yahoo_code': row.yahoo_code if row.yahoo_code else '',
'company_name': row.company_name if row.company_name else 'Sans nom',
'exchange': row.exchange if row.exchange else '',
'pays': row.pays if row.pays else '',
'currency': row.currency if row.currency else '',
'updated_at': row.updated_at.strftime('%Y-%m-%d %H:%M:%S') if row.updated_at else ''
})
@@ -302,4 +293,6 @@ def gestion_societes():
'Mail': row.Mail if row.Mail else ''
})
return render_template('gestion_societes.html', societes=societes)
return render_template('gestion_societes.html', societes=societes)
+86 -73
View File
@@ -2,7 +2,7 @@
endblock %} {% block header_title %}Gestion actions{% endblock %} {% block
content %}
<div class="flex flex-col items-center justify-center py-6 relative">
<!-- Système de Toasts (Notifications flottantes centrées) -->
<!-- Système de Toasts -->
<div
id="toast-container"
class="fixed top-6 left-1/2 transform -translate-x-1/2 z-50 space-y-3 w-full max-w-md px-4 pointer-events-none"
@@ -45,11 +45,11 @@ content %}
<div class="grid grid-cols-1 md:grid-cols-12 gap-8 py-4">
<!-- COLONNE GAUCHE : Recherche et Liste -->
<div
class="md:col-span-4 bg-gray-950 border border-gray-800 rounded-xl p-4 flex flex-col h-[560px]"
class="md:col-span-4 bg-gray-950 border border-gray-800 rounded-xl p-4 flex flex-col h-[580px]"
>
<div class="mb-3">
<label class="block text-xs font-medium text-gray-400 mb-1"
>Rechercher (ISIN, Ticker, Nom)</label
>Rechercher (ISIN, Ticker, Nom, Pays)</label
>
<input
type="text"
@@ -66,32 +66,32 @@ content %}
Actions enregistrées
</h3>
<!-- Liste avec délégation d'événements (Nom et Ticker uniquement) -->
<div
class="overflow-y-auto flex-1 space-y-1 pr-1"
id="actions-list-container"
>
{% for a in actions %}
<div
onclick="selectionnerAction('{{ a.id }}', '{{ a.isin }}', '{{ a.ticker }}', '{{ a.yahoo_code }}', '{{ a.company_name | e }}', '{{ a.updated_at }}')"
class="action-item cursor-pointer px-3 py-2.5 rounded-lg {% if loop.first %}bg-blue-600 text-white{% else %}hover:bg-gray-800 text-gray-300{% endif %} font-medium text-sm transition flex flex-col justify-between"
class="action-item cursor-pointer px-3 py-2.5 rounded-lg {% if loop.first %}bg-blue-600 text-white{% else %}hover:bg-gray-800 text-gray-300{% endif %} font-medium text-sm transition flex justify-between items-center"
data-id="{{ a.id }}"
data-isin="{{ a.isin }}"
data-ticker="{{ a.ticker | lower }}"
data-company="{{ a.company_name | lower }}"
data-search="{{ a.isin | lower }} {{ a.ticker | lower }} {{ a.company_name | lower }}"
data-ticker="{{ a.ticker }}"
data-company="{{ a.company_name }}"
data-exchange="{{ a.exchange }}"
data-pays="{{ a.pays }}"
data-currency="{{ a.currency }}"
data-updated="{{ a.updated_at }}"
data-search="{{ a.isin | lower }} {{ a.ticker | lower }} {{ a.company_name | lower }} {{ a.exchange | lower }} {{ a.pays | lower }}"
>
<div class="flex justify-between items-center w-full">
<span class="truncate font-semibold pr-2"
>{{ a.company_name }}</span
>
<span
class="text-xs {% if loop.first %}bg-blue-800{% else %}bg-gray-800 text-gray-400{% endif %} px-2 py-0.5 rounded whitespace-nowrap"
>{{ a.ticker or 'N/A' }}</span
>
</div>
<span class="text-xs opacity-70 font-mono mt-0.5"
>{{ a.isin }}</span
<span class="truncate font-semibold pr-2"
>{{ a.company_name }}</span
>
<span
class="ticker-badge text-xs {% if loop.first %}bg-blue-800 text-white{% else %}bg-gray-800 text-gray-400{% endif %} px-2 py-0.5 rounded whitespace-nowrap font-mono"
>
{{ a.ticker or 'N/A' }}
</span>
</div>
{% endfor %}
</div>
@@ -107,7 +107,6 @@ content %}
action=""
class="space-y-4 flex-1 flex flex-col justify-between"
>
<!-- ID technique caché pour les modifications -->
<input type="hidden" id="field-id" name="id" />
<div class="space-y-4">
@@ -118,7 +117,6 @@ content %}
</h3>
<div class="grid grid-cols-2 gap-4">
<!-- ISIN (Modifiable) -->
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>ISIN</label
@@ -127,12 +125,9 @@ content %}
type="text"
id="field-isin"
name="isin"
required
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
/>
</div>
<!-- Ticker -->
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>Ticker</label
@@ -147,38 +142,56 @@ content %}
</div>
<div class="grid grid-cols-2 gap-4">
<!-- Yahoo Code -->
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>Yahoo Code</label
>Nom de la société (company_name)</label
>
<input
type="text"
id="field-yahoo_code"
name="yahoo_code"
id="field-company"
name="company_name"
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
/>
</div>
<!-- Company Name -->
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>Company Name</label
>Exchange</label
>
<input
type="text"
id="field-company_name"
name="company_name"
id="field-exchange"
name="exchange"
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
/>
</div>
</div>
<div>
<!-- Updated At -->
<div class="grid grid-cols-3 gap-4">
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>Dernière mise à jour (Lecture seule)</label
>Pays</label
>
<input
type="text"
id="field-pays"
name="pays"
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
/>
</div>
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>Devise (currency)</label
>
<input
type="text"
id="field-currency"
name="currency"
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
/>
</div>
<div>
<label class="block text-xs font-medium text-gray-400 mb-1"
>Mise à jour</label
>
<input
type="text"
@@ -191,7 +204,6 @@ content %}
</div>
</div>
<!-- Bas : Boutons -->
<div
class="flex items-center justify-end space-x-3 pt-6 border-t border-gray-800 mt-6"
>
@@ -250,68 +262,69 @@ content %}
}, 4000);
});
const container = document.getElementById("actions-list-container");
if (container) {
container.addEventListener("click", (e) => {
const item = e.target.closest(".action-item");
if (item) {
selectionnerActionItem(item);
}
});
}
const premiereAction = document.querySelector(".action-item");
if (premiereAction) {
premiereAction.click();
selectionnerActionItem(premiereAction);
}
});
function selectionnerAction(
id,
isin,
ticker,
yahoo_code,
company_name,
updated_at,
) {
document.getElementById("field-id").value = id;
document.getElementById("field-isin").value = isin;
document.getElementById("field-ticker").value = ticker;
document.getElementById("field-yahoo_code").value = yahoo_code;
document.getElementById("field-company_name").value = company_name;
document.getElementById("field-updated_at").value = updated_at;
function selectionnerActionItem(item) {
document.getElementById("field-id").value = item.dataset.id;
document.getElementById("field-isin").value = item.dataset.isin;
document.getElementById("field-ticker").value = item.dataset.ticker;
document.getElementById("field-company").value = item.dataset.company;
document.getElementById("field-exchange").value = item.dataset.exchange;
document.getElementById("field-pays").value = item.dataset.pays;
document.getElementById("field-currency").value = item.dataset.currency;
document.getElementById("field-updated_at").value = item.dataset.updated;
document.querySelectorAll(".action-item").forEach((el) => {
el.classList.remove("bg-blue-600", "text-white");
el.classList.add("hover:bg-gray-800", "text-gray-300");
const badge = el.querySelector("span:nth-child(1) span:last-child");
const badge = el.querySelector(".ticker-badge");
if (badge) {
badge.classList.remove("bg-blue-800");
badge.classList.remove("bg-blue-800", "text-white");
badge.classList.add("bg-gray-800", "text-gray-400");
}
});
const selectedEl = document.querySelector(`[data-id="${id}"]`);
if (selectedEl) {
selectedEl.classList.remove("hover:bg-gray-800", "text-gray-300");
selectedEl.classList.add("bg-blue-600", "text-white");
const badge = selectedEl.querySelector(
"span:nth-child(1) span:last-child",
);
if (badge) {
badge.classList.remove("bg-gray-800", "text-gray-400");
badge.classList.add("bg-blue-800");
}
selectedEl.scrollIntoView({ block: "nearest" });
item.classList.remove("hover:bg-gray-800", "text-gray-300");
item.classList.add("bg-blue-600", "text-white");
const badge = item.querySelector(".ticker-badge");
if (badge) {
badge.classList.remove("bg-gray-800", "text-gray-400");
badge.classList.add("bg-blue-800", "text-white");
}
item.scrollIntoView({ block: "nearest" });
}
function preparerCreation() {
document.getElementById("field-id").value = "";
document.getElementById("field-isin").value = "";
document.getElementById("field-ticker").value = "";
document.getElementById("field-yahoo_code").value = "";
document.getElementById("field-company_name").value = "";
document.getElementById("field-company").value = "";
document.getElementById("field-exchange").value = "";
document.getElementById("field-pays").value = "";
document.getElementById("field-currency").value = "";
document.getElementById("field-updated_at").value =
"Automatique à la création";
// Retirer la sélection visuelle active de la liste
document.querySelectorAll(".action-item").forEach((el) => {
el.classList.remove("bg-blue-600", "text-white");
el.classList.add("hover:bg-gray-800", "text-gray-300");
const badge = el.querySelector("span:nth-child(1) span:last-child");
const badge = el.querySelector(".ticker-badge");
if (badge) {
badge.classList.remove("bg-blue-800");
badge.classList.remove("bg-blue-800", "text-white");
badge.classList.add("bg-gray-800", "text-gray-400");
}
});
@@ -338,7 +351,7 @@ content %}
});
if (premierCorrespondant && terme !== "") {
premierCorrespondant.click();
selectionnerActionItem(premierCorrespondant);
}
}
@@ -347,7 +360,7 @@ content %}
filtrerActions();
const premiereAction = document.querySelector(".action-item");
if (premiereAction) {
premiereAction.click();
selectionnerActionItem(premiereAction);
}
}
</script>
+1 -1
View File
@@ -12,7 +12,6 @@ header_title %}Tableau de bord{% endblock %} {% block content %}
</p>
<!-- Grille principale : 2 colonnes pour les boutons -->
<!-- Grille des 8 gros boutons -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 mb-8">
<!-- btn 1 -->
<a
@@ -86,4 +85,5 @@ header_title %}Tableau de bord{% endblock %} {% block content %}
</div>
</div>
</div>
{% endblock %}
+112
View File
@@ -0,0 +1,112 @@
import time
import requests
import mysql.connector
def importer_actions():
API_KEY = "6a6235c8a742b2.89238466"
MYSQL = {
"host": "db",
"port": 3306,
"user": "root",
"password": "sysadm-1963",
"database": "bourse"
}
EXCHANGES = [
"PA", "US", "BR", "KLSE", "LS", "XETRA", "LU", "OL", "ST", "CO", "IR", "KO", "AU", "SHE", "SHG", "AT",
"F", "STU", "MU", "HA", "DU", "HM", "VI", "SW", "TO", "AU", "HK", "TWO", "MC"
]
print("Connexion à la base de données MySQL...")
try:
db_conn = mysql.connector.connect(**MYSQL)
cursor = db_conn.cursor()
print("Connecté avec succès à la base MySQL : [bourse]")
except Exception as e:
print(f"Erreur de connexion MySQL : {e}")
return
print("Vidage de la table les_actions...")
try:
cursor.execute("TRUNCATE TABLE les_actions;")
db_conn.commit()
except Exception as e:
print(f"Erreur lors du TRUNCATE : {e}")
sql = """
INSERT INTO les_actions (isin, ticker, name, exchange, pays, devise)
VALUES (%s, %s, %s, %s, %s, %s)
"""
total_global = 0
try:
for exchange in EXCHANGES:
url = f"https://eodhd.com/api/exchange-symbol-list/{exchange}?api_token={API_KEY}&fmt=json"
print(f"\n--- Traitement de la bourse : {exchange} ---")
req_timeout = 300 if exchange == "US" else 60
try:
r = requests.get(url, timeout=req_timeout)
if r.status_code != 200:
print(f"[{exchange}] Erreur HTTP {r.status_code}")
continue
data = r.json()
except Exception as err:
print(f"[{exchange}] Erreur requête ou timeout : {err}")
continue
if not isinstance(data, list):
print(f"[{exchange}] Le format reçu n'est pas une liste.")
continue
print(f"[{exchange}] {len(data)} éléments reçus. Insertion en cours...")
lignes = []
for item in data:
isin = item.get("ISIN") or item.get("isin")
ticker = item.get("Code") or item.get("code")
name = item.get("Name") or item.get("name")
exch = item.get("Exchange") or item.get("exchange") or exchange
pays = item.get("Country") or item.get("country")
devise = item.get("Currency") or item.get("currency")
if not ticker:
continue
lignes.append((
str(isin)[:12] if isin else None,
str(ticker)[:20] if ticker else None,
str(name)[:150] if name else None,
str(exch)[:50] if exch else None,
str(pays)[:50] if pays else None,
str(devise)[:10] if devise else None
))
if lignes:
# Utilisation d'executemany par paquets pour un maximum de performance
batch_size = 5000
for i in range(0, len(lignes), batch_size):
batch = lignes[i:i + batch_size]
cursor.executemany(sql, batch)
db_conn.commit()
total_global += len(lignes)
print(f"-> Succès : {len(lignes)} actions insérées pour {exchange}.")
time.sleep(1)
# Vérification finale du nombre de lignes dans la table
cursor.execute("SELECT COUNT(*) FROM les_actions;")
count = cursor.fetchone()[0]
print(f"\nTerminé ! Total global traité : {total_global} lignes. Comptage réel en base : {count} lignes.")
except Exception as e:
print(f"Erreur générale : {e}")
finally:
cursor.close()
db_conn.close()
print("Connexion fermée.")
if __name__ == "__main__":
importer_actions()
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
import csv
import mysql.connector
# ==========================================
# CONFIGURATION
# ==========================================
CSV_FILE = "/app/isin.csv"
# Pays associé au fichier CSV
PAYS = "France"
MYSQL = {
"host": "db",
"port": 3306,
"user": "root",
"password": "sysadm-1963",
"database": "bourse"
}
# ==========================================
# CONNEXION MYSQL
# ==========================================
try:
db = mysql.connector.connect(**MYSQL)
print("Connexion MySQL OK")
except Exception as e:
print("Erreur MySQL :", e)
exit(1)
cursor = db.cursor(dictionary=True)
# ==========================================
# CHARGEMENT DU CSV
# ==========================================
print("Chargement du CSV...")
csv_data = {}
with open(CSV_FILE, newline="", encoding="utf-8-sig") as f:
reader = csv.reader(f, delimiter=";")
for row in reader:
if len(row) < 3:
continue
isin = row[0].strip().upper()
ticker = row[2].strip().upper()
if ticker:
csv_data[(ticker, PAYS.upper())] = isin
print(f"{len(csv_data)} ISIN chargés.")
# ==========================================
# LECTURE DE LA TABLE
# ==========================================
cursor.execute("""
SELECT
id,
ticker,
pays,
isin
FROM les_actions
""")
lignes = cursor.fetchall()
print(f"{len(lignes)} actions lues.")
# ==========================================
# MISE A JOUR
# ==========================================
sql_update = """
UPDATE les_actions
SET isin = %s
WHERE id = %s
"""
update_data = []
for ligne in lignes:
ticker = (ligne["ticker"] or "").strip().upper()
pays = (ligne["pays"] or "").strip().upper()
cle = (ticker, pays)
if cle in csv_data:
update_data.append((
csv_data[cle],
ligne["id"]
))
print(f"{len(update_data)} lignes à mettre à jour.")
if update_data:
cursor.executemany(sql_update, update_data)
db.commit()
print("===================================")
print(f"{len(update_data)} ISIN mis à jour.")
print("===================================")
cursor.close()
db.close()
+141
View File
@@ -0,0 +1,141 @@
ISIN;nom;ticker
BE0003470755g;Solvay;SOLB
BE0003853703g;Montea C.V.A.;MONT
BE0974338700g;Titan Cement;TITC
FI0009000681p;Nokia Corporation;1NOKIA
FR0000031577p;Virbac;VIRP
FR0000031775p;Vicat;VCT
FR0000033219p;Altarea;ALTA
FR0000035081p;Icade;ICAD
FR0000039091p;Robertet;RBT
FR0000039299p;Bollore;BOL
FR0000044448p;Nexans;NEX
FR0000044471p;Ramsay Generale De Sante;GDS
FR0000045072p;Credit Agricole;ACA
FR0000045601p;Robertet Ci;CBE
FR0000045619p;Robertet CDV 87;CBR
FR0000050353p;Lisi;FII
FR0000050809p;Sopra Steria Group;SOP
FR0000051070p;Maurel Et Prom;MAU
FR0000051807p;Teleperformance;TEP
FR0000052292p;Hermes;RMS
FR0000053225p;Metropole Tv;MMT
FR0000054470p;Ubisoft Entertainment;UBI
FR0000054900p;TF1;TFI
FR0000060303p;Covivio Hotels;COVH
FR0000062234p;Compagnie Odet;ODET
FR0000064271p;STEF;STF
FR0000064578p;Covivio;COV
FR0000064784p;Peugeot Invest;PEUG
FR0000065484p;Lectra;LSS
FR0000071946p;Alten;ATE
FR0000073272p;Safran;SAF
FR0000073298p;Ipsos;IPS
FR0000076952p;Artois Nom.;ARTO
FR0000077919p;JC Decaux SE;DEC
FR0000120073p;Air Liquide;AI
FR0000120172p;Carrefour;CA
FR0000120271p;TotalEnergies;TTE
FR0000120321p;L'oreal;OR
FR0000120404p;Accor Hotels;AC
FR0000120503p;Bouygues;EN
FR0000120578p;Sanofi;SAN
FR0000120628p;Axa;CS
FR0000120644p;Danone;BN
FR0000120669p;North Atlantic Energies;NAE
FR0000120693p;Pernod Ricard;RI
FR0000120859p;Imerys;NK
FR0000120966p;Bic;BB
FR0000121014p;Lvmh;MC
FR0000121121p;Eurazeo;RF
FR0000121147p;Forvia;FRVIA
FR0000121204p;Wendel Invest.;MF
FR0000121220p;Sodexo;SW
FR0000121329p;Thales;HO
FR0000121485p;Kering;KER
FR0000121667p;EssilorLuxottica;EL
FR0000121709p;Seb;SK
FR0000121964p;Klepierre;LI
FR0000121972p;Schneider Electric;SU
FR0000124141p;Veolia;VIE
FR0000124570p;OPmobility;OPM
FR0000125007p;Saint Gobain;SGO
FR0000125338p;CapGemini;CAP
FR0000125486p;Vinci;DG
FR0000127771p;Vivendi;VIV
FR0000130213p;Lagardere;MMB
FR0000130395p;Remy Cointreau;RCO
FR0000130403p;Christian Dior;CDI
FR0000130452p;Eiffage;FGR
FR0000130577p;Publicis Groupe;PUB
FR0000130809p;Societe Generale;GLE
FR0000131104p;Bnp Paribas;BNP
FR0000131757p;Eramet;ERA
FR0000131906p;Renault;RNO
FR0000133308p;Orange;ORA
FR0004024222p;Interparfums;ITP
FR0004050250p;Neurones;NRO
FR0004125920p;Amundi;AMUN
FR0005691656p;Trigano;TRI
FR0006174348p;Bureau Veritas;BVI
FR0010040865p;Gecina Nom.;GFC
FR0010208488p;Engie;ENGI
FR0010220475p;Alstom;ALO
FR0010221234p;Eutelsat;ETL
FR0010241638p;Mercialys;MERY
FR0010259150p;Ipsen;IPN
FR0010282822p;Vusion;VU
FR0010307819p;Legrand SA;LR
FR0010313833p;Arkema;AKE
FR0010340141p;Adp;ADP
FR0010411983p;Scor;SCR
FR0010451203p;Rexel;RXL
FR0010481960p;Argan;ARG
FR0010533075p;Getlink;GET
FR0010667147p;Coface;COFA
FR0010828137p;Carmila;CARM
FR0010908533p;Edenred;EDEN
FR0010929125p;ID Logistics;IDL
FR0011726835p;GTT;GTT
FR0011995588p;Voltalia;VLTSA
FR0012435121p;Elis;ELIS
FR0012757854p;Spie;SPIE
FR0013154002p;Sartorius Stedim Biotech;DIM
FR0013176526p;Valeo;FR
FR0013227113p;Soitec Silicon;SOI
FR0013230612p;Tikehau Capital;TKO
FR0013258662p;Ayvens;AYV
FR0013269123p;Rubis;RUI
FR0013280286p;Biomerieux;BIM
FR0013326246p;Unibail Rodamco Westfield;URW
FR0013357621p;Wavestone;WAVE
FR0013447729p;Verallia;VRLA
FR0013451333p;FDJ United;FDJU
FR0013506730p;Vallourec;VK
FR0014000MR3p;Eurofins Scient.;ERF
FR0014003TT8p;Dassault Systemes;DSY
FR0014004L86p;Dassault Aviation;AM
FR0014005AL0p;Antin infra Partn;ANTIN
FR0014005HJ9p;OVHCLOUD;OVH
FR001400AJ45p;Michelin;ML
FR001400J770p;Air France - KLM;AF
FR001400PFU4p;Planisware;PLNW
FR001400Q9V2p;Exosens;EXENS
FR001400SF56p;Ldc;LOUP
FR001400SU99p;Moncey Fin. Nom.;FMONC
FR001400SUB7p;Cambodge Nom.;CBDG
FR001400X2S4p;Atos;ATO
FR00140182K6p;Worldline;WLN
GB00B5ZN1N88p;Segro PLC;SGRO
LU0088087324p;SES Sa;SESG
LU0569974404n;Aperam;APAM
LU1598757687n;Arcelor Mittal;MT
MA0000011488p;Maroc Telecom;IAM
MC0000031187p;Bains Mer Monaco;BAIN
NL0000226223p;Stmicroelectronics;STMPA
NL0000235190p;Airbus;AIR
NL0006294274p;Euronext;ENX
NL0014559478p;Technip Energies;TE
NL00150001Q9p;Stellantis;STLAP
NL0015001W49p;Pluxee;PLX
US2220702037p;Coty Inc.;COTY
1 ISIN nom ticker
2 BE0003470755g Solvay SOLB
3 BE0003853703g Montea C.V.A. MONT
4 BE0974338700g Titan Cement TITC
5 FI0009000681p Nokia Corporation 1NOKIA
6 FR0000031577p Virbac VIRP
7 FR0000031775p Vicat VCT
8 FR0000033219p Altarea ALTA
9 FR0000035081p Icade ICAD
10 FR0000039091p Robertet RBT
11 FR0000039299p Bollore BOL
12 FR0000044448p Nexans NEX
13 FR0000044471p Ramsay Generale De Sante GDS
14 FR0000045072p Credit Agricole ACA
15 FR0000045601p Robertet Ci CBE
16 FR0000045619p Robertet CDV 87 CBR
17 FR0000050353p Lisi FII
18 FR0000050809p Sopra Steria Group SOP
19 FR0000051070p Maurel Et Prom MAU
20 FR0000051807p Teleperformance TEP
21 FR0000052292p Hermes RMS
22 FR0000053225p Metropole Tv MMT
23 FR0000054470p Ubisoft Entertainment UBI
24 FR0000054900p TF1 TFI
25 FR0000060303p Covivio Hotels COVH
26 FR0000062234p Compagnie Odet ODET
27 FR0000064271p STEF STF
28 FR0000064578p Covivio COV
29 FR0000064784p Peugeot Invest PEUG
30 FR0000065484p Lectra LSS
31 FR0000071946p Alten ATE
32 FR0000073272p Safran SAF
33 FR0000073298p Ipsos IPS
34 FR0000076952p Artois Nom. ARTO
35 FR0000077919p JC Decaux SE DEC
36 FR0000120073p Air Liquide AI
37 FR0000120172p Carrefour CA
38 FR0000120271p TotalEnergies TTE
39 FR0000120321p L'oreal OR
40 FR0000120404p Accor Hotels AC
41 FR0000120503p Bouygues EN
42 FR0000120578p Sanofi SAN
43 FR0000120628p Axa CS
44 FR0000120644p Danone BN
45 FR0000120669p North Atlantic Energies NAE
46 FR0000120693p Pernod Ricard RI
47 FR0000120859p Imerys NK
48 FR0000120966p Bic BB
49 FR0000121014p Lvmh MC
50 FR0000121121p Eurazeo RF
51 FR0000121147p Forvia FRVIA
52 FR0000121204p Wendel Invest. MF
53 FR0000121220p Sodexo SW
54 FR0000121329p Thales HO
55 FR0000121485p Kering KER
56 FR0000121667p EssilorLuxottica EL
57 FR0000121709p Seb SK
58 FR0000121964p Klepierre LI
59 FR0000121972p Schneider Electric SU
60 FR0000124141p Veolia VIE
61 FR0000124570p OPmobility OPM
62 FR0000125007p Saint Gobain SGO
63 FR0000125338p CapGemini CAP
64 FR0000125486p Vinci DG
65 FR0000127771p Vivendi VIV
66 FR0000130213p Lagardere MMB
67 FR0000130395p Remy Cointreau RCO
68 FR0000130403p Christian Dior CDI
69 FR0000130452p Eiffage FGR
70 FR0000130577p Publicis Groupe PUB
71 FR0000130809p Societe Generale GLE
72 FR0000131104p Bnp Paribas BNP
73 FR0000131757p Eramet ERA
74 FR0000131906p Renault RNO
75 FR0000133308p Orange ORA
76 FR0004024222p Interparfums ITP
77 FR0004050250p Neurones NRO
78 FR0004125920p Amundi AMUN
79 FR0005691656p Trigano TRI
80 FR0006174348p Bureau Veritas BVI
81 FR0010040865p Gecina Nom. GFC
82 FR0010208488p Engie ENGI
83 FR0010220475p Alstom ALO
84 FR0010221234p Eutelsat ETL
85 FR0010241638p Mercialys MERY
86 FR0010259150p Ipsen IPN
87 FR0010282822p Vusion VU
88 FR0010307819p Legrand SA LR
89 FR0010313833p Arkema AKE
90 FR0010340141p Adp ADP
91 FR0010411983p Scor SCR
92 FR0010451203p Rexel RXL
93 FR0010481960p Argan ARG
94 FR0010533075p Getlink GET
95 FR0010667147p Coface COFA
96 FR0010828137p Carmila CARM
97 FR0010908533p Edenred EDEN
98 FR0010929125p ID Logistics IDL
99 FR0011726835p GTT GTT
100 FR0011995588p Voltalia VLTSA
101 FR0012435121p Elis ELIS
102 FR0012757854p Spie SPIE
103 FR0013154002p Sartorius Stedim Biotech DIM
104 FR0013176526p Valeo FR
105 FR0013227113p Soitec Silicon SOI
106 FR0013230612p Tikehau Capital TKO
107 FR0013258662p Ayvens AYV
108 FR0013269123p Rubis RUI
109 FR0013280286p Biomerieux BIM
110 FR0013326246p Unibail Rodamco Westfield URW
111 FR0013357621p Wavestone WAVE
112 FR0013447729p Verallia VRLA
113 FR0013451333p FDJ United FDJU
114 FR0013506730p Vallourec VK
115 FR0014000MR3p Eurofins Scient. ERF
116 FR0014003TT8p Dassault Systemes DSY
117 FR0014004L86p Dassault Aviation AM
118 FR0014005AL0p Antin infra Partn ANTIN
119 FR0014005HJ9p OVHCLOUD OVH
120 FR001400AJ45p Michelin ML
121 FR001400J770p Air France - KLM AF
122 FR001400PFU4p Planisware PLNW
123 FR001400Q9V2p Exosens EXENS
124 FR001400SF56p Ldc LOUP
125 FR001400SU99p Moncey Fin. Nom. FMONC
126 FR001400SUB7p Cambodge Nom. CBDG
127 FR001400X2S4p Atos ATO
128 FR00140182K6p Worldline WLN
129 GB00B5ZN1N88p Segro PLC SGRO
130 LU0088087324p SES Sa SESG
131 LU0569974404n Aperam APAM
132 LU1598757687n Arcelor Mittal MT
133 MA0000011488p Maroc Telecom IAM
134 MC0000031187p Bains Mer Monaco BAIN
135 NL0000226223p Stmicroelectronics STMPA
136 NL0000235190p Airbus AIR
137 NL0006294274p Euronext ENX
138 NL0014559478p Technip Energies TE
139 NL00150001Q9p Stellantis STLAP
140 NL0015001W49p Pluxee PLX
141 US2220702037p Coty Inc. COTY
+17
View File
@@ -0,0 +1,17 @@
blinker==1.9.0
certifi==2026.7.22
charset-normalizer==3.4.9
click==8.4.2
Flask==3.1.3
Flask-SQLAlchemy==3.1.1
greenlet==3.5.4
idna==3.18
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.3
mysql-connector-python==9.7.0
requests==2.34.2
SQLAlchemy==2.0.51
typing_extensions==4.16.0
urllib3==2.7.0
Werkzeug==3.1.8