diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..fb0ff18 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,44 @@ +name: CI / CD +on: + 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 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/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..7a596a8 --- /dev/null +++ b/Pipfile @@ -0,0 +1,15 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +dailydecisionpackage = {file = ".", editable = true} + +[dev-packages] +pytest = "*" +build = "*" +twine = "*" + +[requires] +python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..78e1ba7 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,468 @@ +{ + "_meta": { + "hash": { + "sha256": "b890b2c732547c69bf871ccc71357440c2abc07344a4c572acace6317d6f6676" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "dailydecisionpackage": { + "editable": true, + "file": "." + } + }, + "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:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80", + "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", + "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31", + "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99", + "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", + "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", + "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", + "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93", + "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", + "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b", + "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5", + "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130", + "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", + "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", + "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", + "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", + "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", + "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", + "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868", + "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", + "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", + "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d", + "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13", + "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", + "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", + "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a" + ], + "markers": "python_version >= '3.8'", + "version": "==0.3.2" + }, + "packaging": { + "hashes": [ + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" + ], + "markers": "python_version >= '3.8'", + "version": "==25.0" + }, + "pluggy": { + "hashes": [ + "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", + "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" + ], + "markers": "python_version >= '3.9'", + "version": "==1.6.0" + }, + "pygments": { + "hashes": [ + "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", + "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" + ], + "markers": "python_version >= '3.8'", + "version": "==2.19.2" + }, + "pyproject-hooks": { + "hashes": [ + "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", + "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913" + ], + "markers": "python_version >= '3.7'", + "version": "==1.2.0" + }, + "pytest": { + "hashes": [ + "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", + "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==8.4.2" + }, + "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/README.md b/README.md index 6022e0e..1fc4190 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ -# Python Package Exercise +[![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. -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_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. + +## 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) 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..c50faea --- /dev/null +++ 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.1" +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"] diff --git a/src/dailyDecisionPackage/__init__.py b/src/dailyDecisionPackage/__init__.py new file mode 100644 index 0000000..ba9fefe --- /dev/null +++ b/src/dailyDecisionPackage/__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/dailyDecisionPackage/dailyDecision.py b/src/dailyDecisionPackage/dailyDecision.py new file mode 100644 index 0000000..cc8395a --- /dev/null +++ b/src/dailyDecisionPackage/dailyDecision.py @@ -0,0 +1,592 @@ +# 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 shirt/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", + "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))}" + ) + 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))}" + ) + # 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))}" + ) + 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))}" + ) + # 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))}" + ) + # 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()]) + ) + 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 + 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", + ], + "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_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", + ], + "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 foods_by_restriction.values(): + allFoods.update(foods) + + # 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: + 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: + # 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_weather.values(): + allActivities.update(activities) + 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: sunny, cloudy, rainy, snowy") + 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: + 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))}" + ) + 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: sunny, cloudy, rainy, snowy") + 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))}" + ) + 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 diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 0000000..701e830 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,434 @@ +import pytest +from dailyDecisionPackage.dailyDecision import pick_color, pick_activity, pick_clothes + + +# 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 shirt/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", + "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_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] + ) + + 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") + 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_invalid_mood(self, capsys): + 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) + 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): + supported_weather = ["sunny", "rainy", "snowy", "windy"] + for weather in supported_weather: + pick_clothes(weather=weather, 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_both_invalid(self, capsys): + pick_clothes(weather="invalid", mood="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 "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 = [ + "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) + + +# 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() + 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"]) + ) + ) + + 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() + 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] + ) + + 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() + 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"]) + ) + ) + + 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_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_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_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_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