You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Lark/metadata.py

169 lines
5.3 KiB
Python

'''
metadata.py
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.
'''
import contextlib
import uuid
import sqlalchemy
import sqlalchemy.ext.declarative
import sqlalchemy.orm
# TODO: l o g g i n g
HASH_CHUNK_SIZE = 10485760 # 10mb
_SQLBase = sqlalchemy.ext.declarative.declarative_base()
'''
Metadata ORM classes for SQLAlchemy. For a detailed description of each piece of data, refer to
metadata/README.md
'''
class Platform(_SQLBase):
'''SQLAlchemy ORM class for platform metadata.'''
__tablename__ = 'platforms'
id = sqlalchemy.Column(
sqlalchemy.Integer, sqlalchemy.Sequence('platform_id_sequence'), primary_key=True
)
fullname = sqlalchemy.Column(sqlalchemy.String, nullable=False)
shortcode = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
regional_names = sqlalchemy.Column(sqlalchemy.String)
release_groups = sqlalchemy.orm.relationship(
'ReleaseGroup', order_by=ReleaseGroup.id, back_populates='platform'
)
def __repr__(self):
return 'Platform: id: %s, fullname: %s, shortcode: %s' % (
self.id, self.fullname, self.shortcode
)
class ReleaseGroup(_SQLBase):
'''SQLAlchemy ORM class for release group metadata.'''
__tablename__ = 'release_groups'
id = sqlalchemy.Column(
sqlalchemy.Integer, sqlalchemy.Sequence('release_group_id_sequence'), primary_key=True
)
name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
platform_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('platforms.id'))
releases = sqlalchemy.orm.relationship('Release', back_populates='release_group')
def __repr__(self):
return 'Release Group: id: %s, name: %s, platform:%s' % (
self.id, self.name, self.platform.fullname
)
class Release(_SQLBase):
'''SQLAlchemy ORM class for release metadata.'''
__tablename__ = 'releases'
id = sqlalchemy.Column(
sqlalchemy.Integer, sqlalchemy.Sequence('release_id_sequence'), primary_key=True
)
sha1sum = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
en_name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
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 ('Release: id: %s, en_name: %s' % (self.id, self.en_name )
class Image(_SQLBase):
'''SQLAlchemy ORM class for ROM image metadata.'''
__tablename__ = 'images'
id = sqlalchemy.Column(
sqlalchemy.Integer, sqlalchemy.Sequence('image_id_sequence'), primary_key=True
)
sha1sum = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
format = sqlalchemy.Column(sqlalchemy.String, nullable=False)
region = sqlalchemy.Column(sqlalchemy.String, nullable=False)
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, sha1sum: %s, release-group: %s, region: %s, version: %s, '
'disambiguation: %s' % (
self.id, self.sha1sum, self.release_group.name, self.region, self.version,
self.disambiguation
)
)
class hashdb:
_engine = None
# TODO: db_path's default should be set here, not in frontend.
def __init__(self, db_path):
'''
Configure and initialize the database for the entire module.
Currently, only SQLite is supported.
db_path: Path for the SQLite database
'''
self._engine = sqlalchemy.create_engine('sqlite:///%s' % db_path)
_SQLBase.metadata.create_all(self._engine)
self._session_maker.configure(bind=self._engine)
def search(self, table_object, **constraints):
'''
Search the database for entries matching the given constraints.
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.
'''
with self._get_db_session() as session:
# TODO: Consider making this return data recursively on items that reference other
# tables.
query = session.query(table_object)
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)
return item_list
def import_json(self, json_file):
'''
Import metadata from a json file.
'''
# json files can be large, but not too large for ram
# do not exit on invalid metadata, log the error and skip the object
pass
@contextlib.contextmanager
def _get_db_session():
'''Get a SQLAlchemy database session with a proper context object. '''
session = sqlalchemy.orm.sessionmaker()
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()