#!/usr/bin/env python3 """ Phone Refurb Web App Accessible depuis le mesh KAWA """ from flask import Flask, request, redirect, url_for, g import sqlite3 from pathlib import Path app = Flask(__name__) DB_PATH = Path(__file__).parent / "phones.db" def get_db(): if 'db' not in g: g.db = sqlite3.connect(DB_PATH) g.db.row_factory = sqlite3.Row return g.db @app.teardown_appcontext def close_db(exception): db = g.pop('db', None) if db is not None: db.close() CSS = """ """ NAV = """ """ def page(content, query=""): return f""" Phone Refurb - KAWA {CSS} {NAV.format(query=query)}
{content}
""" @app.route('/') def index(): db = get_db() phones = db.execute(""" SELECT p.id, p.brand, p.model, p.model_code, p.status, p.updated_at, s.os_version, s.root FROM phones p LEFT JOIN software_state s ON p.id = s.phone_id ORDER BY p.updated_at DESC """).fetchall() stats = { 'stock': len([p for p in phones if p['status'] == 'stock']), 'wip': len([p for p in phones if p['status'] in ('wip', 'paused')]), 'ready': len([p for p in phones if p['status'] == 'ready']), 'sold': len([p for p in phones if p['status'] == 'sold']), } rows = "" for p in phones: rows += f""" {p['id']} {p['brand']} {p['model']} {p['model_code'] or '-'} {p['status']} {p['os_version'] or '-'} {'✓' if p['root'] else '✗'} Voir Action """ content = f"""

📱 Stock

{stats['stock']}

🔧 En cours

{stats['wip']}

✅ Prêts

{stats['ready']}

💰 Vendus

{stats['sold']}

Téléphones

{rows}
IDMarqueModèleCodeStatutOSRootActions
""" return page(content) @app.route('/phone/') def phone(phone_id): db = get_db() phone = db.execute('SELECT * FROM phones WHERE id = ?', (phone_id,)).fetchone() if not phone: return page("

❌ Téléphone non trouvé

") specs = db.execute('SELECT * FROM phone_specs WHERE phone_id = ?', (phone_id,)).fetchone() sw = db.execute('SELECT * FROM software_state WHERE phone_id = ? ORDER BY date_recorded DESC LIMIT 1', (phone_id,)).fetchone() actions = db.execute('SELECT * FROM actions WHERE phone_id = ? ORDER BY action_date DESC LIMIT 20', (phone_id,)).fetchall() specs_html = "" if specs: specs_html = f"""

🔧 Caractéristiques

CPU: {specs['cpu'] or 'N/A'}
GPU: {specs['gpu'] or 'N/A'}
RAM: {specs['ram_mb']} Mo
Stockage: {specs['storage_gb']} Go
Écran: {specs['screen_size'] or 'N/A'}
Batterie: {specs['battery_mah']} mAh
""" sw_html = "" if sw: bootloader = '🔒 Verrouillé' if sw['bootloader_locked'] else '🔓 Déverrouillé' root = '✓ Oui' if sw['root'] else '✗ Non' sw_html = f"""

💾 Logiciel

OS: {sw['os_type']} {sw['os_version'] or ''}
SDK: {sw['sdk_version'] or 'N/A'}
Kernel: {sw['kernel_version'] or 'N/A'}
Bootloader: {bootloader}
Root: {root}
Recovery: {sw['recovery_type'] or 'Stock'}
""" actions_rows = "" for a in actions: actions_rows += f""" {a['action_date'][:16] if a['action_date'] else '-'} {a['action_type']} {a['status']} {a['notes'] or '-'} """ actions_html = f"""

📋 Historique

