Start ankimaker.
This commit is contained in:
commit
aca3d88c19
23
README.md
Normal file
23
README.md
Normal file
@ -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.
|
9
ankimaker/__main__.py
Normal file
9
ankimaker/__main__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from ankimaker.commands import cli
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cli(prog_name='ankimaker')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
7
ankimaker/commands/__init__.py
Normal file
7
ankimaker/commands/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import click
|
||||||
|
|
||||||
|
@click.group("cli")
|
||||||
|
def cli():
|
||||||
|
pass
|
||||||
|
|
||||||
|
from ankimaker.commands.from_csv import generate_anki
|
38
ankimaker/commands/from_csv.py
Normal file
38
ankimaker/commands/from_csv.py
Normal file
@ -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
|
0
ankimaker/commands/from_epub.py
Normal file
0
ankimaker/commands/from_epub.py
Normal file
1
ankimaker/tasks/__init__.py
Normal file
1
ankimaker/tasks/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .basic_csv_to_anki import basic_pandas_to_anki
|
62
ankimaker/tasks/basic_csv_to_anki.py
Normal file
62
ankimaker/tasks/basic_csv_to_anki.py
Normal file
@ -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}}<hr id="answer">{{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)
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
click
|
||||||
|
genanki
|
||||||
|
pandas
|
Loading…
x
Reference in New Issue
Block a user