From 2a95a938447e254ff5af5b10142ad18a09048e09 Mon Sep 17 00:00:00 2001 From: Emily Frost Date: Wed, 4 May 2022 22:56:28 -0500 Subject: [PATCH] Cleaned up and retabulated metadata.py. --- metadata.py | 188 ++++++++++++++++++++++----------------------- metadata/README.md | 1 - 2 files changed, 93 insertions(+), 96 deletions(-) diff --git a/metadata.py b/metadata.py index 95844b6..38acc6b 100644 --- a/metadata.py +++ b/metadata.py @@ -16,126 +16,124 @@ HASH_CHUNK_SIZE = 10485760 # 10mb _db_session_maker = sqlalchemy.orm.sessionmaker() _engine = None _configured = False - -# TODO: Support DAT credit, DAT filenames, and checking DAT completeness. -#DatData = collections.namedtuple('DatData', 'UUID, name, website, version, image_list') +_SQLBase = sqlalchemy.ext.declarative.declarative_base() class MetadataDBSessionException(Exception): - '''This exception is raised when something goes wrong with a database session.''' + '''This exception is raised when something goes wrong with a database session.''' def _uuidgen(): - return str(uuid.uuid4()) + return str(uuid.uuid4()) -_SQLBase = sqlalchemy.ext.declarative.declarative_base() +class Platform(_SQLBase): + '''SQLAlchemy ORM class for platform metadata.''' + __tablename__ = 'platforms' + id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.Sequence('platform_id_sequence'), + primary_key=True) + uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen) + fullname = sqlalchemy.Column(sqlalchemy.String, nullable=False) + shortcode = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False) -class Release(_SQLBase): - '''SQLAlchemy ORM class for ROM image metadata.''' - __tablename__ = 'images' - id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.Sequence('image_id_sequence'), - primary_key=True) - uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen) - sha1sum = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False) - format = sqlalchemy.Column(sqlalchemy.String, nullable=False) - region = sqlalchemy.Column(sqlalchemy.String) - version = sqlalchemy.Column(sqlalchemy.String, nullable=False) - disambiguation = sqlalchemy.Column(sqlalchemy.String) - release_group_id = sqlalchemy.Column(sqlalchemy.Integer, - sqlalchemy.ForeignKey('release_groups.id')) - - release_group = sqlalchemy.orm.relationship('ReleaseGroup', back_populates='releases') - - def __repr__(self): - return ('ROM Image: id: %s, uuid: %s, sha1sum: %s, release-group: %s, region: %s, ' - 'version: %s, disambiguation: %s' % ( - self.id, self.uuid, self.sha1sum, self.release_group.name, self.region, - self.version, self.disambiguation)) + release_groups = sqlalchemy.orm.relationship('ReleaseGroup', order_by=ReleaseGroup.id, + back_populates='platform') -class ReleaseGroup(_SQLBase): - '''SQLAlchemy ORM class for release group metadata.''' - __tablename__ = 'release_groups' - id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.Sequence('image_id_sequence'), - primary_key=True) - uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen) - name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False) - platform_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('platforms.id')) + def __repr__(self): + return 'Platform: id: %s, uuid: %s, fullname: %s, shortcode: %s' % (self.id, self.uuid, + self.fullname, self.shortcode) - platform = sqlalchemy.orm.relationship('Platform', back_populates='release_groups') - images = sqlalchemy.orm.relationship('Release', back_populates='release_group') - def __repr__(self): - return 'Release Group: id: %s, uuid: %s, name: %s, platform:%s' % (self.id, self.uuid, - self.name, self.platform.fullname) +class ReleaseGroup(_SQLBase): + '''SQLAlchemy ORM class for release group metadata.''' + __tablename__ = 'release_groups' + id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.Sequence('image_id_sequence'), + primary_key=True) + uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen) + name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False) + platform_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('platforms.id')) + platform = sqlalchemy.orm.relationship('Platform', back_populates='release_groups') + images = sqlalchemy.orm.relationship('Release', back_populates='release_group') -class Platform(_SQLBase): - '''SQLAlchemy ORM class for platform metadata.''' - __tablename__ = 'platforms' - id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.Sequence('platform_id_sequence'), - primary_key=True) - uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen) - fullname = sqlalchemy.Column(sqlalchemy.String, nullable=False) - shortcode = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False) + def __repr__(self): + return 'Release Group: id: %s, uuid: %s, name: %s, platform:%s' % (self.id, self.uuid, + self.name, self.platform.fullname) - release_groups = sqlalchemy.orm.relationship('ReleaseGroup', order_by=ReleaseGroup.id, - back_populates='platform') - def __repr__(self): - return 'Platform: id: %s, uuid: %s, fullname: %s, shortcode: %s' % (self.id, self.uuid, - self.fullname, self.shortcode) +class Release(_SQLBase): + '''SQLAlchemy ORM class for ROM image metadata.''' + __tablename__ = 'images' + id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.Sequence('image_id_sequence'), + primary_key=True) + uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen) + sha1sum = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False) + format = sqlalchemy.Column(sqlalchemy.String, nullable=False) + region = sqlalchemy.Column(sqlalchemy.String) + version = sqlalchemy.Column(sqlalchemy.String, nullable=False) + disambiguation = sqlalchemy.Column(sqlalchemy.String) + release_group_id = sqlalchemy.Column(sqlalchemy.Integer, + sqlalchemy.ForeignKey('release_groups.id')) + + release_group = sqlalchemy.orm.relationship('ReleaseGroup', back_populates='releases') + + def __repr__(self): + return ('ROM Image: id: %s, uuid: %s, sha1sum: %s, release-group: %s, region: %s, ' + 'version: %s, disambiguation: %s' % ( + self.id, self.uuid, self.sha1sum, self.release_group.name, self.region, + self.version, self.disambiguation)) + def configure(db_path): - ''' - Configure and initialize the database for the entire module. - Currently, only SQLite is supported. + ''' + Configure and initialize the database for the entire module. + Currently, only SQLite is supported. - db_path: Path for the SQLite database - ''' - _engine = sqlalchemy.create_engine('sqlite:///%s' % db_path) + db_path: Path for the SQLite database + ''' + _engine = sqlalchemy.create_engine('sqlite:///%s' % db_path) - _SQLBase.metadata.create_all(_engine) - _db_session_maker.configure(bind=_engine) - _configured = True + _SQLBase.metadata.create_all(_engine) + _db_session_maker.configure(bind=_engine) + _configured = True # TODO: Passing the session object is a little clunky. Maybe there's a way to infer it somehow? def search(session, table_object, **constraints): - ''' - Search the database for entries matching the given constraints. + ''' + Search the database for entries matching the given constraints. - session: SQLAlchemy session, presumably from get_db_session - table_object: SQLAlchemy ORM table object, defined in the file above - constraints: key-value pairs to match against specific fields in the database - Note: Currently, only the query.ilike method is supported. This is intended to - eventually support the entire range of available filters. - ''' - query = session.query(table_object) + session: SQLAlchemy session, presumably from get_db_session + table_object: SQLAlchemy ORM table object, defined in the file above + constraints: key-value pairs to match against specific fields in the database + Note: Currently, only the query.ilike method is supported. This is intended to + eventually support the entire range of available filters. + ''' + query = session.query(table_object) - for key, value in constraints.items(): - query = query.filter(getattr(table_object, key).ilike('%%%s%%' % value)) + for key, value in constraints.items(): + query = query.filter(getattr(table_object, key).ilike('%%%s%%' % value)) - item_list = [] - for item in query: - item_list.append(item) + item_list = [] + for item in query: + item_list.append(item) - return item_list + return item_list @contextlib.contextmanager def get_db_session(): - '''Get a SQLAlchemy database session with a proper context object. ''' - # TODO: There's probably a more reliable way of knowing whether the database was configured. - if not _configured: - raise MetadataDBSessionException('Tried to get session without configuring a database.') - - session = _db_session_maker() - try: - yield session - - except: - # TODO: Decide which exceptions to handle/eat here and which ones belong in UI. - # This one is okay to put off until you start really building UI. - session.rollback() - raise - else: - session.commit() - finally: - session.close() + '''Get a SQLAlchemy database session with a proper context object. ''' + # TODO: There's probably a more reliable way of knowing whether the database was configured. + if not _configured: + raise MetadataDBSessionException('Tried to get session without configuring a database.') + + session = _db_session_maker() + try: + yield session + + except: + # TODO: Decide which exceptions to handle/eat here and which ones belong in UI. + # This one is okay to put off until you start really building UI. + session.rollback() + raise + else: + session.commit() + finally: + session.close() diff --git a/metadata/README.md b/metadata/README.md index 65e0f28..9975ca1 100644 --- a/metadata/README.md +++ b/metadata/README.md @@ -33,7 +33,6 @@ A container for all languages and versions of a release. For example, the Star F * releases - A list containing all releases in this release group. - ## Release A single release of a game. For example: Star Fox 64 (U) v1.2