From 94b0f9f693bf029683ca151960c09e65feba9d9c Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 11 May 2025 09:31:20 +0200 Subject: [PATCH] From Json to PostgreSQL --- app.py | 121 +++++++++++++++++++++++++++++++++++++---------- requirements.txt | 2 + 2 files changed, 97 insertions(+), 26 deletions(-) diff --git a/app.py b/app.py index 4afe255..7587e2b 100644 --- a/app.py +++ b/app.py @@ -1,32 +1,92 @@ -from flask import Flask, render_template, request, jsonify, send_from_directory -import json +from flask import Flask, render_template, request, jsonify +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import func import os app = Flask(__name__) -DATA_FILE = 'data/attendance_data.json' +app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') +db = SQLAlchemy(app) -# Default player names -DEFAULT_PLAYERS = [ - "Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hannah" -] -DEFAULT_GUEST = "Guest" +# Models +class Player(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(50), nullable=False, unique=True) -def load_data(): - if not os.path.exists(DATA_FILE): - data = { - "players": DEFAULT_PLAYERS, - "guest": DEFAULT_GUEST, - "dates": [], - "attendance": {} - } - with open(DATA_FILE, 'w') as f: - json.dump(data, f) - with open(DATA_FILE, 'r') as f: - return json.load(f) +class Date(db.Model): + id = db.Column(db.Integer, primary_key=True) + date_str = db.Column(db.String(20), nullable=False, unique=True) # e.g. '08/05/25' -def save_data(data): - with open(DATA_FILE, 'w') as f: - json.dump(data, f, indent=2) +class Attendance(db.Model): + id = db.Column(db.Integer, primary_key=True) + date_id = db.Column(db.Integer, db.ForeignKey('date.id'), nullable=False) + player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False) + status = db.Column(db.String(10), nullable=False) # 'yes' or 'no' + + __table_args__ = (db.UniqueConstraint('date_id', 'player_id', name='_date_player_uc'),) + +class GuestName(db.Model): + id = db.Column(db.Integer, primary_key=True) + date_id = db.Column(db.Integer, db.ForeignKey('date.id'), nullable=False, unique=True) + name = db.Column(db.String(50), nullable=False) + +def get_initial_data(): + # Ensure tables exist + db.create_all() + # If no players, insert defaults + if Player.query.count() == 0: + for name in ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hannah"]: + db.session.add(Player(name=name)) + db.session.commit() + if not GuestName.query.first(): + # Set default guest name for all dates + pass + +def db_to_json(): + players = [p.name for p in Player.query.order_by(Player.id)] + guest = "Guest" + dates = [d.date_str for d in Date.query.order_by(Date.id)] + attendance = {} + for att in Attendance.query.all(): + date = Date.query.get(att.date_id).date_str + player = Player.query.get(att.player_id) + colIdx = players.index(player.name) if player.name in players else len(players) # guest is last + key = f"{date}|{colIdx}" + attendance[key] = True if att.status == 'yes' else 'no' + guest_names = {Date.query.get(g.date_id).date_str: g.name for g in GuestName.query.all()} + return { + "players": players, + "guest": guest, + "dates": dates, + "attendance": attendance, + "guestNames": guest_names + } + +def json_to_db(data): + db.session.query(Attendance).delete() + db.session.query(Date).delete() + db.session.query(GuestName).delete() + db.session.commit() + # Players are assumed fixed + for date_str in data.get("dates", []): + date = Date(date_str=date_str) + db.session.add(date) + db.session.commit() + # Attendance + for idx, player_name in enumerate(data["players"] + [data["guest"]]): + key = f"{date_str}|{idx}" + status = data["attendance"].get(key) + if status: + if player_name == data["guest"]: + # Guest is not stored as player, skip + continue + player = Player.query.filter_by(name=player_name).first() + if player: + db.session.add(Attendance(date_id=date.id, player_id=player.id, status='yes' if status is True else 'no')) + # Guest name + guest_name = data.get("guestNames", {}).get(date_str) + if guest_name: + db.session.add(GuestName(date_id=date.id, name=guest_name)) + db.session.commit() @app.route('/') def index(): @@ -38,17 +98,26 @@ def reports(): @app.route('/api/data', methods=['GET']) def get_data(): - return jsonify(load_data()) + get_initial_data() + return jsonify(db_to_json()) @app.route('/api/data', methods=['POST']) def update_data(): data = request.json - save_data(data) + json_to_db(data) return jsonify({"status": "success"}) +# Optional: export from DB as JSON (for compatibility) @app.route('/export-data') def export_data(): - return send_from_directory(directory='data', path='attendance_data.json', as_attachment=True) + from flask import Response + get_initial_data() + data = db_to_json() + return Response( + response=json.dumps(data, indent=2), + mimetype='application/json', + headers={'Content-Disposition': 'attachment;filename=attendance_data.json'} + ) if __name__ == '__main__': import os diff --git a/requirements.txt b/requirements.txt index 7e10602..dbc658b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ flask +sqlalchemy +psycopg2-binary