Exemplo n.º 1
0
def main():
    cmdline_args = parse_cmdline()
    connection = AnkiConnectClient()
    for deck in cmdline_args.decks:
        connection.send(action="createDeck", params=dict(deck=deck))

    print("\nDone.\n")
Exemplo n.º 2
0
 def __init__(self):
     self.anki = AnkiConnectClient()
     self.pweb = MyPweb(
         source="anon",
         doctype='md2html',
         informat='markdown',
         mimetype='text/markdown',
     )
     self.pweb.set_anki(self.anki)
Exemplo n.º 3
0
def main():
    cmdline_args = parse_cmdline()
    print("cmdline_args:", cmdline_args)
    connection = AnkiConnectClient()
    connection.send(
        action="deleteDecks",
        params=dict(decks=cmdline_args.decks,
                    cardsToo=True
                    #cardsToo = cmdline_args.cards_too
                    ))

    print("\nDone.\n")
Exemplo n.º 4
0
def load_and_send_individual_flashcards_to_anki(yaml_input_file):
    flashcard_data = yaml.load(yaml_input_file)
    flashcard_params = data_to_flashcards_params(flashcard_data)

    connection = AnkiConnectClient()

    for note in flashcard_params["notes"]:
        http_response, result_data = connection.send_as_json(
            action="addNote", params=dict(note=note))
        if result_data.get("error", None):
            print("\n*** Couldn't add note: {}".format(note, ))
            print("--- AnkiConnect error description:\n{}".format(
                result_data["error"], ))
            print("--- HTTP response:\n{} {}".format(
                http_response.status,
                http_response.reason,
            ))
Exemplo n.º 5
0
def load_and_send_flashcards_to_anki(yaml_input_file):
    flashcard_data = yaml.load(yaml_input_file)
    flashcard_params = data_to_flashcards_params(flashcard_data)

    # Send data to AnkiConnect with request to create flashcards.
    connection = AnkiConnectClient()
    http_response, result_data = send_flashcards_to_anki(
        connection, flashcard_params)

    # Report any errors.
    notes = flashcard_data["notes"]
    results = result_data["result"]
    for note, result in zip(notes, results):
        if result is None:
            print("\n*** Couldn't add note: {}".format(note))

    if result_data["error"]:
        print("\n--- AnkiConnect error description:\n{}".format(
            result_data["error"], ))
        print("--- HTTP response:\n{} {}\n".format(
            http_response.status,
            http_response.reason,
        ))
