diff --git a/.gitignore b/.gitignore index 8a5bf86..3ff719f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,11 @@ argenta/router/__pycache__/ argenta/app/__pycache__/ argenta/__pycache__/ dist -tests \ No newline at end of file +tests/__pycache__ +tests/mock_default_app/handlers/__pycache__/ +tests/mock_default_app/__pycache__/ +tests/mock_app/local_data_func/__pycache__/ +tests/mock_app/handlers/handlers_implementation/__pycache__/ +tests/mock_app/handlers/__pycache__/ +tests/mock_app/business_logic/__pycache__/ +tests/mock_app/__pycache__/ diff --git a/setup.py b/setup.py index 16f4ca4..c09eed2 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setup( name="argenta", - version="0.1.2", + version="0.1.3", author="kolo", author_email="kolo.is.main@gmail.com", description="Python library for creating CLI apps", diff --git a/tests/mock_app/__init__.py b/tests/mock_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_app/business_logic/__init__.py b/tests/mock_app/business_logic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_app/business_logic/script_updater.py b/tests/mock_app/business_logic/script_updater.py new file mode 100644 index 0000000..b39eef0 --- /dev/null +++ b/tests/mock_app/business_logic/script_updater.py @@ -0,0 +1,75 @@ +import os +import shutil +import time +from zipfile import ZipFile +import requests +from ..local_data_func.get_script_release_tag import get_script_tag +from tqdm import tqdm + + +class UpdateScript: + + GITHUB_REPO = "https://api.github.com/repos/koloideal/WordMath" + + @staticmethod + def get_latest_release() -> dict: + response = requests.get(f"{UpdateScript.GITHUB_REPO}/releases/latest") + data = response.json() + return {'tag': data['tag_name'], + 'url': data['zipball_url']} + + + @staticmethod + def download_new_release(zip_url): + response = requests.get(zip_url, stream=True) + response.raise_for_status() + + total_size = int(response.headers.get("content-length", 0)) + block_size = 1024 + + with tqdm(total=total_size, unit="B", unit_scale=True) as progress_bar: + with open('new_release.zip', "wb") as file: + for data in response.iter_content(block_size): + progress_bar.update(len(data)) + file.write(data) + time.sleep(0.3) + + with ZipFile('new_release.zip') as zip_file: + zip_file.extractall("new_release") + + + @staticmethod + def upgrade_script(): + excluded_files = ['venv', '.venv', '.git', 'new_release'] + for obj in os.listdir(): + if obj not in excluded_files: + if os.path.isfile(obj): + os.remove(obj) + elif os.path.isdir(obj): + shutil.rmtree(obj) + + new_release = os.listdir('new_release') + new_release_name = new_release[0] if new_release[0].startswith('koloideal-WordMath') else None + path = f'new_release/{new_release_name}' + all_files = os.listdir(path) + for file in all_files: + shutil.move(path + '/' + file, './' + file) + + shutil.rmtree('new_release') + + + @staticmethod + def start_update() -> bool: + existing_release_tag: str = get_script_tag() + latest_release: dict = UpdateScript.get_latest_release() + latest_release_tag = latest_release['tag'] + latest_release_url = latest_release['url'] + + if latest_release_tag != existing_release_tag: + #UpdateScript.download_new_release(latest_release_url) + #UpdateScript.upgrade_script() + return latest_release_tag + else: + return False + + diff --git a/tests/mock_app/business_logic/word2num_math.py b/tests/mock_app/business_logic/word2num_math.py new file mode 100644 index 0000000..7264a85 --- /dev/null +++ b/tests/mock_app/business_logic/word2num_math.py @@ -0,0 +1,68 @@ +from numpy import ndarray +from word2number import w2n +from ..local_data_func.get_operator_synonyms import get_operator_synonyms +import numexpr + + +def word2num_math(string: str) -> ndarray | ZeroDivisionError | OverflowError: + operator_synonyms: dict[str, list[str]] = get_operator_synonyms() + + def variables_to_operator(synonym): + for key in operator_synonyms.keys(): + if synonym in operator_synonyms[key]: + return key + + action = { + "plus": '+', + "minus": '-', + "divide": '/', + "multiply": '*', + "degree": '**', + } + + result_string: str = '' + all_variables_of_operators: list[str] = [x for l in operator_synonyms.values() for x in l] + operators = {} + + while True: + is_clear = True + number_of_operator = 1 + for word in string.split(): + try: + if word in all_variables_of_operators: + ope_index = string.index(word) + if ope_index in operators.keys(): + is_clear = True + break + else: + raise ValueError + except ValueError: + continue + else: + is_clear = False + operators[number_of_operator] = variables_to_operator(word) + number_of_operator += 1 + string = string.replace(word, "&&", 1) + if is_clear: + break + + num_of_ope = 1 + for number in string.split("&&"): + try: + int_num = w2n.word_to_num(number.strip()) + except ValueError: + return ValueError("Invalid input expression") + if num_of_ope in operators.keys(): + result_string += f'{int_num} {action[operators[num_of_ope]]} ' + num_of_ope += 1 + else: + result_string += f'{int_num}' + + try: + result = numexpr.evaluate(result_string) + except ZeroDivisionError: + return ZeroDivisionError('Except divide by zero') + except OverflowError: + return OverflowError('Too big result') + else: + return result diff --git a/tests/mock_app/handlers/__init__.py b/tests/mock_app/handlers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_app/handlers/handlers_implementation/__init__.py b/tests/mock_app/handlers/handlers_implementation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_app/handlers/handlers_implementation/help_command.py b/tests/mock_app/handlers/handlers_implementation/help_command.py new file mode 100644 index 0000000..4681615 --- /dev/null +++ b/tests/mock_app/handlers/handlers_implementation/help_command.py @@ -0,0 +1,10 @@ +from rich.console import Console + + +console = Console() + + +def help_command(): + console.print("[italic bold]The main functionality of the script is to convert an expression from a string " + "to a mathematical one and then calculate this expression. " + "Project GitHub: https://github.com/koloideal/WordMath[/italic bold]") \ No newline at end of file diff --git a/tests/mock_app/handlers/handlers_implementation/solving_command.py b/tests/mock_app/handlers/handlers_implementation/solving_command.py new file mode 100644 index 0000000..9d7e6f3 --- /dev/null +++ b/tests/mock_app/handlers/handlers_implementation/solving_command.py @@ -0,0 +1,20 @@ +from rich.console import Console +from ...business_logic.word2num_math import word2num_math + + +console = Console() +print_line_separator = lambda: console.print('\n[bold blue]--------------------------------------[/bold blue]\n') + + +def start_solving_command(): + while True: + console.print( + "\n[italic]Enter a string expression or [bold italic green] Q [/bold italic green] for exit:[/italic]") + string_expression = input() + if string_expression.lower() == 'q': + break + else: + print_line_separator() + console.print( + f'[bold green]Answer:[/bold green] [bold blue]{word2num_math(string_expression)}[/bold blue]') + print_line_separator() diff --git a/tests/mock_app/handlers/handlers_implementation/upgrade_command.py b/tests/mock_app/handlers/handlers_implementation/upgrade_command.py new file mode 100644 index 0000000..9dcce1b --- /dev/null +++ b/tests/mock_app/handlers/handlers_implementation/upgrade_command.py @@ -0,0 +1,26 @@ +import requests +from rich.console import Console +from ...business_logic.script_updater import UpdateScript + + +console = Console() +print_line_separator = lambda: console.print('\n[bold green]--------------------------------------[/bold green]\n') + + +def upgrade_command(): + try: + requests.get('https://ya.ru') + except requests.exceptions.ConnectionError: + console.print('[bold red]No internet connection[/bold red]') + else: + latest_tag = UpdateScript.start_update() + if latest_tag: + print_line_separator() + console.print(f"[bold yellow]The newest version ({latest_tag}) of the script has been successfully installed![/bold yellow]") + print_line_separator() + console.print("[bold yellow]Rerun the script for the changes to take effect[/bold yellow]") + print_line_separator() + exit(0) + else: + console.print('[bold red]You have the latest version installed[/bold red]') + diff --git a/tests/mock_app/handlers/routers.py b/tests/mock_app/handlers/routers.py new file mode 100644 index 0000000..fb4f429 --- /dev/null +++ b/tests/mock_app/handlers/routers.py @@ -0,0 +1,34 @@ +from rich.console import Console +from argenta.router import Router + +from ..handlers.handlers_implementation.help_command import help_command +from ..handlers.handlers_implementation.solving_command import start_solving_command +from ..handlers.handlers_implementation.upgrade_command import upgrade_command + + +work_router: Router = Router(name='Work points:', + ignore_command_register=False) +settings_router: Router = Router(name='Settings points:', + ignore_command_register=True) + +console = Console() + + +@work_router.command(command='0', description='Get Help') +def command_help(): + help_command() + + +@work_router.command(command='1', description='Start Solving') +def command_start_solving(): + start_solving_command() + + +@settings_router.command(command='U', description='Update WordMath') +def command_update(): + upgrade_command() + + +@work_router.unknown_command +def command_unknown_command(command): + console.print(f'[bold red]Unknown command: [/bold red]{command}') diff --git a/tests/mock_app/local_data/operator_synonyms.json b/tests/mock_app/local_data/operator_synonyms.json new file mode 100644 index 0000000..f9d1f06 --- /dev/null +++ b/tests/mock_app/local_data/operator_synonyms.json @@ -0,0 +1,44 @@ +{ + "minus": [ + "without", + "lacking", + "deprived_of", + "bereft_of", + "destitute_of", + "minus" + ], + "plus": [ + "added_to", + "add", + "coupled_with", + "with_the_addition_of", + "plus" + ], + "divide": [ + "separate", + "part", + "split", + "divide", + "divide_by" + ], + "multiply": [ + "extend", + "expand", + "spread", + "build_up", + "accumulate", + "augment", + "multiply", + "times" + ], + "degree": [ + "stage", + "extent", + "grade", + "proportion", + "gradation", + "to_the_power_of", + "degree", + "to_the" + ] +} \ No newline at end of file diff --git a/tests/mock_app/local_data/script_release_tag.json b/tests/mock_app/local_data/script_release_tag.json new file mode 100644 index 0000000..3d66095 --- /dev/null +++ b/tests/mock_app/local_data/script_release_tag.json @@ -0,0 +1,3 @@ +{ + "tag": "v4.1.1" +} diff --git a/tests/mock_app/local_data_func/__init__.py b/tests/mock_app/local_data_func/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_app/local_data_func/get_operator_synonyms.py b/tests/mock_app/local_data_func/get_operator_synonyms.py new file mode 100644 index 0000000..b1691c9 --- /dev/null +++ b/tests/mock_app/local_data_func/get_operator_synonyms.py @@ -0,0 +1,8 @@ +import json + + +def get_operator_synonyms() -> dict[str, list[str]]: + with open("tests/mock_app/local_data/operator_synonyms.json", "r", encoding="utf-8") as file: + operator_synonyms: dict = json.load(file) + + return operator_synonyms diff --git a/tests/mock_app/local_data_func/get_script_release_tag.py b/tests/mock_app/local_data_func/get_script_release_tag.py new file mode 100644 index 0000000..ae484dc --- /dev/null +++ b/tests/mock_app/local_data_func/get_script_release_tag.py @@ -0,0 +1,8 @@ +import json + + +def get_script_tag() -> str: + with open("tests/mock_app/local_data/script_release_tag.json", "r", encoding="utf-8") as file: + script_release_tag: str = json.load(file)['tag'] + + return script_release_tag diff --git a/tests/mock_app/main.py b/tests/mock_app/main.py new file mode 100644 index 0000000..4a235ab --- /dev/null +++ b/tests/mock_app/main.py @@ -0,0 +1,31 @@ +from tests.mock_app.handlers.routers import work_router, settings_router +from argenta.app.entity import App +from art import text2art +from rich.console import Console + + +app: App = App(prompt='[italic white bold]What do you want to do(enter number of action)?', + line_separate='[bold green]\n---------------------------------------------\n', + print_func=Console().print, + command_group_description_separate='') + + +def main(): + ascii_name: str = text2art('WordMath', font='nancyj') + initial_greeting: str = f'[bold red]\n\n{ascii_name}' + + ascii_goodbye_message: str = text2art('GoodBye', font='small') + goodbye_message: str = f'[bold red]\n{ascii_goodbye_message}{' '*12}made by kolo\n' + + app.include_router(work_router, is_main=True) + app.include_router(settings_router) + + app.set_initial_greeting(initial_greeting) + app.set_farewell_message(goodbye_message) + + app.set_description_message_pattern('[bold red][{command}][/bold red] [blue]*=*=*[/blue] [bold yellow italic]{description}') + + app.start_polling() + +if __name__ == "__main__": + main() diff --git a/tests/mock_default_app/__init__.py b/tests/mock_default_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_default_app/handlers/__init__.py b/tests/mock_default_app/handlers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/mock_default_app/handlers/routers.py b/tests/mock_default_app/handlers/routers.py new file mode 100644 index 0000000..b7f6579 --- /dev/null +++ b/tests/mock_default_app/handlers/routers.py @@ -0,0 +1,30 @@ +from rich.console import Console +from argenta.router import Router + + +work_router: Router = Router(name='Work points:', + ignore_command_register=False) +settings_router: Router = Router(name='Settings points:', + ignore_command_register=True) + +console = Console() + + +@work_router.command(command='0', description='Get Help') +def command_help(): + print('help command') + + +@work_router.command(command='1', description='Start Solving') +def command_start_solving(): + print('start solving') + + +@settings_router.command(command='U', description='Update WordMath') +def command_update(): + print('update wordmath') + + +@work_router.unknown_command +def command_unknown_command(command): + console.print(f'[bold red]Unknown command: [/bold red]{command}') diff --git a/tests/mock_default_app/main.py b/tests/mock_default_app/main.py new file mode 100644 index 0000000..ef75965 --- /dev/null +++ b/tests/mock_default_app/main.py @@ -0,0 +1,16 @@ +from tests.mock_default_app.handlers.routers import work_router, settings_router +from argenta.app.entity import App +from art import text2art + + +app: App = App() + + +def main(): + app.include_router(work_router, is_main=True) + app.include_router(settings_router) + + app.start_polling() + +if __name__ == "__main__": + main()