diff --git a/.github/workflows/event-logger.yml b/.github/workflows/event-logger.yml index 31f231e..2a6ca80 100644 --- a/.github/workflows/event-logger.yml +++ b/.github/workflows/event-logger.yml @@ -35,20 +35,32 @@ jobs: - name: Log pull request opened if: github.event_name == 'pull_request' && github.event.action == 'opened' run: | + if [ -z "$COMMIT_LOG_API" ]; then + echo "COMMIT_LOG_API is not set"; exit 1 + fi pipenv run gitcommitlogger -r $(echo $REPOSITORY_URL) -t pull_request_opened -d $(echo $PR_CREATED_AT) -un $(echo $GITHUB_LOGIN) -o commit_stats.csv -u $(echo $COMMIT_LOG_API) -v - name: Log pull request closed and merged if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true run: | + if [ -z "$COMMIT_LOG_API" ]; then + echo "COMMIT_LOG_API is not set"; exit 1 + fi echo $COMMITS > commits.json cat commits.json # debugging pipenv run gitcommitlogger -r $(echo $REPOSITORY_URL) -t pull_request_merged -d $(echo $PR_CLOSED_AT) -un $(echo $GITHUB_LOGIN) -i commits.json -o commit_stats.csv -u $(echo $COMMIT_LOG_API) -v - name: Log pull request closed without merge if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == false run: | + if [ -z "$COMMIT_LOG_API" ]; then + echo "COMMIT_LOG_API is not set"; exit 1 + fi pipenv run gitcommitlogger -r $(echo $REPOSITORY_URL) -t pull_request_closed -d $(echo $PR_CLOSED_AT) -un $(echo $GITHUB_LOGIN) -o commit_stats.csv -u $(echo $COMMIT_LOG_API) -v - name: Log push if: github.event_name == 'push' run: | + if [ -z "$COMMIT_LOG_API" ]; then + echo "COMMIT_LOG_API is not set"; exit 1 + fi echo $COMMITS > commits.json cat commits.json # debugging pipenv run gitcommitlogger -r $(echo $REPOSITORY_URL) -t $(echo $EVENT_TYPE) -i commits.json -o commit_stats.csv -u $(echo $COMMIT_LOG_API) -v diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..d61ea53 --- /dev/null +++ b/Pipfile @@ -0,0 +1,11 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] + +[dev-packages] + +[requires] +python_version = "3.13" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..bc0ddb5 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,20 @@ +{ + "_meta": { + "hash": { + "sha256": "494d5b4f482f0ef471f49afe28f00ec1a2ff75da2ce65060d8cabaeb3da2f100" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.13" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": {} +} diff --git a/bloomsays/art.py b/bloomsays/art.py new file mode 100644 index 0000000..ae6b52a --- /dev/null +++ b/bloomsays/art.py @@ -0,0 +1 @@ +#load beard ASCII \ No newline at end of file diff --git a/bloomsays/bloomberg/black.txt b/bloomsays/bloomberg/black.txt new file mode 100644 index 0000000..5267c42 --- /dev/null +++ b/bloomsays/bloomberg/black.txt @@ -0,0 +1,32 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&%%%##(##&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%#%%%%%%%%%#######%@&@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@%%#%%&%%%###%%####(#%&&&%%%%&&@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@%&&&&&#((///((((////**////(#&&&&&&%@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@&&&&&&%#(////***************////(#@@&&&@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@&&&@&#(///***************,*****///(&@&&&@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@&&&&&#(///********,,,,,,,,,,,****///(&&&&&@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@&&&&%(////*****,*,*,,,,,,,,,,,,****//#&&@&@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&&&&&%(////*******,,,,,,,,,,********//(&&&&&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&@&&&%////**********,,,,,,,,,,******//(%&&&&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&@@&&%(///*******,,,,,,,,,,,,,,******//#&@&&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&&@@&%(///(#(####(/****,**//(%%%%##(///(&&&@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@&&&&(//%###%##%%###(/***/(((%&&&&%###((&@&//@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@(((#&&(//(#%#(*##,/(/(/***///(**#*/(((///&%#((/@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@/(//&&(//***//*////////****/*****/******/#(**/*@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@/(/(%&(//***/*******//**,,*/*******,,**//##(*/@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@/(#(&(///*********///*,,,,*//*********//%%(*#@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@///&%(//********/////****///*******///(&%//&@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@(/&&##((///**//((#&##%####(//***//(###&#/&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@(&&&&%#(/(%%###%%%##%##%%%((##(((##%&&&@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@&&&&%##%&%&&%###%##((###%%%%#%%%%%&&&@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@&&&&&&%&%%#//(////////////%&&&#&&&&@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@(%&@&&&&%#((((###%##((((((%&&&&@&%@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@,#%/#&&&&&&&&%##%%#%%%##(#%&&%&&@&@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@..(&//(&&&&&&&&&%%%%#%%(%%&&@&&@%(@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@....,///(%&&&&@&&&&%%%%%&&&&@@@&(/..@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@.......////((#%&&&&&&@&&&@&@&&@%(//*,../@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@............//(((((((##&&@@@@@&&#/////#,,......(@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@%.......... ...*///////(((((((//////*//&(.......... *@@@@@@@@@@@@ +@@@@@@@@@%. ..,..//**///////////****//%/*. ............ .&@@@@@@ \ No newline at end of file diff --git a/bloomsays/bloomberg/color.txt b/bloomsays/bloomberg/color.txt new file mode 100644 index 0000000..a3e6ab3 --- /dev/null +++ b/bloomsays/bloomberg/color.txt @@ -0,0 +1,30 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@&%%%##(##&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%#%%%%%%%%%#######%@&@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@%%#%%&%%%%##%%####(#%&&&%%%%&&@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@%&&&&&#((///((((/////*////(#&&&&&&&@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@&&&&&&%#(/////**************////(#@@&&&@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@&&&@&#(///***************,*****///(&@&&&@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@&&&&&#(////*******,,,,,,,,,,,****///(&&&&&@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@&&&&%((////****,*,*,,,,,,,,,,,,***///#&&@&@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&&&&&%(/////******,,,,,,,,,,********//(&&&&&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&@&&&%//////********,,,,,,,,,,******//(%&&&&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&@@&&%((///******,,,,,,,,,,,,,,******//#&@&&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@&&@@@%(///(#(####(/****,**//(%%%%##(///(&&&@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@&&&&(//%######%%###(/***/(((#&&&&%###((&@&//@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@(((#&&(//(#%#(*##,/(/(/****//(**(**(((///&%#/(/@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@/(//&&(//**///*///*////*,**/*****/******/#(**/*@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@/(/(%&(//***/*******//**,,*********,,**//##(**@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@//#(&#(//*********///*,,,,*//*********//%#(*#@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@///&%(////******/////****///*******///(&%/*&@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@//&&##((///**//((#&##%####(//***//(###&#/&@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@(&&&&%#(/(%%%##%%%##%##%%%((###((##%&&&@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@&&&&&%#%&%&&%%#####((#(#%%%%%%%%%%&&&@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@&&&&&&&&%%#//(///*////////%&&&#&&&&@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@(%&@&&&&%#((((######((((((%&&&&@&%@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@,#%/#&&&&&&&&%##%%#%%%##(#%&&%&&@&@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@..(&//(&&&&&&&&&%%%%#%%(%%&&@&&@%(@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@....,///(%&&&&@&&&&%%%%%&&&&&@@&(/..@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@.......////((#%@&&&&&@&&&@&@&&@%(//,.../@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@............//(((((((##&&@@@@@&&#/////#,, .. ..(@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@%..... .... .*///////(((((((//////*//&(.......... *@@@@@@@@@@@@ +@@@@@@@@@%. .,..//*////////////****//%/*. ........... .&@@@@@@ \ No newline at end of file diff --git a/bloomsays/cli.py b/bloomsays/cli.py new file mode 100644 index 0000000..759cde2 --- /dev/null +++ b/bloomsays/cli.py @@ -0,0 +1 @@ +#command line implementations \ No newline at end of file diff --git a/bloomsays/main.py b/bloomsays/main.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..319f84e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,28 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "bloomsays" +description = "A package that gives quotes from the professor" +version = "0.1.0" +license = { file = "LICENSE" } +readme = "README.md" +keywords = ["python", "package", "build", "lighthearted"] +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "Intended Audience :: Education", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Operating System :: OS Independent", +] + +[project.optional-dependencies] +dev = ["pytest"] + +[project.urls] +"Repository" = "https://github.com/swe-students-fall2025/3-python-package-team_orchid.git" + + +[project.scripts] +bloomsays = "bloomsayspackage.__main__:main" diff --git a/src/bloomsayspackage/__init__.py b/src/bloomsayspackage/__init__.py new file mode 100644 index 0000000..1c2be9c --- /dev/null +++ b/src/bloomsayspackage/__init__.py @@ -0,0 +1,8 @@ +from .bubble import wrap_text, make_bubble + +from . import wisdom +__all__ = [ + "wrap_text", + "make_bubble", + "wisdom", +] diff --git a/src/bloomsayspackage/__main__.py b/src/bloomsayspackage/__main__.py new file mode 100644 index 0000000..cc0c59b --- /dev/null +++ b/src/bloomsayspackage/__main__.py @@ -0,0 +1,30 @@ +import sys +import wisdom + +def main(): + args = sys.argv[1:] + if not args: + print("Usage: bloomsays randomQuote [n] | bloomsays avg num1 num2 ...") + return + + command = args[0] + + if command == "randomQuote": + n = int(args[1]) if len(args) > 1 else 1 + randomQuote(n) + elif command == "avg": + if len(args) < 2: + print("Usage: bloomsays avg num1 num2 ...") + return + try: + numbers = [float(x) for x in args[1:]] + except ValueError: + print("All arguments for avg must be numbers.") + return + print(f"Your average grade is {avg(*numbers):.2f}") + else: + print(f"Unknown command: {command}") + print("Usage: bloomsays randomQuote [n] | bloomsays avg num1 num2 ...") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/bloomsayspackage/bubble.py b/src/bloomsayspackage/bubble.py new file mode 100644 index 0000000..0826c7d --- /dev/null +++ b/src/bloomsayspackage/bubble.py @@ -0,0 +1,66 @@ +from __future__ import annotations +from typing import List + + +def wrap_text(text: str, width: int) -> List[str]: + """ + Pure text wrapper (no printing). + - Splits on whitespace. + - If a single word is longer than width, it is put on its own line (no hyphenation). + """ + if width is None or width < 1: + raise ValueError("width must be >= 1") + + words = text.split() + if not words: + return [""] + + lines: List[str] = [] + cur = words[0] + for w in words[1:]: + if len(cur) + 1 + len(w) <= width: + cur += " " + w + else: + lines.append(cur) + cur = w + lines.append(cur) + return lines + + +def make_bubble(text: str, width: int | None = None) -> str: + """ + Build a speech bubble as a single string. + - If width is provided, wrap the text to that width. + - Supports multi-line input already containing '\n' (each line is treated as a paragraph). + """ + if text is None: + raise ValueError("text must be a string") + + # split paragraphs first + paragraphs = text.split("\n") + if width is not None: + if width < 1: + raise ValueError("width must be >= 1") + lines = [] + for p in paragraphs: + if p.strip() == "": + lines.append("") # preserve empty line + else: + lines.extend(wrap_text(p, width)) + else: + lines = paragraphs + + # compute max visible width + maxw = max((len(line) for line in lines), default=0) + + top = " " + "_" * (maxw + 2) + body = "\n".join(f"| {line.ljust(maxw)} |" for line in lines) + bottom = " " + "=" * (maxw + 2) + tail = " \\\n \\" + + return f"{top}\n {body}\n{bottom}\n{tail}" + +#test - run: python3 -m bloomsays.bubble +if __name__ == "__main__": + print(make_bubble("Ask Bloombot!")) + diff --git a/src/bloomsayspackage/wisdom.py b/src/bloomsayspackage/wisdom.py new file mode 100644 index 0000000..2481c01 --- /dev/null +++ b/src/bloomsayspackage/wisdom.py @@ -0,0 +1,67 @@ +import random; +import textwrap + + +ascii_art = r""" + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&%%%##(##&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%#%%%%%%%%%#######%@&@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@%%#%%&%%%###%%####(#%&&&%%%%&&@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@%&&&&&#((///((((////**////(#&&&&&&%@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@&&&&&&%#(////***************////(#@@&&&@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@&&&@&#(///***************,*****///(&@&&&@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@&&&&&#(///********,,,,,,,,,,,****///(&&&&&@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@&&&&%(////*****,*,*,,,,,,,,,,,,****//#&&@&@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@&&&&&%(////*******,,,,,,,,,,********//(&&&&&@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@&@&&&%////**********,,,,,,,,,,******//(%&&&&@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@&@@&&%(///*******,,,,,,,,,,,,,,******//#&@&&@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@&&@@&%(///(#(####(/****,**//(%%%%##(///(&&&@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@&&&&(//%###%##%%###(/***/(((%&&&&%###((&@&//@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@(((#&&(//(#%#(*##,/(/(/***///(**#*/(((///&%#((/@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@/(//&&(//***//*////////****/*****/******/#(**/*@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@/(/(%&(//***/*******//**,,*/*******,,**//##(*/@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@/(#(&(///*********///*,,,,*//*********//%%(*#@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@///&%(//********/////****///*******///(&%//&@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@(/&&##((///**//((#&##%####(//***//(###&#/&@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@(&&&&%#(/(%%###%%%##%##%%%((##(((##%&&&@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@&&&&%##%&%&&%###%##((###%%%%#%%%%%&&&@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@&&&&&&%&%%#//(////////////%&&&#&&&&@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@(%&@&&&&%#((((###%##((((((%&&&&@&%@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@,#%/#&&&&&&&&%##%%#%%%##(#%&&%&&@&@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@..(&//(&&&&&&&&&%%%%#%%(%%&&@&&@%(@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@....,///(%&&&&@&&&&%%%%%&&&&@@@&(/..@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@.......////((#%&&&&&&@&&&@&@&&@%(//*,../@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@............//(((((((##&&@@@@@&&#/////#,,......(@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@%.......... ...*///////(((((((//////*//&(.......... *@@@@@@@@@@@@ + @@@@@@@@@%. ..,..//**///////////****//%/*. ............ .&@@@@@@ + """ + + +def avg(*grades): + + average = sum(grades)/ len(grades) + message = f"Your average grade is {average:.2f}" + + border = "-" * (len(message) + 2) + bubble = f" {border}\n< {message} >\n {border}" + + + print(f"{bubble}\n{ascii_art}") + + + +def randomQuote(n=1): + profLines = ["everything is due at class time", "ask Bloombot", "Quizzes: 25%", "Exercises & Projects: 75%", "Discord is our main source of communitcation"] + selected_quotes = [random.choice(profLines) for _ in range(n)] + + max_length = max(len(quote) for quote in selected_quotes) + + border = "-" * (max_length + 2) + bubble_lines = [f" {border}"] + for quote in selected_quotes: + bubble_lines.append(f"< {quote.ljust(max_length)} >") + bubble_lines.append(f" {border}") + + print("\n".join(bubble_lines)) + print(ascii_art) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_bubble.py b/tests/test_bubble.py new file mode 100644 index 0000000..ddef51d --- /dev/null +++ b/tests/test_bubble.py @@ -0,0 +1,103 @@ +import pytest + +from bloomsayspackage.bubble import make_bubble, wrap_text + + +def _assert_bubble_matches(out: str, expected_lines: list[str]): + parts = out.splitlines() + # top + maxw = max((len(l) for l in expected_lines), default=0) + assert parts[0] == " " + "_" * (maxw + 2) + + # body + body = parts[1 : 1 + len(expected_lines)] + assert len(body) == len(expected_lines) + for idx, (got, expect) in enumerate(zip(body, expected_lines)): + # the first body line is prefixed with a single space in the output + if idx == 0: + assert got.startswith(" | ") + inner = got[3:-2] + else: + # subsequent lines start with '|' directly + assert got.startswith("| ") + inner = got[2:-2] + assert got.endswith(" |") + assert inner.strip() == expect + + # bottom + bottom = parts[1 + len(expected_lines)] + assert bottom == " " + "=" * (maxw + 2) + + # tail should be two lines that each end with a backslash + assert parts[-2].endswith("\\") + assert parts[-1].endswith("\\") + + +def test_simple_bubble_no_width(): + out = make_bubble("Hi") + _assert_bubble_matches(out, ["Hi"]) + + +def test_wrap_with_width(): + out = make_bubble("one two three", width=6) + # wrapped into three lines + _assert_bubble_matches(out, ["one", "two", "three"]) + + +def test_preserve_empty_line(): + out = make_bubble("a\n\nb") + # empty paragraph should be preserved as an empty line + _assert_bubble_matches(out, ["a", "", "b"]) + + +def test_text_none_raises(): + with pytest.raises(ValueError): + make_bubble(None) + + +# Tests for wrap_text function +def test_wrap_text_simple(): + result = wrap_text("hello world", 10) + assert result == ["hello", "world"] + + +def test_wrap_text_fits_on_one_line(): + result = wrap_text("hello", 10) + assert result == ["hello"] + + +def test_wrap_text_multiple_words_fit(): + result = wrap_text("one two three four", 15) + assert result == ["one two three", "four"] + + +def test_wrap_text_long_word_exceeds_width(): + # A single word longer than width should be on its own line + result = wrap_text("short verylongword short", 10) + assert result == ["short", "verylongword", "short"] + + +def test_wrap_text_empty_string(): + result = wrap_text("", 10) + assert result == [""] + + +def test_wrap_text_width_one(): + result = wrap_text("a b c", 1) + assert result == ["a", "b", "c"] + + +def test_wrap_text_invalid_width_zero(): + with pytest.raises(ValueError, match="width must be >= 1"): + wrap_text("text", 0) + + +def test_wrap_text_invalid_width_negative(): + with pytest.raises(ValueError, match="width must be >= 1"): + wrap_text("text", -5) + + +def test_wrap_text_invalid_width_none(): + with pytest.raises(ValueError, match="width must be >= 1"): + wrap_text("text", None) + diff --git a/tests/test_wisdom.py b/tests/test_wisdom.py new file mode 100644 index 0000000..61957f4 --- /dev/null +++ b/tests/test_wisdom.py @@ -0,0 +1,46 @@ +import pytest +from bloomsayspackage import wisdom + +class Tests: + + + def test_avg_simple(self, capsys): + wisdom.avg(97, 76, 67) + captured = capsys.readouterr() + assert "Your average grade is 80.00" in captured.out + assert "----------------" in captured.out + assert "@@@@" in captured.out + + def test_avg_identical_numbers(self, capsys): + wisdom.avg(67, 67, 67) + captured = capsys.readouterr() + assert "Your average grade is 67.00" in captured.out + assert "----------------" in captured.out + assert "@@@@" in captured.out + + def test_avg_random_floats(self, capsys): + wisdom.avg(5.5, 7.3, 8.2) + captured = capsys.readouterr() + assert "Your average grade is 7.00" in captured.out + assert "----------------" in captured.out + assert "@@@@" in captured.out + + def test_randomQuote_runs(self, capsys): + wisdom.randomQuote(3) + captured = capsys.readouterr() + assert "----------------" in captured.out + assert "@@@@" in captured.out + + def test_randomQuote_default(self, capsys): + wisdom.randomQuote() + captured = capsys.readouterr() + assert any(line.strip().startswith("-") for line in captured.out.splitlines()) + assert "@@@@" in captured.out + + def test_randomQuote_multiple_quotes_in_bubble(self, capsys): + wisdom.randomQuote(2) + captured = capsys.readouterr() + lines = captured.out.splitlines() + bubble_lines = [line for line in lines if line.strip().startswith("<") and line.strip().endswith(">")] + assert len(bubble_lines) >= 2 + assert "@@@@" in captured.out