Cleaned up the database.

Fixed sqlite3 Error import.
Changed attributes from annotations to dataclass fields.
Changed decorators into functions.
Cleaned up formatting (query strings/doc-strings).
This commit is contained in:
SimpleArt
2021-07-08 11:30:34 -04:00
parent b81d1614af
commit 3f2b2cb756

View File

@ -1,4 +1,5 @@
import sqlite3 import sqlite3
from sqlite3 import Error
from tabulate import tabulate from tabulate import tabulate
@ -11,11 +12,13 @@ class SQL_Database:
self.conn = None self.conn = None
self.config_id = None self.config_id = None
self._database_name = 'database.db' self._database_name = 'database.db'
self.config_structure = f""" self.config_structure = """
CREATE TABLE IF NOT EXISTS config ( CREATE TABLE IF NOT EXISTS config (
config_id INTEGER, config_id INTEGER,
attribute_name TEXT, attribute_name TEXT,
attribute_value TEXT)""" attribute_value TEXT
;)
"""
#=====================================# #=====================================#
@ -23,23 +26,18 @@ class SQL_Database:
#=====================================# #=====================================#
def create_all_tables(self, ga): def create_all_tables(self, ga):
"""Create the database if it doenst exist and then the data and config """Create the database if it doenst exist and then the data and config tables."""
tables.""" # Create the database connection.
# Create the database connection
self.create_connection() self.create_connection()
# No connection.
if self.conn is not None: if self.conn is None:
# Create data table
self.create_table(ga.sql_create_data_structure)
# Creare config table
self.create_table(self.config_structure)
# Set the config id
self.config_id = self.get_current_config()
else:
raise Exception("Error! Cannot create the database connection.") raise Exception("Error! Cannot create the database connection.")
# Create data table.
self.create_table(ga.sql_create_data_structure)
# Creare config table.
self.create_table(self.config_structure)
# Set the config id.
self.config_id = self.get_current_config()
def insert_config(self, ga): def insert_config(self, ga):
@ -48,7 +46,7 @@ class SQL_Database:
Notes: Notes:
"Attributes" here refers to ga.__annotations__.keys(), "Attributes" here refers to ga.__dataclass_fields__.keys(),
which allows the attributes to be customized. which allows the attributes to be customized.
Only attributes that are bool, float, int, or str will be used. Only attributes that are bool, float, int, or str will be used.
@ -58,61 +56,26 @@ class SQL_Database:
self.config_id = self.get_current_config() self.config_id = self.get_current_config()
# Setting the config_id index if there is no file # Setting the config_id index if there is no file
if self.config_id == None: if self.config_id is None:
self.config_id = 0 self.config_id = 0
else: else:
self.config_id = self.config_id + 1 self.config_id = self.config_id + 1
# Getting all the attributes from the attributes class # Getting all attribute fields from the attributes class
db_config = [ db_config = [
(self.config_id, attr_name, attr_value) (self.config_id, attr_name, attr_value)
for attr_name for attr_name
in ga.__annotations__ in ga.__dataclass_fields__
if isinstance((attr_value := getattr(ga, attr_name)), (bool, float, int, str)) if isinstance((attr_value := getattr(ga, attr_name)), (bool, float, int, str))
] ]
query = f""" query = """
INSERT INTO config(config_id, attribute_name, attribute_value) INSERT INTO config(config_id, attribute_name, attribute_value)
VALUES (?, ?, ?); VALUES (?, ?, ?);
""" """
self.conn.executemany(query, db_config) self.conn.executemany(query, db_config)
self.config_id = self.get_current_config() self.config_id = self.get_current_config()
#=====================================#
# Decorators: #
#=====================================#
def default_config_id(method):
"""Decorator used to set the default config_id inside other functions."""
def new_method(self, config_id = None):
input_id = self.config_id if config_id is None else config_id
return method(self, input_id)
return new_method
def format_query_data(method):
"""Decorator used to format query data"""
def new_method(self, config_id):
query = method(self, config_id)
# Unpack elements if they are lists with only 1 element
if type(query[0]) in (list, tuple) and len(query[0]) == 1:
query = [i[0] for i in query]
# Unpack list if it is a list with only 1 element
if type(query) in (list, tuple) and len(query) == 1:
query = query[0]
return query
return new_method
#=====================================# #=====================================#
# Request information Queries: # # Request information Queries: #
#=====================================# #=====================================#
@ -125,63 +88,73 @@ class SQL_Database:
def past_runs(self): def past_runs(self):
"""Show a summerization of the past runs that the user has done.""" """Show a summerization of the past runs that the user has done."""
query_data = self.query_all(f""" query_data = self.query_all("""
SELECT config_id,attribute_name,attribute_value SELECT config_id, attribute_name, attribute_value
FROM config;""") FROM config;
""")
print( table = tabulate(
tabulate(
query_data, query_data,
headers = [ headers = [
'config_id', 'config_id',
'attribute_name', 'attribute_name',
'attribute_value' 'attribute_value',
] ]
) )
)
print(table)
return table
@default_config_id
def get_generation_total_fitness(self, config_id): def get_generation_total_fitness(self, config_id):
"""Get each generations total fitness sum from the database """ """Get each generations total fitness sum from the database """
config_id = self.config_id if config_id is None else config_id
return self.query_all(f""" return self.query_all(f"""
SELECT SUM(fitness) SELECT SUM(fitness)
FROM data FROM data
WHERE config_id={config_id} WHERE config_id={config_id}
GROUP BY generation;""") GROUP BY generation;
""")
@default_config_id
def get_total_generations(self, config_id): def get_total_generations(self, config_id):
"""Get the total generations from the database""" """Get the total generations from the database"""
config_id = self.config_id if config_id is None else config_id
return self.query_one_item(f""" return self.query_one_item(f"""
SELECT COUNT(DISTINCT generation) SELECT COUNT(DISTINCT generation)
FROM data FROM data
WHERE config_id={config_id};""") WHERE config_id={config_id};
""")
@default_config_id
def get_highest_chromosome(self, config_id): def get_highest_chromosome(self, config_id):
"""Get the highest fitness of each generation""" """Get the highest fitness of each generation"""
config_id = self.config_id if config_id is None else config_id
return self.query_all(f""" return self.query_all(f"""
SELECT max(fitness) SELECT max(fitness)
FROM data FROM data
WHERE config_id={config_id} WHERE config_id={config_id}
GROUP by generation;""") GROUP by generation;
""")
@default_config_id
def get_lowest_chromosome(self, config_id): def get_lowest_chromosome(self, config_id):
"""Get the lowest fitness of each generation""" """Get the lowest fitness of each generation"""
config_id = self.config_id if config_id is None else config_id
return self.query_all(f""" return self.query_all(f"""
SELECT min(fitness) SELECT min(fitness)
FROM data FROM data
WHERE config_id={config_id} WHERE config_id={config_id}
GROUP by generation;""") GROUP by generation;
""")
def get_all_config_id(self): def get_all_config_id(self):
@ -189,7 +162,8 @@ class SQL_Database:
return self.query_all(f""" return self.query_all(f"""
SELECT DISTINCT config_id SELECT DISTINCT config_id
FROM config;""") FROM config;
""")
def get_each_generation_number(self, config_id): def get_each_generation_number(self, config_id):
"""Get an array of all the generation numbers""" """Get an array of all the generation numbers"""
@ -197,7 +171,8 @@ class SQL_Database:
return self.query_all(f""" return self.query_all(f"""
SELECT DISTINCT generation SELECT DISTINCT generation
FROM data FROM data
WHERE config_id={config_id};""") WHERE config_id={config_id};
""")
@ -214,12 +189,14 @@ class SQL_Database:
self.config_id, self.config_id,
generation, generation,
chromosome.fitness, chromosome.fitness,
repr(chromosome) repr(chromosome),
) )
# Create sql query structure # Create sql query structure
sql = """INSERT INTO data(config_id, generation, fitness, chromosome) sql = """
VALUES(?,?,?,?)""" INSERT INTO data(config_id, generation, fitness, chromosome)
VALUES(?, ?, ?, ?)
"""
cur = self.conn.cursor() cur = self.conn.cursor()
cur.execute(sql, db_chromosome) cur.execute(sql, db_chromosome)
@ -243,8 +220,10 @@ class SQL_Database:
] ]
# Create sql query structure # Create sql query structure
sql = """INSERT INTO data(config_id, generation, fitness, chromosome) sql = """
VALUES(?,?,?,?)""" INSERT INTO data(config_id, generation, fitness, chromosome)
VALUES(?,?,?,?)
"""
cur = self.conn.cursor() cur = self.conn.cursor()
cur.executemany(sql, db_chromosome_list) cur.executemany(sql, db_chromosome_list)
@ -257,8 +236,7 @@ class SQL_Database:
#=====================================# #=====================================#
def create_connection(self): def create_connection(self):
"""Create a database connection to the SQLite database """Create a database connection to the SQLite database specified by db_file."""
specified by db_file."""
try: try:
self.conn = sqlite3.connect(self.database_name) self.conn = sqlite3.connect(self.database_name)
@ -266,6 +244,7 @@ class SQL_Database:
self.conn = None self.conn = None
print(e) print(e)
def create_table(self, create_table_sql): def create_table(self, create_table_sql):
"""Create a table from the create_table_sql statement.""" """Create a table from the create_table_sql statement."""
@ -276,22 +255,31 @@ class SQL_Database:
print(e) print(e)
@format_query_data def format_query_data(self, data):
"""Used to format query data."""
# Unpack elements if they are lists with only 1 element
if isinstance(data[0], (list, tuple)) and len(data[0]) == 1:
data = [i[0] for i in data]
# Unpack list if it is a list with only 1 element
if isinstance(data, (list, tuple)) and len(data) == 1:
data = data[0]
return data
def query_all(self, query): def query_all(self, query):
"""Query for muliple rows of data""" """Query for muliple rows of data"""
cur = self.conn.cursor() cur = self.conn.cursor()
cur.execute(query) cur.execute(query)
return cur.fetchall() return self.format_query_data(cur.fetchall())
@format_query_data
def query_one_item(self, query): def query_one_item(self, query):
"""Query for single data point""" """Query for single data point"""
cur = self.conn.cursor() cur = self.conn.cursor()
cur.execute(query) cur.execute(query)
return cur.fetchone() return self.format_query_data(cur.fetchone())
def remove_database(self): def remove_database(self):
@ -330,7 +318,7 @@ class SQL_Database:
# If the connection doesnt exist then print error # If the connection doesnt exist then print error
except: except:
raise Exception("""You are required to run a ga before you can connect to the database. Run ga.evolve() or ga.active()""") raise Exception("You are required to run a ga before you can connect to the database. Run ga.evolve() or ga.active()")
@conn.setter @conn.setter
@ -357,7 +345,7 @@ class SQL_Database:
# If the config_id doesnt exist then print error # If the config_id doesnt exist then print error
except: except:
raise Exception("""You are required to run a ga before you can connect to the database. Run ga.evolve() or ga.active()""") raise Exception("You are required to run a ga before you can connect to the database. Run ga.evolve() or ga.active()")
@config_id.setter @config_id.setter