"""Automatic migration tools for the da:ta stored in MongoDB.""" import logging from abc import ABC from abc import abstractmethod from typing import List from typing import Type from config import DB logger = logging.getLogger(__name__) # Used to keep track of all the defined migrations _MIGRATIONS: List[Type["Migration"]] = [] def perform() -> None: """Perform all the defined migration.""" for migration in _MIGRATIONS: migration().perform() class Migration(ABC): """Abstract class for migrations.""" def __init__(self) -> None: self.name = self.__class__.__qualname__ self._col = DB.migrations def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) _MIGRATIONS.append(cls) def _apply(self) -> None: self._col.insert_one({"name": self.name}) def _reset(self) -> None: self._col.delete_one({"name": self.name}) def _is_applied(self) -> bool: return bool(self._col.find_one({"name": self.name})) @abstractmethod def migrate(self) -> None: """Expected to be implemented by actual migrations.""" pass def perform(self) -> None: if self._is_applied(): logger.info(f"Skipping migration {self.name} (already applied)") return logger.info(f"Performing migration {self.name}...") self.migrate() self._apply() logger.info("Done")