Exemplo n.º 6
0
def load_and_send_flashcards(filename):
    with open(filename) as yaml_input_file:
        log.info("\nSending file '{}' to Anki...\n".format(filename))
        nodes = yaml.compose(yaml_input_file)
        data = yaml.load(yaml.serialize(nodes))
        defaults = data.get('defaults', None)
        log.debug("defaults: {}".format(defaults))

        def_tags = defaults.get("extraTags", list())
        def_deckName = defaults.get("deckName", "Default")
        def_modelName = defaults.get("modelName", "BasicMathJax")
        def_fields = defaults.get("fields", dict())
        def_useMarkdown = defaults.get("useMarkdown", True)
        def_markdownStyle = defaults.get("markdownStyle", "tango")
        def_markdownLineNums = defaults.get("markdownLineNums", False)
        def_markdownTabLength = defaults.get("markdownTabLength", 4)

        # Extract notes_node
        top_map = {k.value: v.value for k, v in nodes.value}
        note_nodes = top_map['notes']

        connection = AnkiConnectClient()

        # For each note_node in notes_node:
        new_notes_were_created = False
        for note_node in note_nodes:
            # Convert to note_dict
            note = yaml.load(yaml.serialize(note_node))

            tags = note.get('extraTags', def_tags).copy()
            tags.extend(note.get('tags', list()))
            tags = sorted(tags)
            deckName = note.get('deckName', def_deckName)
            modelName = note.get('modelName', def_modelName)

            # Set note's fields to defaults, if not already set.
            fields = dict(def_fields)
            fields.update(note.get("fields", dict()))
            # Convert each field from Markdown (if `useMarkdown` is True).
            fields = {
                k: format_text(
                    str(v),
                    note.get('useMarkdown', def_useMarkdown),
                    note.get('markdownStyle', def_markdownStyle),
                    note.get('markdownLineNums', def_markdownLineNums),
                    note.get('markdownTabLength', def_markdownTabLength),
                )
                for (k, v) in fields.items()
            }

            should_create_new_note = True
            must_replace_existing_note_id = False

            if 'id' in note:
                # Check for note with given ID.
                log.debug("Checking for existing note...")
                note_id = note['id']

                # Get info for existing note.
                response, result = connection.send_as_json(
                    action="notesInfo", params=dict(notes=[note_id]))
                if result.get("error", None) or not result['result'][0]:
                    report_anki_error(result, "\nCan't find note with ID: %s.",
                                      note_id)
                    log.info("The ID will be ignored, and a new note created.")
                    must_replace_existing_note_id = True
                else:
                    should_create_new_note = False

            if should_create_new_note:
                # No provided ID; assume new note should be created.
                log.debug("Creating new note...")

                # Create, obtaining returned ID
                response, result = connection.send_as_json(
                    action="addNote",
                    params=dict(note=dict(
                        deckName=deckName,
                        modelName=modelName,
                        fields=fields,
                        tags=tags,
                    )))
                if result.get("error", None):
                    report_anki_error(result, "Can't create note: %s", note)
                else:
                    # Add ID to note_node
                    note_id = result['result']
                    if must_replace_existing_note_id:
                        prev_id = None
                        for k, v in note_node.value:
                            if k.value == 'id':
                                prev_id, v.value = v.value, str(note_id)
                        if prev_id:
                            log.info("ID %s replaced with %s.", prev_id,
                                     note_id)
                        else:
                            log.warn("Failed to assign new note ID!")
                    else:
                        note_node.value.insert(0, (
                            yaml.ScalarNode(tag='tag:yaml.org,2002:str',
                                            value='id'),
                            yaml.ScalarNode(tag='tag:yaml.org,2002:int',
                                            value=str(note_id)),
                        ))
                    new_notes_were_created = True

            else:
                # Assume provided ID is valid for existing note to be updated.
                log.debug("Updating existing note...")

                # Update note fields...
                params = dict(note=dict(id=note_id, fields=fields))
                log.debug("params: {}".format(params))
                response, result = connection.send_as_json(
                    action="updateNoteFields",
                    params=params,
                )
                if result.get("error", None):
                    report_anki_error(result, "Can't update note: %s", note)
                    continue

                # Update note tags...
                ## First get existing note tags.
                response, result = connection.send_as_json(
                    action="notesInfo", params=dict(notes=[note_id]))
                if result.get("error", None):
                    report_anki_error(result, "Can't get tags for note: %s",
                                      note)
                    continue
                current_tags = sorted(result['result'][0]['tags'])

                # log.debug("current tags: %s", current_tags)
                # log.debug("new tags: %s", tags)
                # log.debug("equal?: %s", current_tags == tags)
                if current_tags != tags:
                    # log.debug("updating tags.")

                    ## Remove existing note tags.
                    response, result = connection.send_as_json(
                        action="removeTags",
                        params=dict(notes=[note_id],
                                    tags=" ".join(current_tags)))
                    if result.get("error", None):
                        report_anki_error(result,
                                          "Can't remove tags for note: %s",
                                          note)

                    ## Add new note tags.
                    response, result = connection.send_as_json(
                        action="addTags",
                        params=dict(notes=[note_id], tags=" ".join(tags)))
                    if result.get("error", None):
                        report_anki_error(result,
                                          "Can't add tags for note: %s", note)

    if new_notes_were_created:
        # If any new notes were created, their IDs must be added to YAML file.
        with open(filename, mode='w') as yaml_output_file:
            log.info(
                "\nUpdating file '{}' with new note IDs...".format(filename))
            yaml_output_file.write(yaml.serialize(nodes))
