Ejemplo n.º 1
0
 def setUp(self):
     set_debug_log_level()
     self.db_folder = (pathlib.Path(__file__).parent / "data" /
                       "few_basic_cards")
     self.version2db = {
         0: load_db(self.db_folder / "collection.anki2"),
         1: load_db(self.db_folder / "collection_v1.anki2"),
     }
Ejemplo n.º 2
0
 def setUp(self):
     set_debug_log_level()
     self.db_read = load_db(self.db_read_path)
     self.db_write_dir = tempfile.TemporaryDirectory()
     self.db_write_path = (pathlib.Path(self.db_write_dir.name) /
                           "collection.anki2")
     self._reset()
Ejemplo n.º 3
0
    def __init__(self, path=None, user=None):
        """ Initialize :class:`~ankipandas.collection.Collection` object.

        Args:
            path: (Search) path to database see
                :py:func:`~ankipandas.paths.db_path_input` for more
                information.
            user: Anki user name. See
                :py:func:`~ankipandas.paths.db_path_input` for more
                information.

        Examples:

        .. code-block:: python

            from ankipandas import Collection

            # Let ankipandas find the db for you
            col = Collection()

            # Let ankipandas find the db for this user (important if you have
            # more than one user account in Anki)
            col = Collection(user="******")

            # Specify full path to Anki's database
            col = Collection("/full/path/to/collection.anki2")

            # Speficy partial path to Anki's database and specify user
            col = Collection("/partial/path/to/collection", user="******")

        """
        path = ankipandas.paths.db_path_input(path, user=user)

        #: Path to currently loaded database
        self.path = path  # type: Path

        log.info("Loaded db from {}".format(self.path))

        #: Opened Anki database (:class:`sqlite3.Connection`)
        self.db = raw.load_db(self.path)  # type: sqlite3.Connection

        #: Should be accessed with _get_item!
        self.__items = {
            "notes": None,
            "cards": None,
            "revs": None,
        }  # type: Dict[str, AnkiDataFrame]

        #: Should be accessed with _get_original_item!
        self.__original_items = {
            "notes": None,
            "cards": None,
            "revs": None,
        }  # type: Dict[str, AnkiDataFrame]
Ejemplo n.º 4
0
    def setUp(self):
        set_debug_log_level()
        self.db = raw.load_db(self.db_path)

        self.col = Collection(self.db_path)

        # Do not modify this one!
        self.notes = self.col.notes
        self.cards = self.col.cards
        self.revs = self.col.revs
        self.table2adf = {
            "notes": self.notes,
            "cards": self.cards,
            "revs": self.revs,
        }
        self.adfs = [self.notes, self.cards, self.revs]

        self.empty_notes = self.col.empty_notes()
        self.empty_cards = self.col.empty_cards()
        self.empty_revs = self.col.empty_revs()
Ejemplo n.º 5
0
    def setUp(self):
        self.db_path = (pathlib.Path(__file__).parent / "data" /
                        "few_basic_cards" / "collection.anki2")
        self.db = raw.load_db(self.db_path)

        self.col = Collection(self.db_path)

        # Do not modify this one!
        self.notes = self.col.notes
        self.cards = self.col.cards
        self.revs = self.col.revs
        self.table2adf = {
            "notes": self.notes,
            "cards": self.cards,
            "revs": self.revs,
        }
        self.adfs = [self.notes, self.cards, self.revs]

        self.empty_notes = self.col.empty_notes()
        self.empty_cards = self.col.empty_cards()
        self.empty_revs = self.col.empty_revs()
Ejemplo n.º 6
0
 def db(self) -> sqlite3.Connection:
     """Opened Anki database. Make sure to call `db.close()` after you're
     done. Better still, use `contextlib.closing`.
     """
     log.debug(f"Opening Db from {self._path}")
     return raw.load_db(self._path)
