import bcrypt import datetime import psycopg2 from textwrap import dedent def create_database_connection(db_name, user, password, host, port=5432): """Create a database connection, then ensure that tables are created Args: db_name (str): name of database user (str): username of database password (str): password to database host (str): host address of database port (int): port of database Returns: psycopg2.connection: connection to database """ connection = psycopg2.connect( dbname=db_name, user=user, password=password, host=host, port=port ) with connection: with connection.cursor() as curs: curs.execute(dedent(""" CREATE TABLE IF NOT EXISTS Users ( id SERIAL PRIMARY KEY, username varchar(255), password_hash varchar(255), name varchar(255), dob date ); CREATE TABLE IF NOT EXISTS Events ( id SERIAL PRIMARY KEY, name varchar(255), datetime timestamp, max_age int, completed boolean, registration varchar(255), results varchar(255) ); CREATE TABLE IF NOT EXISTS Tournaments ( id SERIAL PRIMARY KEY, name varchar(255), start_date date, end_date date, events varchar(255) ); """)) return connection class Users: @staticmethod def create_user(db, login_info, user_info): """Creates a new user in the database Args: db (psycopg2.connection): connection to postgres database login_info (dict of str:str): username and password info user_info (dict of str:value): name (str) and dob (datetime.date) info Returns: int: 0 if the username is non-unique, user id otherwise """ username, password = login_info["username"], login_info["password"] password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() name, dob = user_info["name"], user_info["dob"] with db: with db.cursor() as curs: curs.execute("SELECT * FROM Users WHERE username = %s", (username,)) conflicts = curs.fetchall() if conflicts: return 0 curs.execute("INSERT INTO Users (username, password_hash, name, dob) VALUES (%s, %s, %s, %s) RETURNING id", (username, password_hash, name, dob)) user_id = curs.fetchone()[0] db.commit() return user_id @staticmethod def get_user_from_username(db, username): """Get a user id based on their username Args: db (psycopg2.connection): connection to postgres database username (str): the username used to query Returns: int: 0 if the username is not in the system, user id otherwise """ with db: with db.cursor() as curs: curs.execute("SELECT * FROM Users WHERE username = %s", (username,)) users = curs.fetchall() if not users: return 0 return users[0][0] @staticmethod def get_user_details(db, user_id): """Gets the details of a user based on their id Args: db (psycopg2.connection): connection to postgres database user_id (int): user id Returns: bool, dict of str to str, dict of str to value: user existance, login info (username, password), user info (name (str), dob (datetime.date)) """ with db: with db.cursor() as curs: curs.execute("SELECT * FROM Users WHERE id = %s", (user_id,)) user = curs.fetchall() if not user: return False, None, None user = user[0] return True, {"username": user[1], "password_hash": user[2]}, {"name": user[3], "dob": user[4]} class Tournaments: @staticmethod def create_event(db, name, time, max_age): """Creates a new event in the database Args: db (psycopg2.connection): connection to postgres database name (str): event name time (date.datetime): event time max_age (int): how old a fencer is allowed to be at the time of the event starting Returns: int: id of the event created """ with db: with db.cursor() as curs: curs.execute("INSERT INTO Events (name, datetime, max_age, completed, registration, results) VALUES (%s, %s, %s, False, '', '') RETURNING id", (name, time, max_age)) event_id = curs.fetchone()[0] db.commit() return event_id @staticmethod def get_event(db, event_id): """Gets the details of an event based on its id Args: db (psycopg2.connection): connection to postgres database event_id (int): event id Returns: bool, str, datetime.datetime, int, bool, list of ints, list of ints: existance of event, name, datetime of event, max age, event completion, registration, results """ with db: with db.cursor() as curs: curs.execute("SELECT * FROM Events WHERE id = %s", (event_id,)) event = curs.fetchall() if not event: return False, None, None, None, None, None, None event = event[0] return True, event[1], event[2], event[3], event[4], list(map(int, event[5].split())), list(map(int, event[6].split())) @staticmethod def create_tournament(db, name, event_ids): """Create a new tournament in the database Args: db (psycopg2.connection): connection to postgres database name (str): name of tournament event_ids (list of ints): list of events in the tournament Returns: int: id of event """ event_str = " ".join(map(str, event_ids)) events = [Tournaments.get_event(db, event_id)[2] for event_id in event_ids] print(events) start_date, end_date = min(events).date(), max(events).date() with db: with db.cursor() as curs: curs.execute("INSERT INTO Tournaments (name, start_date, end_date, events) VALUES (%s, %s, %s, %s) RETURNING id", (name, start_date, end_date, event_str)) tournament_id = curs.fetchone()[0] db.commit() return tournament_id @staticmethod def add_event_registration(db, event_id, user_id): pass @staticmethod def add_event_results(db, event): pass