|
|
|
@ -4,6 +4,7 @@ metadatadb.py
|
|
|
|
Everything needed to interact with the metadata database; namely SQL ORM objects, a context
|
|
|
|
Everything needed to interact with the metadata database; namely SQL ORM objects, a context
|
|
|
|
generator for the actual database, and a handful of convenience functions.
|
|
|
|
generator for the actual database, and a handful of convenience functions.
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
|
|
|
|
# TODO: Rename file to metadata.py
|
|
|
|
import contextlib
|
|
|
|
import contextlib
|
|
|
|
import collections
|
|
|
|
import collections
|
|
|
|
import hashlib
|
|
|
|
import hashlib
|
|
|
|
@ -18,6 +19,7 @@ HASH_CHUNK_SIZE = 10485760 # 10mb
|
|
|
|
_db_session_maker = sqlalchemy.orm.sessionmaker()
|
|
|
|
_db_session_maker = sqlalchemy.orm.sessionmaker()
|
|
|
|
_engine = None
|
|
|
|
_engine = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: Support DAT credit, DAT filenames, and checking DAT completeness.
|
|
|
|
DatData = collections.namedtuple('DatData', 'UUID, name, website, version, image_list')
|
|
|
|
DatData = collections.namedtuple('DatData', 'UUID, name, website, version, image_list')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -26,6 +28,7 @@ def _uuidgen():
|
|
|
|
|
|
|
|
|
|
|
|
_SQLBase = sqlalchemy.ext.declarative.declarative_base()
|
|
|
|
_SQLBase = sqlalchemy.ext.declarative.declarative_base()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: Rename Image concept to "Release".
|
|
|
|
class Image(_SQLBase):
|
|
|
|
class Image(_SQLBase):
|
|
|
|
'''SQLAlchemy ORM class for ROM image metadata.'''
|
|
|
|
'''SQLAlchemy ORM class for ROM image metadata.'''
|
|
|
|
# TODO: Split filenames into more meaningful metadata.
|
|
|
|
# TODO: Split filenames into more meaningful metadata.
|
|
|
|
@ -35,12 +38,14 @@ class Image(_SQLBase):
|
|
|
|
uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen)
|
|
|
|
uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen)
|
|
|
|
sha1sum = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
sha1sum = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
filename = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
filename = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
|
|
|
|
release_group_id = sqlalchemy.Column(sqlalchemy.Integer,
|
|
|
|
|
|
|
|
sqlalchemy.ForeignKey('release_groups.id'))
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: Add many-to-one relationship to release groups.
|
|
|
|
release_group = sqlalchemy.orm.relationship('ReleaseGroup', back_populates='images')
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
def __repr__(self):
|
|
|
|
return 'ROM Image: id: %s, uuid: %s, sha1sum: %s, filename: %s' % (self.id, self.uuid,
|
|
|
|
return 'ROM Image: id: %s, uuid: %s, sha1sum: %s, filename: %s, release-group: %s' % (
|
|
|
|
self.sha1sum, self.filename)
|
|
|
|
self.id, self.uuid, self.sha1sum, self.filename, self.release_group.name)
|
|
|
|
|
|
|
|
|
|
|
|
class ReleaseGroup(_SQLBase):
|
|
|
|
class ReleaseGroup(_SQLBase):
|
|
|
|
'''SQLAlchemy ORM class for release group metadata.'''
|
|
|
|
'''SQLAlchemy ORM class for release group metadata.'''
|
|
|
|
@ -49,11 +54,14 @@ class ReleaseGroup(_SQLBase):
|
|
|
|
primary_key=True)
|
|
|
|
primary_key=True)
|
|
|
|
uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen)
|
|
|
|
uuid = sqlalchemy.Column(sqlalchemy.String, nullable=False, default=_uuidgen)
|
|
|
|
name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
# TODO: Add many-to-one relationship to platforms.
|
|
|
|
platform_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('platforms.id'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
platform = sqlalchemy.orm.relationship('Platform', back_populates='release_groups')
|
|
|
|
|
|
|
|
images = sqlalchemy.orm.relationship('Image', back_populates='release_group')
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
def __repr__(self):
|
|
|
|
return 'Release Group: id: %s, uuid: %s, name: %s' % (self.id, self.uuid,
|
|
|
|
return 'Release Group: id: %s, uuid: %s, name: %s, platform:%s' % (self.id, self.uuid,
|
|
|
|
self.name)
|
|
|
|
self.name, self.platform.fullname)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Platform(_SQLBase):
|
|
|
|
class Platform(_SQLBase):
|
|
|
|
@ -65,6 +73,9 @@ class Platform(_SQLBase):
|
|
|
|
fullname = sqlalchemy.Column(sqlalchemy.String, nullable=False)
|
|
|
|
fullname = sqlalchemy.Column(sqlalchemy.String, nullable=False)
|
|
|
|
shortcode = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
shortcode = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
release_groups = sqlalchemy.orm.relationship('ReleaseGroup', order_by=ReleaseGroup.id,
|
|
|
|
|
|
|
|
back_populates='platform')
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
def __repr__(self):
|
|
|
|
return 'Platform: id: %s, uuid: %s, fullname: %s, shortcode: %s' % (self.id, self.uuid,
|
|
|
|
return 'Platform: id: %s, uuid: %s, fullname: %s, shortcode: %s' % (self.id, self.uuid,
|
|
|
|
self.fullname, self.shortcode)
|
|
|
|
self.fullname, self.shortcode)
|
|
|
|
@ -94,6 +105,8 @@ def configure(db_path):
|
|
|
|
_SQLBase.metadata.create_all(_engine)
|
|
|
|
_SQLBase.metadata.create_all(_engine)
|
|
|
|
_db_session_maker.configure(bind=_engine)
|
|
|
|
_db_session_maker.configure(bind=_engine)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: Passing the session object is a little clunky. Maybe there's a way to infer it somehow?
|
|
|
|
|
|
|
|
# Maybe setting a _session class variable?
|
|
|
|
def search(session, table_object, **constraints):
|
|
|
|
def search(session, table_object, **constraints):
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
Search the database for entries matching the given constraints.
|
|
|
|
Search the database for entries matching the given constraints.
|
|
|
|
@ -118,11 +131,14 @@ def search(session, table_object, **constraints):
|
|
|
|
@contextlib.contextmanager
|
|
|
|
@contextlib.contextmanager
|
|
|
|
def get_db_session():
|
|
|
|
def get_db_session():
|
|
|
|
'''Get a SQLAlchemy database session with a proper context object. '''
|
|
|
|
'''Get a SQLAlchemy database session with a proper context object. '''
|
|
|
|
|
|
|
|
# TODO: Raise an exception if _db_session_maker() isn't configured.
|
|
|
|
session = _db_session_maker()
|
|
|
|
session = _db_session_maker()
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
yield session
|
|
|
|
yield session
|
|
|
|
|
|
|
|
|
|
|
|
except:
|
|
|
|
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()
|
|
|
|
session.rollback()
|
|
|
|
raise
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
|