Ejemplo n.º 7
0
    def write(
        self,
        modify=False,
        add=False,
        delete=False,
        backup_folder: Union[PurePath, str] = None,
    ):
        """Creates a backup of the database and then writes back the new
        data.

        .. danger::

            The switches ``modify``, ``add`` and ``delete`` will run additional
            cross-checks, but do not rely on them to 100%!

        .. warning::

            It is recommended to run :meth:`summarize_changes` before to check
            whether the changes match your expectation.

        .. note::

            Please make sure to thoroughly check your collection in Anki after
            every write process!

        Args:
            modify: Allow modification of existing items (notes, cards, etc.)
            add: Allow adding of new items (notes, cards, etc.)
            delete: Allow deletion of items (notes, cards, etc.)
            backup_folder: Path to backup folder. If None is given, the backup
                is created in the Anki backup directory (if found).

        Returns:
            None
        """
        if not modify and not add and not delete:
            log.warning(
                "Please set modify=True, add=True or delete=True, you're"
                " literally not allowing me any modification at all.")
            return None

        try:
            prepared = self._prepare_write_data(modify=modify,
                                                add=add,
                                                delete=delete)
            log.debug("Now getting & updating info.")
            self._get_and_update_info()
        except Exception as e:
            log.critical(
                "Something went wrong preparing the data for writing. "
                "However, no data has been written out, so your "
                "database is save!")
            raise e
        else:
            log.debug("Successfully prepared data for writing.")

        if prepared == {}:
            log.warning(
                "Nothing seems to have been changed. Will not do anything!")
            return None

        backup_path = ankipandas.paths.backup_db(self.path,
                                                 backup_folder=backup_folder)
        log.info("Backup created at %s", backup_path.resolve())
        log.warning(
            "Currently AnkiPandas might not be able to tell Anki to"
            " sync its database. "
            "You might have to manually tell Anki to sync everything "
            "to AnkiDroid.\n"
            "Furthermore, if you run into issues with tag searches not working"
            "anymore, please first do Notes > Clear unused notes and then "
            "Tools > Check Database (from the main menu). This should get them"
            " to work (sorry about this issue).")

        # Actually setting values here, after all conversion tasks have been
        # carried out. That way if any of them fails, we don't have a
        # partially written collection.
        log.debug("Now actually writing to database.")
        try:
            for table, values in prepared.items():
                log.debug("Now setting table %s.", table)
                with closing(self.db) as db:
                    raw.set_table(db,
                                  values["raw"],
                                  table=table,
                                  mode=values["mode"])
                    log.debug("Setting table %s successful.", table)
            # log.debug("Now setting info")
            # raw.set_info(self.db, info)
            # log.debug("Setting info successful.")
        except Exception as e:
            log.critical(
                "Error while writing data to database at %s. "
                "This means that your database might have become corrupted. "
                "It's STRONGLY advised that you manually restore the database "
                "by replacing it with the backup from %s and restart"
                " from scratch. "
                "Please also open a bug report at "
                "https://github.com/klieret/AnkiPandas/issues/, as errors "
                "during the actual writing process should never occur!",
                self.path.resolve(),
                backup_path.resolve(),
            )
            raise e
        # This is important, because we have written to the database but still
        # have it opened, which can cause DatabaseErrors.
        log.debug("I will now drop all copies of the tables")
        for key in self.__items:
            self.__items[key] = None
        for key in self.__original_items:
            self.__original_items[key] = None
        log.debug("I will now reload the connection.")
        self._db = raw.load_db(self.path)
        log.info(
            "In case you're running this from a Jupyter notebook, make "
            "sure to shutdown the kernel or delete all ankipandas objects"
            " before you open anki to take a look at the result (only one "
            "application can use the database at a time).")
Ejemplo n.º 8
0
 def _reset(self):
     shutil.copy(str(self.db_read_path), str(self.db_write_path))
     self.db_write = load_db(self.db_write_path)