From ec44d31ae873692bf33cd7503e9c4b7c7b52a64a Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Mon, 27 Oct 2025 21:53:04 -0400 Subject: [PATCH 01/44] Add structural files --- .github/workflows/test.yml | 1 + Pipfile | 15 + Pipfile.lock | 464 ++++++++++++++++++++ examples/exampleUsage.py | 1 + pyproject.toml | 0 src/decisionMakingPackage/__init__.py | 1 + src/decisionMakingPackage/decisionMaking.py | 1 + tests/tests.py | 2 + 8 files changed, 485 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 examples/exampleUsage.py create mode 100644 pyproject.toml create mode 100644 src/decisionMakingPackage/__init__.py create mode 100644 src/decisionMakingPackage/decisionMaking.py create mode 100644 tests/tests.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9bd3ab7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1 @@ +# This is where we would implement github action \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..ca8b7bb --- /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.9" +python_full_version = "3.9.6" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..ac52e65 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,464 @@ +{ + "_meta": { + "hash": { + "sha256": "5c19eefe9161ee40b3ec50513c91a49eeaeeaadfec26fd4e38c4aa130d881e48" + }, + "pipfile-spec": 6, + "requires": { + "python_full_version": "3.9.6", + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "backports.tarfile": { + "hashes": [ + "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", + "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991" + ], + "markers": "python_version >= '3.8'", + "version": "==1.2.0" + }, + "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" + }, + "docutils": { + "hashes": [ + "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d", + "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8" + ], + "markers": "python_version >= '3.9'", + "version": "==0.22.2" + }, + "exceptiongroup": { + "hashes": [ + "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", + "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, + "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" + }, + "importlib-metadata": { + "hashes": [ + "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", + "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" + ], + "markers": "python_version >= '3.9'", + "version": "==8.7.0" + }, + "iniconfig": { + "hashes": [ + "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", + "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" + ], + "markers": "python_version >= '3.8'", + "version": "==2.1.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:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", + "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" + ], + "markers": "python_version >= '3.8'", + "version": "==3.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:0f454ba4c6aabafcaae964ae6f0a96cecef970216a57335fabd229a265fbe007", + "sha256:1de5c1a35bed19a1b1286bab3c3abfe42e990a8a6c4ce9bb9ab4bde49107ea3b", + "sha256:22b9e9c9eda497b02b7273b79f7d29e1f1170d2b741624c1b8c566aef28b1f48", + "sha256:2cb6d9e192fbe0d451c7cb1350dadedbeae286207dbf101a28210193d019752e", + "sha256:38b4872499ab15b17c5c6e9f091143d070d75ddad4a4d1ce388d043ca556629c", + "sha256:42e426f36e167ed29669b77ae3c4b9e185e4a1b130a86d7c3249194738a1d7b2", + "sha256:474b176124c1b495ccfa1c20f61b7eb83ead5ecccb79ab29f602c148e8378489", + "sha256:48425995d37880281b467f7cf2b3218c1f4750c55bcb1ff4f47f2320a2bb159c", + "sha256:489ca5ecd58555c2865701e65f614b17555179e71ecc76d483b6f3886b813a9b", + "sha256:4a2434668f4eef4eab17c128e565ce6bea42113ce10c40b928e42c578d401800", + "sha256:5a25662b392b06f251da6004a1f8a828dca7f429cd94ac07d8a98ba94d644438", + "sha256:669a908706cd28203d9cfce2f567575686e364a1bc6074d413d88d456066f743", + "sha256:670f18b09f75c86c3865f79543bf5acd4bbe2a5a4475672eef2399dd8cdb69d2", + "sha256:6a854480058683d60bdc7f0456105092dae17bef1f300642856d74bd4201da93", + "sha256:80dc7563a2a3b980e44b221f69848e3645bbf163ab53e3d1add4f47b26120355", + "sha256:8f600ad86114df21efc4a3592faa6b1d099c0eebc7e018efebb1c133376097da", + "sha256:94292dd1bd2a2e142fa5bb94c0ee1d84433a5d9034640710132da7e0376fca3a", + "sha256:a3e810a92fb192373204456cac2834694440af73d749565b4348e30235da7f0b", + "sha256:a5721f59afa0ab3dcaa0d47e58af33a5fcd254882e1900ee4a8968692a40f79d", + "sha256:b0d6c834d3c07366ecbdcecc1f4804c5ce0a77fa52ee4653a2a26d2d909980ea", + "sha256:b222c05ae5139320da6caa1c5aed36dd0ee36e39831541d9b56e048a63b4d701", + "sha256:b74bbd047b361c0f21d827250c865ff0895684d9fcf85ea86131a78cfa0b835b", + "sha256:c0acef923a1c3a2df3ee5825ea79c149b6748c6449781c53ab6923dc75e87d26", + "sha256:d7431b2a39431017f19cd03144005b6c014201b3e73927c05eab6ca37bb1d98c", + "sha256:dd6d1be301123a9af3263739726eeeb208197e5e78fc4f522408c50de77a5354", + "sha256:eaba26591867f697cffdbc539faddeb1d75a36273f5bfe957eb421d3f87d7da1" + ], + "markers": "python_version >= '3.8'", + "version": "==0.3.1" + }, + "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" + }, + "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" + }, + "tomli": { + "hashes": [ + "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", + "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", + "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", + "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", + "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", + "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", + "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", + "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", + "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", + "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", + "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", + "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", + "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", + "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", + "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", + "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", + "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", + "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", + "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", + "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", + "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", + "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", + "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", + "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", + "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", + "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", + "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", + "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", + "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", + "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", + "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", + "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", + "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", + "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", + "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", + "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", + "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", + "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", + "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", + "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", + "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", + "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876" + ], + "markers": "python_version >= '3.8'", + "version": "==2.3.0" + }, + "twine": { + "hashes": [ + "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", + "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf" + ], + "index": "pypi", + "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", + "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" + ], + "markers": "python_version >= '3.9'", + "version": "==2.5.0" + }, + "zipp": { + "hashes": [ + "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", + "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166" + ], + "markers": "python_version >= '3.9'", + "version": "==3.23.0" + } + } +} diff --git a/examples/exampleUsage.py b/examples/exampleUsage.py new file mode 100644 index 0000000..f71c317 --- /dev/null +++ b/examples/exampleUsage.py @@ -0,0 +1 @@ +# This is the exampel program where we would demonstrates our package's complete functionality. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e69de29 diff --git a/src/decisionMakingPackage/__init__.py b/src/decisionMakingPackage/__init__.py new file mode 100644 index 0000000..ba9fefe --- /dev/null +++ b/src/decisionMakingPackage/__init__.py @@ -0,0 +1 @@ +# Ignore for now; we will import functions from decisionMaking.py later \ No newline at end of file diff --git a/src/decisionMakingPackage/decisionMaking.py b/src/decisionMakingPackage/decisionMaking.py new file mode 100644 index 0000000..9bc7398 --- /dev/null +++ b/src/decisionMakingPackage/decisionMaking.py @@ -0,0 +1 @@ +# This is where we will write our actual functions for the package \ No newline at end of file diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 0000000..94dcd90 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,2 @@ +# This is where we will write our unit tests +import pytest From be19a933555071cfa6636966722c0846a991a8a7 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Wed, 29 Oct 2025 11:40:36 -0400 Subject: [PATCH 02/44] Added pick_color function --- src/decisionMakingPackage/decisionMaking.py | 199 +++++++++++++++++++- 1 file changed, 198 insertions(+), 1 deletion(-) diff --git a/src/decisionMakingPackage/decisionMaking.py b/src/decisionMakingPackage/decisionMaking.py index 9bc7398..e370a3f 100644 --- a/src/decisionMakingPackage/decisionMaking.py +++ b/src/decisionMakingPackage/decisionMaking.py @@ -1 +1,198 @@ -# This is where we will write our actual functions for the package \ No newline at end of file +# This is where we will write our actual functions for the package +import random + + +def pick_food(dietary_restriction: str = None) -> None: + print("temp, replace with actual print statement") + + +def pick_color(mood: str = None, season: str = None) -> None: + winter = [ + "blue", + "navy", + "white", + "silver", + "black", + "burgundy", + "emerald green", + "royal purple", + "ice blue", + "charcoal", + ] + fall = [ + "orange", + "rust", + "brown", + "burgundy", + "mustard yellow", + "olive green", + "burnt sienna", + "copper", + "maroon", + "tan", + ] + summer = [ + "yellow", + "coral", + "turquoise", + "hot pink", + "lime green", + "sky blue", + "peach", + "tangerine", + "mint", + "aqua", + ] + spring = [ + "pastel pink", + "lavender", + "mint", + "baby blue", + "lemon yellow", + "peach", + "soft coral", + "light purple", + "sage green", + "cream", + ] + happy = [ + "yellow", + "bright orange", + "sunny gold", + "lime green", + "sky blue", + "pink", + "coral", + "peach", + "turquoise", + ] + sad = ["blue", "grey", "dark purple", "navy", "slate blue", "charcoal", "ice blue"] + calm = [ + "light blue", + "lavender", + "sage green", + "soft grey", + "beige", + "mint", + "powder blue", + "pale pink", + "baby blue", + ] + energetic = [ + "red", + "bright orange", + "electric blue", + "neon green", + "hot pink", + "vibrant yellow", + "magenta", + "lime green", + "tangerine", + ] + angry = [ + "red", + "crimson", + "black", + "dark orange", + "blood red", + "maroon", + "dark grey", + "burgundy", + ] + + # Season and mood dictionaries for easy lookup + seasonColors = {"winter": winter, "fall": fall, "summer": summer, "spring": spring} + moodColors = { + "happy": happy, + "sad": sad, + "calm": calm, + "energetic": energetic, + "angry": angry, + } + # Default color set + allColors = set() + for colors in seasonColors.values(): + allColors.update(colors) + for colors in moodColors.values(): + allColors.update(colors) + + # No arguments - pick from default + if mood is None and season is None: + print(f"Here's a random color for you: {random.choice(list(allColors))}") + # Only mood provided + elif mood is not None and season is None: + mood = mood.lower() + if mood not in moodColors: + print( + f"Sorry, '{mood}' is not a supported mood. Here's the list of accepted moods: {', '.join(moodColors.keys())}" + ) + print( + f"Picking from default list instead: {random.choice(list(allColors))}" + ) + else: + color = random.choice(moodColors[mood]) + print(f"For your {mood} mood, try: {color}") + # Only season provided + elif season is not None and mood is None: + season = season.lower() + if season not in seasonColors: + print( + f"Sorry, '{season}' is not a valid season. Here's the list of accepted seasons: {', '.join(seasonColors.keys())}" + ) + print( + f"Picking from default list instead: {random.choice(list(allColors))}" + ) + else: + color = random.choice(seasonColors[season]) + print(f"For {season} season, try: {color}") + # Both mood and season provided + else: + mood = mood.lower() + season = season.lower() + validMood = mood in moodColors + validSeason = season in seasonColors + # If both invalid, pick from default + if not validMood and not validSeason: + print( + f"Sorry, '{mood}' is not a valid mood and '{season}' is not a valid season." + ) + print(f"Here's the list of accepted moods: {', '.join(moodColors.keys())}") + print( + f"Here's the list of accepted seasons: {', '.join(seasonColors.keys())}" + ) + print( + f"Picking from default list instead: {random.choice(list(allColors))}" + ) + # If only mood invalid, use season + elif not validMood: + print( + f"Sorry, '{mood}' is not a valid mood. Try: {', '.join(moodColors.keys())}" + ) + color = random.choice(seasonColors[season]) + print(f"Using just your {season} season instead, try: {color}") + # If only season invalid, use mood + elif not validSeason: + print( + f"Sorry, '{season}' is not a valid season. Try: {', '.join(seasonColors.keys())}" + ) + color = random.choice(moodColors[mood]) + print(f"Using just your {mood} mood instead, try: {color}") + # Both valid + else: + commonColors = list(set(moodColors[mood]) & set(seasonColors[season])) + if commonColors: + color = random.choice(commonColors) + print(f"Perfect match! For {mood} mood in {season}: {color}") + else: + mood_color = random.choice(moodColors[mood]) + season_color = random.choice(seasonColors[season]) + print( + f"No perfect match, but try {mood} color: {mood_color} or {season} color: {season_color}" + ) + + +def pick_activity(weather: str = None, energy_level: str = None) -> None: + print("") + + +pick_color("calm", "Summer") From a0c182954b7e7a8ce2c85120114aef42c2ab2a33 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Wed, 29 Oct 2025 15:41:04 -0400 Subject: [PATCH 03/44] update README.md to include overview of the package --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6022e0e..4a2b79f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ -# Python Package Exercise +# Python Package Exercise - Daily Decision Helper +## Overview +Do you ever struggle to make everyday decisions? Whether it's choosing what to eat for lunch, picking an outfit color, or deciding what music to listen to, sometimes the smallest choices can feel overwhelming. DailyDecisions is here to help! This Python package takes the stress out of life's minor decisions by providing randomized suggestions when you need them most. -An exercise to create a Python package, build it, test it, distribute it, and use it. See [instructions](./instructions.md) for details. +## Features +This package provides a few functions to help with your daily decision making: +- `pick_food(dietary_restriction, cuisine)` - a function to help you pick a food based on your dietary restriction and the type of cuisine you want. +- `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and the season. +- `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and the weather. + +## Team members: +[Jubilee Tang](https://github.com/MajesticSeagull26) From ef700ee8859ba46456187d048ef77385db32876b Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Wed, 29 Oct 2025 16:06:05 -0400 Subject: [PATCH 04/44] Add team member info --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4a2b79f..1f1f02c 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,6 @@ This package provides a few functions to help with your daily decision making: - `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and the weather. ## Team members: +[Maria Luo](https://github.com/MariaLuo826) +[Reece Huey](https://github.com/Coffee859) [Jubilee Tang](https://github.com/MajesticSeagull26) From b98010900debacad0de9aea8489d84fd2a2543c4 Mon Sep 17 00:00:00 2001 From: Coffee859 <71625056+Coffee859@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:59:48 -0400 Subject: [PATCH 05/44] Update decisionMaking.py --- src/decisionMakingPackage/decisionMaking.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/decisionMakingPackage/decisionMaking.py b/src/decisionMakingPackage/decisionMaking.py index e370a3f..46fd663 100644 --- a/src/decisionMakingPackage/decisionMaking.py +++ b/src/decisionMakingPackage/decisionMaking.py @@ -3,6 +3,22 @@ def pick_food(dietary_restriction: str = None) -> None: + restrictions = ["kosher", + "halal", + "jain", + "vegetarian", + "vegan", + "no_gluten", + "no_soy", + "no_nuts", + "no_dairy", + "no_eggs", + ] + # Default color set + allFoods = set() + for foods in restrictions.values(): + allFoods.update(foods) + print("temp, replace with actual print statement") From 949c82ae82a93ef89e049f0ab44bd20de9024fa7 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Fri, 31 Oct 2025 16:43:22 -0400 Subject: [PATCH 06/44] rename files --- src/{decisionMakingPackage => dailyDecisionPackage}/__init__.py | 0 .../decisionMaking.py => dailyDecisionPackage/dailyDecision.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{decisionMakingPackage => dailyDecisionPackage}/__init__.py (100%) rename src/{decisionMakingPackage/decisionMaking.py => dailyDecisionPackage/dailyDecision.py} (100%) diff --git a/src/decisionMakingPackage/__init__.py b/src/dailyDecisionPackage/__init__.py similarity index 100% rename from src/decisionMakingPackage/__init__.py rename to src/dailyDecisionPackage/__init__.py diff --git a/src/decisionMakingPackage/decisionMaking.py b/src/dailyDecisionPackage/dailyDecision.py similarity index 100% rename from src/decisionMakingPackage/decisionMaking.py rename to src/dailyDecisionPackage/dailyDecision.py From b710c5b4bb330b781366f71c9e22cd6de7cafd99 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Fri, 31 Oct 2025 19:32:47 -0400 Subject: [PATCH 07/44] Configure package build with pyproject.toml --- Pipfile | 1 + Pipfile.lock | 9 +++++++-- pyproject.toml | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Pipfile b/Pipfile index ca8b7bb..c94680e 100644 --- a/Pipfile +++ b/Pipfile @@ -4,6 +4,7 @@ verify_ssl = true name = "pypi" [packages] +dailydecisionpackage = {file = ".", editable = true} [dev-packages] pytest = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ac52e65..772cdff 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "5c19eefe9161ee40b3ec50513c91a49eeaeeaadfec26fd4e38c4aa130d881e48" + "sha256": "b4a4bb2a4970b3fe8e94368a355c895552c7487a1f3d13c89f6239334cd0e3c1" }, "pipfile-spec": 6, "requires": { @@ -16,7 +16,12 @@ } ] }, - "default": {}, + "default": { + "dailydecisionpackage": { + "editable": true, + "file": "." + } + }, "develop": { "backports.tarfile": { "hashes": [ diff --git a/pyproject.toml b/pyproject.toml index e69de29..0e87afb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "dailyDecisionPackage" +description = "A lighthearted Python package that helps you make everyday decisions." +version = "0.1.0" +authors = [ + { name = "Jubilee Tang", email = "wt2187@nyu.edu" }, + { name = "Maria Luo" }, + { name = "Reece Huey" }, +] +license = "GPL-3.0-or-later" +license-files = ["LICENSE"] +readme = "README.md" +keywords = ["python", "package", "daily", "decision", "helper", "color", "food"] +requires-python = ">=3.9" +classifiers = [ + "Programming Language :: Python :: 3", + "Intended Audience :: Education", + "Operating System :: OS Independent", +] + +[project.optional-dependencies] +dev = ["pytest", "build", "twine"] + +[project.urls] +"Homepage" = "https://github.com/swe-students-fall2025/3-python-package-team_aurora" +"Repository" = "https://github.com/swe-students-fall2025/3-python-package-team_aurora.git" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +dailyDecisionPackage = ["*.txt", "*.md"] From 1eef3d4f224e31027947b8333108ceb41ab5cd37 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sat, 1 Nov 2025 01:04:42 -0400 Subject: [PATCH 08/44] Add unit test for pick_color function --- tests/tests.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index 94dcd90..15896fc 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,2 +1,110 @@ -# This is where we will write our unit tests import pytest +from dailyDecisionPackage.dailyDecision import pick_color + +# Unit tests for pick_color function +class TestPickColor: + happy_colors = [ + "yellow", + "bright orange", + "sunny gold", + "lime green", + "sky blue", + "pink", + "coral", + "peach", + "turquoise", + ] + summer_colors = [ + "yellow", + "coral", + "turquoise", + "hot pink", + "lime green", + "sky blue", + "peach", + "tangerine", + "mint", + "aqua", + ] + energetic_colors = [ + "red", + "bright orange", + "electric blue", + "neon green", + "hot pink", + "vibrant yellow", + "magenta", + "lime green", + "tangerine", + ] + def test_no_arguments(self, capsys): + pick_color() + captured = capsys.readouterr() + assert "Here's a random color for you:" in captured.out + assert len(captured.out.strip()) > len("Here's a random color for you:") + + def test_all_supported_moods(self, capsys): + supported_moods = ["happy", "sad", "calm", "energetic", "angry"] + for mood in supported_moods: + pick_color(mood=mood) + captured = capsys.readouterr() + assert f"For your {mood} mood, try:" in captured.out + + def test_all_supported_seasons(self, capsys): + supported_seasons = ["winter", "fall", "summer", "spring"] + for season in supported_seasons: + pick_color(season=season) + captured = capsys.readouterr() + assert f"For {season} season, try:" in captured.out + + def test_mood_happy(self, capsys): + pick_color(mood="happy") + captured = capsys.readouterr() + assert "For your happy mood, try:" in captured.out + assert any(color in captured.out for color in self.happy_colors) + + def test_invalid_mood(self, capsys): + pick_color(mood="confused") + captured = capsys.readouterr() + assert "Sorry, 'confused' is not a supported mood" in captured.out + assert "Picking from default list instead:" in captured.out + + def test_season_summer(self, capsys): + pick_color(season="summer") + captured = capsys.readouterr() + assert "For summer season, try:" in captured.out + assert any(color in captured.out for color in self.summer_colors) + + def test_invalid_season(self, capsys): + pick_color(season="temp") + captured = capsys.readouterr() + assert "Sorry, 'temp' is not a valid season" in captured.out + assert "Picking from default list instead:" in captured.out + + def test_both_valid_mood_and_season(self, capsys): + pick_color(mood="calm", season="spring") + captured = capsys.readouterr() + assert "Perfect match!" in captured.out or "No perfect match" in captured.out + + def test_both_invalid(self, capsys): + pick_color(mood="sleepy", season="temp") + captured = capsys.readouterr() + assert ( + "Sorry, 'sleepy' is not a valid mood and 'temp' is not a valid season" + in captured.out + ) + assert "Picking from default list instead:" in captured.out + + def test_invalid_mood_valid_season(self, capsys): + pick_color(mood="confused", season="summer") + captured = capsys.readouterr() + assert "Sorry, 'confused' is not a valid mood" in captured.out + assert "Using just your summer season instead, try:" in captured.out + assert any(color in captured.out for color in self.summer_colors) + + def test_valid_mood_invalid_season(self, capsys): + pick_color(mood="energetic", season="temp") + captured = capsys.readouterr() + assert "Sorry, 'temp' is not a valid season" in captured.out + assert "Using just your energetic mood instead, try:" in captured.out + assert any(color in captured.out for color in self.energetic_colors) From a2f9320bc4c327230897b20090d652befcb90174 Mon Sep 17 00:00:00 2001 From: MariaLuo826 <112397482+MariaLuo826@users.noreply.github.com> Date: Sun, 2 Nov 2025 01:37:23 -0500 Subject: [PATCH 09/44] Implement pick_food function --- src/decisionMakingPackage/decisionMaking.py | 77 +++++++++++++++++---- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/decisionMakingPackage/decisionMaking.py b/src/decisionMakingPackage/decisionMaking.py index 46fd663..8a2ac46 100644 --- a/src/decisionMakingPackage/decisionMaking.py +++ b/src/decisionMakingPackage/decisionMaking.py @@ -2,24 +2,73 @@ import random + def pick_food(dietary_restriction: str = None) -> None: - restrictions = ["kosher", - "halal", - "jain", - "vegetarian", - "vegan", - "no_gluten", - "no_soy", - "no_nuts", - "no_dairy", - "no_eggs", - ] - # Default color set + # Foods by restriction + foods_by_restriction = { + "kosher": [ + "bagel with lox", "matzo ball soup", "tuna salad", "grilled salmon with potatoes", + "egg salad sandwich", "falafel plate" + ], + "halal": [ + "chicken biryani", "beef kebab plate", "shawarma bowl", "lentil dal with rice", + "falafel wrap", "grilled salmon" + ], + "jain": [ + "vegetable khichdi", "paneer tikka (no onion/garlic)", "sabudana khichdi", + "dal dhokli", "vegetable pulao", "coconut curry" + ], + "vegetarian": [ + "margherita pizza", "mushroom risotto", "spinach ravioli", "caprese sandwich", + "falafel bowl", "paneer tikka" + ], + "vegan": [ + "tofu stir-fry", "chickpea curry", "veggie sushi", "buddha bowl", + "lentil bolognese", "quinoa salad" + ], + "no_gluten": [ + "rice bowl with chicken", "corn tacos", "pho", "sashimi platter", + "thai green curry", "baked sweet potato" + ], + "no_soy": [ + "grilled chicken salad", "roasted veggie pasta", "eggplant parm", "mushroom risotto", + "omelet with veggies", "lentil soup" + ], + "no_nuts": [ + "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", + "rotisserie chicken plate", "tomato soup & grilled cheese" + ], + "no_dairy": [ + "tom yum soup", "poke bowl", "chicken shawarma wrap (no yogurt sauce)", + "vegan ramen", "tofu curry", "bibimbap (no egg)" + ], + "no_eggs": [ + "pasta primavera", "mushroom risotto", "vegetable stir-fry", "falafel wrap", + "vegan curry", "tofu scramble" + ], + } + + # Build full set of all foods allFoods = set() - for foods in restrictions.values(): + for foods in foods_by_restriction.values(): allFoods.update(foods) - print("temp, replace with actual print statement") + # No restriction given — pick from all + if dietary_restriction is None: + print(f"How about: {random.choice(list(allFoods))}") + return + + restriction = dietary_restriction.strip().lower() + + if restriction not in foods_by_restriction: + accepted = ", ".join(sorted(foods_by_restriction.keys())) + print(f"Sorry, '{restriction}' is not a supported restriction.") + print(f"Please choose from: {accepted}") + print(f"In the meantime, try: {random.choice(list(allFoods))}") + return + + choice = random.choice(foods_by_restriction[restriction]) + print(f"For a {restriction} diet, you could try: {choice}") def pick_color(mood: str = None, season: str = None) -> None: From 3385b20cebedc687e553d7dad2c03fdef4dc86e3 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 21:14:34 -0500 Subject: [PATCH 10/44] Add yml file for github action --- .github/workflows/test.yml | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9bd3ab7..cf3eabb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1 +1,44 @@ -# This is where we would implement github action \ No newline at end of file +name: CI / CD +on: + push: + pull_request: + branches: + - pipfile-experiment +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + python-version: ["3.9", "3.11"] + steps: + - uses: actions/checkout@v4 + - name: Install Python, pipenv and Pipfile packages + uses: kojoru/prepare-pipenv@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Turn on 'editable' mode + run: | + pipenv install -e . + - name: Test with pytest + run: | + pipenv install pytest + pipenv --venv + pipenv run python -m pytest + deliver: + 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 + - name: Build package + run: | + pipenv install build + pipenv run python -m build . + - name: Publish to PyPI test server + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository-url: https://test.pypi.org/legacy/ From 38e5fd56535ce79655286ec6094921c388de944e Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 21:22:06 -0500 Subject: [PATCH 11/44] Fix directory for pytest in github action --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cf3eabb..3d41700 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: run: | pipenv install pytest pipenv --venv - pipenv run python -m pytest + pipenv run python -m pytest tests/ -v deliver: needs: [build] runs-on: ubuntu-latest From f674dfb9df2331167c0dd376659454faf896538e Mon Sep 17 00:00:00 2001 From: aa10150 Date: Sun, 2 Nov 2025 21:43:44 -0500 Subject: [PATCH 12/44] Added name to team members --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f1f02c..c9b6660 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,4 @@ This package provides a few functions to help with your daily decision making: [Maria Luo](https://github.com/MariaLuo826) [Reece Huey](https://github.com/Coffee859) [Jubilee Tang](https://github.com/MajesticSeagull26) +[Anshu Aramandla](https://github.com/aa10150) From 2ec298b3fa1bda211a8348cb147ec8c4abb676ac Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 21:48:18 -0500 Subject: [PATCH 13/44] debugging --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3d41700..ca4af27 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,14 +17,11 @@ jobs: uses: kojoru/prepare-pipenv@v1 with: python-version: ${{ matrix.python-version }} - - name: Turn on 'editable' mode - run: | - pipenv install -e . - name: Test with pytest run: | pipenv install pytest pipenv --venv - pipenv run python -m pytest tests/ -v + pipenv run python -m pytest tests/test.py -v deliver: needs: [build] runs-on: ubuntu-latest From 0c6932dc45e6d7bfc68dc50973e9d84e738bed6f Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 22:28:29 -0500 Subject: [PATCH 14/44] Attempt github action fix --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca4af27..3d41700 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,11 +17,14 @@ jobs: uses: kojoru/prepare-pipenv@v1 with: python-version: ${{ matrix.python-version }} + - name: Turn on 'editable' mode + run: | + pipenv install -e . - name: Test with pytest run: | pipenv install pytest pipenv --venv - pipenv run python -m pytest tests/test.py -v + pipenv run python -m pytest tests/ -v deliver: needs: [build] runs-on: ubuntu-latest From 80538e15cf391d9ebf34ff1e2495f036aca12ed7 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 22:31:34 -0500 Subject: [PATCH 15/44] Attempt fix part 2 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3d41700..bed6b7d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 5 strategy: matrix: - python-version: ["3.9", "3.11"] + python-version: ["3.9"] steps: - uses: actions/checkout@v4 - name: Install Python, pipenv and Pipfile packages From 5807726c178644e4a08f1db794a2d4219e21d9d4 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 22:32:57 -0500 Subject: [PATCH 16/44] fix test path --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bed6b7d..7166979 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: run: | pipenv install pytest pipenv --venv - pipenv run python -m pytest tests/ -v + pipenv run python -m pytest tests/tests.py -v deliver: needs: [build] runs-on: ubuntu-latest From 0414b248ca78066e5a6a9d2cfe20956ec26f8567 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 22:39:55 -0500 Subject: [PATCH 17/44] Fix Python version in Pipfile --- .github/workflows/test.yml | 2 +- Pipfile | 3 +- Pipfile.lock | 59 +++++++++++++++++++------------------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7166979..39e8d10 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 5 strategy: matrix: - python-version: ["3.9"] + python-version: ["3.9", "3.11"] steps: - uses: actions/checkout@v4 - name: Install Python, pipenv and Pipfile packages diff --git a/Pipfile b/Pipfile index c94680e..7a596a8 100644 --- a/Pipfile +++ b/Pipfile @@ -12,5 +12,4 @@ build = "*" twine = "*" [requires] -python_version = "3.9" -python_full_version = "3.9.6" +python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock index 772cdff..78e1ba7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,12 +1,11 @@ { "_meta": { "hash": { - "sha256": "b4a4bb2a4970b3fe8e94368a355c895552c7487a1f3d13c89f6239334cd0e3c1" + "sha256": "b890b2c732547c69bf871ccc71357440c2abc07344a4c572acace6317d6f6676" }, "pipfile-spec": 6, "requires": { - "python_full_version": "3.9.6", - "python_version": "3.9" + "python_version": "3" }, "sources": [ { @@ -273,35 +272,35 @@ }, "nh3": { "hashes": [ - "sha256:0f454ba4c6aabafcaae964ae6f0a96cecef970216a57335fabd229a265fbe007", - "sha256:1de5c1a35bed19a1b1286bab3c3abfe42e990a8a6c4ce9bb9ab4bde49107ea3b", - "sha256:22b9e9c9eda497b02b7273b79f7d29e1f1170d2b741624c1b8c566aef28b1f48", - "sha256:2cb6d9e192fbe0d451c7cb1350dadedbeae286207dbf101a28210193d019752e", - "sha256:38b4872499ab15b17c5c6e9f091143d070d75ddad4a4d1ce388d043ca556629c", - "sha256:42e426f36e167ed29669b77ae3c4b9e185e4a1b130a86d7c3249194738a1d7b2", - "sha256:474b176124c1b495ccfa1c20f61b7eb83ead5ecccb79ab29f602c148e8378489", - "sha256:48425995d37880281b467f7cf2b3218c1f4750c55bcb1ff4f47f2320a2bb159c", - "sha256:489ca5ecd58555c2865701e65f614b17555179e71ecc76d483b6f3886b813a9b", - "sha256:4a2434668f4eef4eab17c128e565ce6bea42113ce10c40b928e42c578d401800", - "sha256:5a25662b392b06f251da6004a1f8a828dca7f429cd94ac07d8a98ba94d644438", - "sha256:669a908706cd28203d9cfce2f567575686e364a1bc6074d413d88d456066f743", - "sha256:670f18b09f75c86c3865f79543bf5acd4bbe2a5a4475672eef2399dd8cdb69d2", - "sha256:6a854480058683d60bdc7f0456105092dae17bef1f300642856d74bd4201da93", - "sha256:80dc7563a2a3b980e44b221f69848e3645bbf163ab53e3d1add4f47b26120355", - "sha256:8f600ad86114df21efc4a3592faa6b1d099c0eebc7e018efebb1c133376097da", - "sha256:94292dd1bd2a2e142fa5bb94c0ee1d84433a5d9034640710132da7e0376fca3a", - "sha256:a3e810a92fb192373204456cac2834694440af73d749565b4348e30235da7f0b", - "sha256:a5721f59afa0ab3dcaa0d47e58af33a5fcd254882e1900ee4a8968692a40f79d", - "sha256:b0d6c834d3c07366ecbdcecc1f4804c5ce0a77fa52ee4653a2a26d2d909980ea", - "sha256:b222c05ae5139320da6caa1c5aed36dd0ee36e39831541d9b56e048a63b4d701", - "sha256:b74bbd047b361c0f21d827250c865ff0895684d9fcf85ea86131a78cfa0b835b", - "sha256:c0acef923a1c3a2df3ee5825ea79c149b6748c6449781c53ab6923dc75e87d26", - "sha256:d7431b2a39431017f19cd03144005b6c014201b3e73927c05eab6ca37bb1d98c", - "sha256:dd6d1be301123a9af3263739726eeeb208197e5e78fc4f522408c50de77a5354", - "sha256:eaba26591867f697cffdbc539faddeb1d75a36273f5bfe957eb421d3f87d7da1" + "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.1" + "version": "==0.3.2" }, "packaging": { "hashes": [ From d2df6101f85dd08bd3d29ed3f1f8ca7537d7a5a4 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 22:54:46 -0500 Subject: [PATCH 18/44] Added Github Action badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c9b6660..19e4e80 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![CI / CD](https://github.com/swe-students-fall2025/3-python-package-team_aurora/actions/workflows/test.yml/badge.svg)](https://github.com/swe-students-fall2025/3-python-package-team_aurora/actions/workflows/test.yml) # Python Package Exercise - Daily Decision Helper ## Overview Do you ever struggle to make everyday decisions? Whether it's choosing what to eat for lunch, picking an outfit color, or deciding what music to listen to, sometimes the smallest choices can feel overwhelming. DailyDecisions is here to help! This Python package takes the stress out of life's minor decisions by providing randomized suggestions when you need them most. From acf0f7adf6f21b7788db9b2d7cf05c45de648e41 Mon Sep 17 00:00:00 2001 From: aa10150 Date: Sun, 2 Nov 2025 23:03:50 -0500 Subject: [PATCH 19/44] Implemented pick_activity function --- src/dailyDecisionPackage/dailyDecision.py | 99 ++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 8a2ac46..ac62197 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -257,7 +257,104 @@ def pick_color(mood: str = None, season: str = None) -> None: def pick_activity(weather: str = None, energy_level: str = None) -> None: - print("") + # Activities by weather + activities_by_weather = { + "sunny": [ + "go for a walk", "read a book at the park", "explore a new part of the city", "go for a run", + "go hiking", "bike around your neighborhood", "go to the beach" + ], + "cloudy": [ + "watch the clouds" + ], + "rainy": [ + "dance in the rain" + ], + "snowy": [ + "make snow angels", "build a snowman", "snowball fight" + ], + "any": [ + "watch a movie or TV", "play a video game", "read a book indoors", "listen to a podcast", + "listen to music", "write a journal entry", "call a friend", "play a board game", + "solve a crossword", "clean your bedroom", "light exercise", "dance", "make a home-cooked meal", + "arts & crafts", "yoga", "go to the gym", "go to the club", "go to a party", "clean the house", + "painting", "go for a drive" + ] + } + # Activities by energy level + activities_by_energy_level = { + "low": [ + "watch a movie or TV", "play a video game", "read a book indoors", "read a book at the park", + "listen to a podcast", "listen to music", "write a journal entry", "call a friend", + "play a board game", "solve a crossword", "watch the clouds", "painting" + ], + "medium": [ + "go for a walk", "clean your bedroom", "light exercise", "dance", "dance in the rain", + "make a home-cooked meal", "arts & crafts", "yoga", "make snow angels", "build a snowman", + "go to the beach", "go for a drive" + ], + "high": [ + "go to the gym", "go for a run", "go hiking", "bike around your neighborhood", "go to the club", + "go to a party", "clean the house", "snowball fight", "explore a new part of the city" + ] + } + # create set of all activities + allActivities = set() + for activities in activities_by_energy_level.values(): + allActivities.update(activities) + + # No arguments (pick random activity) + if weather is None and energy_level is None: + print(f"Try this activity: {random.choice(list(allActivities))}") + return + # No energy level argument (weather only) + elif energy_level is None: + # Invalid weather + if weather.lower() not in activities_by_weather: + accepted = ", ".join(sorted(activities_by_weather.keys())) + print(f"Sorry, '{weather}' is not a supported weather type.") + print(f"Please choose from: {accepted}") + print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + return + # Valid weather + validActivities = set(activities_by_weather[weather.lower()]) | set(activities_by_weather["any"]) + print(f"Try this activity: {random.choice(list(validActivities))}") + return + # No weather argument (energy level only) + elif weather is None: + # Invalid energy level + if energy_level.lower() not in activities_by_energy_level: + accepted = ", ".join(sorted(activities_by_energy_level.keys())) + print(f"Sorry, '{energy_level}' is not a supported energy level.") + print(f"Please choose from: {accepted}") + print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + return + # Valid energy level + validActivities = set(activities_by_energy_level[energy_level.lower()]) + print(f"Try this activity: {random.choice(list(validActivities))}") + return + # Arguments for both weather and energy level + else: + # Invalid weather + if weather.lower() not in activities_by_weather: + accepted = ", ".join(sorted(activities_by_weather.keys())) + print(f"Sorry, '{weather}' is not a supported weather type.") + print(f"Please choose from: {accepted}") + print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + return + # Invalid energy level + if energy_level.lower() not in activities_by_energy_level: + accepted = ", ".join(sorted(activities_by_energy_level.keys())) + print(f"Sorry, '{energy_level}' is not a supported energy level.") + print(f"Please choose from: {accepted}") + print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + return + # Valid arguments + validActivities = ( + ( set(activities_by_weather[weather.lower()]) | set(activities_by_weather["any"]) ) & + set(activities_by_energy_level[energy_level.lower()]) + ) + print(f"Try this activity: {random.choice(list(validActivities))}") + return pick_color("calm", "Summer") From 2ff05b8b9b9c3c9fa545c562f6c5f50f9f2680b1 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Sun, 2 Nov 2025 23:05:41 -0500 Subject: [PATCH 20/44] Fix README formatting; commenting out deliver part for during development --- .github/workflows/test.yml | 35 ++++++++++++++++++----------------- README.md | 10 +++++----- pyproject.toml | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39e8d10..a199fa6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,20 +25,21 @@ jobs: pipenv install pytest pipenv --venv pipenv run python -m pytest tests/tests.py -v - deliver: - 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 - - name: Build package - run: | - pipenv install build - pipenv run python -m build . - - name: Publish to PyPI test server - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository-url: https://test.pypi.org/legacy/ +# deliver: +# needs: [build] +# runs-on: ubuntu-latest +# if: github.ref == 'refs/heads/pipfile-experiment' +# timeout-minutes: 5 +# steps: +# - uses: actions/checkout@v4 +# - name: Install Python, pipenv and Pipfile packages +# uses: kojoru/prepare-pipenv@v1 +# - name: Build package +# run: | +# pipenv install build +# pipenv run python -m build . +# - name: Publish to PyPI test server +# 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/README.md b/README.md index 19e4e80..22234f9 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ Do you ever struggle to make everyday decisions? Whether it's choosing what to e ## Features This package provides a few functions to help with your daily decision making: -- `pick_food(dietary_restriction, cuisine)` - a function to help you pick a food based on your dietary restriction and the type of cuisine you want. +- `pick_food(dietary_restriction, cuisine)` - a function to help you pick a food based on your dietary restriction. - `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and the season. - `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and the weather. ## Team members: -[Maria Luo](https://github.com/MariaLuo826) -[Reece Huey](https://github.com/Coffee859) -[Jubilee Tang](https://github.com/MajesticSeagull26) -[Anshu Aramandla](https://github.com/aa10150) +[Maria Luo](https://github.com/MariaLuo826) +[Reece Huey](https://github.com/Coffee859) +[Jubilee Tang](https://github.com/MajesticSeagull26) +[Anshu Aramandla](https://github.com/aa10150) diff --git a/pyproject.toml b/pyproject.toml index 0e87afb..c50faea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "dailyDecisionPackage" description = "A lighthearted Python package that helps you make everyday decisions." -version = "0.1.0" +version = "0.1.1" authors = [ { name = "Jubilee Tang", email = "wt2187@nyu.edu" }, { name = "Maria Luo" }, From d9e88da0c3fb7cad06a63dfb1ba43dc4088b6eea Mon Sep 17 00:00:00 2001 From: aa10150 Date: Sun, 2 Nov 2025 23:56:53 -0500 Subject: [PATCH 21/44] Unit tests for pick_activity --- src/dailyDecisionPackage/dailyDecision.py | 4 +- tests/tests.py | 125 +++++++++++++++++++++- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index ac62197..ba71efe 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -312,7 +312,7 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: if weather.lower() not in activities_by_weather: accepted = ", ".join(sorted(activities_by_weather.keys())) print(f"Sorry, '{weather}' is not a supported weather type.") - print(f"Please choose from: {accepted}") + print(f"Please choose from: sunny, cloudy, rainy, snowy") print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") return # Valid weather @@ -338,7 +338,7 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: if weather.lower() not in activities_by_weather: accepted = ", ".join(sorted(activities_by_weather.keys())) print(f"Sorry, '{weather}' is not a supported weather type.") - print(f"Please choose from: {accepted}") + print(f"Please choose from: sunny, cloudy, rainy, snowy") print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") return # Invalid energy level diff --git a/tests/tests.py b/tests/tests.py index 15896fc..b04a685 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,5 @@ import pytest -from dailyDecisionPackage.dailyDecision import pick_color +from dailyDecisionPackage.dailyDecision import pick_color, pick_activity # Unit tests for pick_color function class TestPickColor: @@ -108,3 +108,126 @@ def test_valid_mood_invalid_season(self, capsys): assert "Sorry, 'temp' is not a valid season" in captured.out assert "Using just your energetic mood instead, try:" in captured.out assert any(color in captured.out for color in self.energetic_colors) + +# Unit tests for pick_activity function +class TestPickActivity: + weather_activities = { + "sunny": [ + "go for a walk", "read a book at the park", "explore a new part of the city", "go for a run", + "go hiking", "bike around your neighborhood", "go to the beach" + ], + "cloudy": [ + "watch the clouds" + ], + "rainy": [ + "dance in the rain" + ], + "snowy": [ + "make snow angels", "build a snowman", "snowball fight" + ], + "any": [ + "watch a movie or TV", "play a video game", "read a book indoors", "listen to a podcast", + "listen to music", "write a journal entry", "call a friend", "play a board game", + "solve a crossword", "clean your bedroom", "light exercise", "dance", "make a home-cooked meal", + "arts & crafts", "yoga", "go to the gym", "go to the club", "go to a party", "clean the house", + "painting", "go for a drive" + ] + } + energy_activities = { + "low": [ + "watch a movie or TV", "play a video game", "read a book indoors", "read a book at the park", + "listen to a podcast", "listen to music", "write a journal entry", "call a friend", + "play a board game", "solve a crossword", "watch the clouds", "painting" + ], + "medium": [ + "go for a walk", "clean your bedroom", "light exercise", "dance", "dance in the rain", + "make a home-cooked meal", "arts & crafts", "yoga", "make snow angels", "build a snowman", + "go to the beach", "go for a drive" + ], + "high": [ + "go to the gym", "go for a run", "go hiking", "bike around your neighborhood", "go to the club", + "go to a party", "clean the house", "snowball fight", "explore a new part of the city" + ] + } + + all_activities = set() + for activities in energy_activities.values(): + all_activities.update(activities) + + def test_no_arguments(self, capsys): + pick_activity() + captured = capsys.readouterr() + assert "Try this activity: " in captured.out + assert len(captured.out.strip()) > len("Try this activity: ") + + def test_all_supported_weather(self, capsys): + supported_weather = ["sunny", "cloudy", "rainy", "snowy"] + for weather in supported_weather: + pick_activity(weather=weather) + captured = capsys.readouterr() + trimmed_output = captured.out[19:] + assert "Try this activity: " in captured.out + assert len(captured.out.strip()) > len("Try this activity: ") + assert trimmed_output.strip() in ( + (set(self.weather_activities[weather]) | set(self.weather_activities["any"])) + ) + + def test_all_supported_energy(self, capsys): + supported_energy = ["low", "medium", "high"] + for energy in supported_energy: + pick_activity(energy_level=energy) + captured = capsys.readouterr() + trimmed_output = captured.out[19:] + assert "Try this activity: " in captured.out + assert len(captured.out.strip()) > len("Try this activity: ") + assert trimmed_output.strip() in set(self.energy_activities[energy]) + + def test_all_valid_weather_energy_combos(self, capsys): + supported_weather = ["sunny", "cloudy", "rainy", "snowy"] + supported_energy = ["low", "medium", "high"] + for weather in supported_weather: + for energy in supported_energy: + pick_activity(weather=weather, energy_level=energy) + captured = capsys.readouterr() + trimmed_output = captured.out[19:] + assert "Try this activity: " in captured.out + assert len(captured.out.strip()) > len("Try this activity: ") + assert trimmed_output.strip() in ( + (set(self.weather_activities[weather]) | set(self.weather_activities["any"])) + ) + assert trimmed_output.strip() in set(self.energy_activities[energy]) + + def test_invalid_weather(self, capsys): + pick_activity(weather="invalid") + captured = capsys.readouterr() + assert "Sorry, 'invalid' is not a supported weather type." in captured.out + assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out + assert "In the meantime, try this activity: " in captured.out + + def test_invalid_energy(self, capsys): + pick_activity(energy="invalid") + captured = capsys.readouterr() + assert "Sorry, 'invalid' is not a supported energy level." in captured.out + assert "Please choose from: low, medium, high" in captured.out + assert "In the meantime, try this activity: " in captured.out + + def test_invalid_weather_valid_energy(self, capsys): + pick_activity(weather="invalid", energy="low") + captured = capsys.readouterr() + assert "Sorry, 'invalid' is not a supported weather type." in captured.out + assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out + assert "In the meantime, try this activity: " in captured.out + + def test_valid_weather_invalid_energy(self, capsys): + pick_activity(weather="sunny", energy="invalid") + captured = capsys.readouterr() + assert "Sorry, 'invalid' is not a supported energy level." in captured.out + assert "Please choose from: low, medium, high" in captured.out + assert "In the meantime, try this activity: " in captured.out + + def test_invalid_weather_invalid_energy(self, capsys): + pick_activity(weather="invalid", energy="invalid") + captured = capsys.readouterr() + assert "Sorry, 'invalid' is not a supported weather type." in captured.out + assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out + assert "In the meantime, try this activity: " in captured.out \ No newline at end of file From 8ba12366636400a0cd9253bf4605db8613a18c6b Mon Sep 17 00:00:00 2001 From: aa10150 Date: Mon, 3 Nov 2025 00:02:33 -0500 Subject: [PATCH 22/44] Fixes to unit tests --- tests/tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index b04a685..10abd99 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -205,28 +205,28 @@ def test_invalid_weather(self, capsys): assert "In the meantime, try this activity: " in captured.out def test_invalid_energy(self, capsys): - pick_activity(energy="invalid") + pick_activity(energy_level="invalid") captured = capsys.readouterr() assert "Sorry, 'invalid' is not a supported energy level." in captured.out assert "Please choose from: low, medium, high" in captured.out assert "In the meantime, try this activity: " in captured.out def test_invalid_weather_valid_energy(self, capsys): - pick_activity(weather="invalid", energy="low") + pick_activity(weather="invalid", energy_level="low") captured = capsys.readouterr() assert "Sorry, 'invalid' is not a supported weather type." in captured.out assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out assert "In the meantime, try this activity: " in captured.out def test_valid_weather_invalid_energy(self, capsys): - pick_activity(weather="sunny", energy="invalid") + pick_activity(weather="sunny", energy_level="invalid") captured = capsys.readouterr() assert "Sorry, 'invalid' is not a supported energy level." in captured.out assert "Please choose from: low, medium, high" in captured.out assert "In the meantime, try this activity: " in captured.out def test_invalid_weather_invalid_energy(self, capsys): - pick_activity(weather="invalid", energy="invalid") + pick_activity(weather="invalid", energy_level="invalid") captured = capsys.readouterr() assert "Sorry, 'invalid' is not a supported weather type." in captured.out assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out From cc1e6370e7b7bf7ac17c7fffac62930a93d7066b Mon Sep 17 00:00:00 2001 From: aa10150 Date: Mon, 3 Nov 2025 00:08:41 -0500 Subject: [PATCH 23/44] More fixes for pick_activity unit tests --- tests/tests.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index 10abd99..ce5c906 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -165,22 +165,20 @@ def test_all_supported_weather(self, capsys): for weather in supported_weather: pick_activity(weather=weather) captured = capsys.readouterr() - trimmed_output = captured.out[19:] assert "Try this activity: " in captured.out assert len(captured.out.strip()) > len("Try this activity: ") - assert trimmed_output.strip() in ( - (set(self.weather_activities[weather]) | set(self.weather_activities["any"])) - ) + assert any(activity in captured.out for activity in ( + set(self.weather_activities[weather]) | set(self.weather_activities["any"]) + )) def test_all_supported_energy(self, capsys): supported_energy = ["low", "medium", "high"] for energy in supported_energy: pick_activity(energy_level=energy) captured = capsys.readouterr() - trimmed_output = captured.out[19:] assert "Try this activity: " in captured.out assert len(captured.out.strip()) > len("Try this activity: ") - assert trimmed_output.strip() in set(self.energy_activities[energy]) + assert any(activity in captured.out for activity in self.energy_activities[energy]) def test_all_valid_weather_energy_combos(self, capsys): supported_weather = ["sunny", "cloudy", "rainy", "snowy"] @@ -189,13 +187,12 @@ def test_all_valid_weather_energy_combos(self, capsys): for energy in supported_energy: pick_activity(weather=weather, energy_level=energy) captured = capsys.readouterr() - trimmed_output = captured.out[19:] assert "Try this activity: " in captured.out assert len(captured.out.strip()) > len("Try this activity: ") - assert trimmed_output.strip() in ( - (set(self.weather_activities[weather]) | set(self.weather_activities["any"])) - ) - assert trimmed_output.strip() in set(self.energy_activities[energy]) + assert any(activity in captured.out for activity in self.energy_activities[energy]) + assert any(activity in captured.out for activity in ( + set(self.weather_activities[weather]) | set(self.weather_activities["any"]) + )) def test_invalid_weather(self, capsys): pick_activity(weather="invalid") From 886c0115a805b4c61edb12a5e9c607ce0b4243ee Mon Sep 17 00:00:00 2001 From: aa10150 Date: Mon, 3 Nov 2025 00:18:00 -0500 Subject: [PATCH 24/44] Pt 3 fixes for pick_activity unit tests --- src/dailyDecisionPackage/dailyDecision.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index ba71efe..658f0db 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -323,9 +323,8 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: elif weather is None: # Invalid energy level if energy_level.lower() not in activities_by_energy_level: - accepted = ", ".join(sorted(activities_by_energy_level.keys())) print(f"Sorry, '{energy_level}' is not a supported energy level.") - print(f"Please choose from: {accepted}") + print(f"Please choose from: low, medium, high") print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") return # Valid energy level @@ -343,9 +342,8 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: return # Invalid energy level if energy_level.lower() not in activities_by_energy_level: - accepted = ", ".join(sorted(activities_by_energy_level.keys())) print(f"Sorry, '{energy_level}' is not a supported energy level.") - print(f"Please choose from: {accepted}") + print(f"Please choose from: low, medium, high") print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") return # Valid arguments From 0f7ade5e2b52e1d72182d8edf57569a500bfe91d Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Mon, 3 Nov 2025 09:45:44 -0500 Subject: [PATCH 25/44] Change Github Action to only run on pull requests --- .github/workflows/test.yml | 1 - src/dailyDecisionPackage/dailyDecision.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a199fa6..fb0ff18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,5 @@ name: CI / CD on: - push: pull_request: branches: - pipfile-experiment diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 658f0db..aa93c64 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -353,6 +353,3 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: ) print(f"Try this activity: {random.choice(list(validActivities))}") return - - -pick_color("calm", "Summer") From 48f8997939caa3734511a96dfae031aa7b19cd52 Mon Sep 17 00:00:00 2001 From: MariaLuo826 <112397482+MariaLuo826@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:17:40 -0500 Subject: [PATCH 26/44] Update pick_food with expanded food restrictions --- src/dailyDecisionPackage/dailyDecision.py | 58 +++++++++++++++-------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 8a2ac46..3c1bebc 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -6,45 +6,65 @@ def pick_food(dietary_restriction: str = None) -> None: # Foods by restriction foods_by_restriction = { - "kosher": [ - "bagel with lox", "matzo ball soup", "tuna salad", "grilled salmon with potatoes", - "egg salad sandwich", "falafel plate" - ], "halal": [ "chicken biryani", "beef kebab plate", "shawarma bowl", "lentil dal with rice", "falafel wrap", "grilled salmon" ], + "high_protein": [ + "grilled chicken breast with quinoa", "salmon with asparagus", "beef stir-fry", + "lentil salad", "tofu and broccoli bowl", "turkey chili" + ], "jain": [ "vegetable khichdi", "paneer tikka (no onion/garlic)", "sabudana khichdi", "dal dhokli", "vegetable pulao", "coconut curry" ], - "vegetarian": [ - "margherita pizza", "mushroom risotto", "spinach ravioli", "caprese sandwich", - "falafel bowl", "paneer tikka" + "keto": [ + "zucchini noodles with pesto", "grilled salmon with avocado", "cauliflower rice stir-fry", + "bunless burger with cheese and salad", "omelet with spinach and mushrooms", "chicken caesar salad (no croutons)" ], - "vegan": [ - "tofu stir-fry", "chickpea curry", "veggie sushi", "buddha bowl", - "lentil bolognese", "quinoa salad" + "kosher": [ + "bagel with lox", "matzo ball soup", "tuna salad", "grilled salmon with potatoes", + "egg salad sandwich", "falafel plate" + ], + "low_carb": [ + "grilled chicken and veggies", "beef lettuce wraps", "zoodle bolognese", + "egg omelet with avocado", "shrimp and broccoli stir-fry", "cauliflower crust pizza" + ], + "no_dairy": [ + "tom yum soup", "poke bowl", "chicken shawarma wrap (no yogurt sauce)", + "vegan ramen", "tofu curry", "bibimbap (no egg)" + ], + "no_eggs": [ + "pasta primavera", "mushroom risotto", "vegetable stir-fry", "falafel wrap", + "vegan curry", "tofu scramble" ], "no_gluten": [ "rice bowl with chicken", "corn tacos", "pho", "sashimi platter", "thai green curry", "baked sweet potato" ], + "no_nuts": [ + "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", + "rotisserie chicken plate", "tomato soup & grilled cheese" + ], "no_soy": [ "grilled chicken salad", "roasted veggie pasta", "eggplant parm", "mushroom risotto", "omelet with veggies", "lentil soup" ], - "no_nuts": [ - "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", - "rotisserie chicken plate", "tomato soup & grilled cheese" + "paleo": [ + "grilled steak with roasted veggies", "salmon with sweet potato mash", + "zucchini noodles with tomato sauce", "chicken lettuce wraps", "baked cod with olive oil", "fruit and nut bowl" ], - "no_dairy": [ - "tom yum soup", "poke bowl", "chicken shawarma wrap (no yogurt sauce)", - "vegan ramen", "tofu curry", "bibimbap (no egg)" + "pescatarian": [ + "salmon poke bowl", "shrimp tacos", "grilled cod with veggies", "tuna niçoise salad", + "sushi combo", "miso-glazed salmon", "fish and chips (light batter)" ], - "no_eggs": [ - "pasta primavera", "mushroom risotto", "vegetable stir-fry", "falafel wrap", - "vegan curry", "tofu scramble" + "vegan": [ + "tofu stir-fry", "chickpea curry", "veggie sushi", "buddha bowl", + "lentil bolognese", "quinoa salad" + ], + "vegetarian": [ + "margherita pizza", "mushroom risotto", "spinach ravioli", "caprese sandwich", + "falafel bowl", "paneer tikka" ], } From 66d631c378d36c1a6a957846af7281f6c212ad86 Mon Sep 17 00:00:00 2001 From: Coffee859 Date: Mon, 3 Nov 2025 15:47:02 -0500 Subject: [PATCH 27/44] Added pick_clothes function to dailyDecision.py --- src/dailyDecisionPackage/dailyDecision.py | 92 +++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index aa93c64..24d2337 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -1,7 +1,96 @@ # This is where we will write our actual functions for the package import random +def pick_clothes(weather: str = None, mood : str = None) -> None: + clothes_by_weather = { + "sunny": [ + "sports bra", "jersey", "T-shirt", "shorts", "sneakers", "khakis", "jeans", "sandals", "sombrero", "high heels" + ], + "rainy": [ + "poncho", "raincoat", "boots" + ], + "snowy": [ + "fleece jacket", "scarf", "neckerchief", "beanie", "mittens" + ], + "windy": [ + "light jacket", "hoodie", "athletic pants", "earmuffs" + ] + } + clothes_by_mood = { + "casual": [ + "T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals" + ], + "formal": [ + "necktie", "neckerchief", "bowtie", "tuxedo", "dress shoes", "button-up shir/blouse", "khakis", "skirt" + ], + "athletic": [ + "sports bra", "jersey", "cleats", "athletic pants", "headband" + ], + "party": [ + "sequin dress", "high heels", "blazer", "fedora", "rings", "slacks", "halter top" + ], + "beach": [ + "sandals", "bikini", "swimtrunks", "thong", "speedo", "sombrero", "cap", "one-piece suit", "crocs" + ] + } + # build full set of clothes + allClothes = set() + for clothes in clothes_by_weather.values(): + allClothes.update(clothes) + for clothes in clothes_by_mood.values(): + allClothes.update(clothes) + + # No arguments (pick random clothes) + if weather is None and mood is None: + print(f"Try these clothes! They look good on you: {random.choice(list(allClothes))}") + # Only weather, no mood + elif mood is None: + # Invalid weather + if weather.lower() not in clothes_by_weather: + accepted = ", ".join(sorted(clothes_by_weather.keys())) + print(f"Oopsie poopsie! :( '{weather}' isn't a weather!") + print(f"Choose from: sunny, rainy, snowy, windy") + print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + # Valid weather + validClothes = set(clothes_by_weather[weather.lower()]) | set(clothes_by_weather["any"]) + print(f"You chose: '{weather}', so why not wear this bad boy? {random.choice(list(validClothes))}") + # Only mood, no weather + elif weather is None: + # Invalid mood + if mood.lower() not in clothes_by_mood: + print(f"Oopsie poopsie! :( '{mood}' isn't a real mood!") + print(f"Choose from: casual, formal, athletic, party, beach") + print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + # Valid mood + validClothes = set(clothes_by_mood[mood.lower()]) | set(clothes_by_mood["any"]) + print(f"You chose: '{mood}', so why not wear this bad boy? {random.choice(list(validClothes))}") + # Arguments for both weather and mood + else: + # Invalid weather + if weather.lower() not in clothes_by_weather: + print(f"Oopsie poopsie! :( '{weather}' isn't a weather!") + print(f"Choose from: sunny, rainy, snowy, windy") + print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + # Invalid mood + if mood.lower() not in clothes_by_mood: + print(f"Oopsie poopsie! :( '{mood}' isn't a real mood!") + print(f"Choose from: casual, formal, athletic, party, beach") + print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + # Both valid weather and mood + validClothes = list(set(clothes_by_mood[mood.lower()]) & set(clothes_by_weather[weather.lower()])) + if validClothes: + clothes = random.choice(validClothes) + print(f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! {clothes}") + # but they may not have clothes in common + else: + rand_weather = random.choice(clothes_by_weather[weather.lower()]) + rand_mood = random.choice(clothes_by_mood[mood.lower()]) + print(f"Sorry but your weather and mood didn't fit! But for {weather.lower()} weather, try on + {rand_weather}") + print(f"For {mood.lower()} mood, why not give {rand_mood} a shot?") + return + def pick_food(dietary_restriction: str = None) -> None: # Foods by restriction @@ -299,6 +388,8 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: } # create set of all activities allActivities = set() + for activities in activites_by_weather.values(): + allActivities.update(activities) for activities in activities_by_energy_level.values(): allActivities.update(activities) @@ -353,3 +444,4 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: ) print(f"Try this activity: {random.choice(list(validActivities))}") return + From b8883ec6004b55f431c8d36726cc0217d3428a22 Mon Sep 17 00:00:00 2001 From: Coffee859 Date: Mon, 3 Nov 2025 17:00:58 -0500 Subject: [PATCH 28/44] Fixed dailyDecision.py and added TestPickClothes --- src/dailyDecisionPackage/dailyDecision.py | 4 +- tests/tests.py | 120 ++++++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 24d2337..2dd2164 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -53,7 +53,7 @@ def pick_clothes(weather: str = None, mood : str = None) -> None: print(f"Choose from: sunny, rainy, snowy, windy") print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") # Valid weather - validClothes = set(clothes_by_weather[weather.lower()]) | set(clothes_by_weather["any"]) + validClothes = set(clothes_by_weather[weather.lower()])) print(f"You chose: '{weather}', so why not wear this bad boy? {random.choice(list(validClothes))}") # Only mood, no weather elif weather is None: @@ -63,7 +63,7 @@ def pick_clothes(weather: str = None, mood : str = None) -> None: print(f"Choose from: casual, formal, athletic, party, beach") print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") # Valid mood - validClothes = set(clothes_by_mood[mood.lower()]) | set(clothes_by_mood["any"]) + validClothes = set(clothes_by_mood[mood.lower()])) print(f"You chose: '{mood}', so why not wear this bad boy? {random.choice(list(validClothes))}") # Arguments for both weather and mood else: diff --git a/tests/tests.py b/tests/tests.py index ce5c906..3b2db14 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,6 +1,126 @@ import pytest from dailyDecisionPackage.dailyDecision import pick_color, pick_activity + + +# Unit tests for pick_clothes function +class TestPickClothes: + clothes_by_weather = { + "sunny": [ + "sports bra", "jersey", "T-shirt", "shorts", "sneakers", "khakis", "jeans", "sandals", "sombrero", "high + heels" + ], + "rainy": [ + "poncho", "raincoat", "boots" + ], + "snowy": [ + "fleece jacket", "scarf", "neckerchief", "beanie", "mittens" + ], + "windy": [ + "light jacket", "hoodie", "athletic pants", "earmuffs" + ] + } + clothes_by_mood = { + "casual": [ + "T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals" + ], + "formal": [ + "necktie", "neckerchief", "bowtie", "tuxedo", "dress shoes", "button-up shir/blouse", "khakis", "skirt" + ], + "athletic": [ + "sports bra", "jersey", "cleats", "athletic pants", "headband" + ], + "party": [ + "sequin dress", "high heels", "blazer", "fedora", "rings", "slacks", "halter top" + ], + "beach": [ + "sandals", "bikini", "swimtrunks", "thong", "speedo", "sombrero", "cap", "one-piece suit", "crocs" + ] + } + + allClothes = set() + for clothes in clothes_by_weather.values(): + allClothes.update(clothes) + for clothes in clothes_by_mood.values(): + allClothes.update(clothes) + + def test_no_arguments(self, capsys): + pick_clothes() + captured = capsys.readouterr() + assert "Try these clothes! They look good on you: " in captured.out + assert len(captured.out.strip()) > len("Try these clothes! They look good on you: ") + + def test_all_supported_weather(self, capsys): + supported_weather = ["sunny", "rainy", "snowy", "windy"] + for weather in supported_weather: + pick_clothes(weather=weather) + captured = capsys.readouterr() + assert f"You chose: '{weather}', so why not wear this bad boy? " in captured.out + assert len(captured.out.strip()) > len(f"You chose: '{weather}', so why not wear this bad boy? ") + assert any(clothes in captured.out for clothes in self.clothes_by_weather[weather]) + + def test_all_supported_mood(self, capsys): + supported_moods = ["casual", "formal", "athletic", "party", "beach"] + for mood in supported_moods: + pick_activity(mood=mood) + captured = capsys.readouterr() + assert f"You chose: '{mood}', so why not wear this bad boy? " in captured.out + assert len(captured.out.strip()) > len(f"You chose: '{mood}', so why not wear this bad boy? ") + assert any(clothes in captured.out for clothes in self.clothes_by_mood[mood]) + + def test_all_valid_weather_mood_combos(self, capsys): + supported_weather = ["sunny", "rainy", "snowy", "windy"] + supported_moods = ["casual", "formal", "athletic", "party", "beach"] + for weather in supported_weather: + for mood in supported_moods: + pick_clothes(weather=weather, mood=mood) + captured = capsys.readouterr() + assert f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! " in captured.out + assert len(captured.out.strip()) > len(f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! ") + assert any(clothes in captured.out for clothes in self.clothes_by_weather[weather]) + assert any(clothes in captured.out for clothes in + self.clothes_by_mood[mood]) + + def test_invalid_weather(self, capsys): + pick_clothes(weather="invalid") + captured = capsys.readouterr() + assert "Oopsie poopsie! :( 'invalid' isn't a weather!" in captured.out + assert "Choose from: casual, formal, athletic, party, beach" in captured.out + assert "Otherwise, what do you think of these clothes? " in captured.out + + def test_invalid_mood(self, capsys): + pick_activity(energy_level="invalid") + captured = capsys.readouterr() + assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out + assert "Choose from: casual, formal, athletic, party, beach" in captured.out + assert "Otherwise, what do you think of these clothes? " in captured.out + + def test_invalid_weather_valid_mood(self, capsys): + supported_moods = ["casual", "formal", "athletic", "party", "beach"] + for mood in supported_moods: + pick_clothes(weather="invalid",mood=mood) + captured = capsys.readouterr() + assert f"Oopsie poopsie! :( '{weather}' isn't a weather!" in captured.out + assert f"Choose from: sunny, rainy, snowy, windy" in captured.out + assert f"Otherwise, what do you think of these clothes? " in captured.out + + def test_valid_weather_invalid_mood(self, capsys): + supported_weather = ["sunny", "rainy", "snowy", "windy"] + for weather in supported_weather: + pick_clothes(weather=weather, mood="invalid") + captured = capsys.readouterr() + assert f"Oopsie poopsie! :( '{mood}' isn't a real mood!" in captured.out + assert f"Choose from: casual, formal, athletic, party, beach" in captured.out + assert f"Otherwise, what do you think of these clothes?" in captured.out + + def test_both_invalid(self, capsys): + pick_clothes(weather="invalid", energy_level="invalid") + captured = capsys.readouterr() + assert f"Oopsie poopsie! :( '{weather}' isn't a weather!" in captured.out + assert f"Choose from: sunny, rainy, snowy, windy" in captured.out + assert f"Otherwise, what do you think of these clothes? " in captured.out + + # Unit tests for pick_color function class TestPickColor: happy_colors = [ From 5c43d1bdbcbd75b1648729fcd9a4c2bab80293ac Mon Sep 17 00:00:00 2001 From: Coffee859 <71625056+Coffee859@users.noreply.github.com> Date: Mon, 3 Nov 2025 17:05:49 -0500 Subject: [PATCH 29/44] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 22234f9..d6c4446 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Do you ever struggle to make everyday decisions? Whether it's choosing what to e ## Features This package provides a few functions to help with your daily decision making: +- `pick_clothes(weather, mood)` - a function to help you pick clothes based on the weather and mood. - `pick_food(dietary_restriction, cuisine)` - a function to help you pick a food based on your dietary restriction. - `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and the season. - `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and the weather. From 40866765c4159a4d253d5d96bcc8855f5ad0757b Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Mon, 3 Nov 2025 20:10:48 -0500 Subject: [PATCH 30/44] Bug fix --- src/dailyDecisionPackage/dailyDecision.py | 321 +++++++++++++++------- tests/tests.py | 289 ++++++++++++------- 2 files changed, 421 insertions(+), 189 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 2dd2164..824f8af 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -1,37 +1,57 @@ # This is where we will write our actual functions for the package import random -def pick_clothes(weather: str = None, mood : str = None) -> None: + +def pick_clothes(weather: str = None, mood: str = None) -> None: clothes_by_weather = { "sunny": [ - "sports bra", "jersey", "T-shirt", "shorts", "sneakers", "khakis", "jeans", "sandals", "sombrero", "high heels" - ], - "rainy": [ - "poncho", "raincoat", "boots" - ], - "snowy": [ - "fleece jacket", "scarf", "neckerchief", "beanie", "mittens" + "sports bra", + "jersey", + "T-shirt", + "shorts", + "sneakers", + "khakis", + "jeans", + "sandals", + "sombrero", + "high heels", ], - "windy": [ - "light jacket", "hoodie", "athletic pants", "earmuffs" - ] + "rainy": ["poncho", "raincoat", "boots"], + "snowy": ["fleece jacket", "scarf", "neckerchief", "beanie", "mittens"], + "windy": ["light jacket", "hoodie", "athletic pants", "earmuffs"], } clothes_by_mood = { - "casual": [ - "T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals" - ], + "casual": ["T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals"], "formal": [ - "necktie", "neckerchief", "bowtie", "tuxedo", "dress shoes", "button-up shir/blouse", "khakis", "skirt" - ], - "athletic": [ - "sports bra", "jersey", "cleats", "athletic pants", "headband" + "necktie", + "neckerchief", + "bowtie", + "tuxedo", + "dress shoes", + "button-up shirt/blouse", + "khakis", + "skirt", ], + "athletic": ["sports bra", "jersey", "cleats", "athletic pants", "headband"], "party": [ - "sequin dress", "high heels", "blazer", "fedora", "rings", "slacks", "halter top" + "sequin dress", + "high heels", + "blazer", + "fedora", + "rings", + "slacks", + "halter top", ], "beach": [ - "sandals", "bikini", "swimtrunks", "thong", "speedo", "sombrero", "cap", "one-piece suit", "crocs" - ] + "sandals", + "bikini", + "swimtrunks", + "speedo", + "sombrero", + "cap", + "one-piece suit", + "crocs", + ], } # build full set of clothes @@ -43,7 +63,9 @@ def pick_clothes(weather: str = None, mood : str = None) -> None: # No arguments (pick random clothes) if weather is None and mood is None: - print(f"Try these clothes! They look good on you: {random.choice(list(allClothes))}") + print( + f"Try these clothes! They look good on you: {random.choice(list(allClothes))}" + ) # Only weather, no mood elif mood is None: # Invalid weather @@ -51,89 +73,155 @@ def pick_clothes(weather: str = None, mood : str = None) -> None: accepted = ", ".join(sorted(clothes_by_weather.keys())) print(f"Oopsie poopsie! :( '{weather}' isn't a weather!") print(f"Choose from: sunny, rainy, snowy, windy") - print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + print( + f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" + ) + return # Valid weather - validClothes = set(clothes_by_weather[weather.lower()])) - print(f"You chose: '{weather}', so why not wear this bad boy? {random.choice(list(validClothes))}") + validClothes = set(clothes_by_weather[weather.lower()]) + print( + f"You chose: '{weather}', so why not wear this bad boy? {random.choice(list(validClothes))}" + ) # Only mood, no weather elif weather is None: # Invalid mood if mood.lower() not in clothes_by_mood: print(f"Oopsie poopsie! :( '{mood}' isn't a real mood!") print(f"Choose from: casual, formal, athletic, party, beach") - print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + print( + f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" + ) + return # Valid mood - validClothes = set(clothes_by_mood[mood.lower()])) - print(f"You chose: '{mood}', so why not wear this bad boy? {random.choice(list(validClothes))}") + validClothes = set(clothes_by_mood[mood.lower()]) + print( + f"You chose: '{mood}', so why not wear this bad boy? {random.choice(list(validClothes))}" + ) # Arguments for both weather and mood else: # Invalid weather if weather.lower() not in clothes_by_weather: print(f"Oopsie poopsie! :( '{weather}' isn't a weather!") print(f"Choose from: sunny, rainy, snowy, windy") - print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + print( + f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" + ) # Invalid mood if mood.lower() not in clothes_by_mood: print(f"Oopsie poopsie! :( '{mood}' isn't a real mood!") print(f"Choose from: casual, formal, athletic, party, beach") - print(f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}") + print( + f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" + ) + # If either is invalid, return early + if ( + weather.lower() not in clothes_by_weather + or mood.lower() not in clothes_by_mood + ): + return # Both valid weather and mood - validClothes = list(set(clothes_by_mood[mood.lower()]) & set(clothes_by_weather[weather.lower()])) + validClothes = list( + set(clothes_by_mood[mood.lower()]) + & set(clothes_by_weather[weather.lower()]) + ) if validClothes: clothes = random.choice(validClothes) - print(f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! {clothes}") + print( + f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! {clothes}" + ) # but they may not have clothes in common else: rand_weather = random.choice(clothes_by_weather[weather.lower()]) rand_mood = random.choice(clothes_by_mood[mood.lower()]) - print(f"Sorry but your weather and mood didn't fit! But for {weather.lower()} weather, try on - {rand_weather}") + print( + f"Sorry but your weather and mood didn't fit! But for {weather.lower()} weather, try on {rand_weather}" + ) print(f"For {mood.lower()} mood, why not give {rand_mood} a shot?") return - + def pick_food(dietary_restriction: str = None) -> None: # Foods by restriction foods_by_restriction = { "kosher": [ - "bagel with lox", "matzo ball soup", "tuna salad", "grilled salmon with potatoes", - "egg salad sandwich", "falafel plate" + "bagel with lox", + "matzo ball soup", + "tuna salad", + "grilled salmon with potatoes", + "egg salad sandwich", + "falafel plate", ], "halal": [ - "chicken biryani", "beef kebab plate", "shawarma bowl", "lentil dal with rice", - "falafel wrap", "grilled salmon" + "chicken biryani", + "beef kebab plate", + "shawarma bowl", + "lentil dal with rice", + "falafel wrap", + "grilled salmon", ], "jain": [ - "vegetable khichdi", "paneer tikka (no onion/garlic)", "sabudana khichdi", - "dal dhokli", "vegetable pulao", "coconut curry" + "vegetable khichdi", + "paneer tikka (no onion/garlic)", + "sabudana khichdi", + "dal dhokli", + "vegetable pulao", + "coconut curry", ], "vegetarian": [ - "margherita pizza", "mushroom risotto", "spinach ravioli", "caprese sandwich", - "falafel bowl", "paneer tikka" + "margherita pizza", + "mushroom risotto", + "spinach ravioli", + "caprese sandwich", + "falafel bowl", + "paneer tikka", ], "vegan": [ - "tofu stir-fry", "chickpea curry", "veggie sushi", "buddha bowl", - "lentil bolognese", "quinoa salad" + "tofu stir-fry", + "chickpea curry", + "veggie sushi", + "buddha bowl", + "lentil bolognese", + "quinoa salad", ], "no_gluten": [ - "rice bowl with chicken", "corn tacos", "pho", "sashimi platter", - "thai green curry", "baked sweet potato" + "rice bowl with chicken", + "corn tacos", + "pho", + "sashimi platter", + "thai green curry", + "baked sweet potato", ], "no_soy": [ - "grilled chicken salad", "roasted veggie pasta", "eggplant parm", "mushroom risotto", - "omelet with veggies", "lentil soup" + "grilled chicken salad", + "roasted veggie pasta", + "eggplant parm", + "mushroom risotto", + "omelet with veggies", + "lentil soup", ], "no_nuts": [ - "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", - "rotisserie chicken plate", "tomato soup & grilled cheese" + "margherita pizza", + "spaghetti pomodoro", + "fried rice", + "beef tacos", + "rotisserie chicken plate", + "tomato soup & grilled cheese", ], "no_dairy": [ - "tom yum soup", "poke bowl", "chicken shawarma wrap (no yogurt sauce)", - "vegan ramen", "tofu curry", "bibimbap (no egg)" + "tom yum soup", + "poke bowl", + "chicken shawarma wrap (no yogurt sauce)", + "vegan ramen", + "tofu curry", + "bibimbap (no egg)", ], "no_eggs": [ - "pasta primavera", "mushroom risotto", "vegetable stir-fry", "falafel wrap", - "vegan curry", "tofu scramble" + "pasta primavera", + "mushroom risotto", + "vegetable stir-fry", + "falafel wrap", + "vegan curry", + "tofu scramble", ], } @@ -349,48 +437,88 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: # Activities by weather activities_by_weather = { "sunny": [ - "go for a walk", "read a book at the park", "explore a new part of the city", "go for a run", - "go hiking", "bike around your neighborhood", "go to the beach" - ], - "cloudy": [ - "watch the clouds" - ], - "rainy": [ - "dance in the rain" - ], - "snowy": [ - "make snow angels", "build a snowman", "snowball fight" + "go for a walk", + "read a book at the park", + "explore a new part of the city", + "go for a run", + "go hiking", + "bike around your neighborhood", + "go to the beach", ], + "cloudy": ["watch the clouds"], + "rainy": ["dance in the rain"], + "snowy": ["make snow angels", "build a snowman", "snowball fight"], "any": [ - "watch a movie or TV", "play a video game", "read a book indoors", "listen to a podcast", - "listen to music", "write a journal entry", "call a friend", "play a board game", - "solve a crossword", "clean your bedroom", "light exercise", "dance", "make a home-cooked meal", - "arts & crafts", "yoga", "go to the gym", "go to the club", "go to a party", "clean the house", - "painting", "go for a drive" - ] + "watch a movie or TV", + "play a video game", + "read a book indoors", + "listen to a podcast", + "listen to music", + "write a journal entry", + "call a friend", + "play a board game", + "solve a crossword", + "clean your bedroom", + "light exercise", + "dance", + "make a home-cooked meal", + "arts & crafts", + "yoga", + "go to the gym", + "go to the club", + "go to a party", + "clean the house", + "painting", + "go for a drive", + ], } # Activities by energy level activities_by_energy_level = { "low": [ - "watch a movie or TV", "play a video game", "read a book indoors", "read a book at the park", - "listen to a podcast", "listen to music", "write a journal entry", "call a friend", - "play a board game", "solve a crossword", "watch the clouds", "painting" + "watch a movie or TV", + "play a video game", + "read a book indoors", + "read a book at the park", + "listen to a podcast", + "listen to music", + "write a journal entry", + "call a friend", + "play a board game", + "solve a crossword", + "watch the clouds", + "painting", ], "medium": [ - "go for a walk", "clean your bedroom", "light exercise", "dance", "dance in the rain", - "make a home-cooked meal", "arts & crafts", "yoga", "make snow angels", "build a snowman", - "go to the beach", "go for a drive" + "go for a walk", + "clean your bedroom", + "light exercise", + "dance", + "dance in the rain", + "make a home-cooked meal", + "arts & crafts", + "yoga", + "make snow angels", + "build a snowman", + "go to the beach", + "go for a drive", ], "high": [ - "go to the gym", "go for a run", "go hiking", "bike around your neighborhood", "go to the club", - "go to a party", "clean the house", "snowball fight", "explore a new part of the city" - ] + "go to the gym", + "go for a run", + "go hiking", + "bike around your neighborhood", + "go to the club", + "go to a party", + "clean the house", + "snowball fight", + "explore a new part of the city", + ], } # create set of all activities allActivities = set() - for activities in activites_by_weather.values(): + for activities in activities_by_weather.values(): allActivities.update(activities) - for activities in activities_by_energy_level.values(): + for activities in activities_by_energy_level.values(): allActivities.update(activities) # No arguments (pick random activity) @@ -404,10 +532,14 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: accepted = ", ".join(sorted(activities_by_weather.keys())) print(f"Sorry, '{weather}' is not a supported weather type.") print(f"Please choose from: sunny, cloudy, rainy, snowy") - print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + print( + f"In the meantime, try this activity: {random.choice(list(allActivities))}" + ) return # Valid weather - validActivities = set(activities_by_weather[weather.lower()]) | set(activities_by_weather["any"]) + validActivities = set(activities_by_weather[weather.lower()]) | set( + activities_by_weather["any"] + ) print(f"Try this activity: {random.choice(list(validActivities))}") return # No weather argument (energy level only) @@ -416,7 +548,9 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: if energy_level.lower() not in activities_by_energy_level: print(f"Sorry, '{energy_level}' is not a supported energy level.") print(f"Please choose from: low, medium, high") - print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + print( + f"In the meantime, try this activity: {random.choice(list(allActivities))}" + ) return # Valid energy level validActivities = set(activities_by_energy_level[energy_level.lower()]) @@ -429,19 +563,22 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: accepted = ", ".join(sorted(activities_by_weather.keys())) print(f"Sorry, '{weather}' is not a supported weather type.") print(f"Please choose from: sunny, cloudy, rainy, snowy") - print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + print( + f"In the meantime, try this activity: {random.choice(list(allActivities))}" + ) return # Invalid energy level if energy_level.lower() not in activities_by_energy_level: print(f"Sorry, '{energy_level}' is not a supported energy level.") print(f"Please choose from: low, medium, high") - print(f"In the meantime, try this activity: {random.choice(list(allActivities))}") + print( + f"In the meantime, try this activity: {random.choice(list(allActivities))}" + ) return # Valid arguments validActivities = ( - ( set(activities_by_weather[weather.lower()]) | set(activities_by_weather["any"]) ) & - set(activities_by_energy_level[energy_level.lower()]) - ) + set(activities_by_weather[weather.lower()]) + | set(activities_by_weather["any"]) + ) & set(activities_by_energy_level[energy_level.lower()]) print(f"Try this activity: {random.choice(list(validActivities))}") return - diff --git a/tests/tests.py b/tests/tests.py index 3b2db14..110578e 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,41 +1,58 @@ import pytest -from dailyDecisionPackage.dailyDecision import pick_color, pick_activity +from dailyDecisionPackage.dailyDecision import pick_color, pick_activity, pick_clothes - -# Unit tests for pick_clothes function +# Unit tests for pick_clothes function class TestPickClothes: clothes_by_weather = { "sunny": [ - "sports bra", "jersey", "T-shirt", "shorts", "sneakers", "khakis", "jeans", "sandals", "sombrero", "high - heels" - ], - "rainy": [ - "poncho", "raincoat", "boots" + "sports bra", + "jersey", + "T-shirt", + "shorts", + "sneakers", + "khakis", + "jeans", + "sandals", + "sombrero", + "high heels", ], - "snowy": [ - "fleece jacket", "scarf", "neckerchief", "beanie", "mittens" - ], - "windy": [ - "light jacket", "hoodie", "athletic pants", "earmuffs" - ] + "rainy": ["poncho", "raincoat", "boots"], + "snowy": ["fleece jacket", "scarf", "neckerchief", "beanie", "mittens"], + "windy": ["light jacket", "hoodie", "athletic pants", "earmuffs"], } clothes_by_mood = { - "casual": [ - "T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals" - ], + "casual": ["T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals"], "formal": [ - "necktie", "neckerchief", "bowtie", "tuxedo", "dress shoes", "button-up shir/blouse", "khakis", "skirt" - ], - "athletic": [ - "sports bra", "jersey", "cleats", "athletic pants", "headband" + "necktie", + "neckerchief", + "bowtie", + "tuxedo", + "dress shoes", + "button-up shir/blouse", + "khakis", + "skirt", ], + "athletic": ["sports bra", "jersey", "cleats", "athletic pants", "headband"], "party": [ - "sequin dress", "high heels", "blazer", "fedora", "rings", "slacks", "halter top" + "sequin dress", + "high heels", + "blazer", + "fedora", + "rings", + "slacks", + "halter top", ], "beach": [ - "sandals", "bikini", "swimtrunks", "thong", "speedo", "sombrero", "cap", "one-piece suit", "crocs" - ] + "sandals", + "bikini", + "swimtrunks", + "speedo", + "sombrero", + "cap", + "one-piece suit", + "crocs", + ], } allClothes = set() @@ -48,26 +65,41 @@ def test_no_arguments(self, capsys): pick_clothes() captured = capsys.readouterr() assert "Try these clothes! They look good on you: " in captured.out - assert len(captured.out.strip()) > len("Try these clothes! They look good on you: ") + assert len(captured.out.strip()) > len( + "Try these clothes! They look good on you: " + ) def test_all_supported_weather(self, capsys): supported_weather = ["sunny", "rainy", "snowy", "windy"] for weather in supported_weather: pick_clothes(weather=weather) captured = capsys.readouterr() - assert f"You chose: '{weather}', so why not wear this bad boy? " in captured.out - assert len(captured.out.strip()) > len(f"You chose: '{weather}', so why not wear this bad boy? ") - assert any(clothes in captured.out for clothes in self.clothes_by_weather[weather]) + assert ( + f"You chose: '{weather}', so why not wear this bad boy? " + in captured.out + ) + assert len(captured.out.strip()) > len( + f"You chose: '{weather}', so why not wear this bad boy? " + ) + assert any( + clothes in captured.out for clothes in self.clothes_by_weather[weather] + ) def test_all_supported_mood(self, capsys): supported_moods = ["casual", "formal", "athletic", "party", "beach"] for mood in supported_moods: - pick_activity(mood=mood) + pick_clothes(mood=mood) captured = capsys.readouterr() - assert f"You chose: '{mood}', so why not wear this bad boy? " in captured.out - assert len(captured.out.strip()) > len(f"You chose: '{mood}', so why not wear this bad boy? ") - assert any(clothes in captured.out for clothes in self.clothes_by_mood[mood]) - + assert ( + f"You chose: '{mood}', so why not wear this bad boy? " in captured.out + ) + assert len(captured.out.strip()) > len( + f"You chose: '{mood}', so why not wear this bad boy? " + ) + assert any( + clothes in captured.out for clothes in self.clothes_by_mood[mood] + ) + def test_all_valid_weather_mood_combos(self, capsys): supported_weather = ["sunny", "rainy", "snowy", "windy"] supported_moods = ["casual", "formal", "athletic", "party", "beach"] @@ -75,52 +107,60 @@ def test_all_valid_weather_mood_combos(self, capsys): for mood in supported_moods: pick_clothes(weather=weather, mood=mood) captured = capsys.readouterr() - assert f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! " in captured.out - assert len(captured.out.strip()) > len(f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! ") - assert any(clothes in captured.out for clothes in self.clothes_by_weather[weather]) - assert any(clothes in captured.out for clothes in - self.clothes_by_mood[mood]) - + assert ( + "Good choice! For your weather" in captured.out + or "Sorry but your weather and mood didn't fit!" in captured.out + ) + assert any( + clothes in captured.out + for clothes in self.clothes_by_weather[weather] + ) + assert any( + clothes in captured.out for clothes in self.clothes_by_mood[mood] + ) + def test_invalid_weather(self, capsys): pick_clothes(weather="invalid") captured = capsys.readouterr() assert "Oopsie poopsie! :( 'invalid' isn't a weather!" in captured.out - assert "Choose from: casual, formal, athletic, party, beach" in captured.out + assert "Choose from: sunny, rainy, snowy, windy" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out def test_invalid_mood(self, capsys): - pick_activity(energy_level="invalid") + pick_clothes(mood="invalid") captured = capsys.readouterr() assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out assert "Choose from: casual, formal, athletic, party, beach" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out - + def test_invalid_weather_valid_mood(self, capsys): supported_moods = ["casual", "formal", "athletic", "party", "beach"] for mood in supported_moods: - pick_clothes(weather="invalid",mood=mood) + pick_clothes(weather="invalid", mood=mood) captured = capsys.readouterr() - assert f"Oopsie poopsie! :( '{weather}' isn't a weather!" in captured.out - assert f"Choose from: sunny, rainy, snowy, windy" in captured.out - assert f"Otherwise, what do you think of these clothes? " in captured.out + assert "Oopsie poopsie! :( 'invalid' isn't a weather!" in captured.out + assert "Choose from: sunny, rainy, snowy, windy" in captured.out + assert "Otherwise, what do you think of these clothes? " in captured.out def test_valid_weather_invalid_mood(self, capsys): supported_weather = ["sunny", "rainy", "snowy", "windy"] for weather in supported_weather: pick_clothes(weather=weather, mood="invalid") captured = capsys.readouterr() - assert f"Oopsie poopsie! :( '{mood}' isn't a real mood!" in captured.out - assert f"Choose from: casual, formal, athletic, party, beach" in captured.out - assert f"Otherwise, what do you think of these clothes?" in captured.out - + assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out + assert "Choose from: casual, formal, athletic, party, beach" in captured.out + assert "Otherwise, what do you think of these clothes? " in captured.out + def test_both_invalid(self, capsys): - pick_clothes(weather="invalid", energy_level="invalid") + pick_clothes(weather="invalid", mood="invalid") captured = capsys.readouterr() - assert f"Oopsie poopsie! :( '{weather}' isn't a weather!" in captured.out - assert f"Choose from: sunny, rainy, snowy, windy" in captured.out - assert f"Otherwise, what do you think of these clothes? " in captured.out + assert "Oopsie poopsie! :( 'invalid' isn't a weather!" in captured.out + assert "Choose from: sunny, rainy, snowy, windy" in captured.out + assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out + assert "Choose from: casual, formal, athletic, party, beach" in captured.out + assert "Otherwise, what do you think of these clothes? " in captured.out + - # Unit tests for pick_color function class TestPickColor: happy_colors = [ @@ -157,12 +197,13 @@ class TestPickColor: "lime green", "tangerine", ] + def test_no_arguments(self, capsys): pick_color() captured = capsys.readouterr() assert "Here's a random color for you:" in captured.out assert len(captured.out.strip()) > len("Here's a random color for you:") - + def test_all_supported_moods(self, capsys): supported_moods = ["happy", "sad", "calm", "energetic", "angry"] for mood in supported_moods: @@ -229,51 +270,92 @@ def test_valid_mood_invalid_season(self, capsys): assert "Using just your energetic mood instead, try:" in captured.out assert any(color in captured.out for color in self.energetic_colors) + # Unit tests for pick_activity function class TestPickActivity: weather_activities = { "sunny": [ - "go for a walk", "read a book at the park", "explore a new part of the city", "go for a run", - "go hiking", "bike around your neighborhood", "go to the beach" - ], - "cloudy": [ - "watch the clouds" - ], - "rainy": [ - "dance in the rain" - ], - "snowy": [ - "make snow angels", "build a snowman", "snowball fight" + "go for a walk", + "read a book at the park", + "explore a new part of the city", + "go for a run", + "go hiking", + "bike around your neighborhood", + "go to the beach", ], + "cloudy": ["watch the clouds"], + "rainy": ["dance in the rain"], + "snowy": ["make snow angels", "build a snowman", "snowball fight"], "any": [ - "watch a movie or TV", "play a video game", "read a book indoors", "listen to a podcast", - "listen to music", "write a journal entry", "call a friend", "play a board game", - "solve a crossword", "clean your bedroom", "light exercise", "dance", "make a home-cooked meal", - "arts & crafts", "yoga", "go to the gym", "go to the club", "go to a party", "clean the house", - "painting", "go for a drive" - ] + "watch a movie or TV", + "play a video game", + "read a book indoors", + "listen to a podcast", + "listen to music", + "write a journal entry", + "call a friend", + "play a board game", + "solve a crossword", + "clean your bedroom", + "light exercise", + "dance", + "make a home-cooked meal", + "arts & crafts", + "yoga", + "go to the gym", + "go to the club", + "go to a party", + "clean the house", + "painting", + "go for a drive", + ], } energy_activities = { "low": [ - "watch a movie or TV", "play a video game", "read a book indoors", "read a book at the park", - "listen to a podcast", "listen to music", "write a journal entry", "call a friend", - "play a board game", "solve a crossword", "watch the clouds", "painting" + "watch a movie or TV", + "play a video game", + "read a book indoors", + "read a book at the park", + "listen to a podcast", + "listen to music", + "write a journal entry", + "call a friend", + "play a board game", + "solve a crossword", + "watch the clouds", + "painting", ], "medium": [ - "go for a walk", "clean your bedroom", "light exercise", "dance", "dance in the rain", - "make a home-cooked meal", "arts & crafts", "yoga", "make snow angels", "build a snowman", - "go to the beach", "go for a drive" + "go for a walk", + "clean your bedroom", + "light exercise", + "dance", + "dance in the rain", + "make a home-cooked meal", + "arts & crafts", + "yoga", + "make snow angels", + "build a snowman", + "go to the beach", + "go for a drive", ], "high": [ - "go to the gym", "go for a run", "go hiking", "bike around your neighborhood", "go to the club", - "go to a party", "clean the house", "snowball fight", "explore a new part of the city" - ] + "go to the gym", + "go for a run", + "go hiking", + "bike around your neighborhood", + "go to the club", + "go to a party", + "clean the house", + "snowball fight", + "explore a new part of the city", + ], } all_activities = set() - for activities in energy_activities.values(): + for activities in energy_activities.values(): all_activities.update(activities) - + def test_no_arguments(self, capsys): pick_activity() captured = capsys.readouterr() @@ -287,9 +369,13 @@ def test_all_supported_weather(self, capsys): captured = capsys.readouterr() assert "Try this activity: " in captured.out assert len(captured.out.strip()) > len("Try this activity: ") - assert any(activity in captured.out for activity in ( - set(self.weather_activities[weather]) | set(self.weather_activities["any"]) - )) + assert any( + activity in captured.out + for activity in ( + set(self.weather_activities[weather]) + | set(self.weather_activities["any"]) + ) + ) def test_all_supported_energy(self, capsys): supported_energy = ["low", "medium", "high"] @@ -298,8 +384,10 @@ def test_all_supported_energy(self, capsys): captured = capsys.readouterr() assert "Try this activity: " in captured.out assert len(captured.out.strip()) > len("Try this activity: ") - assert any(activity in captured.out for activity in self.energy_activities[energy]) - + assert any( + activity in captured.out for activity in self.energy_activities[energy] + ) + def test_all_valid_weather_energy_combos(self, capsys): supported_weather = ["sunny", "cloudy", "rainy", "snowy"] supported_energy = ["low", "medium", "high"] @@ -309,11 +397,18 @@ def test_all_valid_weather_energy_combos(self, capsys): captured = capsys.readouterr() assert "Try this activity: " in captured.out assert len(captured.out.strip()) > len("Try this activity: ") - assert any(activity in captured.out for activity in self.energy_activities[energy]) - assert any(activity in captured.out for activity in ( - set(self.weather_activities[weather]) | set(self.weather_activities["any"]) - )) - + assert any( + activity in captured.out + for activity in self.energy_activities[energy] + ) + assert any( + activity in captured.out + for activity in ( + set(self.weather_activities[weather]) + | set(self.weather_activities["any"]) + ) + ) + def test_invalid_weather(self, capsys): pick_activity(weather="invalid") captured = capsys.readouterr() @@ -327,7 +422,7 @@ def test_invalid_energy(self, capsys): assert "Sorry, 'invalid' is not a supported energy level." in captured.out assert "Please choose from: low, medium, high" in captured.out assert "In the meantime, try this activity: " in captured.out - + def test_invalid_weather_valid_energy(self, capsys): pick_activity(weather="invalid", energy_level="low") captured = capsys.readouterr() @@ -341,10 +436,10 @@ def test_valid_weather_invalid_energy(self, capsys): assert "Sorry, 'invalid' is not a supported energy level." in captured.out assert "Please choose from: low, medium, high" in captured.out assert "In the meantime, try this activity: " in captured.out - + def test_invalid_weather_invalid_energy(self, capsys): pick_activity(weather="invalid", energy_level="invalid") captured = capsys.readouterr() assert "Sorry, 'invalid' is not a supported weather type." in captured.out assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out - assert "In the meantime, try this activity: " in captured.out \ No newline at end of file + assert "In the meantime, try this activity: " in captured.out From f621ddc531180a4d2ecf1b2418be912ff3d99355 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Mon, 3 Nov 2025 20:18:21 -0500 Subject: [PATCH 31/44] bug fix part 2 --- tests/tests.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index 110578e..701e830 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -29,7 +29,7 @@ class TestPickClothes: "bowtie", "tuxedo", "dress shoes", - "button-up shir/blouse", + "button-up shirt/blouse", "khakis", "skirt", ], @@ -100,24 +100,13 @@ def test_all_supported_mood(self, capsys): clothes in captured.out for clothes in self.clothes_by_mood[mood] ) - def test_all_valid_weather_mood_combos(self, capsys): - supported_weather = ["sunny", "rainy", "snowy", "windy"] - supported_moods = ["casual", "formal", "athletic", "party", "beach"] - for weather in supported_weather: - for mood in supported_moods: - pick_clothes(weather=weather, mood=mood) - captured = capsys.readouterr() - assert ( - "Good choice! For your weather" in captured.out - or "Sorry but your weather and mood didn't fit!" in captured.out - ) - assert any( - clothes in captured.out - for clothes in self.clothes_by_weather[weather] - ) - assert any( - clothes in captured.out for clothes in self.clothes_by_mood[mood] - ) + def test_both_valid_weather_and_mood(self, capsys): + pick_clothes(weather="sunny", mood="casual") + captured = capsys.readouterr() + assert ( + "Good choice! For your weather" in captured.out + or "Sorry but your weather and mood didn't fit!" in captured.out + ) def test_invalid_weather(self, capsys): pick_clothes(weather="invalid") From f746617c06a9575f7494adcc9c3e7ae2428cd604 Mon Sep 17 00:00:00 2001 From: MariaLuo826 <112397482+MariaLuo826@users.noreply.github.com> Date: Mon, 3 Nov 2025 21:20:27 -0500 Subject: [PATCH 32/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22234f9..cf52b45 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This package provides a few functions to help with your daily decision making: - `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and the weather. ## Team members: -[Maria Luo](https://github.com/MariaLuo826) +[Maria Lee](https://github.com/MariaLuo826) [Reece Huey](https://github.com/Coffee859) [Jubilee Tang](https://github.com/MajesticSeagull26) [Anshu Aramandla](https://github.com/aa10150) From f05cbfd992b98c0f4b594c1078fd3a2fd8367a21 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Mon, 3 Nov 2025 22:34:42 -0500 Subject: [PATCH 33/44] Added example program that uses all functions --- examples/exampleUsage.py | 163 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/examples/exampleUsage.py b/examples/exampleUsage.py index f71c317..090a859 100644 --- a/examples/exampleUsage.py +++ b/examples/exampleUsage.py @@ -1 +1,162 @@ -# This is the exampel program where we would demonstrates our package's complete functionality. \ No newline at end of file +from dailyDecisionPackage.dailyDecision import ( + pick_color, + pick_activity, + pick_clothes, + pick_food, +) + +print("=" * 70) +print("DAILY DECISION PACKAGE - EXAMPLE USAGE") +print("=" * 70) +print() + +# PICK COLOR FUNCTION +print("\n" + "=" * 70) +print("1. pick_color(mood, season) - Get color suggestions based on mood and/or season") +print("=" * 70) +print() + +print("Example 1.1: No arguments - random color") +print("-" * 70) +pick_color() +print() + +print("Example 1.2: Only mood provided") +print("-" * 70) +pick_color(mood="happy") +print() + +print("Example 1.3: Only season provided") +print("-" * 70) +pick_color(season="summer") +print() + +print("Example 1.4: Both mood and season provided") +print("-" * 70) +pick_color(mood="happy", season="summer") +print() + +print("Example 1.5: Invalid mood") +print("-" * 70) +pick_color(mood="upset") +print() + +print("Example 1.6: Invalid season") +print("-" * 70) +pick_color(season="test") +print() + +print("Example 1.7: Valid season but invalid mood") +print("-" * 70) +pick_color(season="summer", mood="upset") +print() + +print("Example 1.8: Valid mood but invalid season") +print("-" * 70) +pick_color(season="test", mood="energetic") +print() + +print("Example 1.9: Both invalid") +print("-" * 70) +pick_color(season="test", mood="upset") +print() + +# PICK CLOTHES FUNCTION +print("\n" + "=" * 70) +print( + "2. pick_clothes(weather, mood) - Get clothing suggestions based on weather and/or mood" +) +print("=" * 70) +print() + +print("Example 2.1: No arguments - random clothing") +print("-" * 70) +pick_clothes() +print() + +print("Example 2.2: Only weather provided") +print("-" * 70) +pick_clothes(weather="sunny") +print() + +print("Example 2.3: Only mood provided") +print("-" * 70) +pick_clothes(mood="casual") +print() + +print("Example 2.4: Both weather and mood provided") +print("-" * 70) +pick_clothes(weather="rainy", mood="formal") +print() + +print("Example 2.5: Invalid weather") +print("-" * 70) +pick_clothes(weather="invalid") +print() + +print("Example 2.6: Invalid mood") +print("-" * 70) +pick_clothes(mood="invalid") + + +# PICK FOOD FUNCTION +print("\n" + "=" * 70) +print( + "3. pick_food(dietary_restriction) - Get food suggestions based on dietary restrictions" +) +print("=" * 70) +print() + +print("Example 3.1: No restriction - random food") +print("-" * 70) +pick_food() +print() + +print("Example 3.2: dietary_restriction provided") +print("-" * 70) +pick_food("vegetarian") +print() + +print("Example 3.3: Unsupported restriction") +print("-" * 70) +pick_food("invalid") +print() + +# PICK ACTIVITY FUNCTION +print("\n" + "=" * 70) +print( + "4. pick_activity(weather, energy_level) - Get activity suggestions based on weather and/or energy level" +) +print("=" * 70) +print() + +print("Example 4.1: No arguments - random activity") +print("-" * 70) +pick_activity() +print() + +print("Example 4.2: Only weather provided") +print("-" * 70) +pick_activity(weather="sunny") +print() + +print("Example 4.3: Only energy level provided") +print("-" * 70) +pick_activity(energy_level="high") +print() + +print("Example 4.4: Both weather and energy level provided") +print("-" * 70) +pick_activity(weather="snowy", energy_level="low") +print() + +print("Example 4.5: Invalid weather") +print("-" * 70) +pick_activity(weather="invalid") +print() + +print("Example 4.6: Invalid energy level") +print("-" * 70) +pick_activity(energy_level="invalid") +print() + From c94d5062f154974317798aa0815c5aec18db9fcc Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Mon, 3 Nov 2025 22:49:47 -0500 Subject: [PATCH 34/44] Fix merge issue --- src/dailyDecisionPackage/dailyDecision.py | 108 ++++++++-------------- 1 file changed, 40 insertions(+), 68 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index cc8395a..1283975 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -143,93 +143,65 @@ def pick_clothes(weather: str = None, mood: str = None) -> None: def pick_food(dietary_restriction: str = None) -> None: # Foods by restriction foods_by_restriction = { - "kosher": [ - "bagel with lox", - "matzo ball soup", - "tuna salad", - "grilled salmon with potatoes", - "egg salad sandwich", - "falafel plate", - ], "halal": [ - "chicken biryani", - "beef kebab plate", - "shawarma bowl", - "lentil dal with rice", - "falafel wrap", - "grilled salmon", + "chicken biryani", "beef kebab plate", "shawarma bowl", "lentil dal with rice", + "falafel wrap", "grilled salmon" ], "high_protein": [ "grilled chicken breast with quinoa", "salmon with asparagus", "beef stir-fry", "lentil salad", "tofu and broccoli bowl", "turkey chili" ], "jain": [ - "vegetable khichdi", - "paneer tikka (no onion/garlic)", - "sabudana khichdi", - "dal dhokli", - "vegetable pulao", - "coconut curry", + "vegetable khichdi", "paneer tikka (no onion/garlic)", "sabudana khichdi", + "dal dhokli", "vegetable pulao", "coconut curry" ], - "vegetarian": [ - "margherita pizza", - "mushroom risotto", - "spinach ravioli", - "caprese sandwich", - "falafel bowl", - "paneer tikka", + "keto": [ + "zucchini noodles with pesto", "grilled salmon with avocado", "cauliflower rice stir-fry", + "bunless burger with cheese and salad", "omelet with spinach and mushrooms", "chicken caesar salad (no croutons)" ], - "vegan": [ - "tofu stir-fry", - "chickpea curry", - "veggie sushi", - "buddha bowl", - "lentil bolognese", - "quinoa salad", + "kosher": [ + "bagel with lox", "matzo ball soup", "tuna salad", "grilled salmon with potatoes", + "egg salad sandwich", "falafel plate" + ], + "low_carb": [ + "grilled chicken and veggies", "beef lettuce wraps", "zoodle bolognese", + "egg omelet with avocado", "shrimp and broccoli stir-fry", "cauliflower crust pizza" + ], + "no_dairy": [ + "tom yum soup", "poke bowl", "chicken shawarma wrap (no yogurt sauce)", + "vegan ramen", "tofu curry", "bibimbap (no egg)" + ], + "no_eggs": [ + "pasta primavera", "mushroom risotto", "vegetable stir-fry", "falafel wrap", + "vegan curry", "tofu scramble" ], "no_gluten": [ - "rice bowl with chicken", - "corn tacos", - "pho", - "sashimi platter", - "thai green curry", - "baked sweet potato", + "rice bowl with chicken", "corn tacos", "pho", "sashimi platter", + "thai green curry", "baked sweet potato" ], "no_nuts": [ "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", "rotisserie chicken plate", "tomato soup & grilled cheese" ], "no_soy": [ - "grilled chicken salad", - "roasted veggie pasta", - "eggplant parm", - "mushroom risotto", - "omelet with veggies", - "lentil soup", + "grilled chicken salad", "roasted veggie pasta", "eggplant parm", "mushroom risotto", + "omelet with veggies", "lentil soup" ], - "no_nuts": [ - "margherita pizza", - "spaghetti pomodoro", - "fried rice", - "beef tacos", - "rotisserie chicken plate", - "tomato soup & grilled cheese", + "paleo": [ + "grilled steak with roasted veggies", "salmon with sweet potato mash", + "zucchini noodles with tomato sauce", "chicken lettuce wraps", "baked cod with olive oil", "fruit and nut bowl" ], - "no_dairy": [ - "tom yum soup", - "poke bowl", - "chicken shawarma wrap (no yogurt sauce)", - "vegan ramen", - "tofu curry", - "bibimbap (no egg)", + "pescatarian": [ + "salmon poke bowl", "shrimp tacos", "grilled cod with veggies", "tuna niçoise salad", + "sushi combo", "miso-glazed salmon", "fish and chips (light batter)" ], - "no_eggs": [ - "pasta primavera", - "mushroom risotto", - "vegetable stir-fry", - "falafel wrap", - "vegan curry", - "tofu scramble", + "vegan": [ + "tofu stir-fry", "chickpea curry", "veggie sushi", "buddha bowl", + "lentil bolognese", "quinoa salad" + ], + "vegetarian": [ + "margherita pizza", "mushroom risotto", "spinach ravioli", "caprese sandwich", + "falafel bowl", "paneer tikka" ], } @@ -254,7 +226,7 @@ def pick_food(dietary_restriction: str = None) -> None: choice = random.choice(foods_by_restriction[restriction]) print(f"For a {restriction} diet, you could try: {choice}") - + def pick_color(mood: str = None, season: str = None) -> None: winter = [ From 8a0ce7a51b11d52f89903a1cd46fae6909610d7b Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 08:36:23 -0500 Subject: [PATCH 35/44] Test if package can be built and uploaded --- .github/workflows/test.yml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb0ff18..c3caca5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,21 +24,21 @@ jobs: pipenv install pytest pipenv --venv pipenv run python -m pytest tests/tests.py -v -# deliver: -# needs: [build] -# runs-on: ubuntu-latest -# if: github.ref == 'refs/heads/pipfile-experiment' -# timeout-minutes: 5 -# steps: -# - uses: actions/checkout@v4 -# - name: Install Python, pipenv and Pipfile packages -# uses: kojoru/prepare-pipenv@v1 -# - name: Build package -# run: | -# pipenv install build -# pipenv run python -m build . -# - name: Publish to PyPI test server -# uses: pypa/gh-action-pypi-publish@release/v1 -# with: -# password: ${{ secrets.TEST_PYPI_API_TOKEN }} -# repository-url: https://test.pypi.org/legacy/ + deliver: + needs: [build] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/pipfile-experiment' + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - name: Install Python, pipenv and Pipfile packages + uses: kojoru/prepare-pipenv@v1 + - name: Build package + run: | + pipenv install build + pipenv run python -m build . + - name: Publish to PyPI test server + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository-url: https://test.pypi.org/legacy/ From 2f08978af07b91e4a8377a5cd4c6b08c6cdad6d9 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 08:39:17 -0500 Subject: [PATCH 36/44] Remove github.ref --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3caca5..e959e2b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,6 @@ jobs: deliver: needs: [build] runs-on: ubuntu-latest - if: github.ref == 'refs/heads/pipfile-experiment' timeout-minutes: 5 steps: - uses: actions/checkout@v4 From eb0f2502fa2b0fad9db0a979d30f5ca5419c0718 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 08:49:30 -0500 Subject: [PATCH 37/44] Remove email from toml file and add deliver check in workflow --- .github/workflows/test.yml | 2 +- pyproject.toml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e959e2b..fa7344a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,4 +40,4 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository-url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ diff --git a/pyproject.toml b/pyproject.toml index c50faea..805506d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,11 +5,12 @@ build-backend = "setuptools.build_meta" [project] name = "dailyDecisionPackage" description = "A lighthearted Python package that helps you make everyday decisions." -version = "0.1.1" +version = "0.1.0" authors = [ - { name = "Jubilee Tang", email = "wt2187@nyu.edu" }, - { name = "Maria Luo" }, + { name = "Jubilee Tang" }, + { name = "Maria Lee" }, { name = "Reece Huey" }, + { name = "Anshu Aramandla"} ] license = "GPL-3.0-or-later" license-files = ["LICENSE"] From 2b569dcf19c074181dfa8c57f20f023968ef03bc Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 08:51:18 -0500 Subject: [PATCH 38/44] Fix version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 805506d..f19ea86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "dailyDecisionPackage" description = "A lighthearted Python package that helps you make everyday decisions." -version = "0.1.0" +version = "0.1.2" authors = [ { name = "Jubilee Tang" }, { name = "Maria Lee" }, From 7675363e41a5322a60c9a75073f64eaf27243dbe Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 09:22:37 -0500 Subject: [PATCH 39/44] Update README; fix variable names in exampleUsage.py, dailyDecision.py, and test.py --- README.md | 11 +++-- examples/exampleUsage.py | 14 +++---- src/dailyDecisionPackage/__init__.py | 1 - src/dailyDecisionPackage/dailyDecision.py | 49 +++++++++++------------ tests/tests.py | 48 +++++++++++----------- 5 files changed, 63 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 1fc4190..217f8fb 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,17 @@ ## Overview Do you ever struggle to make everyday decisions? Whether it's choosing what to eat for lunch, picking an outfit color, or deciding what music to listen to, sometimes the smallest choices can feel overwhelming. DailyDecisions is here to help! This Python package takes the stress out of life's minor decisions by providing randomized suggestions when you need them most. +## Installation +1. Install the package using pipenv: `pipenv install -i https://pypi.org/simple/ dailyDecisionPackage` +2. Activate the virtual environment: `pipenv shell` +3. Create a Python program file that imports the package, e.g. `from dailyDecisionPackage import dailyDecision` + ## Features This package provides a few functions to help with your daily decision making: -- `pick_clothes(weather, mood)` - a function to help you pick clothes based on the weather and mood. +- `pick_clothes(weather, occasion)` - a function to help you pick clothes based on the weather and/or occasion. - `pick_food(dietary_restriction, cuisine)` - a function to help you pick a food based on your dietary restriction. -- `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and the season. -- `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and the weather. +- `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and/or the season. +- `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and/or the weather. ## Team members: [Maria Lee](https://github.com/MariaLuo826) diff --git a/examples/exampleUsage.py b/examples/exampleUsage.py index 090a859..51daac3 100644 --- a/examples/exampleUsage.py +++ b/examples/exampleUsage.py @@ -64,7 +64,7 @@ # PICK CLOTHES FUNCTION print("\n" + "=" * 70) print( - "2. pick_clothes(weather, mood) - Get clothing suggestions based on weather and/or mood" + "2. pick_clothes(weather, occasion) - Get clothing suggestions based on weather and/or occasion" ) print("=" * 70) print() @@ -79,14 +79,14 @@ pick_clothes(weather="sunny") print() -print("Example 2.3: Only mood provided") +print("Example 2.3: Only occasion provided") print("-" * 70) -pick_clothes(mood="casual") +pick_clothes(occasion="casual") print() -print("Example 2.4: Both weather and mood provided") +print("Example 2.4: Both weather and occasion provided") print("-" * 70) -pick_clothes(weather="rainy", mood="formal") +pick_clothes(weather="rainy", occasion="formal") print() print("Example 2.5: Invalid weather") @@ -94,9 +94,9 @@ pick_clothes(weather="invalid") print() -print("Example 2.6: Invalid mood") +print("Example 2.6: Invalid occasion") print("-" * 70) -pick_clothes(mood="invalid") +pick_clothes(occasion="invalid") # PICK FOOD FUNCTION diff --git a/src/dailyDecisionPackage/__init__.py b/src/dailyDecisionPackage/__init__.py index ba9fefe..e69de29 100644 --- a/src/dailyDecisionPackage/__init__.py +++ b/src/dailyDecisionPackage/__init__.py @@ -1 +0,0 @@ -# Ignore for now; we will import functions from decisionMaking.py later \ No newline at end of file diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 880dee9..ddfd5ed 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -2,7 +2,7 @@ import random -def pick_clothes(weather: str = None, mood: str = None) -> None: +def pick_clothes(weather: str = None, occasion: str = None) -> None: clothes_by_weather = { "sunny": [ "sports bra", @@ -20,7 +20,7 @@ def pick_clothes(weather: str = None, mood: str = None) -> None: "snowy": ["fleece jacket", "scarf", "neckerchief", "beanie", "mittens"], "windy": ["light jacket", "hoodie", "athletic pants", "earmuffs"], } - clothes_by_mood = { + clothes_by_occasion = { "casual": ["T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals"], "formal": [ "necktie", @@ -58,19 +58,18 @@ def pick_clothes(weather: str = None, mood: str = None) -> None: allClothes = set() for clothes in clothes_by_weather.values(): allClothes.update(clothes) - for clothes in clothes_by_mood.values(): + for clothes in clothes_by_occasion.values(): allClothes.update(clothes) # No arguments (pick random clothes) - if weather is None and mood is None: + if weather is None and occasion is None: print( f"Try these clothes! They look good on you: {random.choice(list(allClothes))}" ) - # Only weather, no mood - elif mood is None: + # Only weather, no occasion + elif occasion is None: # Invalid weather if weather.lower() not in clothes_by_weather: - accepted = ", ".join(sorted(clothes_by_weather.keys())) print(f"Oopsie poopsie! :( '{weather}' isn't a weather!") print(f"Choose from: sunny, rainy, snowy, windy") print( @@ -82,22 +81,22 @@ def pick_clothes(weather: str = None, mood: str = None) -> None: print( f"You chose: '{weather}', so why not wear this bad boy? {random.choice(list(validClothes))}" ) - # Only mood, no weather + # Only occasion, no weather elif weather is None: - # Invalid mood - if mood.lower() not in clothes_by_mood: - print(f"Oopsie poopsie! :( '{mood}' isn't a real mood!") + # Invalid occasion + if occasion.lower() not in clothes_by_occasion: + print(f"Oopsie poopsie! :( '{occasion}' isn't a real occasion!") print(f"Choose from: casual, formal, athletic, party, beach") print( f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" ) return - # Valid mood - validClothes = set(clothes_by_mood[mood.lower()]) + # Valid occasion + validClothes = set(clothes_by_occasion[occasion.lower()]) print( - f"You chose: '{mood}', so why not wear this bad boy? {random.choice(list(validClothes))}" + f"You chose: '{occasion}', so why not wear this bad boy? {random.choice(list(validClothes))}" ) - # Arguments for both weather and mood + # Arguments for both weather and occasion else: # Invalid weather if weather.lower() not in clothes_by_weather: @@ -106,9 +105,9 @@ def pick_clothes(weather: str = None, mood: str = None) -> None: print( f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" ) - # Invalid mood - if mood.lower() not in clothes_by_mood: - print(f"Oopsie poopsie! :( '{mood}' isn't a real mood!") + # Invalid occasion + if occasion.lower() not in clothes_by_occasion: + print(f"Oopsie poopsie! :( '{occasion}' isn't a real occasion!") print(f"Choose from: casual, formal, athletic, party, beach") print( f"Otherwise, what do you think of these clothes? {random.choice(list(allClothes))}" @@ -116,27 +115,27 @@ def pick_clothes(weather: str = None, mood: str = None) -> None: # If either is invalid, return early if ( weather.lower() not in clothes_by_weather - or mood.lower() not in clothes_by_mood + or occasion.lower() not in clothes_by_occasion ): return - # Both valid weather and mood + # Both valid weather and occasion validClothes = list( - set(clothes_by_mood[mood.lower()]) + set(clothes_by_occasion[occasion.lower()]) & set(clothes_by_weather[weather.lower()]) ) if validClothes: clothes = random.choice(validClothes) print( - f"Good choice! For your weather {weather} and mood {mood}, try these clothes out! {clothes}" + f"Good choice! For your weather {weather} and occasion {occasion}, try these clothes out! {clothes}" ) # but they may not have clothes in common else: rand_weather = random.choice(clothes_by_weather[weather.lower()]) - rand_mood = random.choice(clothes_by_mood[mood.lower()]) + rand_occasion = random.choice(clothes_by_occasion[occasion.lower()]) print( - f"Sorry but your weather and mood didn't fit! But for {weather.lower()} weather, try on {rand_weather}" + f"Sorry but your weather and occasion didn't fit! But for {weather.lower()} weather, try on {rand_weather}" ) - print(f"For {mood.lower()} mood, why not give {rand_mood} a shot?") + print(f"For {occasion.lower()} occasion, why not give {rand_occasion} a shot?") return diff --git a/tests/tests.py b/tests/tests.py index 701e830..1010adc 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -21,7 +21,7 @@ class TestPickClothes: "snowy": ["fleece jacket", "scarf", "neckerchief", "beanie", "mittens"], "windy": ["light jacket", "hoodie", "athletic pants", "earmuffs"], } - clothes_by_mood = { + clothes_by_occasion = { "casual": ["T-shirt", "shorts", "sneakers", "hoodie", "jeans", "sandals"], "formal": [ "necktie", @@ -58,7 +58,7 @@ class TestPickClothes: allClothes = set() for clothes in clothes_by_weather.values(): allClothes.update(clothes) - for clothes in clothes_by_mood.values(): + for clothes in clothes_by_occasion.values(): allClothes.update(clothes) def test_no_arguments(self, capsys): @@ -85,27 +85,27 @@ def test_all_supported_weather(self, capsys): clothes in captured.out for clothes in self.clothes_by_weather[weather] ) - def test_all_supported_mood(self, capsys): - supported_moods = ["casual", "formal", "athletic", "party", "beach"] - for mood in supported_moods: - pick_clothes(mood=mood) + def test_all_supported_occasion(self, capsys): + supported_occasions = ["casual", "formal", "athletic", "party", "beach"] + for occasion in supported_occasions: + pick_clothes(occasion=occasion) captured = capsys.readouterr() assert ( - f"You chose: '{mood}', so why not wear this bad boy? " in captured.out + f"You chose: '{occasion}', so why not wear this bad boy? " in captured.out ) assert len(captured.out.strip()) > len( - f"You chose: '{mood}', so why not wear this bad boy? " + f"You chose: '{occasion}', so why not wear this bad boy? " ) assert any( - clothes in captured.out for clothes in self.clothes_by_mood[mood] + clothes in captured.out for clothes in self.clothes_by_occasion[occasion] ) - def test_both_valid_weather_and_mood(self, capsys): - pick_clothes(weather="sunny", mood="casual") + def test_both_valid_weather_and_occasion(self, capsys): + pick_clothes(weather="sunny", occasion="casual") captured = capsys.readouterr() assert ( "Good choice! For your weather" in captured.out - or "Sorry but your weather and mood didn't fit!" in captured.out + or "Sorry but your weather and occasion didn't fit!" in captured.out ) def test_invalid_weather(self, capsys): @@ -115,37 +115,37 @@ def test_invalid_weather(self, capsys): assert "Choose from: sunny, rainy, snowy, windy" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out - def test_invalid_mood(self, capsys): - pick_clothes(mood="invalid") + def test_invalid_occasion(self, capsys): + pick_clothes(occasion="invalid") captured = capsys.readouterr() - assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out + assert "Oopsie poopsie! :( 'invalid' isn't a real occasion!" in captured.out assert "Choose from: casual, formal, athletic, party, beach" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out - def test_invalid_weather_valid_mood(self, capsys): - supported_moods = ["casual", "formal", "athletic", "party", "beach"] - for mood in supported_moods: - pick_clothes(weather="invalid", mood=mood) + def test_invalid_weather_valid_occasion(self, capsys): + supported_occasions = ["casual", "formal", "athletic", "party", "beach"] + for occasion in supported_occasions: + pick_clothes(weather="invalid", occasion=occasion) captured = capsys.readouterr() assert "Oopsie poopsie! :( 'invalid' isn't a weather!" in captured.out assert "Choose from: sunny, rainy, snowy, windy" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out - def test_valid_weather_invalid_mood(self, capsys): + def test_valid_weather_invalid_occasion(self, capsys): supported_weather = ["sunny", "rainy", "snowy", "windy"] for weather in supported_weather: - pick_clothes(weather=weather, mood="invalid") + pick_clothes(weather=weather, occasion="invalid") captured = capsys.readouterr() - assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out + assert "Oopsie poopsie! :( 'invalid' isn't a real occasion!" in captured.out assert "Choose from: casual, formal, athletic, party, beach" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out def test_both_invalid(self, capsys): - pick_clothes(weather="invalid", mood="invalid") + pick_clothes(weather="invalid", occasion="invalid") captured = capsys.readouterr() assert "Oopsie poopsie! :( 'invalid' isn't a weather!" in captured.out assert "Choose from: sunny, rainy, snowy, windy" in captured.out - assert "Oopsie poopsie! :( 'invalid' isn't a real mood!" in captured.out + assert "Oopsie poopsie! :( 'invalid' isn't a real occasion!" in captured.out assert "Choose from: casual, formal, athletic, party, beach" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out From eb722a7b1560ef210f10e8b6ea5e2332c6c56a78 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 10:15:08 -0500 Subject: [PATCH 40/44] Update README to include usage; update exampleUsage.py to match the import syntax in README --- README.md | 96 +++++++++++++++++++++++++++++++++++++--- examples/exampleUsage.py | 55 +++++++++++------------ 2 files changed, 116 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 217f8fb..cf897fa 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,108 @@ [![CI / CD](https://github.com/swe-students-fall2025/3-python-package-team_aurora/actions/workflows/test.yml/badge.svg)](https://github.com/swe-students-fall2025/3-python-package-team_aurora/actions/workflows/test.yml) + # Python Package Exercise - Daily Decision Helper + ## Overview + Do you ever struggle to make everyday decisions? Whether it's choosing what to eat for lunch, picking an outfit color, or deciding what music to listen to, sometimes the smallest choices can feel overwhelming. DailyDecisions is here to help! This Python package takes the stress out of life's minor decisions by providing randomized suggestions when you need them most. ## Installation + 1. Install the package using pipenv: `pipenv install -i https://pypi.org/simple/ dailyDecisionPackage` 2. Activate the virtual environment: `pipenv shell` 3. Create a Python program file that imports the package, e.g. `from dailyDecisionPackage import dailyDecision` ## Features + This package provides a few functions to help with your daily decision making: -- `pick_clothes(weather, occasion)` - a function to help you pick clothes based on the weather and/or occasion. -- `pick_food(dietary_restriction, cuisine)` - a function to help you pick a food based on your dietary restriction. -- `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and/or the season. -- `pick_activity(energy_level, weather)` - a function to help you pick an acitvity to do based on your energy level and/or the weather. + +- `pick_clothes(weather, occasion)` - a function to help you pick clothes based on the weather and/or occasion. +- `pick_food(dietary_restriction)` - a function to help you pick a food based on your dietary restriction. +- `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and/or the season. +- `pick_activity(weather, energy_level)` - a function to help you pick an activity to do based on your energy level and/or the weather. + +## Usage +### Import the Package +`from dailyDecisionPackage import dailyDecision` + +### Function Documentation +`dailyDecision.pick_clothes(weather, occasion)` +Suggests clothing items based on weather conditions and/or the occasion. + +**Parameters**: +- `weather` (optional): The weather condition - "sunny", "rainy", "snowy", or "windy" +- `occasion` (optional): The occasion or dress code - "casual", "formal", "athletic", "party", or "beach" + +**Behavior**: +- No arguments: Returns a random clothing item from all available options +- Only weather: Returns clothing appropriate for that weather +- Only occasion: Returns clothing appropriate for that occasion +- Both valid arguments: Returns clothing that fits both criteria (or suggests separate items if no match exists) +- Invalid input: Displays valid options and suggests a random item from all available options +----- +`dailyDecision.pick_food(dietary_restriction)` +Suggests food options based on dietary restrictions. + +**Parameters**: +- `dietary_restriction` (optional): Your dietary need - "halal", "high_protein", "jain", "keto", "kosher", "low_carb", "no_dairy", "no_eggs", "no_gluten", "no_nuts", "no_soy", "paleo", "pescatarian", "vegan", or "vegetarian" + +**Behavior**: +- No argument: Returns a random food from all available options +- Valid restriction: Returns food that meets the specified dietary restriction +- Invalid restriction: Displays valid options and suggests a random food from all available options +----- +`pick_color(mood, season)` +Suggests colors based on your mood and/or the current season. + +**Parameters**: +- `mood` (optional): Your emotional state - "happy", "sad", "calm", "energetic", or "angry" +- `season` (optional): The current season - "winter", "fall", "summer", or "spring" + +**Behavior**: +- No arguments: Returns a random color from all available options +- Only mood: Returns a color matching that mood +- Only season: Returns a color matching that season +- Both valid arguments: Returns a color that fits both (or suggests separate colors if no perfect match) +- Both arguments, but only mood is valid: Displays valid options for seasons and suggests a random color matching the mood +- Both arguments, but only season is valid: Displays valid options for moods and suggests a random color matching the season +- Invalid input: Displays valid options and suggests a random color from all available options +----- +`dailyDecision.pick_activity(weather, energy_level)` +Suggests activities based on weather conditions and/or your energy level. + +**Parameters**: +- `weather` (optional): The weather condition - "sunny", "cloudy", "rainy", or "snowy" +- `energy_level` (optional): Your current energy - "low", "medium", or "high" + +**Behavior**: +- No arguments: Returns a random activity from all available options +- Only weather: Returns activities suitable for that weather +- Only energy level: Returns activities matching your energy level +- Both arguments: Returns activities that fit both criteria +- Invalid input: Displays valid options and suggests a random activity from all available options + +### Example +For a complete example program that demonstrates all four functions with various parameter combinations, see [exampleUsage.py](./examples/exampleUsage.py). +To run the example: `python exampleUsage.py` + +## Contributing +Want to contribute to this project? Here's how to get started: +1. **Clone the repository:** + `https://github.com/swe-students-fall2025/3-python-package-team_aurora.git` +2. **Install pipenv** (if not already installed): `pip install pipenv` +3. **Install dependencies:** `pipenv install --dev` +4. **Activate the virtual environment:** `pipenv shell` + +### Unit tests +Run the unit tests using pytest: `pipenv run pytest tests/tests.py` + +### Building the Package + +To build the package locally: `pipenv run python -m build` ## Team members: + [Maria Lee](https://github.com/MariaLuo826) [Reece Huey](https://github.com/Coffee859) [Jubilee Tang](https://github.com/MajesticSeagull26) -[Anshu Aramandla](https://github.com/aa10150) +[Anshu Aramandla](https://github.com/aa10150) diff --git a/examples/exampleUsage.py b/examples/exampleUsage.py index 51daac3..4a6dd4c 100644 --- a/examples/exampleUsage.py +++ b/examples/exampleUsage.py @@ -1,9 +1,4 @@ -from dailyDecisionPackage.dailyDecision import ( - pick_color, - pick_activity, - pick_clothes, - pick_food, -) +from dailyDecisionPackage import dailyDecision print("=" * 70) print("DAILY DECISION PACKAGE - EXAMPLE USAGE") @@ -18,47 +13,47 @@ print("Example 1.1: No arguments - random color") print("-" * 70) -pick_color() +dailyDecision.pick_color() print() print("Example 1.2: Only mood provided") print("-" * 70) -pick_color(mood="happy") +dailyDecision.pick_color(mood="happy") print() print("Example 1.3: Only season provided") print("-" * 70) -pick_color(season="summer") +dailyDecision.pick_color(season="summer") print() print("Example 1.4: Both mood and season provided") print("-" * 70) -pick_color(mood="happy", season="summer") +dailyDecision.pick_color(mood="happy", season="summer") print() print("Example 1.5: Invalid mood") print("-" * 70) -pick_color(mood="upset") +dailyDecision.pick_color(mood="upset") print() print("Example 1.6: Invalid season") print("-" * 70) -pick_color(season="test") +dailyDecision.pick_color(season="test") print() print("Example 1.7: Valid season but invalid mood") print("-" * 70) -pick_color(season="summer", mood="upset") +dailyDecision.pick_color(season="summer", mood="upset") print() print("Example 1.8: Valid mood but invalid season") print("-" * 70) -pick_color(season="test", mood="energetic") +dailyDecision.pick_color(season="test", mood="energetic") print() print("Example 1.9: Both invalid") print("-" * 70) -pick_color(season="test", mood="upset") +dailyDecision.pick_color(season="test", mood="upset") print() # PICK CLOTHES FUNCTION @@ -71,32 +66,32 @@ print("Example 2.1: No arguments - random clothing") print("-" * 70) -pick_clothes() +dailyDecision.pick_clothes() print() print("Example 2.2: Only weather provided") print("-" * 70) -pick_clothes(weather="sunny") +dailyDecision.pick_clothes(weather="sunny") print() print("Example 2.3: Only occasion provided") print("-" * 70) -pick_clothes(occasion="casual") +dailyDecision.pick_clothes(occasion="casual") print() print("Example 2.4: Both weather and occasion provided") print("-" * 70) -pick_clothes(weather="rainy", occasion="formal") +dailyDecision.pick_clothes(weather="rainy", occasion="formal") print() print("Example 2.5: Invalid weather") print("-" * 70) -pick_clothes(weather="invalid") +dailyDecision.pick_clothes(weather="invalid") print() print("Example 2.6: Invalid occasion") print("-" * 70) -pick_clothes(occasion="invalid") +dailyDecision.pick_clothes(occasion="invalid") # PICK FOOD FUNCTION @@ -109,17 +104,17 @@ print("Example 3.1: No restriction - random food") print("-" * 70) -pick_food() +dailyDecision.pick_food() print() print("Example 3.2: dietary_restriction provided") print("-" * 70) -pick_food("vegetarian") +dailyDecision.pick_food("vegetarian") print() print("Example 3.3: Unsupported restriction") print("-" * 70) -pick_food("invalid") +dailyDecision.pick_food("invalid") print() # PICK ACTIVITY FUNCTION @@ -132,31 +127,31 @@ print("Example 4.1: No arguments - random activity") print("-" * 70) -pick_activity() +dailyDecision.pick_activity() print() print("Example 4.2: Only weather provided") print("-" * 70) -pick_activity(weather="sunny") +dailyDecision.pick_activity(weather="sunny") print() print("Example 4.3: Only energy level provided") print("-" * 70) -pick_activity(energy_level="high") +dailyDecision.pick_activity(energy_level="high") print() print("Example 4.4: Both weather and energy level provided") print("-" * 70) -pick_activity(weather="snowy", energy_level="low") +dailyDecision.pick_activity(weather="snowy", energy_level="low") print() print("Example 4.5: Invalid weather") print("-" * 70) -pick_activity(weather="invalid") +dailyDecision.pick_activity(weather="invalid") print() print("Example 4.6: Invalid energy level") print("-" * 70) -pick_activity(energy_level="invalid") +dailyDecision.pick_activity(energy_level="invalid") print() From d7bf87252d0f1b0b33b3cdb3580177d651332760 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 10:16:40 -0500 Subject: [PATCH 41/44] Fix issue where pick_activity could potentially return nothing --- src/dailyDecisionPackage/dailyDecision.py | 153 ++++++++++++++++------ 1 file changed, 116 insertions(+), 37 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index ddfd5ed..4a6b9cb 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -135,7 +135,9 @@ def pick_clothes(weather: str = None, occasion: str = None) -> None: print( f"Sorry but your weather and occasion didn't fit! But for {weather.lower()} weather, try on {rand_weather}" ) - print(f"For {occasion.lower()} occasion, why not give {rand_occasion} a shot?") + print( + f"For {occasion.lower()} occasion, why not give {rand_occasion} a shot?" + ) return @@ -143,72 +145,141 @@ def pick_food(dietary_restriction: str = None) -> None: # Foods by restriction foods_by_restriction = { "halal": [ - "chicken biryani", "beef kebab plate", "shawarma bowl", "lentil dal with rice", - "falafel wrap", "grilled salmon" + "chicken biryani", + "beef kebab plate", + "shawarma bowl", + "lentil dal with rice", + "falafel wrap", + "grilled salmon", ], "high_protein": [ - "grilled chicken breast with quinoa", "salmon with asparagus", "beef stir-fry", - "lentil salad", "tofu and broccoli bowl", "turkey chili" + "grilled chicken breast with quinoa", + "salmon with asparagus", + "beef stir-fry", + "lentil salad", + "tofu and broccoli bowl", + "turkey chili", ], "high_protein": [ - "grilled chicken breast with quinoa", "salmon with asparagus", "beef stir-fry", - "lentil salad", "tofu and broccoli bowl", "turkey chili" + "grilled chicken breast with quinoa", + "salmon with asparagus", + "beef stir-fry", + "lentil salad", + "tofu and broccoli bowl", + "turkey chili", ], "jain": [ - "vegetable khichdi", "paneer tikka (no onion/garlic)", "sabudana khichdi", - "dal dhokli", "vegetable pulao", "coconut curry" + "vegetable khichdi", + "paneer tikka (no onion/garlic)", + "sabudana khichdi", + "dal dhokli", + "vegetable pulao", + "coconut curry", ], "keto": [ - "zucchini noodles with pesto", "grilled salmon with avocado", "cauliflower rice stir-fry", - "bunless burger with cheese and salad", "omelet with spinach and mushrooms", "chicken caesar salad (no croutons)" + "zucchini noodles with pesto", + "grilled salmon with avocado", + "cauliflower rice stir-fry", + "bunless burger with cheese and salad", + "omelet with spinach and mushrooms", + "chicken caesar salad (no croutons)", ], "kosher": [ - "bagel with lox", "matzo ball soup", "tuna salad", "grilled salmon with potatoes", - "egg salad sandwich", "falafel plate" + "bagel with lox", + "matzo ball soup", + "tuna salad", + "grilled salmon with potatoes", + "egg salad sandwich", + "falafel plate", ], "low_carb": [ - "grilled chicken and veggies", "beef lettuce wraps", "zoodle bolognese", - "egg omelet with avocado", "shrimp and broccoli stir-fry", "cauliflower crust pizza" + "grilled chicken and veggies", + "beef lettuce wraps", + "zoodle bolognese", + "egg omelet with avocado", + "shrimp and broccoli stir-fry", + "cauliflower crust pizza", ], "no_dairy": [ - "tom yum soup", "poke bowl", "chicken shawarma wrap (no yogurt sauce)", - "vegan ramen", "tofu curry", "bibimbap (no egg)" + "tom yum soup", + "poke bowl", + "chicken shawarma wrap (no yogurt sauce)", + "vegan ramen", + "tofu curry", + "bibimbap (no egg)", ], "no_eggs": [ - "pasta primavera", "mushroom risotto", "vegetable stir-fry", "falafel wrap", - "vegan curry", "tofu scramble" + "pasta primavera", + "mushroom risotto", + "vegetable stir-fry", + "falafel wrap", + "vegan curry", + "tofu scramble", ], "no_gluten": [ - "rice bowl with chicken", "corn tacos", "pho", "sashimi platter", - "thai green curry", "baked sweet potato" + "rice bowl with chicken", + "corn tacos", + "pho", + "sashimi platter", + "thai green curry", + "baked sweet potato", ], "no_nuts": [ - "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", - "rotisserie chicken plate", "tomato soup & grilled cheese" + "margherita pizza", + "spaghetti pomodoro", + "fried rice", + "beef tacos", + "rotisserie chicken plate", + "tomato soup & grilled cheese", ], "no_nuts": [ - "margherita pizza", "spaghetti pomodoro", "fried rice", "beef tacos", - "rotisserie chicken plate", "tomato soup & grilled cheese" + "margherita pizza", + "spaghetti pomodoro", + "fried rice", + "beef tacos", + "rotisserie chicken plate", + "tomato soup & grilled cheese", ], "no_soy": [ - "grilled chicken salad", "roasted veggie pasta", "eggplant parm", "mushroom risotto", - "omelet with veggies", "lentil soup" + "grilled chicken salad", + "roasted veggie pasta", + "eggplant parm", + "mushroom risotto", + "omelet with veggies", + "lentil soup", ], "paleo": [ - "grilled steak with roasted veggies", "salmon with sweet potato mash", - "zucchini noodles with tomato sauce", "chicken lettuce wraps", "baked cod with olive oil", "fruit and nut bowl" + "grilled steak with roasted veggies", + "salmon with sweet potato mash", + "zucchini noodles with tomato sauce", + "chicken lettuce wraps", + "baked cod with olive oil", + "fruit and nut bowl", ], "pescatarian": [ - "salmon poke bowl", "shrimp tacos", "grilled cod with veggies", "tuna niçoise salad", - "sushi combo", "miso-glazed salmon", "fish and chips (light batter)" + "salmon poke bowl", + "shrimp tacos", + "grilled cod with veggies", + "tuna niçoise salad", + "sushi combo", + "miso-glazed salmon", + "fish and chips (light batter)", ], "vegan": [ - "tofu stir-fry", "chickpea curry", "veggie sushi", "buddha bowl", - "lentil bolognese", "quinoa salad" + "tofu stir-fry", + "chickpea curry", + "veggie sushi", + "buddha bowl", + "lentil bolognese", + "quinoa salad", ], "vegetarian": [ - "margherita pizza", "mushroom risotto", "spinach ravioli", "caprese sandwich", - "falafel bowl", "paneer tikka" + "margherita pizza", + "mushroom risotto", + "spinach ravioli", + "caprese sandwich", + "falafel bowl", + "paneer tikka", ], } @@ -233,7 +304,7 @@ def pick_food(dietary_restriction: str = None) -> None: choice = random.choice(foods_by_restriction[restriction]) print(f"For a {restriction} diet, you could try: {choice}") - + def pick_color(mood: str = None, season: str = None) -> None: winter = [ @@ -563,9 +634,17 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: ) return # Valid arguments + # Valid arguments validActivities = ( set(activities_by_weather[weather.lower()]) | set(activities_by_weather["any"]) ) & set(activities_by_energy_level[energy_level.lower()]) - print(f"Try this activity: {random.choice(list(validActivities))}") + + if validActivities: + print(f"Try this activity: {random.choice(list(validActivities))}") + else: + # No perfect match - suggest activities from default list + print( + f"Sorry, no perfect match! But for now try: {random.choice(list(allActivities))}" + ) return From 7b56856a57455c27dd3ced0aa8bb2b980a267264 Mon Sep 17 00:00:00 2001 From: MariaLuo826 <112397482+MariaLuo826@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:47:57 -0500 Subject: [PATCH 42/44] Implemented TestPickFood and changes to pick_food --- src/dailyDecisionPackage/dailyDecision.py | 10 ++ tests/tests.py | 168 +++++++++++++++++++++- 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 4a6b9cb..6548a56 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -295,12 +295,22 @@ def pick_food(dietary_restriction: str = None) -> None: restriction = dietary_restriction.strip().lower() + aliases_any = { + "any", "anything", "whatever", "no", "none", + "no restriction", "normal", "idk", "anything works" + } + if restriction in aliases_any: + print(f"How about: {random.choice(list(allFoods))}") + return + + if restriction not in foods_by_restriction: accepted = ", ".join(sorted(foods_by_restriction.keys())) print(f"Sorry, '{restriction}' is not a supported restriction.") print(f"Please choose from: {accepted}") print(f"In the meantime, try: {random.choice(list(allFoods))}") return + choice = random.choice(foods_by_restriction[restriction]) print(f"For a {restriction} diet, you could try: {choice}") diff --git a/tests/tests.py b/tests/tests.py index 1010adc..3390182 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,5 @@ import pytest -from dailyDecisionPackage.dailyDecision import pick_color, pick_activity, pick_clothes +from dailyDecisionPackage.dailyDecision import pick_color, pick_activity, pick_clothes, pick_food # Unit tests for pick_clothes function @@ -149,6 +149,172 @@ def test_both_invalid(self, capsys): assert "Choose from: casual, formal, athletic, party, beach" in captured.out assert "Otherwise, what do you think of these clothes? " in captured.out +class TestPickFood: + foods_by_restriction = { + "halal": [ + "chicken biryani", + "beef kebab plate", + "shawarma bowl", + "lentil dal with rice", + "falafel wrap", + "grilled salmon", + ], + "high_protein": [ + "grilled chicken breast with quinoa", + "salmon with asparagus", + "beef stir-fry", + "lentil salad", + "tofu and broccoli bowl", + "turkey chili", + ], + "high_protein": [ + "grilled chicken breast with quinoa", + "salmon with asparagus", + "beef stir-fry", + "lentil salad", + "tofu and broccoli bowl", + "turkey chili", + ], + "jain": [ + "vegetable khichdi", + "paneer tikka (no onion/garlic)", + "sabudana khichdi", + "dal dhokli", + "vegetable pulao", + "coconut curry", + ], + "keto": [ + "zucchini noodles with pesto", + "grilled salmon with avocado", + "cauliflower rice stir-fry", + "bunless burger with cheese and salad", + "omelet with spinach and mushrooms", + "chicken caesar salad (no croutons)", + ], + "kosher": [ + "bagel with lox", + "matzo ball soup", + "tuna salad", + "grilled salmon with potatoes", + "egg salad sandwich", + "falafel plate", + ], + "low_carb": [ + "grilled chicken and veggies", + "beef lettuce wraps", + "zoodle bolognese", + "egg omelet with avocado", + "shrimp and broccoli stir-fry", + "cauliflower crust pizza", + ], + "no_dairy": [ + "tom yum soup", + "poke bowl", + "chicken shawarma wrap (no yogurt sauce)", + "vegan ramen", + "tofu curry", + "bibimbap (no egg)", + ], + "no_eggs": [ + "pasta primavera", + "mushroom risotto", + "vegetable stir-fry", + "falafel wrap", + "vegan curry", + "tofu scramble", + ], + "no_gluten": [ + "rice bowl with chicken", + "corn tacos", + "pho", + "sashimi platter", + "thai green curry", + "baked sweet potato", + ], + "no_nuts": [ + "margherita pizza", + "spaghetti pomodoro", + "fried rice", + "beef tacos", + "rotisserie chicken plate", + "tomato soup & grilled cheese", + ], + "no_nuts": [ + "margherita pizza", + "spaghetti pomodoro", + "fried rice", + "beef tacos", + "rotisserie chicken plate", + "tomato soup & grilled cheese", + ], + "no_soy": [ + "grilled chicken salad", + "roasted veggie pasta", + "eggplant parm", + "mushroom risotto", + "omelet with veggies", + "lentil soup", + ], + "paleo": [ + "grilled steak with roasted veggies", + "salmon with sweet potato mash", + "zucchini noodles with tomato sauce", + "chicken lettuce wraps", + "baked cod with olive oil", + "fruit and nut bowl", + ], + "pescatarian": [ + "salmon poke bowl", + "shrimp tacos", + "grilled cod with veggies", + "tuna niçoise salad", + "sushi combo", + "miso-glazed salmon", + "fish and chips (light batter)", + ], + "vegan": [ + "tofu stir-fry", + "chickpea curry", + "veggie sushi", + "buddha bowl", + "lentil bolognese", + "quinoa salad", + ], + "vegetarian": [ + "margherita pizza", + "mushroom risotto", + "spinach ravioli", + "caprese sandwich", + "falafel bowl", + "paneer tikka", + ], + } + def test_default_pick(self, capsys): + pick_food() + out = capsys.readouterr().out.lower() + assert "how about:" in out + + def test_valid_restriction(self, capsys): + pick_food("vegan") + out = capsys.readouterr().out.lower() + assert "for a vegan diet" in out + assert "try" in out + + def test_invalid_restriction(self, capsys): + pick_food("carnivore") + out = capsys.readouterr().out.lower() + assert "sorry" in out or "not a supported restriction" in out + assert "choose from" in out + + def test_case_insensitive(self, capsys): + pick_food("VeGeTaRiAn") + out = capsys.readouterr().out.lower() + assert "for a vegetarian diet" in out + + def test_alias_anything(self, capsys): + pick_food("anything") + out = capsys.readouterr().out.lower() + assert "how about:" in out # Unit tests for pick_color function class TestPickColor: From d04c2daf5ae5edf8a52dee22aca7801e1928cf17 Mon Sep 17 00:00:00 2001 From: MajesticSeagull26 Date: Tue, 4 Nov 2025 16:15:05 -0500 Subject: [PATCH 43/44] Update installation instructions --- README.md | 118 +++++++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index cf897fa..147ddeb 100644 --- a/README.md +++ b/README.md @@ -8,82 +8,91 @@ Do you ever struggle to make everyday decisions? Whether it's choosing what to e ## Installation -1. Install the package using pipenv: `pipenv install -i https://pypi.org/simple/ dailyDecisionPackage` +1. Install the package using `pipenv`: `pipenv install -i https://pypi.org/simple/dailyDecisionPackage` 2. Activate the virtual environment: `pipenv shell` -3. Create a Python program file that imports the package, e.g. `from dailyDecisionPackage import dailyDecision` +3. Create a Python program file that imports the package and uses it, e.g.: + ``` + from dailyDecisionPackage import dailyDecision + + # This would print out the randomly suggested color + dailyDecision.pick_color() + ``` +4. Run the program: `python filename.py` +5. Exit the virutal environment: `exit` ## Features This package provides a few functions to help with your daily decision making: -- `pick_clothes(weather, occasion)` - a function to help you pick clothes based on the weather and/or occasion. -- `pick_food(dietary_restriction)` - a function to help you pick a food based on your dietary restriction. -- `pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and/or the season. -- `pick_activity(weather, energy_level)` - a function to help you pick an activity to do based on your energy level and/or the weather. +- `dailyDecision.pick_clothes(weather, occasion)` - a function to help you pick clothes based on the weather and/or occasion. +- `dailyDecision.pick_food(dietary_restriction)` - a function to help you pick a food based on your dietary restriction. +- `dailyDecision.pick_color(mood, season)` - a function to help you pick a color (of clothing) based on your mood and/or the season. +- `dailyDecision.pick_activity(weather, energy_level)` - a function to help you pick an activity to do based on your energy level and/or the weather. ## Usage ### Import the Package `from dailyDecisionPackage import dailyDecision` ### Function Documentation -`dailyDecision.pick_clothes(weather, occasion)` +1. `dailyDecision.pick_clothes(weather, occasion)` Suggests clothing items based on weather conditions and/or the occasion. -**Parameters**: -- `weather` (optional): The weather condition - "sunny", "rainy", "snowy", or "windy" -- `occasion` (optional): The occasion or dress code - "casual", "formal", "athletic", "party", or "beach" - -**Behavior**: -- No arguments: Returns a random clothing item from all available options -- Only weather: Returns clothing appropriate for that weather -- Only occasion: Returns clothing appropriate for that occasion -- Both valid arguments: Returns clothing that fits both criteria (or suggests separate items if no match exists) -- Invalid input: Displays valid options and suggests a random item from all available options ------ -`dailyDecision.pick_food(dietary_restriction)` -Suggests food options based on dietary restrictions. - -**Parameters**: -- `dietary_restriction` (optional): Your dietary need - "halal", "high_protein", "jain", "keto", "kosher", "low_carb", "no_dairy", "no_eggs", "no_gluten", "no_nuts", "no_soy", "paleo", "pescatarian", "vegan", or "vegetarian" - -**Behavior**: -- No argument: Returns a random food from all available options -- Valid restriction: Returns food that meets the specified dietary restriction -- Invalid restriction: Displays valid options and suggests a random food from all available options ------ -`pick_color(mood, season)` + **Parameters**: + - `weather` (optional): The weather condition - "sunny", "rainy", "snowy", or "windy" + - `occasion` (optional): The occasion or dress code - "casual", "formal", "athletic", "party", or "beach" + + **Behavior**: + - No arguments: Returns a random clothing item from all available options + - Only weather: Returns clothing appropriate for that weather + - Only occasion: Returns clothing appropriate for that occasion + - Both valid arguments: Returns clothing that fits both criteria (or suggests separate items if no match exists) + - Invalid input: Displays valid options and suggests a random item from all available options + + +2. `dailyDecision.pick_food(dietary_restriction)` +Suggests food options based on dietary restrictions. + + **Parameters**: + - `dietary_restriction` (optional): Your dietary need - "halal", "high_protein", "jain", "keto", "kosher", "low_carb", "no_dairy", "no_eggs", "no_gluten", "no_nuts", "no_soy", "paleo", "pescatarian", "vegan", or "vegetarian" + + **Behavior**: + - No argument: Returns a random food from all available options + - Valid restriction: Returns food that meets the specified dietary restriction + - Invalid restriction: Displays valid options and suggests a random food from all available options + +3. `dailyDecision.pick_color(mood, season)` Suggests colors based on your mood and/or the current season. -**Parameters**: -- `mood` (optional): Your emotional state - "happy", "sad", "calm", "energetic", or "angry" -- `season` (optional): The current season - "winter", "fall", "summer", or "spring" - -**Behavior**: -- No arguments: Returns a random color from all available options -- Only mood: Returns a color matching that mood -- Only season: Returns a color matching that season -- Both valid arguments: Returns a color that fits both (or suggests separate colors if no perfect match) -- Both arguments, but only mood is valid: Displays valid options for seasons and suggests a random color matching the mood -- Both arguments, but only season is valid: Displays valid options for moods and suggests a random color matching the season -- Invalid input: Displays valid options and suggests a random color from all available options ------ -`dailyDecision.pick_activity(weather, energy_level)` + **Parameters**: + - `mood` (optional): Your emotional state - "happy", "sad", "calm", "energetic", or "angry" + - `season` (optional): The current season - "winter", "fall", "summer", or "spring" + + **Behavior**: + - No arguments: Returns a random color from all available options + - Only mood: Returns a color matching that mood + - Only season: Returns a color matching that season + - Both valid arguments: Returns a color that fits both (or suggests separate colors if no perfect match) + - Both arguments, but only mood is valid: Displays valid options for seasons and suggests a random color matching the mood + - Both arguments, but only season is valid: Displays valid options for moods and suggests a random color matching the season + - Invalid input: Displays valid options and suggests a random color from all available options + +4. `dailyDecision.pick_activity(weather, energy_level)` Suggests activities based on weather conditions and/or your energy level. -**Parameters**: -- `weather` (optional): The weather condition - "sunny", "cloudy", "rainy", or "snowy" -- `energy_level` (optional): Your current energy - "low", "medium", or "high" + **Parameters**: + - `weather` (optional): The weather condition - "sunny", "cloudy", "rainy", or "snowy" + - `energy_level` (optional): Your current energy - "low", "medium", or "high" -**Behavior**: -- No arguments: Returns a random activity from all available options -- Only weather: Returns activities suitable for that weather -- Only energy level: Returns activities matching your energy level -- Both arguments: Returns activities that fit both criteria -- Invalid input: Displays valid options and suggests a random activity from all available options + **Behavior**: + - No arguments: Returns a random activity from all available options + - Only weather: Returns activities suitable for that weather + - Only energy level: Returns activities matching your energy level + - Both arguments: Returns activities that fit both criteria + - Invalid input: Displays valid options and suggests a random activity from all available options ### Example For a complete example program that demonstrates all four functions with various parameter combinations, see [exampleUsage.py](./examples/exampleUsage.py). -To run the example: `python exampleUsage.py` +To run the example from root: `python examples/exampleUsage.py` ## Contributing Want to contribute to this project? Here's how to get started: @@ -97,7 +106,6 @@ Want to contribute to this project? Here's how to get started: Run the unit tests using pytest: `pipenv run pytest tests/tests.py` ### Building the Package - To build the package locally: `pipenv run python -m build` ## Team members: From 81669b78500cf374edde05bbecd6a5af272b1952 Mon Sep 17 00:00:00 2001 From: nateisnataliehan Date: Tue, 4 Nov 2025 21:52:40 -0500 Subject: [PATCH 44/44] Add pick_music function with unit test --- src/dailyDecisionPackage/dailyDecision.py | 296 ++++++++++++++++++- tests/tests.py | 328 +++++++++++++++++++++- 2 files changed, 622 insertions(+), 2 deletions(-) diff --git a/src/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py index 6548a56..5bc0529 100644 --- a/src/dailyDecisionPackage/dailyDecision.py +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -1,6 +1,6 @@ # This is where we will write our actual functions for the package import random - +import re def pick_clothes(weather: str = None, occasion: str = None) -> None: clothes_by_weather = { @@ -658,3 +658,297 @@ def pick_activity(weather: str = None, energy_level: str = None) -> None: f"Sorry, no perfect match! But for now try: {random.choice(list(allActivities))}" ) return + +def pick_music(prompt: str = None) -> None: + # Songs by prompt + songs_by_mood = { + "happy": [ + "Happy - Pharrell Williams", + "Walking on Sunshine - Katrina & The Waves", + "Shut Up and Dance - WALK THE MOON", + "Good as Hell - Lizzo", + "Uptown Funk - Mark Ronson ft. Bruno Mars", + "I Gotta Feeling - The Black Eyed Peas", + "Best Day of My Life - American Authors", + "Can’t Stop The Feeling! - Justin Timberlake", + ], + "sad": [ + "Someone Like You - Adele", + "Fix You - Coldplay", + "Skinny Love - Bon Iver", + "The Night We Met - Lord Huron", + "All I Want - Kodaline", + "Happier - Ed Sheeran", + "When The Party's Over - Billie Eilish", + "Let Her Go - Passenger", + ], + "calm": [ + "Holocene - Bon Iver", + "Bloom - The Paper Kites", + "Budapest - George Ezra", + "Photograph - Ed Sheeran", + "Lost in Japan - Shawn Mendes", + "Rivers and Roads - The Head and the Heart", + "Banana Pancakes - Jack Johnson", + "Ocean Eyes - Billie Eilish", + ], + "focused": [ + "Weightless - Marconi Union", + "Experience - Ludovico Einaudi", + "Sunset Lover - Petit Biscuit", + "Intro - The xx", + "Comptine d’un autre été - Yann Tiersen", + "We Move Lightly - Dustin O’Halloran", + "Midnight - Lane 8", + "Night Owl - Galimatias", + ], + "angry": [ + "Smells Like Teen Spirit - Nirvana", + "In the End - Linkin Park", + "Killing In The Name - Rage Against The Machine", + "Enter Sandman - Metallica", + "Duality - Slipknot", + "Hail to the King - Avenged Sevenfold", + "Papercut - Linkin Park", + "Break Stuff - Limp Bizkit", + ], + "romantic": [ + "All of Me - John Legend", + "Perfect - Ed Sheeran", + "Just The Way You Are - Bruno Mars", + "Make You Feel My Love - Adele", + "Stay With Me - Sam Smith", + "Yellow - Coldplay", + "Say You Won’t Let Go - James Arthur", + "Die For You - The Weeknd", + ], + "nostalgic": [ + "Wonderwall - Oasis", + "Mr. Brightside - The Killers", + "Iris - Goo Goo Dolls", + "Chasing Cars - Snow Patrol", + "Hey There Delilah - Plain White T’s", + "Viva La Vida - Coldplay", + "Stacy’s Mom - Fountains of Wayne", + "Seven Nation Army - The White Stripes", + ], + } + + songs_by_activity = { + "study": [ + "Experience - Ludovico Einaudi", + "River Flows in You - Yiruma", + "Sunset Lover - Petit Biscuit", + "Night Owl - Galimatias", + "Weightless - Marconi Union", + "Intro - The xx", + "We Move Lightly - Dustin O’Halloran", + "Gymnopédie No.1 - Erik Satie", + ], + "workout": [ + "Stronger - Kanye West", + "Lose Yourself - Eminem", + "Can’t Hold Us - Macklemore & Ryan Lewis", + "Don’t Start Now - Dua Lipa", + "Eye of the Tiger - Survivor", + "Till I Collapse - Eminem", + "Believer - Imagine Dragons", + "Remember the Name - Fort Minor", + ], + "commute": [ + "Budapest - George Ezra", + "Riptide - Vance Joy", + "Viva La Vida - Coldplay", + "Paris - The Chainsmokers", + "Feel It Still - Portugal. The Man", + "Blinding Lights - The Weeknd", + "Pocket Full of Sunshine - Natasha Bedingfield", + "Electric Feel - MGMT", + ], + "party": [ + "Uptown Funk - Mark Ronson ft. Bruno Mars", + "Levitating - Dua Lipa", + "One Kiss - Calvin Harris & Dua Lipa", + "Hey Ya! - OutKast", + "Turn Down for What - DJ Snake & Lil Jon", + "Starboy - The Weeknd", + "I Like It - Cardi B", + "Low - Flo Rida", + ], + "relax": [ + "Better Together - Jack Johnson", + "Holocene - Bon Iver", + "Banana Pancakes - Jack Johnson", + "Bloom - The Paper Kites", + "I’m Yours - Jason Mraz", + "Budapest - George Ezra", + "Ocean Eyes - Billie Eilish", + "Sunflower - Rex Orange County", + ], + "focus": [ + "Experience - Ludovico Einaudi", + "Midnight - Lane 8", + "Open Eye Signal - Jon Hopkins", + "Saturn - Sleeping at Last", + "Prelude in E Minor - Chopin", + "Outro - M83", + "We Move Lightly - Dustin O’Halloran", + "Sunset Lover - Petit Biscuit", + ], + "drive": [ + "Midnight City - M83", + "Blinding Lights - The Weeknd", + "Shut Up and Drive - Rihanna", + "Ride - Twenty One Pilots", + "On the Road Again - Willie Nelson", + "Take Me Out - Franz Ferdinand", + "Go Your Own Way - Fleetwood Mac", + "Feel Good Inc. - Gorillaz", + ], + "cook": [ + "Put Your Records On - Corinne Bailey Rae", + "Sunday Morning - Maroon 5", + "Mariposa - Peach Tree Rascals", + "Budapest - George Ezra", + "Brown Eyed Girl - Van Morrison", + "Best Part - Daniel Caesar ft. H.E.R.", + "I’m Yours - Jason Mraz", + "Dreams - Fleetwood Mac", + ], + } + + songs_by_rhythm = { + "fast": [ + "Don’t Start Now - Dua Lipa", + "Levitating - Dua Lipa", + "On Top of the World - Imagine Dragons", + "Blinding Lights - The Weeknd", + "Can’t Hold Us - Macklemore & Ryan Lewis", + "Don’t Stop Me Now - Queen", + "We Found Love - Rihanna", + "Titanium - David Guetta ft. Sia", + ], + "mid": [ + "Viva La Vida - Coldplay", + "Counting Stars - OneRepublic", + "Shut Up and Dance - WALK THE MOON", + "Riptide - Vance Joy", + "Feel It Still - Portugal. The Man", + "Send Me On My Way - Rusted Root", + "Electric Feel - MGMT", + "Stolen Dance - Milky Chance", + ], + "slow": [ + "All of Me - John Legend", + "Let Her Go - Passenger", + "Skinny Love - Bon Iver", + "Stay With Me - Sam Smith", + "River Flows in You - Yiruma", + "Holocene - Bon Iver", + "Make You Feel My Love - Adele", + "Fix You - Coldplay", + ], + "chill": [ + "Sunset Lover - Petit Biscuit", + "Night Owl - Galimatias", + "Lovely - Billie Eilish & Khalid", + "Lost in Japan - Shawn Mendes", + "Beyond - Leon Bridges", + "Talk - Khalid", + "Put It All on Me - Ed Sheeran", + "Warm - Majid Jordan", + ], + } + + # Build full set of all songs + allSongs = set() + for lst in list(songs_by_mood.values()) + list(songs_by_activity.values()) + list(songs_by_rhythm.values()): + allSongs.update(lst) + + # No prompts given then pick from all + if prompt is None: + print(f"How about: {random.choice(list(allSongs))}") + return + + text = prompt.strip().lower() + aliases_any = {"any", "anything", "whatever", "no", "none", "idk", "anything works"} + if text in aliases_any: + print(f"How about: {random.choice(list(allSongs))}") + return + + # Disassemble the prompts into set of single prompt + tokens = re.findall(r"[a-zA-Z]+", text) + + mood_keys = set(songs_by_mood.keys()) + activity_keys = set(songs_by_activity.keys()) + rhythm_keys = set(songs_by_rhythm.keys()) + + # Check the keywords + found_moods = [t for t in tokens if t in mood_keys] + found_activities = [t for t in tokens if t in activity_keys] + found_rhythms = [t for t in tokens if t in rhythm_keys] + + # When keywords don't match + if not (found_moods or found_activities or found_rhythms): + accepted = ( + "moods: " + ", ".join(sorted(mood_keys)) + "; " + + "activities: "+ ", ".join(sorted(activity_keys)) + "; " + + "rhythms: " + ", ".join(sorted(rhythm_keys)) + ) + print(f"Sorry, we couldn't recognize any keywords from: '{text}'.") + print(f"Please include one of these keywords: {accepted}") + print(f"But we've picked something for you to try: {random.choice(list(allSongs))}") + return + + # Collect possible songs based on all recognized keyword sets + candidate_sets = [] + + if found_moods: + mood_union = set() + for m in found_moods: + mood_union.update(songs_by_mood[m]) + candidate_sets.append(mood_union) + + if found_activities: + act_union = set() + for a in found_activities: + act_union.update(songs_by_activity[a]) + candidate_sets.append(act_union) + + if found_rhythms: + rhythm_union = set() + for s in found_rhythms: + rhythm_union.update(songs_by_rhythm[s]) + candidate_sets.append(rhythm_union) + + # Try to find songs that satisfy ALL categories + if candidate_sets: + inter = set.intersection(*candidate_sets) if len(candidate_sets) > 1 else candidate_sets[0] + + # If found perfect match across all categories + if inter: + picked = random.choice(list(inter)) + summary_parts = [] + if found_moods: + summary_parts.append("/".join(found_moods)) + if found_activities: + summary_parts.append("/".join(found_activities)) + if found_rhythms: + summary_parts.append("/".join(found_rhythms)) + summary = " & ".join(summary_parts) + print(f"Perfect match for {summary}. Try: {picked}") + return + + # If no intersection then give one suggestion per category + suggestions = [] + if found_moods: + suggestions.append(("mood", random.choice(list(set().union(*[set(songs_by_mood[m]) for m in found_moods]))))) + if found_activities: + suggestions.append(("activity", random.choice(list(set().union(*[set(songs_by_activity[a]) for a in found_activities]))))) + if found_rhythms: + suggestions.append(("rhythm", random.choice(list(set().union(*[set(songs_by_rhythm[s]) for s in found_rhythms]))))) + + print("No perfect match between your keywords. Here are the tailored picks:") + for k, v in suggestions: + print(f"- For {k}, try: {v}") + return diff --git a/tests/tests.py b/tests/tests.py index 3390182..64389b9 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,5 +1,5 @@ import pytest -from dailyDecisionPackage.dailyDecision import pick_color, pick_activity, pick_clothes, pick_food +from dailyDecisionPackage.dailyDecision import pick_color, pick_activity, pick_clothes, pick_food, pick_music # Unit tests for pick_clothes function @@ -598,3 +598,329 @@ def test_invalid_weather_invalid_energy(self, capsys): assert "Sorry, 'invalid' is not a supported weather type." in captured.out assert "Please choose from: sunny, cloudy, rainy, snowy" in captured.out assert "In the meantime, try this activity: " in captured.out + +# Unit tests for pick_music function +class TestPickMusic: + songs_by_mood = { + "happy": [ + "Happy - Pharrell Williams", + "Walking on Sunshine - Katrina & The Waves", + "Shut Up and Dance - WALK THE MOON", + "Good as Hell - Lizzo", + "Uptown Funk - Mark Ronson ft. Bruno Mars", + "I Gotta Feeling - The Black Eyed Peas", + "Best Day of My Life - American Authors", + "Can’t Stop The Feeling! - Justin Timberlake", + ], + "sad": [ + "Someone Like You - Adele", + "Fix You - Coldplay", + "Skinny Love - Bon Iver", + "The Night We Met - Lord Huron", + "All I Want - Kodaline", + "Happier - Ed Sheeran", + "When The Party's Over - Billie Eilish", + "Let Her Go - Passenger", + ], + "calm": [ + "Holocene - Bon Iver", + "Bloom - The Paper Kites", + "Budapest - George Ezra", + "Photograph - Ed Sheeran", + "Lost in Japan - Shawn Mendes", + "Rivers and Roads - The Head and the Heart", + "Banana Pancakes - Jack Johnson", + "Ocean Eyes - Billie Eilish", + ], + "focused": [ + "Weightless - Marconi Union", + "Experience - Ludovico Einaudi", + "Sunset Lover - Petit Biscuit", + "Intro - The xx", + "Comptine d’un autre été - Yann Tiersen", + "We Move Lightly - Dustin O’Halloran", + "Midnight - Lane 8", + "Night Owl - Galimatias", + ], + "angry": [ + "Smells Like Teen Spirit - Nirvana", + "In the End - Linkin Park", + "Killing In The Name - Rage Against The Machine", + "Enter Sandman - Metallica", + "Duality - Slipknot", + "Hail to the King - Avenged Sevenfold", + "Papercut - Linkin Park", + "Break Stuff - Limp Bizkit", + ], + "romantic": [ + "All of Me - John Legend", + "Perfect - Ed Sheeran", + "Just The Way You Are - Bruno Mars", + "Make You Feel My Love - Adele", + "Stay With Me - Sam Smith", + "Yellow - Coldplay", + "Say You Won’t Let Go - James Arthur", + "Die For You - The Weeknd", + ], + "nostalgic": [ + "Wonderwall - Oasis", + "Mr. Brightside - The Killers", + "Iris - Goo Goo Dolls", + "Chasing Cars - Snow Patrol", + "Hey There Delilah - Plain White T’s", + "Viva La Vida - Coldplay", + "Stacy’s Mom - Fountains of Wayne", + "Seven Nation Army - The White Stripes", + ], + } + + songs_by_activity = { + "study": [ + "Experience - Ludovico Einaudi", + "River Flows in You - Yiruma", + "Sunset Lover - Petit Biscuit", + "Night Owl - Galimatias", + "Weightless - Marconi Union", + "Intro - The xx", + "We Move Lightly - Dustin O’Halloran", + "Gymnopédie No.1 - Erik Satie", + ], + "workout": [ + "Stronger - Kanye West", + "Lose Yourself - Eminem", + "Can’t Hold Us - Macklemore & Ryan Lewis", + "Don’t Start Now - Dua Lipa", + "Eye of the Tiger - Survivor", + "Till I Collapse - Eminem", + "Believer - Imagine Dragons", + "Remember the Name - Fort Minor", + ], + "commute": [ + "Budapest - George Ezra", + "Riptide - Vance Joy", + "Viva La Vida - Coldplay", + "Paris - The Chainsmokers", + "Feel It Still - Portugal. The Man", + "Blinding Lights - The Weeknd", + "Pocket Full of Sunshine - Natasha Bedingfield", + "Electric Feel - MGMT", + ], + "party": [ + "Uptown Funk - Mark Ronson ft. Bruno Mars", + "Levitating - Dua Lipa", + "One Kiss - Calvin Harris & Dua Lipa", + "Hey Ya! - OutKast", + "Turn Down for What - DJ Snake & Lil Jon", + "Starboy - The Weeknd", + "I Like It - Cardi B", + "Low - Flo Rida", + ], + "relax": [ + "Better Together - Jack Johnson", + "Holocene - Bon Iver", + "Banana Pancakes - Jack Johnson", + "Bloom - The Paper Kites", + "I’m Yours - Jason Mraz", + "Budapest - George Ezra", + "Ocean Eyes - Billie Eilish", + "Sunflower - Rex Orange County", + ], + "focus": [ + "Experience - Ludovico Einaudi", + "Midnight - Lane 8", + "Open Eye Signal - Jon Hopkins", + "Saturn - Sleeping at Last", + "Prelude in E Minor - Chopin", + "Outro - M83", + "We Move Lightly - Dustin O’Halloran", + "Sunset Lover - Petit Biscuit", + ], + "drive": [ + "Midnight City - M83", + "Blinding Lights - The Weeknd", + "Shut Up and Drive - Rihanna", + "Ride - Twenty One Pilots", + "On the Road Again - Willie Nelson", + "Take Me Out - Franz Ferdinand", + "Go Your Own Way - Fleetwood Mac", + "Feel Good Inc. - Gorillaz", + ], + "cook": [ + "Put Your Records On - Corinne Bailey Rae", + "Sunday Morning - Maroon 5", + "Mariposa - Peach Tree Rascals", + "Budapest - George Ezra", + "Brown Eyed Girl - Van Morrison", + "Best Part - Daniel Caesar ft. H.E.R.", + "I’m Yours - Jason Mraz", + "Dreams - Fleetwood Mac", + ], + } + + songs_by_rhythm = { + "fast": [ + "Don’t Start Now - Dua Lipa", + "Levitating - Dua Lipa", + "On Top of the World - Imagine Dragons", + "Blinding Lights - The Weeknd", + "Can’t Hold Us - Macklemore & Ryan Lewis", + "Don’t Stop Me Now - Queen", + "We Found Love - Rihanna", + "Titanium - David Guetta ft. Sia", + ], + "mid": [ + "Viva La Vida - Coldplay", + "Counting Stars - OneRepublic", + "Shut Up and Dance - WALK THE MOON", + "Riptide - Vance Joy", + "Feel It Still - Portugal. The Man", + "Send Me On My Way - Rusted Root", + "Electric Feel - MGMT", + "Stolen Dance - Milky Chance", + ], + "slow": [ + "All of Me - John Legend", + "Let Her Go - Passenger", + "Skinny Love - Bon Iver", + "Stay With Me - Sam Smith", + "River Flows in You - Yiruma", + "Holocene - Bon Iver", + "Make You Feel My Love - Adele", + "Fix You - Coldplay", + ], + "chill": [ + "Sunset Lover - Petit Biscuit", + "Night Owl - Galimatias", + "Lovely - Billie Eilish & Khalid", + "Lost in Japan - Shawn Mendes", + "Beyond - Leon Bridges", + "Talk - Khalid", + "Put It All on Me - Ed Sheeran", + "Warm - Majid Jordan", + ], + } + + all_mood_songs = set() + for songs in songs_by_mood.values(): + all_mood_songs.update(songs) + all_activity_songs = set() + for songs in songs_by_activity.values(): + all_activity_songs.update(songs) + all_rhythm_songs = set() + for songs in songs_by_rhythm.values(): + all_rhythm_songs.update(songs) + + allSongs = set() + for lst in [all_mood_songs, all_activity_songs, all_rhythm_songs]: + allSongs.update(lst) + + def test_default_pick_from_catalog(self, capsys): + pick_music() + out = capsys.readouterr().out + assert "How about:" in out + assert any(song in out for song in self.allSongs) + + def test_alias_anything_from_catalog(self, capsys): + pick_music("anything") + out = capsys.readouterr().out + assert "How about:" in out + assert any(song in out for song in self.allSongs) + + def test_unrecognized_prompt_guidance_and_fallback(self, capsys): + pick_music("unicorn vibes xyz") + out = capsys.readouterr().out.lower() + assert "sorry, we couldn't recognize any keywords" in out + assert "please include one of these keywords" in out + assert "but we've picked something for you to try" in out + assert any(song.lower() in out for song in self.allSongs) + + def test_single_mood(self, capsys): + pick_music("calm") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in self.songs_by_mood["calm"]) + + def test_single_activity(self, capsys): + pick_music("study") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in self.songs_by_activity["study"]) + + def test_single_rhythm(self, capsys): + pick_music("slow") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in self.songs_by_rhythm["slow"]) + + def test_multiple_moods_union_still_perfect_match(self, capsys): + union_set = set(self.songs_by_mood["calm"]) | set(self.songs_by_mood["happy"]) + pick_music("calm happy") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in union_set) + + def test_multiple_activities_union_still_perfect_match(self, capsys): + union_set = set(self.songs_by_activity["study"]) | set(self.songs_by_activity["focus"]) + pick_music("study focus") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in union_set) + + def test_activity_and_rhythm_with_intersection(self, capsys): + act_set = set(self.songs_by_activity["workout"]) + tmp_set = set(self.songs_by_rhythm["fast"]) + inter = act_set & tmp_set + assert len(inter) > 0 + pick_music("workout fast") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in inter) + + def test_mood_and_rhythm_no_intersection_tailored(self, capsys): + mood_set = set(self.songs_by_mood["angry"]) + tmp_set = set(self.songs_by_rhythm["chill"]) + inter = mood_set & tmp_set + assert len(inter) == 0 + pick_music("angry chill") + out = capsys.readouterr().out + assert "No perfect match between your keywords." in out + assert "Here are the tailored picks:" in out + + def test_valid_rhythm_with_invalid_token_still_works(self, capsys): + pick_music("abcdefg fast ???") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in self.songs_by_rhythm["fast"]) + + def test_alias_variants_any_anything_works(self, capsys): + for alias in ["any", "Any", "anything works", "NONE", "no", "idk"]: + pick_music(alias) + out = capsys.readouterr().out + assert "How about:" in out + assert any(song in out for song in self.allSongs) + + def test_punctuation_and_spacing_robustness(self, capsys): + pick_music(" calm, study!!! slow?? ") + out = capsys.readouterr().out + assert ("Perfect match" in out) or ("No perfect match between your keywords." in out) + + def test_invalid_then_valid_category_only_uses_valid(self, capsys): + pick_music("zzz study") + out = capsys.readouterr().out + assert "Perfect match" in out + assert "Sorry" not in out + assert any(song in out for song in self.songs_by_activity["study"]) + + def test_upper_mixed_case_keywords(self, capsys): + pick_music("HaPpY") + out = capsys.readouterr().out + assert "Perfect match" in out + assert any(song in out for song in self.songs_by_mood["happy"]) + + def test_blank_string_treated_as_unrecognized(self, capsys): + pick_music(" ") + out = capsys.readouterr().out.lower() + assert "sorry, we couldn't recognize any keywords" in out + assert "please include one of these keywords" in out + assert "but we've picked something for you to try" in out + assert any(song.lower() in out for song in self.allSongs)