From aca3d88c1919c3966787d9aff8ed2b7a64e896d4 Mon Sep 17 00:00:00 2001 From: gabriel becker Date: Tue, 15 Nov 2022 17:38:56 +1100 Subject: [PATCH] Start ankimaker. --- README.md | 23 +++++++++++ ankimaker/__main__.py | 9 ++++ ankimaker/commands/__init__.py | 7 ++++ ankimaker/commands/from_csv.py | 38 +++++++++++++++++ ankimaker/commands/from_epub.py | 0 ankimaker/tasks/__init__.py | 1 + ankimaker/tasks/basic_csv_to_anki.py | 62 ++++++++++++++++++++++++++++ requirements.txt | 3 ++ 8 files changed, 143 insertions(+) create mode 100644 README.md create mode 100644 ankimaker/__main__.py create mode 100644 ankimaker/commands/__init__.py create mode 100644 ankimaker/commands/from_csv.py create mode 100644 ankimaker/commands/from_epub.py create mode 100644 ankimaker/tasks/__init__.py create mode 100644 ankimaker/tasks/basic_csv_to_anki.py create mode 100644 requirements.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..6fcafc5 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Ankimaker + +WIP + +A CLI app to generate anki decks. + +From csv file, with configurable parameters, filters and media. + +From epub, finding difficult* words in the book and getting their translations. + +*I still don't know what 'difficult' will mean. Probably difficult words will be less frequent words that are more frequent in the text than in some corpus, cut above a grade threshold. The grades will map percentiles of frequency. + +| Language Level | Number of Base Words Needed | +| ----- | ------ | +| A1 | 500| +| A2 | 1000| +| B1 | 2000| +| B2 | 4000| +| C1 | 8000| +| C2 | 16000| + + +This project is only possible because of the awesome work of genanki team. \ No newline at end of file diff --git a/ankimaker/__main__.py b/ankimaker/__main__.py new file mode 100644 index 0000000..c50f7e5 --- /dev/null +++ b/ankimaker/__main__.py @@ -0,0 +1,9 @@ +from ankimaker.commands import cli + + +def main(): + cli(prog_name='ankimaker') + + +if __name__ == '__main__': + main() diff --git a/ankimaker/commands/__init__.py b/ankimaker/commands/__init__.py new file mode 100644 index 0000000..10cefc4 --- /dev/null +++ b/ankimaker/commands/__init__.py @@ -0,0 +1,7 @@ +import click + +@click.group("cli") +def cli(): + pass + +from ankimaker.commands.from_csv import generate_anki diff --git a/ankimaker/commands/from_csv.py b/ankimaker/commands/from_csv.py new file mode 100644 index 0000000..22b3f9c --- /dev/null +++ b/ankimaker/commands/from_csv.py @@ -0,0 +1,38 @@ +import click +import re +from ankimaker.commands import cli +from ankimaker.tasks import basic_pandas_to_anki + + +@cli.command('csv') +@click.option('-i', '--input', 'input_file', type=click.Path(exists=True)) +@click.option('-o', '--output', 'output_file', type=click.Path(exists=False)) +@click.option('-c', '--conf', 'config_file', default=None, type=click.STRING) +@click.option('-n', '--name', 'name', default=None, type=click.STRING) +def generate_anki( + input_file, + output_file, + name, + config_file, +): + output_file = parse_output(output_file) + if name is None: + name = get_name_from_output(output_file) + basic_pandas_to_anki(input_file, output_file, name) + + +def parse_output(filename): + filetype = filename.split('.')[-1] if len(filename.split('.')) > 0 else None + if filetype is None: + return filename + '.apkg' + elif filetype != 'apkg': + filename.replace(filetype, 'apkg') + return filename+filetype + else: + return filename + + +def get_name_from_output(filename): + updated_file = filename.split('/')[-1] if len(filename.split('/')) > 0 else filename + updated_file = re.sub(r'(.apkg)', '', updated_file) + return updated_file diff --git a/ankimaker/commands/from_epub.py b/ankimaker/commands/from_epub.py new file mode 100644 index 0000000..e69de29 diff --git a/ankimaker/tasks/__init__.py b/ankimaker/tasks/__init__.py new file mode 100644 index 0000000..026693a --- /dev/null +++ b/ankimaker/tasks/__init__.py @@ -0,0 +1 @@ +from .basic_csv_to_anki import basic_pandas_to_anki diff --git a/ankimaker/tasks/basic_csv_to_anki.py b/ankimaker/tasks/basic_csv_to_anki.py new file mode 100644 index 0000000..7384955 --- /dev/null +++ b/ankimaker/tasks/basic_csv_to_anki.py @@ -0,0 +1,62 @@ +import genanki +import pandas as pd + + +def create_model(): + my_model = genanki.Model( + 1607392319, + 'Simple Model', + fields=[ + {'name': 'Question'}, + {'name': 'Answer'}, + ], + templates=[ + { + 'name': 'Card 1', + 'qfmt': '{{Question}}', + 'afmt': '{{FrontSide}}
{{Answer}}', + }, + ]) + return my_model + + +def create_note(model, fields): + note = genanki.Note( + model=model, + fields=fields + ) + return note + + +def create_deck(name): + deck = genanki.Deck( + 2059400110, + name + ) + return deck + + +def save_deck(deck, destination): + my_package = genanki.Package(deck) + # my_package.media_files = ['sound.mp3', 'images/image.jpg'] + my_package.write_to_file(destination) + + +def load_csv(path): + df = pd.read_csv(path, header=None) + return df + + +def add_df_to_deck(df: pd.DataFrame, deck: genanki.Deck): + model = create_model() + for x in df.to_dict('records'): + note = create_note(model, fields=(x[2], x[3])) + deck.add_note(note) + return deck + + +def basic_pandas_to_anki(csv_path, output_path, name): + df = load_csv(csv_path) + deck = create_deck(name) + add_df_to_deck(df, deck) + save_deck(deck, output_path) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a7792c2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +click +genanki +pandas \ No newline at end of file