from mediagoblin.db.indexes import ACTIVE_INDEXES, DEPRECATED_INDEXES
+################
+# Indexing tools
+################
+
+
def add_new_indexes(database, active_indexes=ACTIVE_INDEXES):
"""
Add any new indexes to the database.
indexes_removed.append((collection_name, index_name))
return indexes_removed
+
+
+#################
+# Migration tools
+#################
+
+# The default migration registry...
+#
+# Don't set this yourself! RegisterMigration will automatically fill
+# this with stuff via decorating methods in migrations.py
+
+MIGRATIONS = {}
+
+
+class RegisterMigration(object):
+ def __init__(self, migration_number, migration_registry=MIGRATIONS):
+ self.migration_number = migration_number
+ self.migration_registry = migration_registry
+
+ def __call__(self, migration):
+ self.migration_registry[self.migration_number] = migration
+ return migration
+
+
+class MigrationManager(object):
+ """
+ Migration handling tool.
+
+ Takes information about a database, lets you update the database
+ to the latest migrations, etc.
+ """
+ def __init__(self, database, migration_registry=MIGRATIONS):
+ """
+ Args:
+ - database: database we're going to migrate
+ - migration_registry: where we should find all migrations to
+ run
+ """
+ self.database = database
+ self.migration_registry = migration_registry
+ self._sorted_migrations = None
+
+ @property
+ def sorted_migrations(self):
+ """
+ Sort migrations if necessary and store in self._sorted_migrations
+ """
+ if not self._sorted_migrations:
+ self._sorted_migrations = sorted(
+ self.migration_registry.items(),
+ # sort on the key... the migration number
+ key=lambda migration_tuple: migration_tuple[0])
+
+ return self._sorted_migrations
+
+ def latest_migration(self):
+ """
+ Return a tuple like:
+ (migration_number, migration_func)
+
+ Where migration_number is the number of the latest migration
+ and migration func is the actual function that would be run.
+ """
+ return self.sorted_migrations[-1]
+
+ def set_current_migration(self, migration_number=None):
+ """
+ Set the migration in the database to migration_number
+ """
+ # Add the mediagoblin migration if necessary
+ self.database['app_metadata'].update(
+ {'_id': 'mediagoblin'},
+ {'$set': {'current_migration': migration_number}},
+ upsert=True)
+
+ def database_current_migration(self, install_if_missing=True):
+ """
+ Return the current migration in the database.
+ """
+ mgoblin_metadata = self.database['app_metadata'].find_one(
+ {'_id': 'mediagoblin'})
+ if not mgoblin_metadata:
+ if install_if_missing:
+ latest_migration = self.latest_migration()
+ self.set_current_migration(latest_migration)
+ return latest_migration
+ else:
+ return None
+ else:
+ return mgoblin_metadata['current_migration']
+
+ def database_at_latest_migration(self):
+ """
+ See if the database is at the latest migration.
+ Returns a boolean.
+ """
+ current_migration = self.database_current_migration()
+ return current_migration == self.latest_migration()
+
+ def migrations_to_run(self):
+ """
+ Get a list of migrations to run still, if any.
+ """
+ db_current_migration = self.database_current_migration()
+ return [
+ (migration_number, migration_func)
+ for migration_number, migration_func in self.sorted_migrations
+ if migration_number > db_current_migration]
+
+ def iteratively_migrate(self):
+ """
+ Iteratively run all migrations.
+
+ Useful if you need to print some message about each migration
+ after you run it.
+
+ Each time you loop over this, it'll return the migration
+ number and migration function.
+ """
+ for migration_number, migration_func in self.migrations_to_run():
+ migration_func(self.database)
+ self.set_current_migration(migration_number)
+ yield migration_number, migration_func
+
+ def run_outdated_migrations(self):
+ """
+ Install all migrations that need to be installed, quietly.
+ """
+ for migration_number, migration_func in self.iteratively_migrate():
+ # No need to say anything... we're just migrating iteratively.
+ pass