diff --git a/app/routes.py b/app/routes.py index 49e440e..3de8bf9 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,7 +1,9 @@ -from flask import Blueprint, render_template, request, redirect, url_for, session, flash +from flask import Blueprint, render_template, request, redirect, url_for, session, flash, Response, session import time import requests import mysql.connector +import csv +import io from werkzeug.security import check_password_hash, generate_password_hash from sqlalchemy import text from app import db @@ -296,3 +298,115 @@ def gestion_societes(): return render_template('gestion_societes.html', societes=societes) +# Routes pour la gestion des imports exports des actions csv +@main.route('/gestion-import-export-actions-csv', methods=['GET']) +def gestion_import_export_actions_csv(): + if 'user_id' not in session: + return redirect(url_for('main.index')) + return render_template('gestion_import_export_actions_csv.html') + +@main.route('/export-actions-csv', methods=['GET']) +def export_actions_csv(): + if 'user_id' not in session: + return redirect(url_for('main.index')) + + try: + # Récupération de toutes les données de la table actions + query = text("SELECT id, isin, ticker, company_name, exchange, pays, currency, updated_at FROM actions") + result = db.session.execute(query) + + # Création du fichier CSV en mémoire + output = io.StringIO() + writer = csv.writer(output, delimiter=';', quoting=csv.QUOTE_MINIMAL) + + # Écriture de l'en-tête du CSV + writer.writerow(['id', 'isin', 'ticker', 'company_name', 'exchange', 'pays', 'currency', 'updated_at']) + + # Écriture des lignes + for row in result: + writer.writerow([ + row.id, + row.isin or '', + row.ticker or '', + row.company_name or '', + row.exchange or '', + row.pays or '', + row.currency or '', + row.updated_at.strftime('%Y-%m-%d %H:%M:%S') if row.updated_at else '' + ]) + + output.seek(0) + + # Envoi du fichier en téléchargement + return Response( + output.getvalue(), + mimetype="text/csv", + headers={"Content-Disposition": "attachment;filename=actions_export.csv"} + ) + except Exception as e: + flash("Erreur lors de l'exportation du fichier CSV.", "danger") + return redirect(url_for('main.gestion_import_export_actions_csv')) + +@main.route('/import-actions-csv', methods=['POST']) +def import_actions_csv(): + if 'user_id' not in session: + return redirect(url_for('main.index')) + + if 'file' not in request.files: + flash("Aucun fichier sélectionné.", "danger") + return redirect(url_for('main.gestion_import_export_actions_csv')) + + file = request.files['file'] + + if file.filename == '': + flash("Aucun fichier sélectionné.", "danger") + return redirect(url_for('main.gestion_import_export_actions_csv')) + + if file and file.filename.endswith('.csv'): + try: + stream = io.TextIOWrapper(file.stream, encoding="utf-8") + csv_reader = csv.reader(stream, delimiter=';') + + header = next(csv_reader, None) + if not header: + flash("Le fichier CSV est vide.", "danger") + return redirect(url_for('main.gestion_import_export_actions_csv')) + + count = 0 + for row in csv_reader: + # Vérifie qu'on a au moins les 6 colonnes de base + if len(row) >= 6: + isin = row[0].strip() if row[0] != '' else None + ticker = row[1].strip() if row[1] != '' else None + company_name = row[2].strip() if row[2] != '' else None + exchange = row[3].strip() if row[3] != '' else '' + pays = row[4].strip() if row[4] != '' else '' + currency = row[5].strip() if row[5] != '' else '' + + if not isin or not ticker: + continue # Ignore les lignes mal formatées + + # Insertion ou mise à jour si le code ISIN existe déjà + query = text(""" + INSERT INTO actions (isin, ticker, company_name, exchange, pays, currency, updated_at) + VALUES (:isin, :ticker, :company_name, :exchange, :pays, :currency, NOW()) + ON DUPLICATE KEY UPDATE + ticker = :ticker, company_name = :company_name, + exchange = :exchange, pays = :pays, currency = :currency, updated_at = NOW() + """) + db.session.execute(query, { + "isin": isin, "ticker": ticker, "company_name": company_name, + "exchange": exchange, "pays": pays, "currency": currency + }) + count += 1 + + db.session.commit() + flash(f"Importation réussie : {count} actions traitées.", "success") + except Exception as e: + db.session.rollback() + flash(f"Erreur lors de l'importation du fichier : {str(e)}", "danger") + + return redirect(url_for('main.gestion_import_export_actions_csv')) + else: + flash("Veuillez fournir un fichier au format .csv valide.", "warning") + return redirect(url_for('main.gestion_import_export_actions_csv')) \ No newline at end of file diff --git a/app/templates/gestion_import_export_actions_csv.html b/app/templates/gestion_import_export_actions_csv.html new file mode 100644 index 0000000..a837fe0 --- /dev/null +++ b/app/templates/gestion_import_export_actions_csv.html @@ -0,0 +1,192 @@ +{% extends "base.html" %} {% block title %}Import / Export Actions - Bolsa{% +endblock %} {% block header_title %}Gestion CSV{% endblock %} {% block content +%} +
+ Gestion des transferts de données au format CSV. +
+
+ Cliquez sur le bouton Exporter en CSV pour
+ télécharger une copie de sauvegarde de la table
+ actions
+ sur votre poste. Le fichier contiendra l'ensemble des colonnes
+ actuelles (isin, ticker, company_name, exchange, pays, currency).
+
+ Préparez votre fichier au format CSV en respectant scrupuleusement
+ l'ordre et le nom des en-têtes de colonnes (les champs
+ id
+ et
+ updated_at
+ sont facultatifs et gérés automatiquement) :
+ isin;ticker;company_name;exchange;pays;currency. Les données d'une même ligne doivent être séparées par un
+ point-virgule (;) et chaque enregistrement doit
+ simplement se trouver sur une nouvelle ligne. Cliquez sur
+ Importer un CSV pour sélectionner et téléverser
+ votre fichier vers la base de données.
+