From b47d3c23112878cd13d654f63ae1c12fc9014e0a Mon Sep 17 00:00:00 2001 From: HelenZhutt Date: Mon, 3 Nov 2025 14:42:39 -0500 Subject: [PATCH 1/7] initialize the repo and add two functions --- pyflirt/__init__.py | 2 ++ pyflirt/api.py | 76 +++++++++++++++++++++++++++++++++++++++++++ pyflirt/data.py | 30 +++++++++++++++++ pyproject.toml | 13 ++++++++ tests/test_pyflirt.py | 19 +++++++++++ 5 files changed, 140 insertions(+) create mode 100644 pyflirt/__init__.py create mode 100644 pyflirt/api.py create mode 100644 pyflirt/data.py create mode 100644 pyproject.toml create mode 100644 tests/test_pyflirt.py diff --git a/pyflirt/__init__.py b/pyflirt/__init__.py new file mode 100644 index 0000000..ebb0266 --- /dev/null +++ b/pyflirt/__init__.py @@ -0,0 +1,2 @@ +from .api import line, lines, categories +__all__ = ["line", "lines", "categories"] diff --git a/pyflirt/api.py b/pyflirt/api.py new file mode 100644 index 0000000..ea54c1d --- /dev/null +++ b/pyflirt/api.py @@ -0,0 +1,76 @@ +import random +from typing import List, Optional +from .data import BANK, categories as _categories + +__all__ = ["line", "lines", "categories"] + +def categories() -> List[str]: + return _categories() + +def _check_cat(cat: Optional[str]) -> Optional[str]: + if cat is None: + return None + if cat not in BANK: + valid = ", ".join(_categories()) + raise ValueError(f"Unknown category {cat!r}. Choose from {valid}.") + return cat + +def _pool(cat: Optional[str], cheese: int) -> List[dict]: + """Return candidate lines filtered by category and cheese. + If filtering wipes everything out, fall back to the unfiltered pool. + """ + def ok(e: dict) -> bool: + return e.get("cheese", 3) <= cheese + + if cat is None: + picks = [e for items in BANK.values() for e in items if ok(e)] + else: + picks = [e for e in BANK[cat] if ok(e)] + + if not picks: + picks = BANK[cat] if cat else [e for items in BANK.values() for e in items] + return picks + +def _with_name(text: str, name: Optional[str]) -> str: + if "{name}" in text: + return text.replace("{name}", name or "you") + return text + +def line( + category: Optional[str] = "nerdy", + name: Optional[str] = None, + cheese: int = 2, + seed: Optional[int] = None, +) -> str: + if not (1 <= int(cheese) <= 5): + raise ValueError("cheese must be in 1..5") + category = _check_cat(category) + rng = random.Random(seed) + choice = rng.choice(_pool(category, cheese)) + return _with_name(choice["text"], name) + +def lines( + n: int = 5, + category: Optional[str] = None, + name: Optional[str] = None, + cheese: int = 2, + seed: Optional[int] = None, +) -> List[str]: + if n <= 0: + return [] + if not (1 <= int(cheese) <= 5): + raise ValueError("cheese must be in 1..5") + category = _check_cat(category) + rng = random.Random(seed) + pool = _pool(category, cheese) + + out: List[str] = [] + if len(pool) >= n: + for e in rng.sample(pool, n): + out.append(_with_name(e["text"], name)) + return out + + for _ in range(n): + e = rng.choice(pool) + out.append(_with_name(e["text"], name)) + return out diff --git a/pyflirt/data.py b/pyflirt/data.py new file mode 100644 index 0000000..a24b92a --- /dev/null +++ b/pyflirt/data.py @@ -0,0 +1,30 @@ +# pyflirt/data.py +BANK = { + "nerdy": [ + {"text": "Are you made of copper and tellurium? Because you’re Cu-Te.", "cheese": 2}, + {"text": "Are you a quantum tunnel? Because you went straight through my barriers.", "cheese": 3}, + {"text": "Is your name Wi-Fi? Because I feel a strong connection.", "cheese": 3}, + {"text": "Are you a neural net, {name}? Because I keep overfitting to you.", "cheese": 4}, + ], + "poetic": [ + {"text": "{name}, shall I compare thee to a stable release? Thou art rarer and far more dependable.", "cheese": 4}, + {"text": "I’d cross the version gulf for thee, and tag a release upon thy smile.", "cheese": 3}, + ], + "cs": [ + {"text": "If love were a bug, I’d still refuse to close your ticket.", "cheese": 1}, + {"text": "Do you believe in love at first compile, {name}, or should I re-run?", "cheese": 2}, + {"text": "You must be Git—my heart commits to you.", "cheese": 2}, + ], + "math": [ + {"text": "You must be my limit—I’m approaching you from every direction.", "cheese": 4}, + {"text": "If we were vectors, we’d be perfectly aligned.", "cheese": 2}, + {"text": "Are you √-1? You’re unreal—and I can’t stop imagining us.", "cheese": 4}, + {"text": "We are coprime; the only common divisor is one heart.", "cheese": 3}, + ], + "classic": [ + {"text": "Are you a magician? Because whenever I look at you, everyone else disappears.", "cheese": 2}, + ], +} + +def categories(): + return sorted(BANK.keys()) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..93599c0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +# pyproject.toml +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "pyflirt" +version = "0.1.0" +description = "Playful, seedable pickup lines for the terminal." +readme = "README.md" +requires-python = ">=3.9" +authors = [{ name = "Team Quartz" }] +dependencies = [] \ No newline at end of file diff --git a/tests/test_pyflirt.py b/tests/test_pyflirt.py new file mode 100644 index 0000000..e397366 --- /dev/null +++ b/tests/test_pyflirt.py @@ -0,0 +1,19 @@ +from pyflirt import line, lines, categories + +def test_categories_present(): + assert "nerdy" in categories() + +def test_line_seed_stable(): + assert line(category="nerdy", seed=42) == line(category="nerdy", seed=42) + +def test_lines_len_and_content(): + arr = lines(n=3, name="Sam", seed=123) + assert len(arr) == 3 + assert all(isinstance(s, str) and s for s in arr) + +def test_cheese_bounds(): + import pytest + with pytest.raises(ValueError): + line(cheese=0) + with pytest.raises(ValueError): + lines(n=2, cheese=6) From 2e48b360c7ac7b577ce1dfee452275ca660e1c40 Mon Sep 17 00:00:00 2001 From: danielleesignup Date: Mon, 3 Nov 2025 15:28:10 -0500 Subject: [PATCH 2/7] Set up project dirs and wrote compliment function --- .github/workflows/ci.yml | 55 ++++++ Pipfile | 15 ++ Pipfile.lock | 392 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 41 ++++ src/pyflirt/__init__.py | 20 ++ src/pyflirt/core.py | 118 ++++++++++++ src/pyflirt/main.py | 8 + tests/test_compliment.py | 35 ++++ 8 files changed, 684 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 pyproject.toml create mode 100644 src/pyflirt/__init__.py create mode 100644 src/pyflirt/core.py create mode 100644 src/pyflirt/main.py create mode 100644 tests/test_compliment.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ef9940c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: + pull_request: + branches: [ pipfile-experiment ] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + test-and-build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.12"] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + + - name: Upgrade pip and install build tools + run: | + python -m pip install --upgrade pip + python -m pip install build pytest pipenv + + - name: Install dependencies with Pipenv + run: | + pipenv install --dev --ignore-pipfile + pipenv run pip install -e . + + - name: Run tests + run: | + pipenv run pytest -q + + - name: Build package (sdist & wheel) + run: | + pipenv run python -m build + + - name: Upload dist artifacts + uses: actions/upload-artifact@v4 + with: + name: pyflirt-dist-${{ matrix.python-version }} + path: dist/* diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..b80fa08 --- /dev/null +++ b/Pipfile @@ -0,0 +1,15 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] + +[dev-packages] +pytest = "*" +build = "*" +twine = "*" + +[requires] +python_version = "3.12" +python_full_version = "3.12.4" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..6c4d705 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,392 @@ +{ + "_meta": { + "hash": { + "sha256": "04147162fb671d30f16143546df2d8184d009fca6fe12b1f9a5362c5ce374bdf" + }, + "pipfile-spec": 6, + "requires": { + "python_full_version": "3.12.4", + "python_version": "3.12" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "build": { + "hashes": [ + "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", + "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==1.3.0" + }, + "certifi": { + "hashes": [ + "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", + "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43" + ], + "markers": "python_version >= '3.7'", + "version": "==2025.10.5" + }, + "charset-normalizer": { + "hashes": [ + "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", + "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", + "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", + "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", + "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", + "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", + "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63", + "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", + "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", + "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", + "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", + "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", + "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", + "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af", + "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", + "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", + "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", + "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", + "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", + "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", + "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576", + "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", + "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", + "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", + "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", + "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", + "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", + "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", + "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", + "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", + "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", + "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", + "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a", + "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", + "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", + "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", + "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", + "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", + "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7", + "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", + "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", + "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", + "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", + "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", + "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", + "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2", + "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", + "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", + "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", + "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", + "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", + "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", + "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", + "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", + "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa", + "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", + "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", + "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", + "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", + "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", + "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", + "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", + "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", + "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", + "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", + "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", + "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", + "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", + "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", + "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", + "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3", + "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", + "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", + "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", + "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", + "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", + "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", + "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf", + "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", + "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", + "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", + "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", + "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", + "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", + "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", + "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", + "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", + "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4", + "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", + "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", + "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", + "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", + "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", + "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", + "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", + "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", + "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", + "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", + "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074", + "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", + "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", + "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", + "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", + "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d", + "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", + "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", + "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", + "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", + "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", + "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", + "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", + "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", + "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.4" + }, + "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" + }, + "docutils": { + "hashes": [ + "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d", + "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8" + ], + "markers": "python_version >= '3.9'", + "version": "==0.22.2" + }, + "id": { + "hashes": [ + "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", + "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.0" + }, + "idna": { + "hashes": [ + "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", + "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" + ], + "markers": "python_version >= '3.8'", + "version": "==3.11" + }, + "iniconfig": { + "hashes": [ + "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", + "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12" + ], + "markers": "python_version >= '3.10'", + "version": "==2.3.0" + }, + "jaraco.classes": { + "hashes": [ + "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", + "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" + ], + "markers": "python_version >= '3.8'", + "version": "==3.4.0" + }, + "jaraco.context": { + "hashes": [ + "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", + "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" + ], + "markers": "python_version >= '3.8'", + "version": "==6.0.1" + }, + "jaraco.functools": { + "hashes": [ + "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", + "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294" + ], + "markers": "python_version >= '3.9'", + "version": "==4.3.0" + }, + "keyring": { + "hashes": [ + "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", + "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd" + ], + "markers": "python_version >= '3.9'", + "version": "==25.6.0" + }, + "markdown-it-py": { + "hashes": [ + "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", + "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" + ], + "markers": "python_version >= '3.10'", + "version": "==4.0.0" + }, + "mdurl": { + "hashes": [ + "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", + "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" + ], + "markers": "python_version >= '3.7'", + "version": "==0.1.2" + }, + "more-itertools": { + "hashes": [ + "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", + "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd" + ], + "markers": "python_version >= '3.9'", + "version": "==10.8.0" + }, + "nh3": { + "hashes": [ + "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80", + "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", + "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31", + "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99", + "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", + "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", + "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", + "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93", + "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", + "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b", + "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5", + "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130", + "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", + "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", + "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", + "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", + "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", + "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", + "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868", + "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", + "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", + "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d", + "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13", + "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", + "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", + "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a" + ], + "markers": "python_version >= '3.8'", + "version": "==0.3.2" + }, + "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" + }, + "pyproject-hooks": { + "hashes": [ + "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", + "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913" + ], + "markers": "python_version >= '3.7'", + "version": "==1.2.0" + }, + "pytest": { + "hashes": [ + "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", + "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==8.4.2" + }, + "pywin32-ctypes": { + "hashes": [ + "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", + "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755" + ], + "markers": "python_version >= '3.6'", + "version": "==0.2.3" + }, + "readme-renderer": { + "hashes": [ + "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", + "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1" + ], + "markers": "python_version >= '3.9'", + "version": "==44.0" + }, + "requests": { + "hashes": [ + "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", + "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" + ], + "markers": "python_version >= '3.9'", + "version": "==2.32.5" + }, + "requests-toolbelt": { + "hashes": [ + "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", + "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.0.0" + }, + "rfc3986": { + "hashes": [ + "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", + "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "rich": { + "hashes": [ + "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", + "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==14.2.0" + }, + "twine": { + "hashes": [ + "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", + "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==6.2.0" + }, + "urllib3": { + "hashes": [ + "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", + "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" + ], + "markers": "python_version >= '3.9'", + "version": "==2.5.0" + } + } +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..56ead1f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "pyflirt" +description = "A lighthearted Python package for developer-themed pickup lines." +version = "0.1.0" +authors = [ + { name = "Daniel", email = "dl4458@nyu.edu" }, +] +license = { file = "LICENSE" } +readme = "README.md" +keywords = ["fun", "pickup lines", "developer humor"] +requires-python = ">=3.9" +classifiers = [ + "Programming Language :: Python :: 3", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +dependencies = [] + +[project.optional-dependencies] +dev = ["pytest"] + +[project.urls] +"Homepage" = "https://github.com/swe-students-fall2025/3-python-package-team_quartz" +"Repository" = "https://github.com/swe-students-fall2025/3-python-package-team_quartz.git" +"Bug Tracker" = "https://github.com/swe-students-fall2025/3-python-package-team_quartz/issues" + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] diff --git a/src/pyflirt/__init__.py b/src/pyflirt/__init__.py new file mode 100644 index 0000000..84611f4 --- /dev/null +++ b/src/pyflirt/__init__.py @@ -0,0 +1,20 @@ +""" +pyflirt 💘 + +APIs: +- line(category, name, cheese, seed) +- lines(n, categories, name, cheese, seed) +- compliment(role, mood, name, emojis, seed) +- rate_line(text, metric, seed) +""" + +from .core import ( + compliment, +) + +__all__ = [ + "compliment", + +] + +__version__ = "0.1.0" diff --git a/src/pyflirt/core.py b/src/pyflirt/core.py new file mode 100644 index 0000000..a25b874 --- /dev/null +++ b/src/pyflirt/core.py @@ -0,0 +1,118 @@ +""" +core.py — core logic for pyflirt 💘 + +Currently includes: +- compliment(): generate a developer-themed compliment +""" + +import random + + +__all__ = ["compliment"] + + +def compliment(role="developer", mood="sweet", name=None, emojis=0, seed=None): + """ + Generate a developer-themed compliment. + + Args: + role (str): One of ["developer", "designer", "manager", "data"]. + mood (str): One of ["sweet", "cheeky", "nerdy"]. + name (str, optional): Person's name to personalize. + emojis (int): Number of 💖 emojis to append. + seed (int, optional): Random seed for deterministic selection. + + Returns: + str: A playful compliment string. + """ + rng = random.Random(seed) if seed is not None else random + + templates = { + "developer": { + "sweet": [ + "Your code is cleaner than a freshly cloned repo{name_bit}", + "You commit kindness with every push{name_bit}", + "You’re the pull request everyone approves instantly{name_bit}", + ], + "cheeky": [ + "You refactor hearts, not just code{name_bit}", + "You’ve got more charm than a recursive function{name_bit}", + "You must be a keyboard shortcut—because you’re my type{name_bit}", + ], + "nerdy": [ + "You debug my sadness faster than VSCode{name_bit}", + "You’re the semicolon that completes my statement{name_bit}", + "If beauty were an algorithm, you’d be O(1){name_bit}", + ], + }, + "designer": { + "sweet": [ + "Your aesthetic sense brightens every UI{name_bit}", + "You bring color theory to my grayscale days{name_bit}", + "Pixels align themselves just to please you{name_bit}", + ], + "cheeky": [ + "You must be a vector—because you’ve got direction{name_bit}", + "Are you a grid system? Because my heart is well-aligned{name_bit}", + "You kerningly complete me{name_bit}", + ], + "nerdy": [ + "You optimize whitespace like a legend{name_bit}", + "Your Figma files are pure poetry{name_bit}", + "Even Helvetica blushes when you walk in{name_bit}", + ], + }, + "manager": { + "sweet": [ + "You lead with empathy{name_bit}", + "Your standups make Mondays bearable{name_bit}", + "You’re the reason meetings actually end early{name_bit}", + ], + "cheeky": [ + "You manage hearts better than timelines{name_bit}", + "You’re my favorite deliverable{name_bit}", + "You’ve got more charisma than a sprint demo{name_bit}", + ], + "nerdy": [ + "You allocate my attention like a well-balanced backlog{name_bit}", + "KPIs envy your energy{name_bit}", + "Your OKRs? Outrageously Kind & Radiant{name_bit}", + ], + }, + "data": { + "sweet": [ + "You turn noise into beauty{name_bit}", + "Every dataset wishes it were as clean as your heart{name_bit}", + "You make outliers feel included{name_bit}", + ], + "cheeky": [ + "You must be a correlation—because you complete my regression{name_bit}", + "You’re my favorite variable{name_bit}", + "You pivot-table my emotions{name_bit}", + ], + "nerdy": [ + "Your confidence interval? 100%{name_bit}", + "You’re statistically significant in my life{name_bit}", + "Your curves fit any model{name_bit}", + ], + }, + } + + # Normalize and validate + role = role.lower() + mood = mood.lower() + if role not in templates: + raise ValueError(f"Unknown role '{role}'. Choose from {list(templates.keys())}.") + if mood not in templates[role]: + raise ValueError(f"Unknown mood '{mood}'. Choose from {list(templates[role].keys())}.") + + # Select random compliment + template = rng.choice(templates[role][mood]) + name_bit = f", {name}" if name else "" + compliment_text = template.format(name_bit=name_bit) + + # Add emojis if requested + if emojis > 0: + compliment_text += " " + "💖" * emojis + + return compliment_text diff --git a/src/pyflirt/main.py b/src/pyflirt/main.py new file mode 100644 index 0000000..42a2ffe --- /dev/null +++ b/src/pyflirt/main.py @@ -0,0 +1,8 @@ +from .core import compliment + +def main(): + print("💘 Welcome to pyflirt!") + print(compliment(role="developer", mood="cheeky", name="Alex", emojis=2)) + +if __name__ == "__main__": + main() diff --git a/tests/test_compliment.py b/tests/test_compliment.py new file mode 100644 index 0000000..93f7c30 --- /dev/null +++ b/tests/test_compliment.py @@ -0,0 +1,35 @@ +import pytest +from pyflirt import compliment + + +def test_compliment_returns_string(): + """Should always return a non-empty string.""" + out = compliment() + assert isinstance(out, str) + assert len(out) > 0 + + +def test_compliment_includes_name(): + """Name should appear in the compliment when provided.""" + result = compliment(role="developer", mood="nerdy", name="Alex", seed=1) + assert "Alex" in result + + +def test_compliment_emojis_count(): + """Correct number of emojis should be appended.""" + result = compliment(role="data", emojis=3, seed=2) + assert result.endswith("💖💖💖") + + +def test_compliment_is_deterministic_with_seed(): + """Using the same seed should produce the same output.""" + a = compliment(role="designer", mood="cheeky", seed=42) + b = compliment(role="designer", mood="cheeky", seed=42) + assert a == b + + +def test_compliment_differs_with_different_seeds(): + """Different seeds should generally give different compliments.""" + a = compliment(role="designer", mood="cheeky", seed=42) + b = compliment(role="designer", mood="cheeky", seed=43) + assert a != b From 3d15746685ab137f6c48071781a8abeaf8e5274b Mon Sep 17 00:00:00 2001 From: danielleesignup Date: Mon, 3 Nov 2025 16:06:28 -0500 Subject: [PATCH 3/7] Integrated compliment function to existing file structure --- pyflirt/__init__.py | 3 +- pyflirt/data.py | 30 -------- src/pyflirt/__init__.py | 5 +- {pyflirt => src/pyflirt}/api.py | 32 +++++++-- src/pyflirt/core.py | 118 -------------------------------- src/pyflirt/data.py | 102 +++++++++++++++++++++++++++ src/pyflirt/main.py | 8 --- tests/test_compliment.py | 35 ---------- tests/test_pyflirt.py | 35 ++++++++++ 9 files changed, 170 insertions(+), 198 deletions(-) delete mode 100644 pyflirt/data.py rename {pyflirt => src/pyflirt}/api.py (60%) delete mode 100644 src/pyflirt/core.py create mode 100644 src/pyflirt/data.py delete mode 100644 src/pyflirt/main.py delete mode 100644 tests/test_compliment.py diff --git a/pyflirt/__init__.py b/pyflirt/__init__.py index ebb0266..8b13789 100644 --- a/pyflirt/__init__.py +++ b/pyflirt/__init__.py @@ -1,2 +1 @@ -from .api import line, lines, categories -__all__ = ["line", "lines", "categories"] + diff --git a/pyflirt/data.py b/pyflirt/data.py deleted file mode 100644 index a24b92a..0000000 --- a/pyflirt/data.py +++ /dev/null @@ -1,30 +0,0 @@ -# pyflirt/data.py -BANK = { - "nerdy": [ - {"text": "Are you made of copper and tellurium? Because you’re Cu-Te.", "cheese": 2}, - {"text": "Are you a quantum tunnel? Because you went straight through my barriers.", "cheese": 3}, - {"text": "Is your name Wi-Fi? Because I feel a strong connection.", "cheese": 3}, - {"text": "Are you a neural net, {name}? Because I keep overfitting to you.", "cheese": 4}, - ], - "poetic": [ - {"text": "{name}, shall I compare thee to a stable release? Thou art rarer and far more dependable.", "cheese": 4}, - {"text": "I’d cross the version gulf for thee, and tag a release upon thy smile.", "cheese": 3}, - ], - "cs": [ - {"text": "If love were a bug, I’d still refuse to close your ticket.", "cheese": 1}, - {"text": "Do you believe in love at first compile, {name}, or should I re-run?", "cheese": 2}, - {"text": "You must be Git—my heart commits to you.", "cheese": 2}, - ], - "math": [ - {"text": "You must be my limit—I’m approaching you from every direction.", "cheese": 4}, - {"text": "If we were vectors, we’d be perfectly aligned.", "cheese": 2}, - {"text": "Are you √-1? You’re unreal—and I can’t stop imagining us.", "cheese": 4}, - {"text": "We are coprime; the only common divisor is one heart.", "cheese": 3}, - ], - "classic": [ - {"text": "Are you a magician? Because whenever I look at you, everyone else disappears.", "cheese": 2}, - ], -} - -def categories(): - return sorted(BANK.keys()) diff --git a/src/pyflirt/__init__.py b/src/pyflirt/__init__.py index 84611f4..31d78b3 100644 --- a/src/pyflirt/__init__.py +++ b/src/pyflirt/__init__.py @@ -7,6 +7,7 @@ - compliment(role, mood, name, emojis, seed) - rate_line(text, metric, seed) """ +from .api import line, lines, categories, compliment from .core import ( compliment, @@ -14,7 +15,9 @@ __all__ = [ "compliment", - + "line", + "lines", + "categories", ] __version__ = "0.1.0" diff --git a/pyflirt/api.py b/src/pyflirt/api.py similarity index 60% rename from pyflirt/api.py rename to src/pyflirt/api.py index ea54c1d..405fb61 100644 --- a/pyflirt/api.py +++ b/src/pyflirt/api.py @@ -1,10 +1,13 @@ import random from typing import List, Optional -from .data import BANK, categories as _categories +from .data import BANK, COMPLIMENT_TEMPLATES, categories as _categories +from typing import cast, Dict + __all__ = ["line", "lines", "categories"] def categories() -> List[str]: + """Return a sorted list of all available pickup line categories.""" return _categories() def _check_cat(cat: Optional[str]) -> Optional[str]: @@ -42,7 +45,8 @@ def line( cheese: int = 2, seed: Optional[int] = None, ) -> str: - if not (1 <= int(cheese) <= 5): + """Return one random pickup line for the given category, name, and cheese level.""" + if not 1 <= int(cheese) <= 5: raise ValueError("cheese must be in 1..5") category = _check_cat(category) rng = random.Random(seed) @@ -56,9 +60,10 @@ def lines( cheese: int = 2, seed: Optional[int] = None, ) -> List[str]: + """Return a list of n pickup lines matching the given category and cheese level.""" if n <= 0: return [] - if not (1 <= int(cheese) <= 5): + if not 1 <= int(cheese) <= 5: raise ValueError("cheese must be in 1..5") category = _check_cat(category) rng = random.Random(seed) @@ -67,10 +72,29 @@ def lines( out: List[str] = [] if len(pool) >= n: for e in rng.sample(pool, n): - out.append(_with_name(e["text"], name)) + out.append(_with_name(e["text"], name)) # type: ignore[index] return out for _ in range(n): e = rng.choice(pool) out.append(_with_name(e["text"], name)) return out + +def compliment(role="developer", mood="sweet", name=None, emojis=0, seed=None): + """Return a list of n pickup lines matching the given category and cheese level.""" + rng = random.Random(seed) if seed is not None else random + + role = role.lower() + mood = mood.lower() + if role not in COMPLIMENT_TEMPLATES: + raise ValueError(f"Unknown role '{role}'. Choose from {list(COMPLIMENT_TEMPLATES.keys())}.") + if mood not in COMPLIMENT_TEMPLATES[role]: + raise ValueError(f"Unknown mood '{mood}'. Choose from {list(COMPLIMENT_TEMPLATES[role].keys())}.") + + template = rng.choice(COMPLIMENT_TEMPLATES[role][mood]) + name_bit = f", {name}" if name else "" + text = template.format(name_bit=name_bit) + + if emojis > 0: + text += " " + "💖" * emojis + return text \ No newline at end of file diff --git a/src/pyflirt/core.py b/src/pyflirt/core.py deleted file mode 100644 index a25b874..0000000 --- a/src/pyflirt/core.py +++ /dev/null @@ -1,118 +0,0 @@ -""" -core.py — core logic for pyflirt 💘 - -Currently includes: -- compliment(): generate a developer-themed compliment -""" - -import random - - -__all__ = ["compliment"] - - -def compliment(role="developer", mood="sweet", name=None, emojis=0, seed=None): - """ - Generate a developer-themed compliment. - - Args: - role (str): One of ["developer", "designer", "manager", "data"]. - mood (str): One of ["sweet", "cheeky", "nerdy"]. - name (str, optional): Person's name to personalize. - emojis (int): Number of 💖 emojis to append. - seed (int, optional): Random seed for deterministic selection. - - Returns: - str: A playful compliment string. - """ - rng = random.Random(seed) if seed is not None else random - - templates = { - "developer": { - "sweet": [ - "Your code is cleaner than a freshly cloned repo{name_bit}", - "You commit kindness with every push{name_bit}", - "You’re the pull request everyone approves instantly{name_bit}", - ], - "cheeky": [ - "You refactor hearts, not just code{name_bit}", - "You’ve got more charm than a recursive function{name_bit}", - "You must be a keyboard shortcut—because you’re my type{name_bit}", - ], - "nerdy": [ - "You debug my sadness faster than VSCode{name_bit}", - "You’re the semicolon that completes my statement{name_bit}", - "If beauty were an algorithm, you’d be O(1){name_bit}", - ], - }, - "designer": { - "sweet": [ - "Your aesthetic sense brightens every UI{name_bit}", - "You bring color theory to my grayscale days{name_bit}", - "Pixels align themselves just to please you{name_bit}", - ], - "cheeky": [ - "You must be a vector—because you’ve got direction{name_bit}", - "Are you a grid system? Because my heart is well-aligned{name_bit}", - "You kerningly complete me{name_bit}", - ], - "nerdy": [ - "You optimize whitespace like a legend{name_bit}", - "Your Figma files are pure poetry{name_bit}", - "Even Helvetica blushes when you walk in{name_bit}", - ], - }, - "manager": { - "sweet": [ - "You lead with empathy{name_bit}", - "Your standups make Mondays bearable{name_bit}", - "You’re the reason meetings actually end early{name_bit}", - ], - "cheeky": [ - "You manage hearts better than timelines{name_bit}", - "You’re my favorite deliverable{name_bit}", - "You’ve got more charisma than a sprint demo{name_bit}", - ], - "nerdy": [ - "You allocate my attention like a well-balanced backlog{name_bit}", - "KPIs envy your energy{name_bit}", - "Your OKRs? Outrageously Kind & Radiant{name_bit}", - ], - }, - "data": { - "sweet": [ - "You turn noise into beauty{name_bit}", - "Every dataset wishes it were as clean as your heart{name_bit}", - "You make outliers feel included{name_bit}", - ], - "cheeky": [ - "You must be a correlation—because you complete my regression{name_bit}", - "You’re my favorite variable{name_bit}", - "You pivot-table my emotions{name_bit}", - ], - "nerdy": [ - "Your confidence interval? 100%{name_bit}", - "You’re statistically significant in my life{name_bit}", - "Your curves fit any model{name_bit}", - ], - }, - } - - # Normalize and validate - role = role.lower() - mood = mood.lower() - if role not in templates: - raise ValueError(f"Unknown role '{role}'. Choose from {list(templates.keys())}.") - if mood not in templates[role]: - raise ValueError(f"Unknown mood '{mood}'. Choose from {list(templates[role].keys())}.") - - # Select random compliment - template = rng.choice(templates[role][mood]) - name_bit = f", {name}" if name else "" - compliment_text = template.format(name_bit=name_bit) - - # Add emojis if requested - if emojis > 0: - compliment_text += " " + "💖" * emojis - - return compliment_text diff --git a/src/pyflirt/data.py b/src/pyflirt/data.py new file mode 100644 index 0000000..0f85766 --- /dev/null +++ b/src/pyflirt/data.py @@ -0,0 +1,102 @@ +# pyflirt/data.py +BANK = { + "nerdy": [ + {"text": "Are you made of copper and tellurium? Because you’re Cu-Te.", "cheese": 2}, + {"text": "Are you a quantum tunnel? Because you went straight through my barriers.", "cheese": 3}, + {"text": "Is your name Wi-Fi? Because I feel a strong connection.", "cheese": 3}, + {"text": "Are you a neural net, {name}? Because I keep overfitting to you.", "cheese": 4}, + ], + "poetic": [ + {"text": "{name}, shall I compare thee to a stable release? Thou art rarer and far more dependable.", "cheese": 4}, + {"text": "I’d cross the version gulf for thee, and tag a release upon thy smile.", "cheese": 3}, + ], + "cs": [ + {"text": "If love were a bug, I’d still refuse to close your ticket.", "cheese": 1}, + {"text": "Do you believe in love at first compile, {name}, or should I re-run?", "cheese": 2}, + {"text": "You must be Git—my heart commits to you.", "cheese": 2}, + ], + "math": [ + {"text": "You must be my limit—I’m approaching you from every direction.", "cheese": 4}, + {"text": "If we were vectors, we’d be perfectly aligned.", "cheese": 2}, + {"text": "Are you √-1? You’re unreal—and I can’t stop imagining us.", "cheese": 4}, + {"text": "We are coprime; the only common divisor is one heart.", "cheese": 3}, + ], + "classic": [ + {"text": "Are you a magician? Because whenever I look at you, everyone else disappears.", "cheese": 2}, + ], +} + +COMPLIMENT_TEMPLATES = { + "developer": { + "sweet": [ + "Your code is cleaner than a freshly cloned repo{name_bit}", + "You commit kindness with every push{name_bit}", + "You’re the pull request everyone approves instantly{name_bit}", + ], + "cheeky": [ + "You refactor hearts, not just code{name_bit}", + "You’ve got more charm than a recursive function{name_bit}", + "You must be a keyboard shortcut—because you’re my type{name_bit}", + ], + "nerdy": [ + "You debug my sadness faster than VSCode{name_bit}", + "You’re the semicolon that completes my statement{name_bit}", + "If beauty were an algorithm, you’d be O(1){name_bit}", + ], + }, + "designer": { + "sweet": [ + "Your aesthetic sense brightens every UI{name_bit}", + "You bring color theory to my grayscale days{name_bit}", + "Pixels align themselves just to please you{name_bit}", + ], + "cheeky": [ + "You must be a vector—because you’ve got direction{name_bit}", + "Are you a grid system? Because my heart is well-aligned{name_bit}", + "You kerningly complete me{name_bit}", + ], + "nerdy": [ + "You optimize whitespace like a legend{name_bit}", + "Your Figma files are pure poetry{name_bit}", + "Even Helvetica blushes when you walk in{name_bit}", + ], + }, + "manager": { + "sweet": [ + "You lead with empathy{name_bit}", + "Your standups make Mondays bearable{name_bit}", + "You’re the reason meetings actually end early{name_bit}", + ], + "cheeky": [ + "You manage hearts better than timelines{name_bit}", + "You’re my favorite deliverable{name_bit}", + "You’ve got more charisma than a sprint demo{name_bit}", + ], + "nerdy": [ + "You allocate my attention like a well-balanced backlog{name_bit}", + "KPIs envy your energy{name_bit}", + "Your OKRs? Outrageously Kind & Radiant{name_bit}", + ], + }, + "data": { + "sweet": [ + "You turn noise into beauty{name_bit}", + "Every dataset wishes it were as clean as your heart{name_bit}", + "You make outliers feel included{name_bit}", + ], + "cheeky": [ + "You must be a correlation—because you complete my regression{name_bit}", + "You’re my favorite variable{name_bit}", + "You pivot-table my emotions{name_bit}", + ], + "nerdy": [ + "Your confidence interval? 100%{name_bit}", + "You’re statistically significant in my life{name_bit}", + "Your curves fit any model{name_bit}", + ], + }, +} + +def categories(): + """Return a sorted list of all available pickup line categories.""" + return sorted(BANK.keys()) diff --git a/src/pyflirt/main.py b/src/pyflirt/main.py deleted file mode 100644 index 42a2ffe..0000000 --- a/src/pyflirt/main.py +++ /dev/null @@ -1,8 +0,0 @@ -from .core import compliment - -def main(): - print("💘 Welcome to pyflirt!") - print(compliment(role="developer", mood="cheeky", name="Alex", emojis=2)) - -if __name__ == "__main__": - main() diff --git a/tests/test_compliment.py b/tests/test_compliment.py deleted file mode 100644 index 93f7c30..0000000 --- a/tests/test_compliment.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest -from pyflirt import compliment - - -def test_compliment_returns_string(): - """Should always return a non-empty string.""" - out = compliment() - assert isinstance(out, str) - assert len(out) > 0 - - -def test_compliment_includes_name(): - """Name should appear in the compliment when provided.""" - result = compliment(role="developer", mood="nerdy", name="Alex", seed=1) - assert "Alex" in result - - -def test_compliment_emojis_count(): - """Correct number of emojis should be appended.""" - result = compliment(role="data", emojis=3, seed=2) - assert result.endswith("💖💖💖") - - -def test_compliment_is_deterministic_with_seed(): - """Using the same seed should produce the same output.""" - a = compliment(role="designer", mood="cheeky", seed=42) - b = compliment(role="designer", mood="cheeky", seed=42) - assert a == b - - -def test_compliment_differs_with_different_seeds(): - """Different seeds should generally give different compliments.""" - a = compliment(role="designer", mood="cheeky", seed=42) - b = compliment(role="designer", mood="cheeky", seed=43) - assert a != b diff --git a/tests/test_pyflirt.py b/tests/test_pyflirt.py index e397366..cda6cf6 100644 --- a/tests/test_pyflirt.py +++ b/tests/test_pyflirt.py @@ -1,4 +1,7 @@ from pyflirt import line, lines, categories +import pytest +from pyflirt import compliment + def test_categories_present(): assert "nerdy" in categories() @@ -17,3 +20,35 @@ def test_cheese_bounds(): line(cheese=0) with pytest.raises(ValueError): lines(n=2, cheese=6) + +def test_compliment_returns_string(): + """Should always return a non-empty string.""" + out = compliment() + assert isinstance(out, str) + assert len(out) > 0 + + +def test_compliment_includes_name(): + """Name should appear in the compliment when provided.""" + result = compliment(role="developer", mood="nerdy", name="Alex", seed=1) + assert "Alex" in result + + +def test_compliment_emojis_count(): + """Correct number of emojis should be appended.""" + result = compliment(role="data", emojis=3, seed=2) + assert result.endswith("💖💖💖") + + +def test_compliment_is_deterministic_with_seed(): + """Using the same seed should produce the same output.""" + a = compliment(role="designer", mood="cheeky", seed=42) + b = compliment(role="designer", mood="cheeky", seed=42) + assert a == b + + +def test_compliment_differs_with_different_seeds(): + """Different seeds should generally give different compliments.""" + a = compliment(role="designer", mood="cheeky", seed=42) + b = compliment(role="designer", mood="cheeky", seed=43) + assert a != b From 285bb741003e8d3dc6a534c95f20dafd01dba398 Mon Sep 17 00:00:00 2001 From: danielleesignup Date: Mon, 3 Nov 2025 16:27:31 -0500 Subject: [PATCH 4/7] Changed github actions file to use any python --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef9940c..a770f0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,9 @@ jobs: - name: Install dependencies with Pipenv run: | + pipenv --python $(which python) pipenv install --dev --ignore-pipfile - pipenv run pip install -e . + - name: Run tests run: | From 8b03720ed341e42206f5141231b12faa8f8ce553 Mon Sep 17 00:00:00 2001 From: danielleesignup Date: Mon, 3 Nov 2025 16:37:30 -0500 Subject: [PATCH 5/7] Added exception group to Pipfile to enable CI --- Pipfile | 1 + Pipfile.lock | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index b80fa08..6e7f35b 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,7 @@ name = "pypi" pytest = "*" build = "*" twine = "*" +exceptiongroup = "*" [requires] python_version = "3.12" diff --git a/Pipfile.lock b/Pipfile.lock index 6c4d705..30b1919 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "04147162fb671d30f16143546df2d8184d009fca6fe12b1f9a5362c5ce374bdf" + "sha256": "2c1f1d55d2e512c81ddded026b91f06a67e274ebcbb394429800093941faeb2e" }, "pipfile-spec": 6, "requires": { @@ -170,6 +170,15 @@ "markers": "python_version >= '3.9'", "version": "==0.22.2" }, + "exceptiongroup": { + "hashes": [ + "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", + "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, "id": { "hashes": [ "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", @@ -380,6 +389,14 @@ "markers": "python_version >= '3.9'", "version": "==6.2.0" }, + "typing-extensions": { + "hashes": [ + "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", + "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" + ], + "markers": "python_version >= '3.9'", + "version": "==4.15.0" + }, "urllib3": { "hashes": [ "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", From db9b28d49e433e1a1ad663339b2f1babea4951f5 Mon Sep 17 00:00:00 2001 From: danielleesignup Date: Mon, 3 Nov 2025 16:39:47 -0500 Subject: [PATCH 6/7] Added command to install exceptiongroup to enable CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a770f0f..8dcb2ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: run: | pipenv --python $(which python) pipenv install --dev --ignore-pipfile + pipenv run pip install exceptiongroup - name: Run tests From 83323e1cf26676330042c7c303c3b493b47d8315 Mon Sep 17 00:00:00 2001 From: danielleesignup Date: Mon, 3 Nov 2025 16:43:47 -0500 Subject: [PATCH 7/7] Cleaned dir structure and removed duplicate import line --- .github/workflows/ci.yml | 3 +-- pyflirt/__init__.py | 1 - src/pyflirt/__init__.py | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 pyflirt/__init__.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8dcb2ea..95e3893 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.12"] + python-version: ["3.12"] steps: - name: Checkout @@ -39,7 +39,6 @@ jobs: run: | pipenv --python $(which python) pipenv install --dev --ignore-pipfile - pipenv run pip install exceptiongroup - name: Run tests diff --git a/pyflirt/__init__.py b/pyflirt/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/pyflirt/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/pyflirt/__init__.py b/src/pyflirt/__init__.py index 31d78b3..9865804 100644 --- a/src/pyflirt/__init__.py +++ b/src/pyflirt/__init__.py @@ -9,10 +9,6 @@ """ from .api import line, lines, categories, compliment -from .core import ( - compliment, -) - __all__ = [ "compliment", "line",