"""Flask app to display a table with the result of a postgreSQL query""" from flask import Flask, render_template import pandas as pd from dotenv import load_dotenv import os import psycopg2 import logging import sys import datetime import warnings warnings.filterwarnings("ignore") load_dotenv() app = Flask(__name__) def connect_to_database(args): try: conn = psycopg2.connect( host=args.dbhost, database=args.dbname, user=args.dbuser, password=args.dbpass, sslmode='require', options=f'project={args.project}' ) cur = conn.cursor() except Exception as e: logging.error('Failed to connect to database: %s', e) sys.exit(1) table_name = vars(args).get('table', 'minecraft') # if args.clean doesn't exist, it will be False if vars(args).get('clean', False): # remove table if it exists try: cur.execute(f'DROP TABLE IF EXISTS {table_name}') conn.commit() except Exception as e: logging.error('Failed to drop table: %s', e) sys.exit(1) # Create table if it doesn't exist try: cur.execute(f'CREATE TABLE IF NOT EXISTS {table_name} (timestamp TIMESTAMP, server_name TEXT, version TEXT, protocol INT, players INT, max_players INT, latency INT)') conn.commit() except Exception as e: logging.error('Failed to create table: %s', e) sys.exit(1) return conn @app.route("/") def index(): # make a fake args object that acts the same way as argparse.Namespace class Args: def __init__(self, **kwargs): self.__dict__.update(kwargs) args = Args( dbhost=os.environ.get("DB_HOST", "localhost"), dbname=os.environ.get("DB_NAME", "minecraft"), dbuser=os.environ.get("DB_USER", "postgres"), dbpass=os.environ.get("DB_PASS", ""), project=os.environ.get("DB_PROJ", "default"), table=os.environ.get("DB_TABLE", "minecraft"), ) conn = connect_to_database(args) table_name = vars(args).get("table", "minecraft") df = pd.read_sql( f"SELECT * FROM {table_name} ORDER BY timestamp DESC LIMIT 100;", conn ) online = df["players"].tolist()[0] df = df[['timestamp', 'players', 'latency']] # format timestamp df['timestamp'] = df['timestamp'].dt.strftime('%Y-%m-%d %H:%M:%S') # if the timestamp is more than 5 minutes old, the server is offline latest_timestamp = datetime.datetime.strptime(df['timestamp'].tolist()[0], '%Y-%m-%d %H:%M:%S') if (datetime.datetime.now() - latest_timestamp) > datetime.timedelta(minutes=5): status = "The server is currently offline. Please try again later." else: if online > 0: status = f"There are currently { online } players online." else: status = "There are currently no players online." return render_template("index.html", data=df.to_html(justify="center"), status=status) if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=9993)