feat: database migration function mentioned in #133

This commit is contained in:
Terry Geng 2020-04-24 10:41:47 +08:00
parent 7f8b9eab5e
commit 09df681ebe
4 changed files with 100 additions and 67 deletions

4
.gitignore vendored
View File

@ -108,6 +108,8 @@ venv.bak/
configuration.ini
.vscode/settings.json
2019-07-27 22_09_08-radiobrowser.py - botamusique - Visual Studio Code.png
.DS_Store
*.pem
music_folder/
tmp/
@ -115,4 +117,4 @@ tmp/
database.db
# Pycharm
.idea/
.idea/

View File

@ -3,7 +3,9 @@ import sqlite3
import json
import datetime
import time
import logging
log = logging.getLogger("bot")
class DatabaseError(Exception):
pass
@ -187,7 +189,7 @@ class Condition:
return self
SETTING_DB_VERSION = 1
MUSIC_DB_VERSION = 1
MUSIC_DB_VERSION = 2
class SettingsDatabase:
def __init__(self, db_path):
@ -319,60 +321,9 @@ class MusicDatabase:
def __init__(self, db_path):
self.db_path = db_path
self.db_version_check_and_create()
MusicDatabaseMigration(self).migrate()
self.manage_special_tags()
def has_table(self, table):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
tables = cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?;", (table,)).fetchall()
conn.close()
if len(tables) == 0:
return False
return True
def create_table(self):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("CREATE TABLE music ("
"id TEXT PRIMARY KEY, "
"type TEXT, "
"title TEXT, "
"keywords TEXT, "
"metadata TEXT, "
"tags TEXT, "
"path TEXT, "
"create_at DATETIME DEFAULT CURRENT_TIMESTAMP"
")")
cursor.execute("INSERT INTO music (id, title) "
"VALUES ('info', ?)", (MUSIC_DB_VERSION,))
conn.commit()
conn.close()
def db_version_check_and_create(self):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if self.has_table('music'):
ver = cursor.execute("SELECT title FROM music WHERE id='info'").fetchone()
if ver and int(ver[0]) == MUSIC_DB_VERSION:
conn.close()
return True
else:
cursor.execute("ALTER TABLE music RENAME TO music_old")
conn.commit()
self.create_table()
cursor.execute("INSERT INTO music (id, type, title, metadata, tags)"
"SELECT id, type, title, metadata, tags FROM music_old")
cursor.execute("DROP TABLE music_old")
conn.commit()
conn.close()
else:
self.create_table()
def insert_music(self, music_dict, _conn=None):
conn = sqlite3.connect(self.db_path) if _conn is None else _conn
cursor = conn.cursor()
@ -567,8 +518,6 @@ class MusicDatabase:
if 'path' not in music_dict or result[5]:
music_dict['path'] = result[5]
music_dict['keywords'] = result[6]
if not music_dict['tags'][0]:
music_dict['tags'] = []
music_dicts.append(music_dict)
@ -589,3 +538,89 @@ class MusicDatabase:
cursor = conn.cursor()
cursor.execute("DROP TABLE music")
conn.close()
class MusicDatabaseMigration:
def __init__(self, db: MusicDatabase):
self.db = db
self.migrate_func = {}
self.migrate_func[0] = self.migrate_from_0_to_1
self.migrate_func[1] = self.migrate_from_1_to_2
def migrate(self):
conn = sqlite3.connect(self.db.db_path)
cursor = conn.cursor()
if self.has_table('music', conn):
current_version = 0
ver = cursor.execute("SELECT title FROM music WHERE id='info'").fetchone()
if ver:
current_version = int(ver[0])
if current_version == MUSIC_DB_VERSION:
conn.close()
return
else:
log.info(f"database: migrating from music table version {current_version} to {MUSIC_DB_VERSION}...")
while current_version < MUSIC_DB_VERSION:
log.debug(f"database: migrate step {current_version}/{MUSIC_DB_VERSION - 1}")
current_version = self.migrate_func[current_version](conn)
log.info(f"database: migration done.")
else:
log.info(f"database: no music table found. Creating music table version {MUSIC_DB_VERSION}.")
self.create_table_version_2(conn)
conn.close()
def has_table(self, table, conn):
cursor = conn.cursor()
tables = cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?;", (table,)).fetchall()
if len(tables) == 0:
return False
return True
def create_table_version_1(self, conn):
cursor = conn.cursor()
cursor.execute("CREATE TABLE music ("
"id TEXT PRIMARY KEY, "
"type TEXT, "
"title TEXT, "
"keywords TEXT, "
"metadata TEXT, "
"tags TEXT, "
"path TEXT, "
"create_at DATETIME DEFAULT CURRENT_TIMESTAMP"
")")
cursor.execute("INSERT INTO music (id, title) "
"VALUES ('info', ?)", (MUSIC_DB_VERSION,))
conn.commit()
def create_table_version_2(self, conn):
self.create_table_version_1(conn)
def migrate_from_0_to_1(self, conn):
cursor = conn.cursor()
cursor.execute("ALTER TABLE music RENAME TO music_old")
conn.commit()
self.create_table_version_1(conn)
cursor.execute("INSERT INTO music (id, type, title, metadata, tags)"
"SELECT id, type, title, metadata, tags FROM music_old")
cursor.execute("DROP TABLE music_old")
conn.commit()
return 1 # return new version number
def migrate_from_1_to_2(self, conn):
items_to_update = self.db.query_music(Condition(), conn)
for item in items_to_update:
item['keywords'] = item['title']
if 'artist' in item:
item['keywords'] += ' ' + item['artist']
self.db.insert_music(item)
conn.commit()
return 2 # return new version number

View File

@ -73,10 +73,6 @@ class FileItem(BaseItem):
"file: music file missed for %s" % self.format_debug_string())
raise ValidationFailedError(constants.strings('file_missed', file=self.path))
if not self.keywords:
self.keywords = self.title + " " + self.artist # migrate from previous version
self.version += 1
# self.version += 1 # 0 -> 1, notify the wrapper to save me when validate() is visited the first time
self.ready = "yes"
return True

View File

@ -48,13 +48,6 @@ class MumbleBot:
else:
self.channel = var.config.get("server", "channel", fallback=None)
if args.verbose:
self.log.setLevel(logging.DEBUG)
self.log.debug("Starting in DEBUG loglevel")
elif args.quiet:
self.log.setLevel(logging.ERROR)
self.log.error("Starting in ERROR loglevel")
var.user = args.user
var.music_folder = util.solve_filepath(var.config.get('bot', 'music_folder'))
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
@ -711,6 +704,13 @@ if __name__ == '__main__':
formatter = logging.Formatter('[%(asctime)s %(levelname)s %(threadName)s] %(message)s', "%b %d %H:%M:%S")
bot_logger.setLevel(logging.INFO)
if args.verbose:
bot_logger.setLevel(logging.DEBUG)
bot_logger.debug("Starting in DEBUG loglevel")
elif args.quiet:
bot_logger.setLevel(logging.ERROR)
bot_logger.error("Starting in ERROR loglevel")
logfile = util.solve_filepath(var.config.get('bot', 'logfile'))
handler = None
if logfile: