Пример #1
0
class MnemosyneProxy(SRSProxy):
    """
    An abstraction over Mnemosyne interface.
    """

    CLOSE_MODEL = "5"
    DEFAULT_DECK = None
    DEFAULT_MODEL = "1"
    SYMBOL_EQ_OPEN = "<$>"
    SYMBOL_EQ_CLOSE = "</$>"

    def __init__(self, path=None):
        from mnemosyne.script import Mnemosyne

        try:
            self.mnemo = Mnemosyne(path)

            # Activate the Cloze plugin
            # Note: The import needs to be here, since it relies on the
            # presence of the translation engine, which is initialized with
            # the mnemosyne object.
            from mnemosyne.libmnemosyne.card_types.cloze import ClozePlugin
            for plugin in self.mnemo.plugins():
                if isinstance(plugin, ClozePlugin):
                    plugin.activate()
                    break

        except SystemExit:
            raise KnowledgeException(
                "Mnemosyne is running. Please close it and reopen the file."
            )

    def cleanup(self):
        self.mnemo.finalise()
        del self.mnemo

    def extract_data(self, fields, model):
        """
        Extracts the data dict from the given fields, depending
        on the model being used.
        """

        # Transform the fields data to mnemosyne format
        if model == self.CLOSE_MODEL:
            data = {'text': fields.get("Text")}
        else:
            data = {
                'f': fields.get("Front"),
                'b': fields.get("Back"),
            }

        return data

    def add_note(self, deck, model, fields, tags=None):
        """
        Adds a new fact with specified fields, model name and tags.
        Returns the ID of the fact.
        """

        # Pre-process data in fields
        fields = self.process_all(fields)
        data = self.extract_data(fields, model)

        # Convert the deck name to the tag
        tags = (tags or set())
        if deck is not None:
            tags.add(deck.replace('.', '::'))

        try:
            card_type = self.mnemo.card_type_with_id(model)
        except KeyError:
            raise KnowledgeException("Model (card type) '{0}' does "
                                     "not exist".format(model))
        controller = self.mnemo.controller()

        try:
            cards = controller.create_new_cards(
                data,
                card_type,
                grade=-1,
                tag_names=tags,
                check_for_duplicates=False,
                save=False,
            )
        except AssertionError:
            raise KnowledgeException("Fact '{0}' could not be added, it "
                                     "most likely contains invalid "
                                     "data".format(fields))

        # We expect exactly one card created for regular cards,
        # or at least one for closes
        if model == self.DEFAULT_MODEL:
            assert len(cards) == 1
        elif model == self.CLOSE_MODEL:
            assert len(cards) >= 1

        # Return the fact ID
        return cards[0].fact.id

    def update_note(self, identifier, fields, deck=None, model=None, tags=None):
        # Get the fact from Mnemosyne
        db = self.mnemo.database()

        try:
            fact = db.fact(identifier, is_id_internal=False)
        except TypeError:
            # Mnemosyne raises TypeError in case ID is not found
            raise FactNotFoundException("Fact with ID '{0}' could not be found"
                                        .format(identifier))

        cards = db.cards_from_fact(fact)
        if not cards:
            raise FactNotFoundException("Fact with ID '{0}' does not have any"
                                        "cards assigned".format(identifier))

        # Convert the deck name to the tag
        tags = (tags or set())
        if deck is not None:
            tags.add(deck.replace('.', '::'))

        # Pre-process data in fields
        fields = self.process_all(fields)

        # Transform the fields data to mnemosyne format
        data = self.extract_data(fields, model)

        current_data = fact.data
        current_tags = set([tag.name for tag in cards[0].tags])

        # Bail out if no modifications to be performed
        if current_tags == tags and current_data == data:
            return

        # Update the fact
        card_type = self.mnemo.card_type_with_id(model)
        new, edited, deleted = card_type.edit_fact(fact, data)
        fact.data = data
        db.update_fact(fact)

        # Create, delete and edit all cards that were affected by this update
        # This mainly happens with card types that generate multiple cards, like
        # questions with multiple closes
        for card in deleted:
            db.delete_card(card)

        for card in new:
            db.add_card(card)

        for card in edited:
            db.update_card(card)

        # Refetch the list of cards
        cards = db.cards_from_fact(fact)

        # Set new tags for each card
        old_tag_objects = set()
        new_tag_objects = db.get_or_create_tags_with_names(tags)

        # Fetch the current time
        modification_time = int(time.time())

        for card in cards:
            old_tag_objects |= card.tags
            card.modification_time = modification_time
            card.tags = new_tag_objects
            db.update_card(card)

        # Remove redundant tags
        for tag in old_tag_objects:
            db.delete_tag_if_unused(tag)

    def commit(self):
        db = self.mnemo.database()
        db.save()
