#!/usr/bin/env python3 from flask import Flask, request, jsonify, redirect, send_file import json import os import requests import csv import io app = Flask(__name__) CONFIG_FILE = '/home/h3r7/depenses_trello/config.json' def load_config(): with open(CONFIG_FILE, 'r') as f: return json.load(f) def save_config(data): with open(CONFIG_FILE, 'w') as f: json.dump(data, f, indent=2) def parse_date(d): if not d: return "" if "/" in d: parts = d.split("/") if len(parts) == 3: return f"{parts[2]}-{parts[1]}-{parts[0]}" return d @app.route('/') def index(): with open('/home/h3r7/depenses_trello/templates/index.html', 'r') as f: return f.read() @app.route('/dashboard') def dashboard(): with open('/home/h3r7/depenses_trello/templates/dashboard.html', 'r') as f: return f.read() # API Config @app.route('/api/config') def get_config(): return jsonify(load_config()) # Add depense @app.route('/api/add', methods=['POST']) def add_depense(): config = load_config() data = { 'id': len(config.get('depenses', [])) + 1, 'prenom': request.form.get('prenom'), 'date': parse_date(request.form.get('date', '')), 'libelle': request.form.get('libelle'), 'montant': float(request.form.get('montant', 0)), 'status': 'En attente' } if 'depenses' not in config: config['depenses'] = [] config['depenses'].append(data) save_config(config) return redirect('/?page=saisie') # Get depenses @app.route('/api/depenses') def get_depenses(): config = load_config() return jsonify(config.get('depenses', [])) # Delete depense @app.route('/api/del/') def delete_depense(id): config = load_config() config['depenses'] = [d for d in config.get('depenses', []) if d.get('id') != id] save_config(config) return redirect('/?page=saisie') # Clear all @app.route('/api/clear', methods=['POST']) def clear_depenses(): config = load_config() config['depenses'] = [] save_config(config) return jsonify({'success': True}) # Export CSV @app.route('/api/export') def export_csv(): config = load_config() depenses = config.get('depenses', []) output = io.StringIO() writer = csv.writer(output) writer.writerow(['prenom', 'date', 'libelle', 'montant', 'status']) for d in depenses: writer.writerow([ d.get('prenom', ''), d.get('date', ''), d.get('libelle', ''), d.get('montant', 0), d.get('status', '') ]) output.seek(0) return send_file( io.BytesIO(output.getvalue().encode('utf-8')), mimetype='text/csv', as_attachment=True, download_name='depenses_2026.csv' ) # Import CSV @app.route('/api/import', methods=['POST']) def import_csv(): if 'file' not in request.files: return jsonify({'error': 'Aucun fichier'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'Fichier vide'}), 400 try: content = file.read().decode('utf-8') reader = csv.reader(content.splitlines()) next(reader) # Skip header config = load_config() if 'depenses' not in config: config['depenses'] = [] max_id = max([d.get('id', 0) for d in config.get('depenses', [])], default=0) imported = 0 for row in reader: if len(row) >= 4: max_id += 1 config['depenses'].append({ 'id': max_id, 'prenom': row[0].strip(), 'date': row[1].strip(), 'libelle': row[2].strip(), 'montant': float(row[3]) if row[3] else 0, 'status': row[4].strip() if len(row) > 4 else 'En attente' }) imported += 1 save_config(config) return jsonify({'success': True, 'imported': imported}) except Exception as e: return jsonify({'error': str(e)}), 400 # Generate text for ONE expense @app.route('/api/generate_one/', methods=['GET']) def generate_one(id): config = load_config() d = None for exp in config.get('depenses', []): if exp.get('id') == id: d = exp break if not d: return jsonify({'text': ''}) template = config.get('format', '{prenom} - {date} - {libelle} - {montant}€') line = template ddate = d.get('date', '') or '' if ddate: ddate = '/'.join(ddate.split('-')[::-1]) line = line.replace('{prenom}', d.get('prenom', '')) line = line.replace('{date}', ddate) line = line.replace('{libelle}', d.get('libelle', '')) line = line.replace('{montant}', str(d.get('montant', 0))) return jsonify({'text': line}) # Send to Trello - ONE card per pending expense @app.route('/api/trello/send_one/', methods=['POST']) def send_one(id): config = load_config() t = config.get('trello', {}) if not t.get('api_key') or not t.get('token') or not t.get('list_id'): return jsonify({'error': 'Config Trello manquante'}), 400 d = None for exp in config.get('depenses', []): if exp.get('id') == id: d = exp break if not d: return jsonify({'error': 'Dépense non trouvée'}), 404 if d.get('status') == 'Envoyé ✅': return jsonify({'error': 'Déjà envoyé'}), 400 template = config.get('format', '{prenom} - {date} - {libelle} - {montant}€') line = template ddate = d.get('date', '') or '' if ddate: ddate = '/'.join(ddate.split('-')[::-1]) line = line.replace('{prenom}', d.get('prenom', '')) line = line.replace('{date}', ddate) line = line.replace('{libelle}', d.get('libelle', '')) line = line.replace('{montant}', str(d.get('montant', 0))) url = 'https://api.trello.com/1/cards' params = { 'key': t.get('api_key'), 'token': t.get('token'), 'idList': t.get('list_id'), 'name': line, 'desc': line } try: r = requests.post(url, params=params) if r.status_code == 200: for exp in config.get('depenses', []): if exp.get('id') == id: exp['status'] = 'Envoyé ✅' save_config(config) return jsonify({'success': True}) return jsonify({'error': r.text}), r.status_code except Exception as e: return jsonify({'error': str(e)}), 500 # Legacy - send all (for compatibility) @app.route('/api/trello/send', methods=['POST']) def send_trello(): config = load_config() t = config.get('trello', {}) if not t.get('api_key') or not t.get('token') or not t.get('list_id'): return jsonify({'error': 'Config Trello manquante'}), 400 sent_count = 0 for d in config.get('depenses', []): if d.get('status') == 'En attente': template = config.get('format', '{prenom} - {date} - {libelle} - {montant}€') line = template ddate = d.get('date', '') or '' if ddate: ddate = '/'.join(ddate.split('-')[::-1]) line = line.replace('{prenom}', d.get('prenom', '')) line = line.replace('{date}', ddate) line = line.replace('{libelle}', d.get('libelle', '')) line = line.replace('{montant}', str(d.get('montant', 0))) url = 'https://api.trello.com/1/cards' params = { 'key': t.get('api_key'), 'token': t.get('token'), 'idList': t.get('list_id'), 'name': line, 'desc': line } try: r = requests.post(url, params=params) if r.status_code == 200: d['status'] = 'Envoyé ✅' sent_count += 1 except: pass save_config(config) return jsonify({'success': True, 'sent': sent_count}) # Generate text (all) @app.route('/api/generate', methods=['POST']) def generate_text(): config = load_config() data = request.json template = config.get('format', '{prenom} - {date} - {libelle} - {montant}€') lines = [] for d in data.get('depenses', []): line = template ddate = d.get('date', '') or '' if ddate: ddate = '/'.join(ddate.split('-')[::-1]) line = line.replace('{prenom}', d.get('prenom', '')) line = line.replace('{date}', ddate) line = line.replace('{libelle}', d.get('libelle', '')) line = line.replace('{montant}', str(d.get('montant', 0))) lines.append(line) return jsonify({'text': '\n'.join(lines)}) # Add prenom @app.route('/api/prenom/add', methods=['POST']) def add_prenom(): config = load_config() prenom = request.form.get('prenom', '').strip() if prenom and prenom not in config.get('prenoms', []): if 'prenoms' not in config: config['prenoms'] = [] config['prenoms'].append(prenom) save_config(config) return redirect('/?page=config') # Del prenom @app.route('/api/prenom/del/') def del_prenom(idx): config = load_config() if 0 <= idx < len(config.get('prenoms', [])): config['prenoms'].pop(idx) save_config(config) return redirect('/?page=config') # Save format @app.route('/api/format', methods=['POST']) def save_format(): config = load_config() config['format'] = request.form.get('format', '{prenom} - {date} - {libelle} - {montant}€') save_config(config) return redirect('/?page=config') # Save trello config @app.route('/api/trello/save', methods=['POST']) def save_trello(): config = load_config() config['trello'] = { 'api_key': request.form.get('api_key', ''), 'token': request.form.get('token', ''), 'list_id': request.form.get('list_id', '') } save_config(config) return redirect('/?page=config') if __name__ == '__main__': app.run(host='0.0.0.0', port=8769, debug=False)