Create filters in configuration and implement it.

This commit is contained in:
gabriel becker 2022-12-08 18:43:24 +11:00
parent 01eb182c84
commit 791caa3624
10 changed files with 155 additions and 46 deletions

View File

@ -1,2 +1,3 @@
from .load_config import load_config_file from .load_config import load_config_file
from .configuration import AnkimakerConfig as Config from .configuration import AnkimakerConfig as Config
from .filters import FilterConfig

View File

@ -1,5 +1,8 @@
import yaml import yaml
from typing import Iterable from typing import List
from .filters import FilterConfig
_empty_list = () _empty_list = ()
@ -9,22 +12,27 @@ class AnkimakerConfig(yaml.YAMLObject):
question_column = None question_column = None
answer_column = None answer_column = None
separators = ',' separators = ','
filters: Iterable[dict] = list() filters: List[List[FilterConfig]] = list()
def __init__( def __init__(self, header=None, answer_column=None, question_column=None, filters=tuple()):
self, header=None, answer_column=None, question_column=None, filters=_empty_list
):
AnkimakerConfig.answer_column = answer_column AnkimakerConfig.answer_column = answer_column
AnkimakerConfig.question_column = question_column AnkimakerConfig.question_column = question_column
AnkimakerConfig.header = header AnkimakerConfig.header = header
AnkimakerConfig.filters = filters
AnkimakerConfig.AnkimakerConfig = AnkimakerConfig AnkimakerConfig.AnkimakerConfig = AnkimakerConfig
AnkimakerConfig.filters = list(map(lambda x: FilterConfig, filters))
@staticmethod @staticmethod
def loader(configuration_content): def loader(configuration_content):
content = configuration_content['AnkimakerConfig'] if isinstance(configuration_content, dict):
content = configuration_content['AnkimakerConfig']
else:
content = configuration_content
AnkimakerConfig.header = content.header AnkimakerConfig.header = content.header
AnkimakerConfig.question_column = content.question_column AnkimakerConfig.question_column = content.question_column
AnkimakerConfig.answer_column = content.answer_column AnkimakerConfig.answer_column = content.answer_column
AnkimakerConfig.separators = content.separators AnkimakerConfig.separators = content.separators
AnkimakerConfig.filters = content.filters AnkimakerConfig.filters = [
[FilterConfig(**x) for x in or_filter]
for or_filter in content.filters
]

View File

@ -0,0 +1,16 @@
from typing import List, Union
class FilterConfig:
column: Union[str, int]
values: Union[List[Union[int, str]], Union[int, str]]
def __init__(self, column: str, values: Union[List[Union[int, str]], Union[int, str]]):
self.column = column
self.values = values
def __str__(self):
return f'<ankimaker.config.filters.FilterConfig {self.column}: {self.values} >'
def __repr__(self):
return self.__str__()

View File

@ -1,5 +1,6 @@
from pathlib import Path import os
import yaml import yaml
from pathlib import Path
from .configuration import AnkimakerConfig from .configuration import AnkimakerConfig
@ -10,7 +11,7 @@ def load_config_file(file_path: str):
:param file_path: Path to yaml file with configuration :param file_path: Path to yaml file with configuration
:return: Dict config :return: Dict config
""" """
file_path = Path(file_path) file_path = Path(file_path if '~' not in file_path else os.path.expanduser(file_path))
assert file_path.exists() assert file_path.exists()
assert file_path.is_file() assert file_path.is_file()
with open(file_path, 'r') as file: with open(file_path, 'r') as file:

View File

@ -1,5 +1,5 @@
from . import ( from . import (
deck, deck,
# models,
# card
) )
from .card import create_note
from .model import create_model

View File

@ -0,0 +1,9 @@
import genanki
def create_note(model, fields):
note = genanki.Note(
model=model,
fields=fields
)
return note

View File

@ -0,0 +1,20 @@
import genanki
def create_model():
my_model = genanki.Model(
1607392319,
'Simple Model',
fields=[
{'name': 'Question'},
{'name': 'Answer'},
],
templates=[
{
'name': 'Card 1',
'qfmt': '<div style="text-align: center;">{{Question}}</div>',
'afmt': '{{FrontSide}}<hr id="answer"><div style="text-align: center;">{{Answer}}</div>',
},
]
)
return my_model

View File

@ -0,0 +1,17 @@
import genanki as anki
simple_flashcard = anki.Model(
16073923194617823,
name='simple_flashcard',
fields=[
{'name': 'word'},
{'name': 'meaning'}
],
templates=[
{
'name': 'geneticname',
'qfmt': '{{word}}',
'afmt': '{{FrontSide}}<hr id="answer">{{meaning}}'
}
]
)

View File