{'' + actions_rows + '
DateActionStatutNotes
' if actions else '

Aucune action

'} ➕ Ajouter une action
""" content = f""" ← Retour

📱 {phone['brand']} {phone['model']}

Code: {phone['model_code'] or 'N/A'}

IMEI: {phone['imei'] or 'N/A'}

Serial: {phone['serial'] or 'N/A'}

Statut: {phone['status']}

Notes: {phone['notes'] or 'Aucune'}

{specs_html} {sw_html} {actions_html} """ return page(content) @app.route('/add', methods=['GET', 'POST']) def add(): if request.method == 'POST': db = get_db() db.execute(""" INSERT INTO phones (brand, model, model_code, imei, serial, status, notes) VALUES (?, ?, ?, ?, ?, ?, ?) """, ( request.form['brand'], request.form['model'], request.form['model_code'], request.form.get('imei') or None, request.form.get('serial') or None, request.form.get('status', 'stock'), request.form.get('notes') or None )) db.commit() return redirect('/') content = """ ← Retour

➕ Ajouter un téléphone

""" return page(content) @app.route('/action/', methods=['GET', 'POST']) def add_action(phone_id): db = get_db() phone = db.execute('SELECT brand, model FROM phones WHERE id = ?', (phone_id,)).fetchone() if request.method == 'POST': db.execute(""" INSERT INTO actions (phone_id, action_type, status, notes) VALUES (?, ?, ?, ?) """, ( phone_id, request.form['action_type'], request.form['status'], request.form.get('notes') or None )) db.execute('UPDATE phones SET updated_at = CURRENT_TIMESTAMP WHERE id = ?', (phone_id,)) db.commit() return redirect(f'/phone/{phone_id}') content = f""" ← Retour

📝 Action sur {phone['brand']} {phone['model']}

""" return page(content) @app.route('/roms', methods=['GET', 'POST']) def roms(): db = get_db() if request.method == 'POST': db.execute(""" INSERT INTO roms (name, version, android_version, device_code, source_url, file_path, file_size_mb, rom_type, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( request.form['name'], request.form.get('version') or None, request.form.get('android_version') or None, request.form['device_code'], request.form.get('source_url') or None, request.form.get('file_path') or None, request.form.get('file_size_mb') or None, request.form.get('rom_type', 'custom'), request.form.get('notes') or None )) db.commit() return redirect('/roms') roms = db.execute('SELECT * FROM roms ORDER BY device_code, name').fetchall() rows = "" for r in roms: rows += f""" {r['id']} {r['name']} {r['version'] or '-'} {r['android_version'] or '-'} {r['device_code']} {r['file_size_mb']}M {r['rom_type']} """ content = f"""

💾 ROMs disponibles

{rows}
IDNomVersionAndroidDeviceTailleType

➕ Ajouter une ROM

""" return page(content) @app.route('/stats') def stats(): db = get_db() total_phones = db.execute('SELECT COUNT(*) FROM phones').fetchone()[0] total_actions = db.execute('SELECT COUNT(*) FROM actions').fetchone()[0] total_roms = db.execute('SELECT COUNT(*) FROM roms').fetchone()[0] action_stats = db.execute('SELECT status, COUNT(*) as cnt FROM actions GROUP BY status').fetchall() type_stats = db.execute('SELECT action_type, COUNT(*) as cnt FROM actions GROUP BY action_type ORDER BY cnt DESC').fetchall() action_rows = "" for s in action_stats: action_rows += f""" {s['status']} {s['cnt']} """ type_rows = "" for t in type_stats: type_rows += f""" {t['action_type']} {t['cnt']} """ content = f"""

📊 Statistiques

📱 Téléphones

{total_phones}

🔧 Actions

{total_actions}

💾 ROMs

{total_roms}

Actions par statut

{action_rows}
StatutNombre

Actions par type

{type_rows}
TypeNombre
""" return page(content) @app.route('/search') def search(): query = request.args.get('q', '') db = get_db() phones = db.execute(""" SELECT * FROM phones WHERE brand LIKE ? OR model LIKE ? OR model_code LIKE ? OR notes LIKE ? ORDER BY updated_at DESC """, (f'%{query}%', f'%{query}%', f'%{query}%', f'%{query}%')).fetchall() rows = "" for p in phones: rows += f""" {p['id']} {p['brand']} {p['model']} {p['model_code'] or '-'} {p['status']} """ content = f"""

🔍 Résultats pour "{query}"

{'' + rows + '
IDMarqueModèleCodeStatut
' if phones else '

Aucun résultat

'} """ return page(content, query=query) if __name__ == '__main__': app.run(host='0.0.0.0', port=5050, debug=False)