From 218b341f970dd7e1a53182730e8c6adc6b9cfea2 Mon Sep 17 00:00:00 2001 From: Xuan4781 Date: Thu, 30 Oct 2025 23:34:17 -0400 Subject: [PATCH 01/19] create initial folder setup --- examples/demo.py | 0 src/morse_code/__init__.py | 0 tests/test_decode.py | 0 tests/test_encode.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/demo.py create mode 100644 src/morse_code/__init__.py create mode 100644 tests/test_decode.py create mode 100644 tests/test_encode.py diff --git a/examples/demo.py b/examples/demo.py new file mode 100644 index 0000000..e69de29 diff --git a/src/morse_code/__init__.py b/src/morse_code/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_decode.py b/tests/test_decode.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_encode.py b/tests/test_encode.py new file mode 100644 index 0000000..e69de29 From f7513194761af18b058b6353392df4d2668a394b Mon Sep 17 00:00:00 2001 From: Xuan4781 Date: Thu, 30 Oct 2025 23:43:02 -0400 Subject: [PATCH 02/19] Add initial pyproject.toml for morse_code package --- pyproject.toml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4e81985 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "morse_code" +version = "0.1.0" +description = "A Python package for encoding and decoding Morse code." +readme = "README.md" +requires-python = ">=3.10" +license = { file = "LICENSE" } +authors = [{ name = "Team Lumen" }] +keywords = ["python", "morse code", "encoder", "decoder"] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent" +] + +[project.urls] +Homepage = "https://github.com/swe-students-fall2025/3-python-package-team_lumen" +Issues = "https://github.com/swe-students-fall2025/3-python-package-team_lumen/issues" + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] + +[project.scripts] +morse_code = "morse_code.core:cli" From 761c6345914d7c8f18914a516da65c7b21ee7a1b Mon Sep 17 00:00:00 2001 From: Xuan4781 Date: Thu, 30 Oct 2025 23:48:40 -0400 Subject: [PATCH 03/19] Add Pipfile with Python version and dependencies --- Pipfile | 13 +++++++++ Pipfile.lock | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 Pipfile create mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..65722d5 --- /dev/null +++ b/Pipfile @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pytest = "*" +morse_code = {file = ".", editable = true} + +[dev-packages] + +[requires] +python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..2f9e06a --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,74 @@ +{ + "_meta": { + "hash": { + "sha256": "30b7f7af999b98edc5254671e6cb948a61005dc6efd8a65a82a4b35cb500fba6" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.10" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, + "iniconfig": { + "hashes": [ + "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", + "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12" + ], + "markers": "python_version >= '3.10'", + "version": "==2.3.0" + }, + "morse-code": { + "editable": true, + "file": "." + }, + "packaging": { + "hashes": [ + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" + ], + "markers": "python_version >= '3.8'", + "version": "==25.0" + }, + "pluggy": { + "hashes": [ + "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", + "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" + ], + "markers": "python_version >= '3.9'", + "version": "==1.6.0" + }, + "pygments": { + "hashes": [ + "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", + "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" + ], + "markers": "python_version >= '3.8'", + "version": "==2.19.2" + }, + "pytest": { + "hashes": [ + "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", + "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==8.4.2" + } + }, + "develop": {} +} From 6dbc02709ce5f7c090e0773be5159dfcda6202fa Mon Sep 17 00:00:00 2001 From: Xuan4781 Date: Thu, 30 Oct 2025 23:56:14 -0400 Subject: [PATCH 04/19] add Github build.yml for CI/CD --- .github/workflows/build.yml | 61 +++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..019f533 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,61 @@ +name: CI / CD - Morse Code + +on: + pull_request: + branches: [pipfile-experiment] + push: + tags: ["v*"] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + python-version: ["3.10", "3.11"] + + steps: + - uses: actions/checkout@v4 + + - name: Install Python and Pipenv + uses: kojoru/prepare-pipenv@v1 + with: + python-version: ${{ matrix.python-version }} + + + - name: Turn on 'editable' mode + run: | + pipenv install -e . + + - name: Run pytest + run: | + pipenv install pytest + pipenv --venv + pipenv run python -m pytest + + deliver: + if: github.event_name != 'pull_request' + needs: [build] + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + + - name: Install Python, pipenv and Pipfile packages + uses: kojoru/prepare-pipenv@v1 + with: + python-version: "3.10" + + - name: Build package + run: | + pipenv install build + pipenv run python -m build . + + # publish to PyPI Test server + # - name: Publish to TestPyPI + # uses: pypa/gh-action-pypi-publish@release/v1 + # with: + # password: ${{ secrets.TEST_PYPI_API_TOKEN }} + # repository-url: https://test.pypi.org/legacy/ From bc0d10fc0e2574b6abde8322fcc2ca8a107fee9a Mon Sep 17 00:00:00 2001 From: Xuan4781 Date: Fri, 31 Oct 2025 00:08:22 -0400 Subject: [PATCH 05/19] Add placeholder tests --- tests/test_decode.py | 2 ++ tests/test_encode.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/test_decode.py b/tests/test_decode.py index e69de29..26af617 100644 --- a/tests/test_decode.py +++ b/tests/test_decode.py @@ -0,0 +1,2 @@ +def test_encode(): + assert True \ No newline at end of file diff --git a/tests/test_encode.py b/tests/test_encode.py index e69de29..03bb3d5 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -0,0 +1,2 @@ +def test_decode(): + assert True \ No newline at end of file From 5f7494fb43df08e42f15717ac7812f37f766f502 Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Fri, 31 Oct 2025 01:16:56 -0400 Subject: [PATCH 06/19] add main file --- src/morse_code/__init__.py | 25 +++++++++++++++++++++++++ src/morse_code/__main__.py | 16 ++++++++++++++++ src/morse_code/core.py | 0 3 files changed, 41 insertions(+) create mode 100644 src/morse_code/__main__.py create mode 100644 src/morse_code/core.py diff --git a/src/morse_code/__init__.py b/src/morse_code/__init__.py index e69de29..8c7b2c4 100644 --- a/src/morse_code/__init__.py +++ b/src/morse_code/__init__.py @@ -0,0 +1,25 @@ +""" +Morse Code Package +A Python package for encoding and decoding Morse code with additional utilities. +""" + +from morse_code.core import ( + encode, + decode, + is_valid, + normalize_text, + normalize_code, + explain, + quiz +) + +__all__ = [ + 'encode', + 'decode', + 'is_valid', + 'normalize_text', + 'normalize_code', + 'explain', + 'quiz' +] + diff --git a/src/morse_code/__main__.py b/src/morse_code/__main__.py new file mode 100644 index 0000000..895938c --- /dev/null +++ b/src/morse_code/__main__.py @@ -0,0 +1,16 @@ +from .core import encode, decode, is_valid, explain + + +def main(): + print("Morse Code Demo") + text = "HELLO WORLD" + encoded = encode(text) + decoded = decode(encoded) + print(f"Original: {text}") + print(f"Encoded : {encoded}") + print(f"Decoded : {decoded}") + # print(f"Valid? {is_valid(encoded)}") + # print(f"Explain : {explain(encoded)}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/morse_code/core.py b/src/morse_code/core.py new file mode 100644 index 0000000..e69de29 From ae4f73733c5e558c78ba6c8248c482ba957bf16b Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Fri, 31 Oct 2025 01:19:51 -0400 Subject: [PATCH 07/19] add mapping morse code data --- src/morse_code/mapping.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/morse_code/mapping.py diff --git a/src/morse_code/mapping.py b/src/morse_code/mapping.py new file mode 100644 index 0000000..78e2a87 --- /dev/null +++ b/src/morse_code/mapping.py @@ -0,0 +1,13 @@ +LETTER_TO_MORSE = { + "A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", + "F": "..-.", "G": "--.", "H": "....", "I": "..", "J": ".---", + "K": "-.-", "L": ".-..", "M": "--", "N": "-.", "O": "---", + "P": ".--.", "Q": "--.-", "R": ".-.", "S": "...", "T": "-", + "U": "..-", "V": "...-", "W": ".--", "X": "-..-", "Y": "-.--", + "Z": "--..", + "0": "-----", "1": ".----", "2": "..---", "3": "...--", "4": "....-", + "5": ".....", "6": "-....", "7": "--...", "8": "---..", "9": "----.", + ".": ".-.-.-", ",": "--..--", "?": "..--..", "/": "-..-.", "-": "-....-", + "(": "-.--.", ")": "-.--.-" +} +MORSE_TO_LETTER = {v: k for k, v in LETTER_TO_MORSE.items()} From 6df246cf21c6c4f93b60168614c283267d11c69a Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Fri, 31 Oct 2025 01:26:36 -0400 Subject: [PATCH 08/19] add function TODOs --- src/morse_code/__main__.py | 5 +++++ src/morse_code/core.py | 18 ++++++++++++++++++ src/morse_code/explain.py | 3 +++ src/morse_code/normalize.py | 8 ++++++++ src/morse_code/quiz.py | 3 +++ 5 files changed, 37 insertions(+) create mode 100644 src/morse_code/explain.py create mode 100644 src/morse_code/normalize.py create mode 100644 src/morse_code/quiz.py diff --git a/src/morse_code/__main__.py b/src/morse_code/__main__.py index 895938c..c5198a1 100644 --- a/src/morse_code/__main__.py +++ b/src/morse_code/__main__.py @@ -12,5 +12,10 @@ def main(): # print(f"Valid? {is_valid(encoded)}") # print(f"Explain : {explain(encoded)}") + # text = input("Enter text to encode: ") + # encoded = encode(text) + # print("Encoded:", encoded) + # print("Decoded back:", decode(encoded)) + if __name__ == "__main__": main() \ No newline at end of file diff --git a/src/morse_code/core.py b/src/morse_code/core.py index e69de29..3cf5c77 100644 --- a/src/morse_code/core.py +++ b/src/morse_code/core.py @@ -0,0 +1,18 @@ +def encode(text): + # TODO: Implement encoding logic + pass + + +def decode(morse_code): + # TODO: Implement decoding logic + pass + + +def is_valid(morse_code): + # TODO: Implement validation logic + pass + + + + + diff --git a/src/morse_code/explain.py b/src/morse_code/explain.py new file mode 100644 index 0000000..d17e65a --- /dev/null +++ b/src/morse_code/explain.py @@ -0,0 +1,3 @@ +def explain(morse_message): + # TODO: Implement explanation logic + pass \ No newline at end of file diff --git a/src/morse_code/normalize.py b/src/morse_code/normalize.py new file mode 100644 index 0000000..b793cde --- /dev/null +++ b/src/morse_code/normalize.py @@ -0,0 +1,8 @@ +def normalize_text(text): + # TODO: Implement text normalization logic + pass + + +def normalize_code(morse_code): + # TODO: Implement code normalization logic + pass \ No newline at end of file diff --git a/src/morse_code/quiz.py b/src/morse_code/quiz.py new file mode 100644 index 0000000..8cdba94 --- /dev/null +++ b/src/morse_code/quiz.py @@ -0,0 +1,3 @@ +def quiz(morse_code): + # TODO: Implement quiz logic + pass From 20619d2d9926e07cfe584e35b4f8c7648d270ff3 Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Fri, 31 Oct 2025 01:36:36 -0400 Subject: [PATCH 09/19] add encode function --- src/morse_code/__init__.py | 10 ++++++---- src/morse_code/__main__.py | 3 ++- src/morse_code/core.py | 37 ++++++++++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/morse_code/__init__.py b/src/morse_code/__init__.py index 8c7b2c4..c2d0d6b 100644 --- a/src/morse_code/__init__.py +++ b/src/morse_code/__init__.py @@ -6,12 +6,14 @@ from morse_code.core import ( encode, decode, - is_valid, + is_valid +) +from morse_code.normalize import ( normalize_text, - normalize_code, - explain, - quiz + normalize_code ) +from morse_code.explain import explain +from morse_code.quiz import quiz __all__ = [ 'encode', diff --git a/src/morse_code/__main__.py b/src/morse_code/__main__.py index c5198a1..2219f5c 100644 --- a/src/morse_code/__main__.py +++ b/src/morse_code/__main__.py @@ -1,4 +1,5 @@ -from .core import encode, decode, is_valid, explain +from .core import encode, decode, is_valid +from .explain import explain def main(): diff --git a/src/morse_code/core.py b/src/morse_code/core.py index 3cf5c77..3b3a42d 100644 --- a/src/morse_code/core.py +++ b/src/morse_code/core.py @@ -1,10 +1,41 @@ def encode(text): - # TODO: Implement encoding logic - pass + """ + Convert English text to morse code. + + Args: + text: English text string to encode + + Returns: + Morse code string + """ + from morse_code.mapping import LETTER_TO_MORSE + + # Normalize the text first(delete after normalize function is implemented) + if text is None: + return None + normalized = text.upper().strip() + + # change to this: + # normalized = normalize_text(text) + + # Convert each character to morse code + result = [] + for char in normalized: + if char == ' ': + # Space between words becomes ' / ' + result.append('/') + else: + # Get morse code for character + morse_char = LETTER_TO_MORSE.get(char, '') + if morse_char: + result.append(morse_char) + + # Join with spaces between letters + return ' '.join(result) def decode(morse_code): - # TODO: Implement decoding logic + # TODO: Implement decoding logic: convert morse code to English pass From fd1215ac4718038588924d5a4194d8ca0e62836c Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Fri, 31 Oct 2025 01:39:08 -0400 Subject: [PATCH 10/19] change project name to morseify --- src/{morse_code => morseify}/__init__.py | 8 ++++---- src/{morse_code => morseify}/__main__.py | 0 src/{morse_code => morseify}/core.py | 4 +++- src/{morse_code => morseify}/explain.py | 0 src/{morse_code => morseify}/mapping.py | 0 src/{morse_code => morseify}/normalize.py | 0 src/{morse_code => morseify}/quiz.py | 0 7 files changed, 7 insertions(+), 5 deletions(-) rename src/{morse_code => morseify}/__init__.py (69%) rename src/{morse_code => morseify}/__main__.py (100%) rename src/{morse_code => morseify}/core.py (95%) rename src/{morse_code => morseify}/explain.py (100%) rename src/{morse_code => morseify}/mapping.py (100%) rename src/{morse_code => morseify}/normalize.py (100%) rename src/{morse_code => morseify}/quiz.py (100%) diff --git a/src/morse_code/__init__.py b/src/morseify/__init__.py similarity index 69% rename from src/morse_code/__init__.py rename to src/morseify/__init__.py index c2d0d6b..7b188a8 100644 --- a/src/morse_code/__init__.py +++ b/src/morseify/__init__.py @@ -3,17 +3,17 @@ A Python package for encoding and decoding Morse code with additional utilities. """ -from morse_code.core import ( +from morseify.core import ( encode, decode, is_valid ) -from morse_code.normalize import ( +from morseify.normalize import ( normalize_text, normalize_code ) -from morse_code.explain import explain -from morse_code.quiz import quiz +from morseify.explain import explain +from morseify.quiz import quiz __all__ = [ 'encode', diff --git a/src/morse_code/__main__.py b/src/morseify/__main__.py similarity index 100% rename from src/morse_code/__main__.py rename to src/morseify/__main__.py diff --git a/src/morse_code/core.py b/src/morseify/core.py similarity index 95% rename from src/morse_code/core.py rename to src/morseify/core.py index 3b3a42d..35a830b 100644 --- a/src/morse_code/core.py +++ b/src/morseify/core.py @@ -1,3 +1,5 @@ +from morseify.mapping import LETTER_TO_MORSE + def encode(text): """ Convert English text to morse code. @@ -8,7 +10,7 @@ def encode(text): Returns: Morse code string """ - from morse_code.mapping import LETTER_TO_MORSE + # Normalize the text first(delete after normalize function is implemented) if text is None: diff --git a/src/morse_code/explain.py b/src/morseify/explain.py similarity index 100% rename from src/morse_code/explain.py rename to src/morseify/explain.py diff --git a/src/morse_code/mapping.py b/src/morseify/mapping.py similarity index 100% rename from src/morse_code/mapping.py rename to src/morseify/mapping.py diff --git a/src/morse_code/normalize.py b/src/morseify/normalize.py similarity index 100% rename from src/morse_code/normalize.py rename to src/morseify/normalize.py diff --git a/src/morse_code/quiz.py b/src/morseify/quiz.py similarity index 100% rename from src/morse_code/quiz.py rename to src/morseify/quiz.py From 47d1e03bfeda3b2d011294dc4c2b46d12a0d15ff Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Fri, 31 Oct 2025 01:58:07 -0400 Subject: [PATCH 11/19] add decode function --- Pipfile | 2 +- Pipfile.lock | 10 +--------- src/morseify/__main__.py | 42 ++++++++++++++++++++++++++++------------ src/morseify/core.py | 40 ++++++++++++++++++++++++++++++++++---- 4 files changed, 68 insertions(+), 26 deletions(-) diff --git a/Pipfile b/Pipfile index 65722d5..7e1d224 100644 --- a/Pipfile +++ b/Pipfile @@ -5,7 +5,7 @@ name = "pypi" [packages] pytest = "*" -morse_code = {file = ".", editable = true} +morse-code = {file = ".", editable = true} [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 2f9e06a..7c5eb6e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "30b7f7af999b98edc5254671e6cb948a61005dc6efd8a65a82a4b35cb500fba6" + "sha256": "8d4fce0340a506ab07fcfec2c61ecc8853335394bbda7148e9f28d01a844aded" }, "pipfile-spec": 6, "requires": { @@ -16,14 +16,6 @@ ] }, "default": { - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==0.4.6" - }, "iniconfig": { "hashes": [ "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", diff --git a/src/morseify/__main__.py b/src/morseify/__main__.py index 2219f5c..5490ec8 100644 --- a/src/morseify/__main__.py +++ b/src/morseify/__main__.py @@ -3,20 +3,38 @@ def main(): - print("Morse Code Demo") - text = "HELLO WORLD" - encoded = encode(text) - decoded = decode(encoded) - print(f"Original: {text}") - print(f"Encoded : {encoded}") - print(f"Decoded : {decoded}") - # print(f"Valid? {is_valid(encoded)}") - # print(f"Explain : {explain(encoded)}") - + print("Morse Code Encode/Decode Test") + # Test cases + test_cases = [ + "HELLO", + "HELLO WORLD", + "SOS", + "PYTHON", + "123", + "A1B2C3" + ] + + for text in test_cases: + encoded = encode(text) + decoded = decode(encoded) + + print(f"\nOriginal: '{text}'") + print(f"Encoded : '{encoded}'") + print(f"Decoded : '{decoded}'") + + # Check if round-trip works + if decoded == text: + print("✓ Round-trip successful!") + else: + print(f"✗ Round-trip failed! Expected: '{text}'") + + # Uncomment for interactive mode + # print("\nInteractive mode:") # text = input("Enter text to encode: ") # encoded = encode(text) - # print("Encoded:", encoded) - # print("Decoded back:", decode(encoded)) + # print(f"Encoded: {encoded}") + # decoded = decode(encoded) + # print(f"Decoded: {decoded}") if __name__ == "__main__": main() \ No newline at end of file diff --git a/src/morseify/core.py b/src/morseify/core.py index 35a830b..124361a 100644 --- a/src/morseify/core.py +++ b/src/morseify/core.py @@ -1,4 +1,5 @@ from morseify.mapping import LETTER_TO_MORSE +from morseify.mapping import MORSE_TO_LETTER def encode(text): """ @@ -10,8 +11,7 @@ def encode(text): Returns: Morse code string """ - - + # Normalize the text first(delete after normalize function is implemented) if text is None: return None @@ -37,8 +37,40 @@ def encode(text): def decode(morse_code): - # TODO: Implement decoding logic: convert morse code to English - pass + """ + Convert morse code to English text. + + Args: + morse_code: Morse code string to decode + + Returns: + English text string + """ + # check if the morse code is valid + # valid_morse = is_valid(morse_code) + + valid_morse = morse_code + + # Split morse code by spaces to get individual morse sequences + morse_words = valid_morse.split(' ') + # print(morse_words) + + result = [] + for i in morse_words: + if i == '': + # Skip empty sequences (multiple spaces) + continue + elif i == '/': + # Word separator + result.append(' ') + else: + # Look up the morse sequence in the dictionary + letter = MORSE_TO_LETTER.get(i, '') + result.append(letter) + + return ''.join(result) + + def is_valid(morse_code): From e3411b4bea4e0c205ac8d7d6ff4c46ebe1474ebc Mon Sep 17 00:00:00 2001 From: Angela Gao Date: Fri, 31 Oct 2025 14:45:02 -0400 Subject: [PATCH 12/19] add is_valid function & test file setup --- src/morseify/core.py | 25 ++++++++++++++++++++----- tests/test_is_valid.py | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 tests/test_is_valid.py diff --git a/src/morseify/core.py b/src/morseify/core.py index 124361a..99f3ba7 100644 --- a/src/morseify/core.py +++ b/src/morseify/core.py @@ -74,10 +74,25 @@ def decode(morse_code): def is_valid(morse_code): - # TODO: Implement validation logic - pass - - - + # check if morse_code is str or empty + try: + morse_code = morse_code.strip() + except AttributeError: + return False + + if not morse_code: + return False + + #check if valid chars or sequence + validchars = {'.', '-', '/', ' '} + if any(ch not in validchars for ch in morse_code): + return False + + for seq in morse_code.split(' '): + if not seq or seq == '/': + continue + if seq not in MORSE_TO_LETTER: + return False + return True diff --git a/tests/test_is_valid.py b/tests/test_is_valid.py new file mode 100644 index 0000000..32a6635 --- /dev/null +++ b/tests/test_is_valid.py @@ -0,0 +1,2 @@ +def test_is_valid(): + assert True \ No newline at end of file From 53ed5f84c502339c5e72d5ce263a58263e710bbf Mon Sep 17 00:00:00 2001 From: phoebelh <153004907+phoebelh@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:02:27 -0400 Subject: [PATCH 13/19] add: normalize_text and normalize_code function --- src/morseify/normalize.py | 56 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/morseify/normalize.py b/src/morseify/normalize.py index b793cde..38d3392 100644 --- a/src/morseify/normalize.py +++ b/src/morseify/normalize.py @@ -1,8 +1,56 @@ +import re +import string + def normalize_text(text): - # TODO: Implement text normalization logic - pass + """ + Clean and standardize English text before encoding to Morse. + + Args: + text (str): Input text string + + Returns: + str: Normalized text ready for encoding + """ + if not isinstance(text, str): + return "" + # convert to uppercase + text = text.upper() + + # keep only supporting text + morse_punct = ".,?/-()" + allowed_chars = string.ascii_uppercase + string.digits + ' ' + morse_punct + cleaned = ''.join(ch for ch in text if ch in allowed_chars) + + # clean multiple or leading/trailing spaces + cleaned = re.sub(r'\s+', ' ', cleaned) + cleaned = cleaned.strip() + + return cleaned def normalize_code(morse_code): - # TODO: Implement code normalization logic - pass \ No newline at end of file + """ + Clean and standardize Morse code text before decoding. + + Args: + morse_code (str): Raw Morse code string + + Returns: + str: Normalized Morse code ready for decoding + """ + if not isinstance(morse_code, str): + return "" + + # replace any tabs with a space + morse_code = morse_code.replace('\t', ' ') + + # clean multiple spaces + morse_code = re.sub(r'\s+', ' ', morse_code) + + # clean multiple slashes + morse_code = re.sub(r'/+', '/', morse_code) + + # clean any leading/trailing space or slashes + morse_code = morse_code.strip(' /') + + return morse_code From 022d60eb00d0948cc0142bc6868d598fcc9caa48 Mon Sep 17 00:00:00 2001 From: phoebelh <153004907+phoebelh@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:03:14 -0400 Subject: [PATCH 14/19] add: normalize_text and normalize_code test file setup --- tests/test_normalize_code.py | 2 ++ tests/test_normalize_text.py | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 tests/test_normalize_code.py create mode 100644 tests/test_normalize_text.py diff --git a/tests/test_normalize_code.py b/tests/test_normalize_code.py new file mode 100644 index 0000000..abcd8d4 --- /dev/null +++ b/tests/test_normalize_code.py @@ -0,0 +1,2 @@ +def test_normalize_code(): + assert True \ No newline at end of file diff --git a/tests/test_normalize_text.py b/tests/test_normalize_text.py new file mode 100644 index 0000000..14519ca --- /dev/null +++ b/tests/test_normalize_text.py @@ -0,0 +1,2 @@ +def test_normalize_text(): + assert True \ No newline at end of file From 77018f887e4a6ac690b51551c46d32ec96285378 Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Sat, 1 Nov 2025 14:36:26 -0400 Subject: [PATCH 15/19] add is_valid and normalize funcs to decode/encode --- pyproject.toml | 2 +- src/morseify/core.py | 36 +++++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4e81985..451501e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,4 +28,4 @@ package-dir = {"" = "src"} where = ["src"] [project.scripts] -morse_code = "morse_code.core:cli" +morse_code = "morseify.core:cli" diff --git a/src/morseify/core.py b/src/morseify/core.py index 99f3ba7..04c9343 100644 --- a/src/morseify/core.py +++ b/src/morseify/core.py @@ -1,5 +1,7 @@ from morseify.mapping import LETTER_TO_MORSE from morseify.mapping import MORSE_TO_LETTER +from morseify.normalize import normalize_text +from morseify.normalize import normalize_code def encode(text): """ @@ -13,12 +15,11 @@ def encode(text): """ # Normalize the text first(delete after normalize function is implemented) - if text is None: - return None - normalized = text.upper().strip() + # if text is None: + # return None + # normalized = text.upper().strip() - # change to this: - # normalized = normalize_text(text) + normalized = normalize_text(text) # Convert each character to morse code result = [] @@ -44,16 +45,17 @@ def decode(morse_code): morse_code: Morse code string to decode Returns: - English text string + English text string, or error message if morse code is invalid """ - # check if the morse code is valid - # valid_morse = is_valid(morse_code) + # Check if the morse code is valid + if not is_valid(morse_code): + return "Morse code is not valid" - valid_morse = morse_code + # If valid: normalize and decode + normalized = normalize_code(morse_code) # Split morse code by spaces to get individual morse sequences - morse_words = valid_morse.split(' ') - # print(morse_words) + morse_words = normalized.split(' ') result = [] for i in morse_words: @@ -73,7 +75,19 @@ def decode(morse_code): + + def is_valid(morse_code): + """ + Check if morse code is valid. + + Args: + morse_code: Morse code string to check if valid + + Returns: + Boolean: True if valid, False otherwise + """ + # check if morse_code is str or empty try: morse_code = morse_code.strip() From f5847a68bc91d0c063f6ba33c3bb66eb05548f5e Mon Sep 17 00:00:00 2001 From: yiminliu2004 Date: Sat, 1 Nov 2025 14:46:04 -0400 Subject: [PATCH 16/19] add cli --- pyproject.toml | 2 +- src/morseify/cli.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/morseify/cli.py diff --git a/pyproject.toml b/pyproject.toml index 451501e..6413373 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,4 +28,4 @@ package-dir = {"" = "src"} where = ["src"] [project.scripts] -morse_code = "morseify.core:cli" +morseify = "morseify.cli:cli" diff --git a/src/morseify/cli.py b/src/morseify/cli.py new file mode 100644 index 0000000..296591b --- /dev/null +++ b/src/morseify/cli.py @@ -0,0 +1,41 @@ +""" +Command-line interface for morseify. +""" + +import sys +from morseify.core import encode, decode, is_valid + + +def cli(): + """ + Command-line interface for morseify. + Auto-detects whether input is text (encode) or morse code (decode). + + Usage: + morseify "HELLO" # Encodes text to morse code + morseify ".... . .-.. .-.. ---" # Decodes morse code to text + """ + if len(sys.argv) < 2: + print("Usage: morseify ") + print("Examples:") + print(' morseify "HELLO"') + print(' morseify ".... . .-.. .-.. ---"') + sys.exit(1) + + # Join all arguments in case user passes multiple words + input_text = ' '.join(sys.argv[1:]) + + # Auto-detect: if input contains only morse characters (., -, /, space), decode it + # Otherwise, encode it + valid_morse_chars = {'.', '-', '/', ' '} + is_morse = all(char in valid_morse_chars for char in input_text) and input_text.strip() + + if is_morse and is_valid(input_text): + # It's valid morse code, decode it + result = decode(input_text) + print(result) + else: + # It's text, encode it + result = encode(input_text) + print(result) + From 0515325e748b34fa481f531c6a2a631347c1a135 Mon Sep 17 00:00:00 2001 From: gkbichara Date: Sun, 2 Nov 2025 01:11:00 -0400 Subject: [PATCH 17/19] Started quiz functionality --- src/morseify/__main__.py | 1 + src/morseify/quiz.py | 68 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/morseify/__main__.py b/src/morseify/__main__.py index 5490ec8..e974edb 100644 --- a/src/morseify/__main__.py +++ b/src/morseify/__main__.py @@ -1,5 +1,6 @@ from .core import encode, decode, is_valid from .explain import explain +from .quiz import quiz def main(): diff --git a/src/morseify/quiz.py b/src/morseify/quiz.py index 8cdba94..cfb0e8c 100644 --- a/src/morseify/quiz.py +++ b/src/morseify/quiz.py @@ -1,3 +1,67 @@ -def quiz(morse_code): - # TODO: Implement quiz logic +import random +import sys +from pathlib import Path + +# Add parent directory to path if running as script +if __name__ == "__main__": + sys.path.insert(0, str(Path(__file__).parent.parent)) + +try: + from morseify.core import encode, decode +except ModuleNotFoundError: + from .core import encode, decode, is_valid + +# List of sentences for the quiz +QUIZ_SENTENCES = [ + "HELLO WORLD", + "LET US TRY", + "MORSE CODE IS FUN", + "PYTHON ROCKS", + "LEARNING TAKES TIME", + "FORZA ROMA", + "SWE IS USEFUL", + "RUNNING OUT OF IDEAS", + "THIS SHOULD BE ENOUGH", + "I AM NOT VERY CREATIVE" +] + + +def quiz(sentence=None, mode=None): + """ + Interactive morse code quiz. + + Args: + sentence: Optional - specific sentence to use, or None for random + mode: Optional - 'reading' or 'writing', or None to ask + """ + if sentence is None: + selected_sentence = random.choice(QUIZ_SENTENCES) + else: + selected_sentence = sentence.upper().strip() + + + if mode is None: + mode = input("Enter the mode: 'reading' or 'writing': ") + while mode not in ['reading', 'writing']: + print() + print("Invalid mode. Please enter 'reading' or 'writing'") + print("Please try again.") + print() + mode = input("Enter the mode: 'reading' or 'writing': ") + + if mode == 'reading': + print(f"Your sentence in morse code is: {encode(selected_sentence)}") + answer = input("Enter the answer: ").upper().strip() + if answer == (selected_sentence): + print("Correct!") + else: + print("Incorrect!") + else: + print(f"Writing mode: {selected_sentence}") + + # TODO: Rest of quiz logic pass + + +if __name__ == "__main__": + quiz() \ No newline at end of file From 513d1737415ad5f0afb628315c29726e4f68b735 Mon Sep 17 00:00:00 2001 From: gkbichara Date: Sun, 2 Nov 2025 01:32:08 -0400 Subject: [PATCH 18/19] Added write function, and prettified output --- src/morseify/quiz.py | 61 ++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/src/morseify/quiz.py b/src/morseify/quiz.py index cfb0e8c..cf5821f 100644 --- a/src/morseify/quiz.py +++ b/src/morseify/quiz.py @@ -7,7 +7,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) try: - from morseify.core import encode, decode + from morseify.core import encode, decode, is_valid except ModuleNotFoundError: from .core import encode, decode, is_valid @@ -40,27 +40,56 @@ def quiz(sentence=None, mode=None): selected_sentence = sentence.upper().strip() + # Ask for mode if not provided if mode is None: - mode = input("Enter the mode: 'reading' or 'writing': ") + print("\n" + "=" * 60) + print("MORSE CODE QUIZ") + print("=" * 60) + print("Choose your mode:") + print("'reading' - Decode morse code → text") + print("'writing' - Encode text → morse code") + print() + mode = input("Enter the mode ('reading' or 'writing'): ").strip().lower() + while mode not in ['reading', 'writing']: - print() - print("Invalid mode. Please enter 'reading' or 'writing'") - print("Please try again.") - print() - mode = input("Enter the mode: 'reading' or 'writing': ") + print("\nInvalid mode!") + print("Please enter 'reading' or 'writing'") + mode = input("\nEnter the mode: ").strip().lower() + print("\n" + "=" * 60) if mode == 'reading': - print(f"Your sentence in morse code is: {encode(selected_sentence)}") - answer = input("Enter the answer: ").upper().strip() - if answer == (selected_sentence): - print("Correct!") + print("READING MODE: Decode morse code → text") + print("=" * 60) + morse_to_decode = encode(selected_sentence) + print(f"\nMorse code: {morse_to_decode}") + print() + answer = input("Your answer: ").upper().strip() + + if answer == selected_sentence: + print("\nCorrect! Well done!") else: - print("Incorrect!") - else: - print(f"Writing mode: {selected_sentence}") + print("\nIncorrect!") + + else: # writing mode + print("WRITING MODE: Encode text → morse code") + print("=" * 60) + print(f"\nText to encode: {selected_sentence}") + print() + + answer = input("Your answer: ").strip() + while not is_valid(answer): + print("\nInvalid morse code format!") + print("Please use only dots (.), dashes (-), spaces, and slashes (/)") + print("Example: ... --- ... (for SOS)") + answer = input("\nYour answer: ").strip() + + # Now check if the valid morse code is correct + if answer == encode(selected_sentence): + print("\nCorrect! Well done!") + else: + print("\nIncorrect!") - # TODO: Rest of quiz logic - pass + print("=" * 60) if __name__ == "__main__": From 300d411f4cee2ed66b362ccb494f0988db1be0ab Mon Sep 17 00:00:00 2001 From: gkbichara Date: Sun, 2 Nov 2025 01:45:26 -0400 Subject: [PATCH 19/19] Finished quiz functionality --- src/morseify/quiz.py | 70 +++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/morseify/quiz.py b/src/morseify/quiz.py index cf5821f..da116b5 100644 --- a/src/morseify/quiz.py +++ b/src/morseify/quiz.py @@ -63,31 +63,67 @@ def quiz(sentence=None, mode=None): morse_to_decode = encode(selected_sentence) print(f"\nMorse code: {morse_to_decode}") print() - answer = input("Your answer: ").upper().strip() - if answer == selected_sentence: - print("\nCorrect! Well done!") - else: - print("\nIncorrect!") + # Loop until correct or user gives up + while True: + answer = input("Your answer: ").upper().strip() + + if answer == selected_sentence: + print("\nCorrect! Well done!") + break + else: + print("\nIncorrect!") + retry = input("Try again? (yes/no): ").strip().lower() + + if retry == 'yes': + continue # Try again + else: # 'no' or anything else means give up + print("\n" + "-" * 60) + print("ANSWER REVEALED") + print("-" * 60) + print(f"Your answer: {answer}") + print(f"Correct answer: {selected_sentence}") + print(f"\nThe morse code '{morse_to_decode}' translates to '{selected_sentence}'") + print("-" * 60) + break else: # writing mode print("WRITING MODE: Encode text → morse code") print("=" * 60) print(f"\nText to encode: {selected_sentence}") print() + correct_morse = encode(selected_sentence) - answer = input("Your answer: ").strip() - while not is_valid(answer): - print("\nInvalid morse code format!") - print("Please use only dots (.), dashes (-), spaces, and slashes (/)") - print("Example: ... --- ... (for SOS)") - answer = input("\nYour answer: ").strip() - - # Now check if the valid morse code is correct - if answer == encode(selected_sentence): - print("\nCorrect! Well done!") - else: - print("\nIncorrect!") + # Loop until correct or user gives up + while True: + answer = input("Your answer: ").strip() + + # Validate morse code format first + while not is_valid(answer): + print("\nInvalid morse code format!") + print("Please use only dots (.), dashes (-), spaces, and slashes (/)") + print("Example: ... --- ... (for SOS)") + answer = input("\nYour answer: ").strip() + + # Check if correct + if answer == correct_morse: + print("\nCorrect! Well done!") + break + else: + print("\nIncorrect!") + retry = input("Try again? (yes/no): ").strip().lower() + + if retry == 'yes': + continue # Try again + else: # 'no' or anything else means give up + print("\n" + "-" * 60) + print("ANSWER REVEALED") + print("-" * 60) + print(f"Your answer: {answer}") + print(f"Correct answer: {correct_morse}") + print(f"\n'{selected_sentence}' in morse code is: {correct_morse}") + print("-" * 60) + break print("=" * 60)