@ -1,54 +1,30 @@
import genanki import genanki
import pandas as pd import pandas as pd
from typing import List
from functools import reduce
from ankimaker.config import Config from ankimaker.config import Config, FilterConfig
from ankimaker import generator, config from ankimaker import generator, config
def create_model():
my_model = genanki.Model(
1607392319,
'Simple Model',
fields=[
{'name': 'Question'},
{'name': 'Answer'},
],
templates=[
{
'name': 'Card 1',
'qfmt': '<div style="text-align: center;">{{Question}}</div>',
'afmt': '{{FrontSide}}<hr id="answer"><div style="text-align: center;">{{Answer}}</div>',
},
]
)
return my_model
def create_note(model, fields):
note = genanki.Note(
model=model,
fields=fields
)
return note
def load_csv(path): def load_csv(path):
df = pd.read_csv(path, header=Config.header, sep=Config.separators) df = pd.read_csv(path, header=Config.header, sep=Config.separators)
df_columns_are_unnamed = all(map(lambda x: str(x).isnumeric(), df.columns)) df_columns_are_unnamed = all(map(lambda x: str(x).isnumeric(), df.columns))
if df_columns_are_unnamed: if df_columns_are_unnamed:
Config.answer_column = int(Config.answer_column) Config.answer_column = int(Config.answer_column)
Config.question_column = int(Config.question_column) Config.question_column = int(Config.question_column)
df = apply_filters(df)
return df return df
def add_df_to_deck(df: pd.DataFrame, deck: genanki.Deck): def add_df_to_deck(df: pd.DataFrame, deck: genanki.Deck):
model = create_model() model = generator.create_model()
for entry in df.to_dict('records'): for entry in df.to_dict('records'):
question = entry[Config.question_column] question = entry[Config.question_column]
answer = entry[Config.answer_column] answer = entry[Config.answer_column]
content_fields = (question, answer) content_fields = (question, answer)
note = create_note(model, fields=content_fields) note = generator.create_note(model, fields=content_fields)
deck.add_note(note) deck.add_note(note)
return deck return deck
@ -62,6 +38,53 @@ def handle_config(config_file_path):
config.load_config_file(config_file_path) config.load_config_file(config_file_path)
def apply_filters(df: pd.DataFrame) -> pd.DataFrame:
"""
:param df:
:return:
"""
there_are_no_filter_to_apply = len(Config.filters) == 0
if there_are_no_filter_to_apply:
return df
is_in_configured_filter_rules = load_filter_from_config(df)
df_filtered = df[is_in_configured_filter_rules]
return df_filtered
def load_filter_from_config(df: pd.DataFrame) -> pd.Series:
group_filters: List[pd.Series] = list()
for group in Config.filters:
if len(group) > 0:
group_filters.append(
create_group_filter(df, group)
)
config_filter = reduce(lambda a, b: a | b, group_filters)
return config_filter
def create_group_filter(df: pd.DataFrame, group: List[FilterConfig]) -> pd.Series:
"""
:param df:
:param group:
:return:
"""
rule: FilterConfig
query: List[pd.Series] = list()
for rule in group:
assert_rule_is_valid(df, rule)
is_in_rule = df[rule.column].apply(lambda x: x in rule.values)
query.append(is_in_rule)
is_in_group = reduce(lambda a, b: a & b, query)
return is_in_group
def assert_rule_is_valid(df: pd.DataFrame, rule: FilterConfig):
assert rule.column in df.columns
def basic_pandas_to_anki(csv_path, output_path, name, config_file_path): def basic_pandas_to_anki(csv_path, output_path, name, config_file_path):
handle_config(config_file_path) handle_config(config_file_path)
df = load_csv(csv_path) df = load_csv(csv_path)

View File

@ -1,3 +1,4 @@
import os
import yaml import yaml
import click import click
import pandas as pd import pandas as pd
@ -23,6 +24,7 @@ __COMMAND_SAMPLE = """ankimaker csv \
--conf {output} --conf {output}
""" """
def create_config(input_file, output_path): def create_config(input_file, output_path):
new_config = Config() new_config = Config()
@ -33,10 +35,20 @@ def create_config(input_file, output_path):
input_file, read_option='header', header=new_config.header, input_file, read_option='header', header=new_config.header,
sep=new_config.separators, option_type=int sep=new_config.separators, option_type=int
) )
new_config.question_column = get_column('question')
new_config.answer_column = get_column('answer')
save_file(new_config, output_path)
finish_message = __SUCCESS_MESSAGE.format(command=make_sample_command(input_file, output_path)) finish_message = __SUCCESS_MESSAGE.format(command=make_sample_command(input_file, output_path))
click.echo(finish_message) click.echo(finish_message)
def get_column(name: str) -> str:
answer = click.prompt(f'Which is your {name} column?', type=str, confirmation_prompt=True)
return answer
def handle_read_option(input_file, read_option, option_type: Type = str, **kargs): def handle_read_option(input_file, read_option, option_type: Type = str, **kargs):
preview: str preview: str
is_finished = False is_finished = False
@ -66,12 +78,14 @@ def load_preview(input_file, *args, **kargs):
def save_file(config: Config, file_path): def save_file(config: Config, file_path):
f = open(file_path, 'w') if '~' in file_path:
yaml.dump(config, f) file_path = os.path.expanduser(file_path)
with open(file_path, 'w') as f:
yaml.dump(config, f)
def make_sample_command(inputf, output): def make_sample_command(input_config, output):
command = __COMMAND_SAMPLE.format( command = __COMMAND_SAMPLE.format(
input=inputf, output=output input=input_config, output=output
) )
return command return command