Пример #2
0
class MnemosyneProxy(SRSProxy):
    """
    An abstraction over Mnemosyne interface.
    """

    CLOSE_MODEL = "5"
    DEFAULT_DECK = None
    DEFAULT_MODEL = "1"
    SYMBOL_EQ_OPEN = "<$>"
    SYMBOL_EQ_CLOSE = "</$>"
    SYMBOL_B_OPEN = "<b>"
    SYMBOL_B_CLOSE = "</b>"
    SYMBOL_I_OPEN = "<i>"
    SYMBOL_I_CLOSE = "</i>"
    SYMBOL_IMG_OPEN = "<img src=\""
    SYMBOL_IMG_CLOSE = "\">"

    def __init__(self, path=None):
        from mnemosyne.script import Mnemosyne

        try:
            self.mnemo = Mnemosyne(path)

            # Activate the Cloze plugin
            # Note: The import needs to be here, since it relies on the
            # presence of the translation engine, which is initialized with
            # the mnemosyne object.
            from mnemosyne.libmnemosyne.card_types.cloze import ClozePlugin
            for plugin in self.mnemo.plugins():
                if isinstance(plugin, ClozePlugin):
                    plugin.activate()
                    break

        except SystemExit:
            raise KnowledgeException(
                "Mnemosyne is running. Please close it and reopen the file.")

    def cleanup(self):
        try:
            self.mnemo.finalise()
        except Exception as e:
            pass

        del self.mnemo

    def extract_data(self, fields, model):
        """
        Extracts the data dict from the given fields, depending
        on the model being used.
        """

        # Transform the fields data to mnemosyne format
        if model == self.CLOSE_MODEL:
            data = {'text': fields.get("Text")}
        else:
            data = {
                'f': fields.get("Front"),
                'b': fields.get("Back"),
            }

        return data

    def add_media_file(self, filename):
        """
        Adds a new media file to the media directory.
        """

        from mnemosyne.libmnemosyne.utils import copy_file_to_dir, contract_path
        media_dir = self.mnemo.database().media_dir()

        # Make sure the path is proper absolute filesystem path
        filename_expanded = os.path.expanduser(filename)
        if os.path.isabs(filename_expanded):
            filename_abs = filename_expanded
        else:
            filename_abs = os.path.join(
                os.path.dirname(utils.get_absolute_filepath()),
                filename_expanded)

        copy_file_to_dir(filename_abs, media_dir)
        return contract_path(filename_abs, media_dir)

    def add_note(self, deck, model, fields, tags=None):
        """
        Adds a new fact with specified fields, model name and tags.
        Returns the ID of the fact.
        """

        # Pre-process data in fields
        fields = self.process_all(fields)
        data = self.extract_data(fields, model)

        # Convert the deck name to the tag
        tags = (tags or set())
        if deck is not None:
            tags.add(deck.replace('.', '::'))

        try:
            card_type = self.mnemo.card_type_with_id(model)
        except KeyError:
            raise KnowledgeException("Model (card type) '{0}' does "
                                     "not exist".format(model))
        controller = self.mnemo.controller()

        try:
            cards = controller.create_new_cards(
                data,
                card_type,
                grade=-1,
                tag_names=tags,
                check_for_duplicates=False,
                save=False,
            )
        except AssertionError:
            raise KnowledgeException("Fact '{0}' could not be added, it "
                                     "most likely contains invalid "
                                     "data".format(fields))

        # We expect exactly one card created for regular cards,
        # or at least one for closes
        if model == self.DEFAULT_MODEL:
            assert len(cards) == 1
        elif model == self.CLOSE_MODEL:
            assert len(cards) >= 1

        # Return the fact ID
        return cards[0].fact.id

    def update_note(self,
                    identifier,
                    fields,
                    deck=None,
                    model=None,
                    tags=None):
        # Get the fact from Mnemosyne
        db = self.mnemo.database()

        try:
            fact = db.fact(identifier, is_id_internal=False)
        except TypeError:
            # Mnemosyne raises TypeError in case ID is not found
            raise FactNotFoundException(
                "Fact with ID '{0}' could not be found".format(identifier))

        cards = db.cards_from_fact(fact)
        if not cards:
            raise FactNotFoundException("Fact with ID '{0}' does not have any"
                                        "cards assigned".format(identifier))

        # Convert the deck name to the tag
        tags = (tags or set())
        if deck is not None:
            tags.add(deck.replace('.', '::'))

        # Pre-process data in fields
        fields = self.process_all(fields)

        # Transform the fields data to mnemosyne format
        data = self.extract_data(fields, model)

        current_data = fact.data
        current_tags = set([tag.name for tag in cards[0].tags])

        # Bail out if no modifications to be performed
        if current_tags == tags and current_data == data:
            return

        # Update the fact
        card_type = self.mnemo.card_type_with_id(model)
        new, edited, deleted = card_type.edit_fact(fact, data)
        fact.data = data
        db.update_fact(fact)

        # Create, delete and edit all cards that were affected by this update
        # This mainly happens with card types that generate multiple cards, like
        # questions with multiple closes
        for card in deleted:
            db.delete_card(card)

        for card in new:
            db.add_card(card)

        for card in edited:
            db.update_card(card)

        # Refetch the list of cards
        cards = db.cards_from_fact(fact)

        # Set new tags for each card
        old_tag_objects = set()
        new_tag_objects = db.get_or_create_tags_with_names(tags)

        # Fetch the current time
        modification_time = int(time.time())

        for card in cards:
            old_tag_objects |= card.tags
            card.modification_time = modification_time
            card.tags = new_tag_objects
            db.update_card(card)

        # Remove redundant tags
        for tag in old_tag_objects:
            db.delete_tag_if_unused(tag)

    def commit(self):
        db = self.mnemo.database()
        db.save()