From 79e77143952a397ce63c7ee82b0d9ffcecc34ed9 Mon Sep 17 00:00:00 2001 From: gabriel becker Date: Fri, 9 Dec 2022 15:42:00 +1100 Subject: [PATCH] Add filter creation in configuration creation command. --- .gitignore | 5 +- requirements.txt | 3 +- setup.py | 3 +- src/ankimaker/config/configuration.py | 34 ++++--- src/ankimaker/config/filters.py | 7 +- .../tasks/config_tasks/create_config.py | 90 ++++++++++++++++--- 6 files changed, 117 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 68bc17f..4397a80 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ + +# Project Specific +scripts/ \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 77d9e4f..35c4c73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ click genanki pandas -pyyaml \ No newline at end of file +pyyaml +bullet \ No newline at end of file diff --git a/setup.py b/setup.py index 09f4ab5..dd33cfe 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ def readme(): setup( name='ankimaker', - version='0.0.4', + version='0.0.5', description='Makes anki with files', url="https://git.lgoon.xyz/gabriel/ankimaker", license="BSD-3-Clause", @@ -27,6 +27,7 @@ setup( "genanki", "pandas", "pyyaml", + "bullet" ], long_description_content_type='text/markdown', ) diff --git a/src/ankimaker/config/configuration.py b/src/ankimaker/config/configuration.py index 31e452f..16125cf 100644 --- a/src/ankimaker/config/configuration.py +++ b/src/ankimaker/config/configuration.py @@ -14,12 +14,15 @@ class AnkimakerConfig(yaml.YAMLObject): separators = ',' filters: List[List[FilterConfig]] = list() - def __init__(self, header=None, answer_column=None, question_column=None, filters=tuple()): - AnkimakerConfig.answer_column = answer_column - AnkimakerConfig.question_column = question_column - AnkimakerConfig.header = header - AnkimakerConfig.AnkimakerConfig = AnkimakerConfig - AnkimakerConfig.filters = list(map(lambda x: FilterConfig, filters)) + def __init__( + self, separators=',', header=None, answer_column=None, question_column=None, + filters=tuple(), *args, **karhs + ): + self.answer_column = answer_column + self.question_column = question_column + self.header = header + self.separators = separators + self.filters = _conditionally_create_new_filters(filters) @staticmethod def loader(configuration_content): @@ -31,8 +34,19 @@ class AnkimakerConfig(yaml.YAMLObject): AnkimakerConfig.question_column = content.question_column AnkimakerConfig.answer_column = content.answer_column AnkimakerConfig.separators = content.separators - AnkimakerConfig.filters = [ - [FilterConfig(**x) for x in or_filter] - for or_filter in content.filters - ] + AnkimakerConfig.filters = _conditionally_create_new_filters(content.filters) + +def _conditionally_create_new_filters(filters): + conf_has_filters = len(filters) > 0 + if conf_has_filters: + should_cast_filter = not isinstance(filters[0][0], FilterConfig) + if should_cast_filter: + new_filters = [ + [FilterConfig(**x) for x in or_filter] + for or_filter in filters + ] + else: + new_filters = filters + return new_filters + return list() diff --git a/src/ankimaker/config/filters.py b/src/ankimaker/config/filters.py index 5207449..238af0b 100644 --- a/src/ankimaker/config/filters.py +++ b/src/ankimaker/config/filters.py @@ -1,7 +1,10 @@ +import yaml + from typing import List, Union -class FilterConfig: +class FilterConfig(yaml.YAMLObject): + yaml_tag = '!fitlerconfig' column: Union[str, int] values: Union[List[Union[int, str]], Union[int, str]] @@ -10,7 +13,7 @@ class FilterConfig: self.values = values def __str__(self): - return f'' + return f'' def __repr__(self): return self.__str__() diff --git a/src/ankimaker/tasks/config_tasks/create_config.py b/src/ankimaker/tasks/config_tasks/create_config.py index 32e06d1..5e65f5b 100644 --- a/src/ankimaker/tasks/config_tasks/create_config.py +++ b/src/ankimaker/tasks/config_tasks/create_config.py @@ -1,10 +1,12 @@ import os + import yaml import click import pandas as pd -from typing import Type +from typing import Type, List +from bullet import Bullet, Input, YesNo -from ankimaker.config import Config +from ankimaker.config import Config, FilterConfig __CONFIRMATION_QUESTION = """ @@ -25,25 +27,93 @@ __COMMAND_SAMPLE = """ankimaker csv \ """ +__ADD_FILTER_QUESTION = """Do you want do add a filter to the configuration?""" + + def create_config(input_file, output_path): - new_config = Config() - new_config.separators = handle_read_option( - input_file, read_option='sep', sep=new_config.separators + separators = handle_read_option( + input_file, read_option='sep', sep=',' ) - new_config.header = handle_read_option( - input_file, read_option='header', header=new_config.header, - sep=new_config.separators, option_type=int + header = handle_read_option( + input_file, read_option='header', header=None, + sep=separators, option_type=int ) - new_config.question_column = get_column('question') - new_config.answer_column = get_column('answer') + question_column = get_column('question') + answer_column = get_column('answer') + + filters = process_filters(input_file, header, separators) + new_config = Config( + separators=separators, + header=header, + question_column=question_column, + answer_column=answer_column, + filters=filters + ) save_file(new_config, output_path) + finish_message = __SUCCESS_MESSAGE.format(command=make_sample_command(input_file, output_path)) + click.clear() click.echo(finish_message) +def process_filters(input_file, header, separators): + df = pd.read_csv(input_file, header=header, sep=separators) + filters = add_filters_to_config(df) + return filters + + +def __inline_yes_or_no_question(question): + answer = YesNo(prompt=question, default='n').launch() + return answer + + +def add_filters_to_config(df: pd.DataFrame) -> List[List[FilterConfig]]: + config = Config() + should_add_filter = __inline_yes_or_no_question(__ADD_FILTER_QUESTION) + while should_add_filter: + config = add_filter_to_or_create_filter_group(df, config) + should_add_filter = __inline_yes_or_no_question(__ADD_FILTER_QUESTION) + return config.filters + + +def add_filter_to_or_create_filter_group(df: pd.DataFrame, config: Config) -> Config: + config_has_filters = len(config.filters) > 0 + chosen_group = -1 + if config_has_filters: + filter_options = [f'({"|".join(map(str, group)):.45s})' for group in config.filters] + filter_options = [f'Group{i+1}{s}' for i, s in enumerate(filter_options)] + cli = Bullet( + prompt="Select group: ", + choices=["Create new", *filter_options], + return_index=True, + ) + chosen_group = cli.launch()[1] - 1 + new_filter = create_filter_config(df) + if chosen_group < 0: + config.filters.append([new_filter]) + else: + config.filters[chosen_group].append(new_filter) + return config + + +def create_filter_config(df: pd.DataFrame) -> FilterConfig: + options = list(df.columns) + cli = Bullet( + prompt="Select a columns to filter: ", + choices=list(map(str, options)), + return_index=True + ) + chosen = cli.launch()[1] + filter_column = options[chosen] + columns_values = df[filter_column].unique() + values = Input(f'Which values fo filter out? values[{columns_values}]: ').launch() + new_filter = FilterConfig(column=filter_column, values=values) + return new_filter + + def get_column(name: str) -> str: answer = click.prompt(f'Which is your {name} column?', type=str, confirmation_prompt=True) return answer