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/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..7e1d224 --- /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..7c5eb6e --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,66 @@ +{ + "_meta": { + "hash": { + "sha256": "8d4fce0340a506ab07fcfec2c61ecc8853335394bbda7148e9f28d01a844aded" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.10" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "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": {} +} diff --git a/examples/demo.py b/examples/demo.py new file mode 100644 index 0000000..e69de29 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" diff --git a/src/morseify/__init__.py b/src/morseify/__init__.py new file mode 100644 index 0000000..7b188a8 --- /dev/null +++ b/src/morseify/__init__.py @@ -0,0 +1,27 @@ +""" +Morse Code Package +A Python package for encoding and decoding Morse code with additional utilities. +""" + +from morseify.core import ( + encode, + decode, + is_valid +) +from morseify.normalize import ( + normalize_text, + normalize_code +) +from morseify.explain import explain +from morseify.quiz import quiz + +__all__ = [ + 'encode', + 'decode', + 'is_valid', + 'normalize_text', + 'normalize_code', + 'explain', + 'quiz' +] + diff --git a/src/morseify/__main__.py b/src/morseify/__main__.py new file mode 100644 index 0000000..5490ec8 --- /dev/null +++ b/src/morseify/__main__.py @@ -0,0 +1,40 @@ +from .core import encode, decode, is_valid +from .explain import explain + + +def main(): + 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(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 new file mode 100644 index 0000000..99f3ba7 --- /dev/null +++ b/src/morseify/core.py @@ -0,0 +1,98 @@ +from morseify.mapping import LETTER_TO_MORSE +from morseify.mapping import MORSE_TO_LETTER + +def encode(text): + """ + Convert English text to morse code. + + Args: + text: English text string to encode + + Returns: + Morse code string + """ + + # 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): + """ + 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): + # 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/src/morseify/explain.py b/src/morseify/explain.py new file mode 100644 index 0000000..d17e65a --- /dev/null +++ b/src/morseify/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/morseify/mapping.py b/src/morseify/mapping.py new file mode 100644 index 0000000..78e2a87 --- /dev/null +++ b/src/morseify/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()} diff --git a/src/morseify/normalize.py b/src/morseify/normalize.py new file mode 100644 index 0000000..b793cde --- /dev/null +++ b/src/morseify/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/morseify/quiz.py b/src/morseify/quiz.py new file mode 100644 index 0000000..8cdba94 --- /dev/null +++ b/src/morseify/quiz.py @@ -0,0 +1,3 @@ +def quiz(morse_code): + # TODO: Implement quiz logic + pass diff --git a/tests/test_decode.py b/tests/test_decode.py new file mode 100644 index 0000000..26af617 --- /dev/null +++ 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 new file mode 100644 index 0000000..03bb3d5 --- /dev/null +++ b/tests/test_encode.py @@ -0,0 +1,2 @@ +def test_decode(): + assert True \ No newline at end of file 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