''' hashdb.py ''' import collections import hashlib import sqlite3 import uuid # TODO: l o g g i n g HASH_CHUNK_SIZE = 10485760 # 10mb SQL_AND = ' AND ' SQL_OR = ' OR ' # TODO: Figure out how to do some kind of type checking for these named tuples. # TODO: Switch these over to using traditional classes. # TODO: UUID generation/editing is probably best done in here. ImageData = collections.namedtuple('ImageData', 'UUID, sha1sum, filename, release_group') ReleaseGroupData = collections.namedtuple('ReleaseGroupData', 'UUID, name, platform') PlatformData = collections.namedtuple('PlatformData', 'UUID, name, shortcode') DatData = collections.namedtuple('DatData', 'UUID, name, website, version, image_list') # TODO: This should go in the eventual romdb class. def get_file_sha1sum(filename): sha1sum = hashlib.sha1() with open(filename, 'rb') as file_contents: while True: chunk = file_contents.read(HASH_CHUNK_SIZE) if not chunk: break sha1sum.update(chunk) return sha1sum.hexdigest() def _build_sql_constraints(inclusive, constraints): '''Build an SQL constraint clause out of a dictionary. inclusive - If True, uses SQL AND operator, otherwise OR will be used. constraints - A dictionary of arbitrary constraints returns a tuple containing the appropriately formatted SQL string and a list of parameters ''' if constraints == {}: return ('', []) if inclusive: logical_separator = SQL_AND else: logical_separator = SQL_OR sql_constraint_string = 'WHERE ' sql_parameter_list = [] for key, value in constraints.items(): sql_constraint_string += '%s=?%s' % (key, logical_separator) sql_parameter_list.append(value) # Trim off the last ', ' sql_constraint_string = sql_constraint_string[0:-len(logical_separator)] return (sql_constraint_string, sql_parameter_list) class MetadataDB: def __init__(self, db_path): ''' If db file does not exist, create it and create necessary tables. Either way, create a connection object. ''' # TODO: This process needs real error handling. self._connection = sqlite3.connect(db_path) with self._connection: self._connection.execute('CREATE TABLE IF NOT EXISTS images (uuid PRIMARY KEY, ' 'sha1sum UNIQUE NOT NULL, filename NOT NULL, release_group);') self._connection.execute('CREATE TABLE IF NOT EXISTS release_groups (UUID PRIMARY KEY, ' 'name NOT NULL, platform NOT NULL);') self._connection.execute('CREATE TABLE IF NOT EXISTS platforms (UUID PRIMARY KEY, ' 'name NOT NULL, shortcode UNIQUE NOT NULL);') # TODO: Add DAT import support. print('Database initialized.') def add_image(self, image_data): pass def update_image(self, image_data): pass def remove_image(self, image_data): pass def add_release_group(self, release_group_data): pass def update_release_group(self, release_group_data): pass def remove_release_group(self, release_group_data): pass def add_platform(self, platform_data): ''' Add a platform shortcode to the database. ''' with self._connection: self._connection.execute('INSERT INTO platforms VALUES (?, ?, ?);', platform_data) def update_platform(self, platform_data): pass # TODO: Switch this to use more generic constraints language def remove_platform(self, platform_shortcode): with self._connection: self._connection.execute('DELETE FROM platforms WHERE shortcode=?;', platform_shortcode) def search_platforms(self, inclusive=True, **constraints): '''Search for platforms, given the parameters. ''' sql_where_clause, sql_parameters = _build_sql_constraints(inclusive, constraints) platform_data_list = [] with self._connection: cursor = self._connection.cursor() sql_query = 'SELECT * FROM platforms %s;' % sql_where_clause cursor.execute(sql_query, sql_parameters) rows = cursor.fetchall() for row in rows: platform_data = PlatformData(*row) platform_data_list.append(platform_data) return platform_data_list def add_dat(self, dat_data): pass def update_dat(self, dat_data): pass def remove_dat(self, dat_data): pass