Menu 1 4 5 ok + bugs
This commit is contained in:
+1
-1
@@ -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
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
@@ -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
|
||||
|
@@ -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
|
||||
Reference in New Issue
Block a user