Exemplo n.º 7
0
class FlashcardSender(object):
    def __init__(self):
        self.anki = AnkiConnectClient()
        self.pweb = MyPweb(
            source="anon",
            doctype='md2html',
            informat='markdown',
            mimetype='text/markdown',
        )
        self.pweb.set_anki(self.anki)

    def format_text(
        self,
        text,
        use_md,
        md_sty,
        md_lineno,
        md_tablen,
        md_mathext,
        note_id="anon",
    ):
        noclasses = md_sty != 'default'
        self.pweb.set_markdown_opts(md_sty, md_lineno, md_tablen, md_mathext)
        if str(use_md).lower() == 'pweave':
            self.pweb.read(string=text,
                           basename=str(note_id),
                           reader='markdown')
            self.pweb.run()
            self.pweb.format()
            html_ish = self.pweb.formatted
        elif use_md:
            body = parse_markdown(
                text,
                noclasses,
                md_sty,
                md_lineno,
                md_tablen,
                md_mathext,
            )
            html_ish = "{}\n{}\n{}".format(
                self.pweb.formatter.header,
                body,
                self.pweb.formatter.footer,
            )
        else:
            html_ish = text.replace('\n', '<br>').replace(' ', '&nbsp;')
        return html_ish

    def load_and_send_flashcards(self, filename, opts):
        with open(filename) as yaml_input_file:
            log.info("\nSending file '{}' to Anki...\n".format(filename))
            new_notes_were_created = False
            # The reason for the lower-level nodes representation of the YAML file is
            # that it can be used to make a modified version of the original YAML
            # file. We do this in two sections of code. In both cases, we add new note
            # IDs to the YAML file.
            nodes = yaml.compose(yaml_input_file)
            query_results, defaults, data, note_nodes, notes = query_note_nodes(
                nodes, opts)
            if query_results.empty:
                log.warning("Query returned no results.")
            else:
                log.debug("query_results:\n %s", str(query_results))

            # For each note_node in notes_node that matches query:
            for i in query_results.index:
                note_id = str(query_results.id[i])
                deck = query_results.deckName[i]
                model = query_results.modelName[i]
                use_md = query_results.useMarkdown[i]
                md_sty = query_results.markdownStyle[i]
                md_lineno = query_results.markdownLineNums[i]
                md_tablen = query_results.markdownTabLength[i]
                md_mathext = query_results.useMarkdownMathExt[i]
                tags = query_results.tags[i].replace(',', '\n').split()
                fields = query_results.fields[i]
                note_node = query_results.node[i]
                description = "{}:{}".format(note_id, fields)

                log.info("Processing note with ID: {}".format(note_id))
                # Check for note with given ID.
                # Get info for existing note.
                should_create_new_note = True
                must_replace_existing_note_id = False
                result = self.anki.notesInfo([note_id])
                if result.get("error", None) or not result['result'][0]:
                    log.info(
                        "Can't find note with ID %s; a new note will be created.",
                        note_id)
                    must_replace_existing_note_id = True
                else:
                    should_create_new_note = False

                if should_create_new_note:
                    # No provided ID; assume new note should be created.
                    log.debug("Creating new note...")
                    temporary_fields = {
                        k: self.format_text(
                            str(v),
                            False,
                            md_sty,
                            md_lineno,
                            md_tablen,
                            md_mathext,
                        )
                        for (k, v) in fields.items()
                    }

                    # Create, obtaining returned ID
                    result = self.anki.addNote(deck, model, temporary_fields,
                                               tags)
                    if result.get("error", None):
                        log.warning("Can't create note: %s", description)
                    else:
                        # Add ID to note_node
                        note_id = result['result']
                        if must_replace_existing_note_id:
                            prev_id = None
                            for k, v in note_node.value:
                                if k.value == 'id':
                                    prev_id, v.value = v.value, str(note_id)
                            if prev_id:
                                log.info("ID %s replaced with %s.", prev_id,
                                         note_id)
                            else:
                                log.warn("Failed to assign new note ID!")
                        else:
                            # We want to update the YAML file with a new note ID, but
                            # modifying a YAML file is complicated. One way to do it is via a
                            # lower-level interface. Here we use the nodes representation,
                            # which we modify directly. The nodes representation can then be
                            # converted into a YAML file that fairly faithfully resembles the
                            # original, but with the newly added note ID.
                            note_node.value.insert(0, (
                                yaml.ScalarNode(tag='tag:yaml.org,2002:str',
                                                value='id'),
                                yaml.ScalarNode(tag='tag:yaml.org,2002:int',
                                                value=str(note_id)),
                            ))
                        new_notes_were_created = True

                log.debug("Updating existing note...")
                # Assume provided ID is valid for existing note to be updated.
                # Convert each field from Markdown (if `use_md` is True).
                note_uid = uuid.uuid1()
                converted_fields = {
                    k: self.format_text(str(v),
                                        use_md,
                                        md_sty,
                                        md_lineno,
                                        md_tablen,
                                        md_mathext,
                                        note_id="%s-%s-%s" %
                                        (note_id, note_uid, field_no))
                    for (field_no, (k, v)) in enumerate(fields.items())
                }

                # Update converted note fields...
                result = self.anki.updateNoteFields(note_id, converted_fields)
                if result.get("error", None):
                    log.warning("Can't update note: %s", description)
                    continue

                # Update note tags...
                ## First get existing note tags.
                result = self.anki.notesInfo([note_id])
                if result.get("error", None):
                    log.warning("Can't get tags for note: %s", description)
                    continue

                current_tags = sorted(result['result'][0]['tags'])
                if current_tags != tags:
                    ## Remove existing note tags.
                    result = self.anki.removeTags([note_id],
                                                  " ".join(current_tags))
                    if result.get("error", None):
                        log.warning("Can't remove tags for note: %s",
                                    description)
                    ## Add new note tags.
                    result = self.anki.addTags([note_id], " ".join(tags))
                    if result.get("error", None):
                        log.warning("Can't add tags for note: %s", description)

        if new_notes_were_created:
            # If any new notes were created, their IDs must be added to YAML file.
            with open(filename, mode='w') as yaml_output_file:
                log.info("\nUpdating file '{}' with new note IDs...".format(
                    filename))
                yaml_output_file.write(yaml.serialize(nodes))

    def load_and_send(self, opts):
        for input_filename in opts.input:
            self.load_and_send_flashcards(input_filename, opts)
Exemplo n.º 8
0
 def set_anki(self, anki=None):
     if anki is None:
         anki = AnkiConnectClient()
     self.anki = anki