added export/import functionality

main
oscar 1 month ago
parent a1bbcdd6ab
commit 908e481271

@ -1,8 +1,9 @@
from __future__ import annotations
import os, sqlite3, csv, io, json, datetime as dt
from typing import Optional, Dict, Any
from flask import Flask, g, request, redirect, url_for, render_template, send_file, jsonify
from flask import Flask, g, request, redirect, url_for, render_template, send_file, jsonify, Response
from config import *
import sqlite3
app = Flask(__name__)
@ -281,25 +282,54 @@ def edit(entry_id: int):
_audit("edit", entry_id, old_row=_row_to_dict(old), new_row=_row_to_dict(new))
return redirect(url_for("index"))
@app.get("/export.csv")
def export_csv():
db = get_db()
rows = db.execute("""
SELECT id, created_at, kind, total, payer, a_share, method, note
FROM entries
ORDER BY datetime(created_at) DESC, id DESC
""").fetchall()
buff = io.StringIO()
w = csv.writer(buff)
w.writerow(["id","created_at","kind","payer","a_share_pct","total","method","note","delta"])
for r in rows:
a_share_pct = float(r["a_share"]) * 100.0
delta = _delta_for_entry(r["kind"], r["total"], r["payer"], r["a_share"])
w.writerow([r["id"], r["created_at"], r["kind"], r["payer"], f"{a_share_pct:.2f}",
f"{r['total']:.2f}", r["method"], r["note"] or "", f"{delta:.2f}"])
buff.seek(0)
return send_file(io.BytesIO(buff.read().encode("utf-8")), mimetype="text/csv",
as_attachment=True, download_name="splitbuddy_export.csv")
@app.get("/export")
def export_db():
if not os.path.exists(DB_PATH):
return {"error": "Database not found"}, 404
conn = sqlite3.connect(DB_PATH)
sio = io.StringIO()
for line in conn.iterdump():
sio.write(f"{line}\n")
conn.close()
sio.seek(0)
return Response(
sio.getvalue(),
mimetype="text/sql",
headers={
"Content-Disposition": "attachment; filename=splitbuddy_export.sql"
}
)
@app.post("/import")
def import_db():
if "file" not in request.files:
return {"error": "No file uploaded"}, 400
file = request.files["file"]
sql_text = file.read().decode("utf-8")
# delete old DB
if os.path.exists(DB_PATH):
os.remove(DB_PATH)
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
try:
cur.executescript(sql_text)
conn.commit()
except Exception as e:
conn.close()
return {"error": str(e)}, 500
conn.close()
return {"status": "import successful"}
# ----- Audit viewers -----
@app.get("/audit")

@ -9,11 +9,14 @@
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script defer src="{{ url_for('static', filename='app.js') }}"></script>
</head>
<body>
<div class="wrap">
<header>
<div class="h1">SplitBuddy</div>
<div>
<!-- balance indicator -->
<span class="pill {{ 'bad' if summary.total>0 else ('ok' if summary.total<0 else '') }}">
{% if summary.total > 0 %}
{{ A }} owes {{ B }} <strong>{{ currency }}{{ '%.2f'|format(summary.total) }}</strong>
@ -22,11 +25,24 @@
{% else %}All settled ✨{% endif %}
</span>
<span class="pill muted">Balance: {{ currency }}{{ '%.2f'|format(summary.total) }}</span>
<a class="pill" href="{{ url_for('export_csv') }}">Export CSV</a>
<!-- export / import SQL buttons -->
<a class="pill" href="{{ url_for('export_db') }}">Export SQL</a>
<form action="{{ url_for('import_db') }}" method="POST" enctype="multipart/form-data" style="display:inline">
<label class="pill" style="cursor:pointer">
Import SQL
<input type="file" name="file" accept=".sql"
onchange="this.form.submit()" style="display:none">
</label>
</form>
</div>
</header>
<div class="grid">
<!-- add entry form -->
<section class="card">
<h2>Add entry</h2>
<form method="post" action="{{ url_for('add') }}">
@ -61,6 +77,7 @@
</form>
</section>
<!-- stats -->
<section class="card">
<h2>Stats</h2>
<div class="muted">Entries: {{ summary.count }}</div>
@ -73,6 +90,7 @@
</section>
</div>
<!-- ledger -->
<section class="card" style="margin-top:16px">
<h2>Ledger</h2>
<table>

Loading…
Cancel
Save