From c70600ded717a9f060587dfa6d368098ff10f2cb Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 11:38:46 +0330 Subject: [PATCH 01/14] Move previous files to old directory --- .vscode/settings.json | 6 ++++++ .../Updated inputtingFunction.py | 0 choosingFunction => old/choosingFunction.py | 0 choosingFunctions => old/choosingFunctions.py | 0 4 files changed, 6 insertions(+) create mode 100644 .vscode/settings.json rename Updated inputtingFunction => old/Updated inputtingFunction.py (100%) rename choosingFunction => old/choosingFunction.py (100%) rename choosingFunctions => old/choosingFunctions.py (100%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3ecb8fc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[python]": { + "editor.defaultFormatter": "ms-python.autopep8" + }, + "python.formatting.provider": "none" +} diff --git a/Updated inputtingFunction b/old/Updated inputtingFunction.py similarity index 100% rename from Updated inputtingFunction rename to old/Updated inputtingFunction.py diff --git a/choosingFunction b/old/choosingFunction.py similarity index 100% rename from choosingFunction rename to old/choosingFunction.py diff --git a/choosingFunctions b/old/choosingFunctions.py similarity index 100% rename from choosingFunctions rename to old/choosingFunctions.py From c0c150f2ff74e3f623fed05053a7126a63c40554 Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 11:43:17 +0330 Subject: [PATCH 02/14] Update .gitignore file to exclude poetry.lock --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 68bc17f..7a18fca 100644 --- a/.gitignore +++ b/.gitignore @@ -99,7 +99,7 @@ ipython_config.py # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock +poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. From 358a7b1bee135a1177ff6fb869579d8df4ffd21d Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 11:43:38 +0330 Subject: [PATCH 03/14] Initialize poetry and install inquirer --- pyproject.toml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b6bf513 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "base-converter" +version = "1.0.0" +description = "A Python program to convert numbers between different bases." +authors = ["Your Name "] +license = "CC0-1.0" +readme = "README.md" +packages = [{include = "base_converter"}] + +[tool.poetry.dependencies] +python = "^3.11" +inquirer = "^3.1.3" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From dccabcde5a582f8c8867c3014d284388dd9f4d0d Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 11:49:36 +0330 Subject: [PATCH 04/14] Add the main entry to the app --- main.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..9ef46e9 --- /dev/null +++ b/main.py @@ -0,0 +1,10 @@ +from sys import path + + +def main(): + pass + + +if __name__ == "__main__": + path.append('../') + main() From 822b5199082154e2b25402f2e7dbb37b1d6bca39 Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 12:56:44 +0330 Subject: [PATCH 05/14] Install pylint --- .vscode/settings.json | 5 ++++- pyproject.toml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3ecb8fc..5e3ed8f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,8 @@ "[python]": { "editor.defaultFormatter": "ms-python.autopep8" }, - "python.formatting.provider": "none" + "python.formatting.provider": "none", + "python.linting.pylintArgs": ["--disable=C0116", "--disable=C0114"], + "python.linting.enabled": true, + "python.linting.pylintEnabled": true } diff --git a/pyproject.toml b/pyproject.toml index b6bf513..e3abfe8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ packages = [{include = "base_converter"}] [tool.poetry.dependencies] python = "^3.11" inquirer = "^3.1.3" +pylint = "^2.17.5" [build-system] From 3aaf07c5ba189b1601c6c7bbe1cfe28a9e23698c Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 13:00:17 +0330 Subject: [PATCH 06/14] Install pytest --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e3abfe8..2ef1195 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ packages = [{include = "base_converter"}] python = "^3.11" inquirer = "^3.1.3" pylint = "^2.17.5" +pytest = "^7.4.0" [build-system] From 8087b5c0d5535bef41b38dec0e804a0d3d6e1e76 Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 14:53:47 +0330 Subject: [PATCH 07/14] Define an enum for common bases --- src/bases/base_names.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/bases/base_names.py diff --git a/src/bases/base_names.py b/src/bases/base_names.py new file mode 100644 index 0000000..f632783 --- /dev/null +++ b/src/bases/base_names.py @@ -0,0 +1,9 @@ +from enum import Enum + + +class Base(Enum): + """Base enum""" + BINARY = 2 + OCTAL = 8 + DECIMAL = 10 + HEXADECIMAL = 16 From 7296285f80c3651155d0c1df9d2decf1bc92b73f Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 14:50:24 +0330 Subject: [PATCH 08/14] Test prompt function Test if the return value of the select_base function is an empty string or null --- tests/test_prompt.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/test_prompt.py diff --git a/tests/test_prompt.py b/tests/test_prompt.py new file mode 100644 index 0000000..46b9503 --- /dev/null +++ b/tests/test_prompt.py @@ -0,0 +1,7 @@ +from src.cli.prompt import select_base + + +def test_input_value(): + # Test if the input is not an empty string or null + selected_base = select_base() + assert selected_base["input"] != "" or None From 14a8641a4266a02fd736fd5de6b8f2da32e26dca Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 14:52:46 +0330 Subject: [PATCH 09/14] Add select_base function to prompt the user for a base --- src/cli/prompt.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/cli/prompt.py diff --git a/src/cli/prompt.py b/src/cli/prompt.py new file mode 100644 index 0000000..d136038 --- /dev/null +++ b/src/cli/prompt.py @@ -0,0 +1,21 @@ +import inquirer +from src.bases.base_names import Base + + +def select_base() -> dict[str, str]: + """prompts the user to select a base + + Returns: + dict[str, str]: user selected base + """ + base_list = [ + inquirer.List( + "input", + message="Select base", + choices=[base.value for base in Base], + ), + ] + + answer = inquirer.prompt(base_list) + + return answer From 40a43579f0851e14fde425e07be96487a11ee8e5 Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 18:22:34 +0330 Subject: [PATCH 10/14] Move validation methods to Validator class --- src/utils/validator.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/utils/validator.py diff --git a/src/utils/validator.py b/src/utils/validator.py new file mode 100644 index 0000000..bc743ee --- /dev/null +++ b/src/utils/validator.py @@ -0,0 +1,18 @@ +import re + + +class Validator: + @staticmethod + def is_valid_base(number: str, base: int) -> bool: + if base < 16: + pattern = rf'^[0-{base - 1}]$' + else: + pattern = r'^[0-9A-Fa-f]+$' + + return re.match(pattern, number) is not None + + @staticmethod + def validate_input(entered_number: str, selected_base: int): + for digit in entered_number: + if not Validator.is_valid_base(digit, selected_base): + raise ValueError() From 3fab3abc7858e294519298c31147c6ebc7859e1e Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 18:23:48 +0330 Subject: [PATCH 11/14] Change the return type of select_base --- src/cli/prompt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/prompt.py b/src/cli/prompt.py index d136038..8dd3a6e 100644 --- a/src/cli/prompt.py +++ b/src/cli/prompt.py @@ -2,7 +2,7 @@ from src.bases.base_names import Base -def select_base() -> dict[str, str]: +def select_base() -> int: """prompts the user to select a base Returns: @@ -18,4 +18,4 @@ def select_base() -> dict[str, str]: answer = inquirer.prompt(base_list) - return answer + return answer["input"] From 134e19892571b1ab8ec1c93c5ac0ef5ca6b679e6 Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 19:17:59 +0330 Subject: [PATCH 12/14] Test convert_base --- tests/test_convert_base.py | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/test_convert_base.py diff --git a/tests/test_convert_base.py b/tests/test_convert_base.py new file mode 100644 index 0000000..ad101ec --- /dev/null +++ b/tests/test_convert_base.py @@ -0,0 +1,89 @@ +from src.core.convert import convert_base + +# Binary to other bases + + +def test_bin_to_bin(): + result = convert_base("1001", 2, 2) + assert result == "1001" + + +def test_bin_to_oct(): + result = convert_base("1101", 2, 8) + assert result == "15" + + +def test_bin_to_dec(): + result = convert_base("1100", 2, 10) + assert result == "12" + + +def test_bin_to_hex(): + result = convert_base("1111", 2, 16) + assert result == "F" + +# Octal to other bases + + +def test_oct_to_oct(): + result = convert_base("73", 8, 8) + assert result == "73" + + +def test_oct_to_bin(): + result = convert_base("73", 8, 2) + assert result == "111011" + + +def test_oct_to_dec(): + result = convert_base("73", 8, 10) + assert result == "59" + + +def test_oct_to_hex(): + result = convert_base("73", 8, 16) + assert result == "3B" + +# Decimal to other bases + + +def test_dec_to_dec(): + result = convert_base("94", 10, 10) + assert result == "94" + + +def test_dec_to_bin(): + result = convert_base("94", 10, 2) + assert result == "1011110" + + +def test_dec_to_oct(): + result = convert_base("94", 10, 8) + assert result == "136" + + +def test_dec_to_hex(): + result = convert_base("94", 10, 16) + assert result == "5E" + +# Hexadecimal to other bases + + +def test_hex_to_hex(): + result = convert_base("DEA5", 16, 16) + assert result == "DEA5" + + +def test_hex_to_bin(): + result = convert_base("DEA5", 16, 2) + assert result == "1101111010100101" + + +def test_hex_to_oct(): + result = convert_base("DEA5", 16, 8) + assert result == "157245" + + +def test_hex_to_dec(): + result = convert_base("DEA5", 16, 10) + assert result == "56997" From 7eafb6d2a11c8696d94aca00b02c7a4e585e6f80 Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 19:19:04 +0330 Subject: [PATCH 13/14] Implement a working base converasion method --- src/core/convert.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/core/convert.py diff --git a/src/core/convert.py b/src/core/convert.py new file mode 100644 index 0000000..9f9ff61 --- /dev/null +++ b/src/core/convert.py @@ -0,0 +1,15 @@ +def convert_base(number: str, origin_base: int, destination_base: int) -> str: + if origin_base == destination_base: + return number + converted_number = "" + number = str(int(number, base=origin_base)) + + if destination_base == 2: + converted_number = bin(int(number))[2:] + elif destination_base == 8: + converted_number = oct(int(number))[2:] + elif destination_base == 10: + converted_number = number + elif destination_base == 16: + converted_number = hex(int(number)).upper()[2:] + return converted_number From 0fd94ef7c3dfa8bbd09112dc0f1a27820c6c38ab Mon Sep 17 00:00:00 2001 From: Navid Date: Sun, 6 Aug 2023 19:43:33 +0330 Subject: [PATCH 14/14] Update main --- main.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 9ef46e9..8040b4b 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,20 @@ from sys import path +from src.cli.prompt import select_base +from src.utils.validator import Validator +from src.core.convert import convert_base def main(): - pass + origin_base = select_base() + entered_number = input("Enter number:") + + Validator.validate_input(entered_number, origin_base) + + destination_base = select_base() + + converted_number = convert_base( + entered_number, origin_base, destination_base) + print(converted_number) if __name__ == "__main__":