From be93f414536514ea2e014fdfe2325b1afb3827d8 Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 15:19:48 +0300 Subject: [PATCH 1/8] feat: add Freebie model and seed script - Add Faker package to Pipfile - Create Alembic migration for Freebie model - Define Freebie model in models.py - Implement seed script to generate fake data for Freebie model --- Pipfile | 1 + Pipfile.lock | 448 +++++++++++------- lib/freebies.db | Bin 20480 -> 24576 bytes .../34f6305e2835_create_freebie_model.py | 37 ++ lib/models.py | 18 +- lib/seed.py | 65 ++- 6 files changed, 399 insertions(+), 170 deletions(-) create mode 100644 lib/migrations/versions/34f6305e2835_create_freebie_model.py diff --git a/Pipfile b/Pipfile index 63c79cd98..f31991967 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,7 @@ importlib-metadata = "6.0.0" importlib-resources = "5.10.0" ipdb = "0.13.9" sqlalchemy = "1.4.42" +faker = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 3923f4a97..9c96f909d 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "fa62c636b7ae5acb4993fc7b007a651c55b562aa29962a9a8585ec314b352ae6" + "sha256": "e6c61c0f5c1b795f874456f2c675d57c67594f1614a5e1eb4bc37717a41b1762" }, "pipfile-spec": 6, "requires": { @@ -18,26 +18,19 @@ "default": { "alembic": { "hashes": [ - "sha256:6880dec4f28dd7bd999d2ed13fbe7c9d4337700a44d11a524c0ce0c59aaf0dbd", - "sha256:e8a6ff9f3b1887e1fed68bfb8fb9a000d8f61c21bdcc85b67bb9f87fcbc4fce3" + "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4", + "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa" ], "index": "pypi", - "version": "==1.9.2" - }, - "appnope": { - "hashes": [ - "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24", - "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e" - ], - "markers": "sys_platform == 'darwin'", - "version": "==0.1.3" + "markers": "python_version >= '3.7'", + "version": "==1.8.1" }, "asttokens": { "hashes": [ - "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3", - "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c" + "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", + "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0" ], - "version": "==2.2.1" + "version": "==2.4.1" }, "backcall": { "hashes": [ @@ -51,15 +44,97 @@ "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" ], - "markers": "python_version < '3.11' and python_version >= '3.7'", + "markers": "python_version >= '3.7'", "version": "==5.1.1" }, "executing": { "hashes": [ - "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc", - "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107" + "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", + "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab" ], - "version": "==1.2.0" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" + }, + "faker": { + "hashes": [ + "sha256:4294d169255a045990720d6f3fa4134b764a4cdf46ef0d3c7553d2506f1adaa1", + "sha256:e59c01d1e8b8e20a83255ab8232c143cb2af3b4f5ab6a3f5ce495f385ad8ab4c" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==28.4.1" + }, + "greenlet": { + "hashes": [ + "sha256:01059afb9b178606b4b6e92c3e710ea1635597c3537e44da69f4531e111dd5e9", + "sha256:037d9ac99540ace9424cb9ea89f0accfaff4316f149520b4ae293eebc5bded17", + "sha256:0e49a65d25d7350cca2da15aac31b6f67a43d867448babf997fe83c7505f57bc", + "sha256:13ff8c8e54a10472ce3b2a2da007f915175192f18e6495bad50486e87c7f6637", + "sha256:1544b8dd090b494c55e60c4ff46e238be44fdc472d2589e943c241e0169bcea2", + "sha256:184258372ae9e1e9bddce6f187967f2e08ecd16906557c4320e3ba88a93438c3", + "sha256:1ddc7bcedeb47187be74208bc652d63d6b20cb24f4e596bd356092d8000da6d6", + "sha256:221169d31cada333a0c7fd087b957c8f431c1dba202c3a58cf5a3583ed973e9b", + "sha256:243a223c96a4246f8a30ea470c440fe9db1f5e444941ee3c3cd79df119b8eebf", + "sha256:24fc216ec7c8be9becba8b64a98a78f9cd057fd2dc75ae952ca94ed8a893bf27", + "sha256:2651dfb006f391bcb240635079a68a261b227a10a08af6349cba834a2141efa1", + "sha256:26811df4dc81271033a7836bc20d12cd30938e6bd2e9437f56fa03da81b0f8fc", + "sha256:26d9c1c4f1748ccac0bae1dbb465fb1a795a75aba8af8ca871503019f4285e2a", + "sha256:28fe80a3eb673b2d5cc3b12eea468a5e5f4603c26aa34d88bf61bba82ceb2f9b", + "sha256:2cd8518eade968bc52262d8c46727cfc0826ff4d552cf0430b8d65aaf50bb91d", + "sha256:2d004db911ed7b6218ec5c5bfe4cf70ae8aa2223dffbb5b3c69e342bb253cb28", + "sha256:3d07c28b85b350564bdff9f51c1c5007dfb2f389385d1bc23288de51134ca303", + "sha256:3e7e6ef1737a819819b1163116ad4b48d06cfdd40352d813bb14436024fcda99", + "sha256:44151d7b81b9391ed759a2f2865bbe623ef00d648fed59363be2bbbd5154656f", + "sha256:44cd313629ded43bb3b98737bba2f3e2c2c8679b55ea29ed73daea6b755fe8e7", + "sha256:4a3dae7492d16e85ea6045fd11cb8e782b63eac8c8d520c3a92c02ac4573b0a6", + "sha256:4b5ea3664eed571779403858d7cd0a9b0ebf50d57d2cdeafc7748e09ef8cd81a", + "sha256:4c3446937be153718250fe421da548f973124189f18fe4575a0510b5c928f0cc", + "sha256:5415b9494ff6240b09af06b91a375731febe0090218e2898d2b85f9b92abcda0", + "sha256:5fd6e94593f6f9714dbad1aaba734b5ec04593374fa6638df61592055868f8b8", + "sha256:619935a44f414274a2c08c9e74611965650b730eb4efe4b2270f91df5e4adf9a", + "sha256:655b21ffd37a96b1e78cc48bf254f5ea4b5b85efaf9e9e2a526b3c9309d660ca", + "sha256:665b21e95bc0fce5cab03b2e1d90ba9c66c510f1bb5fdc864f3a377d0f553f6b", + "sha256:6a4bf607f690f7987ab3291406e012cd8591a4f77aa54f29b890f9c331e84989", + "sha256:6cea1cca3be76c9483282dc7760ea1cc08a6ecec1f0b6ca0a94ea0d17432da19", + "sha256:713d450cf8e61854de9420fb7eea8ad228df4e27e7d4ed465de98c955d2b3fa6", + "sha256:726377bd60081172685c0ff46afbc600d064f01053190e4450857483c4d44484", + "sha256:76b3e3976d2a452cba7aa9e453498ac72240d43030fdc6d538a72b87eaff52fd", + "sha256:76dc19e660baea5c38e949455c1181bc018893f25372d10ffe24b3ed7341fb25", + "sha256:76e5064fd8e94c3f74d9fd69b02d99e3cdb8fc286ed49a1f10b256e59d0d3a0b", + "sha256:7f346d24d74c00b6730440f5eb8ec3fe5774ca8d1c9574e8e57c8671bb51b910", + "sha256:81eeec4403a7d7684b5812a8aaa626fa23b7d0848edb3a28d2eb3220daddcbd0", + "sha256:90b5bbf05fe3d3ef697103850c2ce3374558f6fe40fd57c9fac1bf14903f50a5", + "sha256:9730929375021ec90f6447bff4f7f5508faef1c02f399a1953870cdb78e0c345", + "sha256:9eb4a1d7399b9f3c7ac68ae6baa6be5f9195d1d08c9ddc45ad559aa6b556bce6", + "sha256:a0409bc18a9f85321399c29baf93545152d74a49d92f2f55302f122007cfda00", + "sha256:a22f4e26400f7f48faef2d69c20dc055a1f3043d330923f9abe08ea0aecc44df", + "sha256:a53dfe8f82b715319e9953330fa5c8708b610d48b5c59f1316337302af5c0811", + "sha256:a771dc64fa44ebe58d65768d869fcfb9060169d203446c1d446e844b62bdfdca", + "sha256:a814dc3100e8a046ff48faeaa909e80cdb358411a3d6dd5293158425c684eda8", + "sha256:a8870983af660798dc1b529e1fd6f1cefd94e45135a32e58bd70edd694540f33", + "sha256:ac0adfdb3a21dc2a24ed728b61e72440d297d0fd3a577389df566651fcd08f97", + "sha256:b395121e9bbe8d02a750886f108d540abe66075e61e22f7353d9acb0b81be0f0", + "sha256:b9505a0c8579899057cbefd4ec34d865ab99852baf1ff33a9481eb3924e2da0b", + "sha256:c0a5b1c22c82831f56f2f7ad9bbe4948879762fe0d59833a4a71f16e5fa0f682", + "sha256:c3967dcc1cd2ea61b08b0b276659242cbce5caca39e7cbc02408222fb9e6ff39", + "sha256:c6f4c2027689093775fd58ca2388d58789009116844432d920e9147f91acbe64", + "sha256:c9d86401550b09a55410f32ceb5fe7efcd998bd2dad9e82521713cb148a4a15f", + "sha256:cd468ec62257bb4544989402b19d795d2305eccb06cde5da0eb739b63dc04665", + "sha256:cfcfb73aed40f550a57ea904629bdaf2e562c68fa1164fa4588e752af6efdc3f", + "sha256:d0dd943282231480aad5f50f89bdf26690c995e8ff555f26d8a5b9887b559bcc", + "sha256:d3c59a06c2c28a81a026ff11fbf012081ea34fb9b7052f2ed0366e14896f0a1d", + "sha256:d45b75b0f3fd8d99f62eb7908cfa6d727b7ed190737dec7fe46d993da550b81a", + "sha256:d46d5069e2eeda111d6f71970e341f4bd9aeeee92074e649ae263b834286ecc0", + "sha256:d58ec349e0c2c0bc6669bf2cd4982d2f93bf067860d23a0ea1fe677b0f0b1e09", + "sha256:db1b3ccb93488328c74e97ff888604a8b95ae4f35f4f56677ca57a4fc3a4220b", + "sha256:dd65695a8df1233309b701dec2539cc4b11e97d4fcc0f4185b4a12ce54db0491", + "sha256:f9482c2ed414781c0af0b35d9d575226da6b728bd1a720668fa05837184965b7", + "sha256:f9671e7282d8c6fcabc32c0fb8d7c0ea8894ae85cee89c9aadc2d7129e1a9954", + "sha256:fad7a051e07f64e297e6e8399b4d6a3bdcad3d7297409e9a06ef8cbccff4f501", + "sha256:ffb08f2a1e59d38c7b8b9ac8083c9c8b9875f0955b1e9b9b9a965607a51f8e54" + ], + "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", + "version": "==3.1.0" }, "importlib-metadata": { "hashes": [ @@ -67,127 +142,139 @@ "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==6.0.0" }, "importlib-resources": { "hashes": [ - "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6", - "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484" + "sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668", + "sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437" ], "index": "pypi", - "version": "==5.10.2" + "markers": "python_version >= '3.7'", + "version": "==5.10.0" }, "ipdb": { "hashes": [ - "sha256:c23b6736f01fd4586cc2ecbebdf79a5eb454796853e1cd8f2ed3b7b91d4a3e93", - "sha256:f74c2f741c18b909eaf89f19fde973f745ac721744aa1465888ce45813b63a9c" + "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5" ], "index": "pypi", - "version": "==0.13.11" + "markers": "python_version >= '2.7'", + "version": "==0.13.9" }, "ipython": { "hashes": [ - "sha256:da01e6df1501e6e7c32b5084212ddadd4ee2471602e2cf3e0190f4de6b0ea481", - "sha256:f3bf2c08505ad2c3f4ed5c46ae0331a8547d36bf4b21a451e8ae80c0791db95b" + "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", + "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c" ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==8.8.0" + "markers": "python_version >= '3.7'", + "version": "==8.12.3" }, "jedi": { "hashes": [ - "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e", - "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612" + "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", + "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0" ], "markers": "python_version >= '3.6'", - "version": "==0.18.2" + "version": "==0.19.1" }, "mako": { "hashes": [ - "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818", - "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34" + "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a", + "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc" ], - "markers": "python_version >= '3.7'", - "version": "==1.2.4" + "markers": "python_version >= '3.8'", + "version": "==1.3.5" }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", + "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", + "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", + "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", + "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", + "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", + "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", + "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", + "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", + "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", + "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", + "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", + "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", + "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", + "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", + "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", + "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", + "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", + "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", + "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", + "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", + "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", + "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", + "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", + "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", + "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", + "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", + "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", + "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", + "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", + "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", + "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", + "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", + "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", + "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", + "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", + "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", + "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", + "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", + "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", + "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", + "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", + "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", + "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", + "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", + "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", + "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", + "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", + "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", + "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", + "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", + "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", + "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", + "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", + "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", + "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", + "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", + "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", + "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", + "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" ], "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "version": "==2.1.5" }, "matplotlib-inline": { "hashes": [ - "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", - "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304" + "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", + "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca" ], - "markers": "python_version >= '3.5'", - "version": "==0.1.6" + "markers": "python_version >= '3.8'", + "version": "==0.1.7" }, "parso": { "hashes": [ - "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", - "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75" + "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", + "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d" ], "markers": "python_version >= '3.6'", - "version": "==0.8.3" + "version": "==0.8.4" }, "pexpect": { "hashes": [ - "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", - "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" + "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", + "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f" ], "markers": "sys_platform != 'win32'", - "version": "==4.8.0" + "version": "==4.9.0" }, "pickleshare": { "hashes": [ @@ -198,11 +285,11 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63", - "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305" + "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10", + "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360" ], - "markers": "python_full_version >= '3.6.2'", - "version": "==3.0.36" + "markers": "python_full_version >= '3.7.0'", + "version": "==3.0.47" }, "ptyprocess": { "hashes": [ @@ -213,18 +300,34 @@ }, "pure-eval": { "hashes": [ - "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350", - "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3" + "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", + "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42" ], - "version": "==0.2.2" + "version": "==0.2.3" }, "pygments": { "hashes": [ - "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297", - "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717" + "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", + "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" ], - "markers": "python_version >= '3.6'", - "version": "==2.14.0" + "markers": "python_version >= '3.8'", + "version": "==2.18.0" + }, + "python-dateutil": { + "hashes": [ + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.9.0.post0" + }, + "setuptools": { + "hashes": [ + "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2", + "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538" + ], + "markers": "python_version >= '3.8'", + "version": "==75.1.0" }, "six": { "hashes": [ @@ -236,88 +339,97 @@ }, "sqlalchemy": { "hashes": [ - "sha256:07e48cbcdda6b8bc7a59d6728bd3f5f574ffe03f2c9fb384239f3789c2d95c2e", - "sha256:18cafdb27834fa03569d29f571df7115812a0e59fd6a3a03ccb0d33678ec8420", - "sha256:1b1e5e96e2789d89f023d080bee432e2fef64d95857969e70d3cadec80bd26f0", - "sha256:315676344e3558f1f80d02535f410e80ea4e8fddba31ec78fe390eff5fb8f466", - "sha256:31de1e2c45e67a5ec1ecca6ec26aefc299dd5151e355eb5199cd9516b57340be", - "sha256:3d94682732d1a0def5672471ba42a29ff5e21bb0aae0afa00bb10796fc1e28dd", - "sha256:3ec187acf85984263299a3f15c34a6c0671f83565d86d10f43ace49881a82718", - "sha256:4847f4b1d822754e35707db913396a29d874ee77b9c3c3ef3f04d5a9a6209618", - "sha256:4d112b0f3c1bc5ff70554a97344625ef621c1bfe02a73c5d97cac91f8cd7a41e", - "sha256:51e1ba2884c6a2b8e19109dc08c71c49530006c1084156ecadfaadf5f9b8b053", - "sha256:535377e9b10aff5a045e3d9ada8a62d02058b422c0504ebdcf07930599890eb0", - "sha256:5dbf17ac9a61e7a3f1c7ca47237aac93cabd7f08ad92ac5b96d6f8dea4287fc1", - "sha256:5f752676fc126edc1c4af0ec2e4d2adca48ddfae5de46bb40adbd3f903eb2120", - "sha256:64cb0ad8a190bc22d2112001cfecdec45baffdf41871de777239da6a28ed74b6", - "sha256:6913b8247d8a292ef8315162a51931e2b40ce91681f1b6f18f697045200c4a30", - "sha256:69fac0a7054d86b997af12dc23f581cf0b25fb1c7d1fed43257dee3af32d3d6d", - "sha256:7001f16a9a8e06488c3c7154827c48455d1c1507d7228d43e781afbc8ceccf6d", - "sha256:7b81b1030c42b003fc10ddd17825571603117f848814a344d305262d370e7c34", - "sha256:7f8267682eb41a0584cf66d8a697fef64b53281d01c93a503e1344197f2e01fe", - "sha256:887865924c3d6e9a473dc82b70977395301533b3030d0f020c38fd9eba5419f2", - "sha256:9167d4227b56591a4cc5524f1b79ccd7ea994f36e4c648ab42ca995d28ebbb96", - "sha256:939f9a018d2ad04036746e15d119c0428b1e557470361aa798e6e7d7f5875be0", - "sha256:955162ad1a931fe416eded6bb144ba891ccbf9b2e49dc7ded39274dd9c5affc5", - "sha256:984ee13543a346324319a1fb72b698e521506f6f22dc37d7752a329e9cd00a32", - "sha256:9883f5fae4fd8e3f875adc2add69f8b945625811689a6c65866a35ee9c0aea23", - "sha256:a1ad90c97029cc3ab4ffd57443a20fac21d2ec3c89532b084b073b3feb5abff3", - "sha256:a3714e5b33226131ac0da60d18995a102a17dddd42368b7bdd206737297823ad", - "sha256:ae067ab639fa499f67ded52f5bc8e084f045d10b5ac7bb928ae4ca2b6c0429a5", - "sha256:b33ffbdbbf5446cf36cd4cc530c9d9905d3c2fe56ed09e25c22c850cdb9fac92", - "sha256:b6e4cb5c63f705c9d546a054c60d326cbde7421421e2d2565ce3e2eee4e1a01f", - "sha256:b7f4b6aa6e87991ec7ce0e769689a977776db6704947e562102431474799a857", - "sha256:c04144a24103135ea0315d459431ac196fe96f55d3213bfd6d39d0247775c854", - "sha256:c522e496f9b9b70296a7675272ec21937ccfc15da664b74b9f58d98a641ce1b6", - "sha256:c5a99282848b6cae0056b85da17392a26b2d39178394fc25700bcf967e06e97a", - "sha256:c7a46639ba058d320c9f53a81db38119a74b8a7a1884df44d09fbe807d028aaf", - "sha256:d4b1cc7835b39835c75cf7c20c926b42e97d074147c902a9ebb7cf2c840dc4e2", - "sha256:d4d164df3d83d204c69f840da30b292ac7dc54285096c6171245b8d7807185aa", - "sha256:d61e9ecc849d8d44d7f80894ecff4abe347136e9d926560b818f6243409f3c86", - "sha256:d68e1762997bfebf9e5cf2a9fd0bcf9ca2fdd8136ce7b24bbd3bbfa4328f3e4a", - "sha256:e3c1808008124850115a3f7e793a975cfa5c8a26ceeeb9ff9cbb4485cac556df", - "sha256:f8cb80fe8d14307e4124f6fad64dfd87ab749c9d275f82b8b4ec84c84ecebdbe" + "sha256:04f2598c70ea4a29b12d429a80fad3a5202d56dce19dd4916cc46a965a5ca2e9", + "sha256:0501f74dd2745ec38f44c3a3900fb38b9db1ce21586b691482a19134062bf049", + "sha256:0ee377eb5c878f7cefd633ab23c09e99d97c449dd999df639600f49b74725b80", + "sha256:11b2ec26c5d2eefbc3e6dca4ec3d3d95028be62320b96d687b6e740424f83b7d", + "sha256:15d878929c30e41fb3d757a5853b680a561974a0168cd33a750be4ab93181628", + "sha256:177e41914c476ed1e1b77fd05966ea88c094053e17a85303c4ce007f88eff363", + "sha256:1811a0b19a08af7750c0b69e38dec3d46e47c4ec1d74b6184d69f12e1c99a5e0", + "sha256:1d0c23ecf7b3bc81e29459c34a3f4c68ca538de01254e24718a7926810dc39a6", + "sha256:22459fc1718785d8a86171bbe7f01b5c9d7297301ac150f508d06e62a2b4e8d2", + "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124", + "sha256:2e56dfed0cc3e57b2f5c35719d64f4682ef26836b81067ee6cfad062290fd9e2", + "sha256:2fd49af453e590884d9cdad3586415922a8e9bb669d874ee1dc55d2bc425aacd", + "sha256:3ab7c158f98de6cb4f1faab2d12973b330c2878d0c6b689a8ca424c02d66e1b3", + "sha256:4948b6c5f4e56693bbeff52f574279e4ff972ea3353f45967a14c30fb7ae2beb", + "sha256:4e1c5f8182b4f89628d782a183d44db51b5af84abd6ce17ebb9804355c88a7b5", + "sha256:5ce6929417d5dce5ad1d3f147db81735a4a0573b8fb36e3f95500a06eaddd93e", + "sha256:5ede1495174e69e273fad68ad45b6d25c135c1ce67723e40f6cf536cb515e20b", + "sha256:5f966b64c852592469a7eb759615bbd351571340b8b344f1d3fa2478b5a4c934", + "sha256:6045b3089195bc008aee5c273ec3ba9a93f6a55bc1b288841bd4cfac729b6516", + "sha256:6c9d004eb78c71dd4d3ce625b80c96a827d2e67af9c0d32b1c1e75992a7916cc", + "sha256:6e39e97102f8e26c6c8550cb368c724028c575ec8bc71afbbf8faaffe2b2092a", + "sha256:723e3b9374c1ce1b53564c863d1a6b2f1dc4e97b1c178d9b643b191d8b1be738", + "sha256:876eb185911c8b95342b50a8c4435e1c625944b698a5b4a978ad2ffe74502908", + "sha256:9256563506e040daddccaa948d055e006e971771768df3bb01feeb4386c242b0", + "sha256:934472bb7d8666727746a75670a1f8d91a9cae8c464bba79da30a0f6faccd9e1", + "sha256:97ff50cd85bb907c2a14afb50157d0d5486a4b4639976b4a3346f34b6d1b5272", + "sha256:9b01d9cd2f9096f688c71a3d0f33f3cd0af8549014e66a7a7dee6fc214a7277d", + "sha256:9e3a65ce9ed250b2f096f7b559fe3ee92e6605fab3099b661f0397a9ac7c8d95", + "sha256:a7dd5b7b34a8ba8d181402d824b87c5cee8963cb2e23aa03dbfe8b1f1e417cde", + "sha256:a85723c00a636eed863adb11f1e8aaa36ad1c10089537823b4540948a8429798", + "sha256:b42c59ffd2d625b28cdb2ae4cde8488543d428cba17ff672a543062f7caee525", + "sha256:bd448b262544b47a2766c34c0364de830f7fb0772d9959c1c42ad61d91ab6565", + "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88", + "sha256:df76e9c60879fdc785a34a82bf1e8691716ffac32e7790d31a98d7dec6e81545", + "sha256:e12c6949bae10f1012ab5c0ea52ab8db99adcb8c7b717938252137cdf694c775", + "sha256:e4ef8cb3c5b326f839bfeb6af5f406ba02ad69a78c7aac0fbeeba994ad9bb48a", + "sha256:e7e740453f0149437c101ea4fdc7eea2689938c5760d7dcc436c863a12f1f565", + "sha256:effc89e606165ca55f04f3f24b86d3e1c605e534bf1a96e4e077ce1b027d0b71", + "sha256:f0f574465b78f29f533976c06b913e54ab4980b9931b69aa9d306afff13a9471", + "sha256:fa5b7eb2051e857bf83bade0641628efe5a88de189390725d3e6033a1fff4257", + "sha256:fdb94a3d1ba77ff2ef11912192c066f01e68416f554c194d769391638c8ad09a" ], "index": "pypi", - "version": "==1.4.46" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.4.42" }, "stack-data": { "hashes": [ - "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815", - "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8" + "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", + "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695" ], - "version": "==0.6.2" + "version": "==0.6.3" }, - "tomli": { + "toml": { "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==2.0.1" + "markers": "python_version >= '3.7'", + "version": "==0.10.2" }, "traitlets": { "hashes": [ - "sha256:32500888f5ff7bbf3b9267ea31748fa657aaf34d56d85e60f91dda7dc7f5785b", - "sha256:a1ca5df6414f8b5760f7c5f256e326ee21b581742114545b462b35ffe3f04861" + "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", + "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f" ], - "markers": "python_version >= '3.7'", - "version": "==5.8.1" + "markers": "python_version >= '3.8'", + "version": "==5.14.3" + }, + "typing-extensions": { + "hashes": [ + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + ], + "markers": "python_version < '3.10'", + "version": "==4.12.2" }, "wcwidth": { "hashes": [ - "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e", - "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0" + "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", + "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5" ], - "version": "==0.2.6" + "version": "==0.2.13" }, "zipp": { "hashes": [ - "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa", - "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766" + "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", + "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29" ], "markers": "python_version < '3.10'", - "version": "==3.11.0" + "version": "==3.20.2" } }, "develop": {} diff --git a/lib/freebies.db b/lib/freebies.db index 12beb1c963e832db481e7a7493e3029e691ac4dc..274aa2e7628b8670ece5901c8e9948b91de69806 100644 GIT binary patch literal 24576 zcmeI)Pi)&%90%~{{Biztw%ev@*h<%jSfNp2VhcqeghUeDu3DP5NlLZDzEBgg77$To6|zwmTQJ%Y?*<8wWs$6VG+iCTTh@oT~3rE!#i8_w(<4 zo_nxbxx860Tt*wV)6`v>A>%|4$h(vhLZW;V`POS8e&B7rCiD)Tv5%7L-A^RtZxX)r z6;UMRd-+583O_`H00bZa0SG_<0uX=z1R!v(z(YG6NzKj*AKh^Es>vD-V^xE-dwVfo zQnMA6R~HLD{6@r*DJKRRVchddDvx5&C;8UzMn1SZ)FFsc6GDE zMvkkqu6CNz%T%+QE!{d8if)uv*RrKM^tO73E*SN+*E7FfELTcdUJh;SX}uz~(*(zd zd|q^Uy`-+L6ut2arybI?q%Nx^wU}4S)DL@||KkRJ%^Keg=Jn$PIgk2y>Eh{HD3VGh zg~Rj_xOc(Z!UNEL_j8f^eLa9-lz94ZEEq}6%?XF>=q@KyIyndp-0vj(jFla}X~XVV zb;H`x4w&xr*7Tfe(n2J)xF|e~9aW>7tXVZ`T9-L(!?uP_0|S)}UG(ZZ9;R8HX73?e z$lOd1JcfOb#83vUwO6A9V14=c^1KreQ=5wdF|0cKpuKPM2U^oRu6=Z_4JW%X6bSNP zH1GYMNPH8N-wFRgg8&2|009U<00Izz00bZa0SG|gf(Xb$U_SGD8oJxpfp7-3a+wQA|ON+M4gvS$;{`jqOj=tv@ zykjCcF`}fyz5oCK delta 94 zcmZoTz}T>Wae}lUD+2=q2*UvLL>*&MRtCLzSzi7h3@m(74E((OJNfGQq&5o*EaTlQ e#dk}P$285{DB08^Db2(pd6U8yeiVU41_}T{Q53rX diff --git a/lib/migrations/versions/34f6305e2835_create_freebie_model.py b/lib/migrations/versions/34f6305e2835_create_freebie_model.py new file mode 100644 index 000000000..8ecbb01ae --- /dev/null +++ b/lib/migrations/versions/34f6305e2835_create_freebie_model.py @@ -0,0 +1,37 @@ +"""Create Freebie model + +Revision ID: 34f6305e2835 +Revises: 5f72c58bf48c +Create Date: 2024-09-19 14:45:17.074792 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '34f6305e2835' +down_revision = '5f72c58bf48c' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('freebies', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('item_name', sa.String(), nullable=False), + sa.Column('value', sa.Integer(), nullable=False), + sa.Column('dev_id', sa.Integer(), nullable=True), + sa.Column('company_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['companies.id'], name=op.f('fk_freebies_company_id_companies')), + sa.ForeignKeyConstraint(['dev_id'], ['devs.id'], name=op.f('fk_freebies_dev_id_devs')), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('freebies') + # ### end Alembic commands ### diff --git a/lib/models.py b/lib/models.py index 2681bee5a..d211e4718 100644 --- a/lib/models.py +++ b/lib/models.py @@ -9,6 +9,7 @@ Base = declarative_base(metadata=metadata) + class Company(Base): __tablename__ = 'companies' @@ -19,11 +20,26 @@ class Company(Base): def __repr__(self): return f'' + class Dev(Base): __tablename__ = 'devs' id = Column(Integer(), primary_key=True) - name= Column(String()) + name = Column(String()) def __repr__(self): return f'' + + +class Freebie(Base): + # Mapped table name at the database level + __tablename__ = 'freebies' + + # Column definitions + id = Column(Integer(), primary_key=True) + item_name = Column(String(), nullable=False) + value = Column(Integer(), nullable=False) + + # Foreign Keys definitions + dev_id = Column(Integer(), ForeignKey('devs.id')) + company_id = Column(Integer(), ForeignKey('companies.id')) diff --git a/lib/seed.py b/lib/seed.py index b16becbbb..6d8f05485 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -1,3 +1,66 @@ #!/usr/bin/env python3 -# Script goes here! +# This is a script to generate some fake swag data to be used in the +# freebies database. + +# We import Faker, a Python package that generates fake data. +from faker import Faker + +# We import random to generate random numbers. +import random + +# We import the required classes from sqlalchemy. +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +# We import the Freebie class from the models module. +from models import Freebie + +if __name__ == '__main__': + # We create an engine object that represents the core interface to + # the database. + engine = create_engine('sqlite:///freebies.db') + + # We create a sessionmaker object that creates new Session objects + # bound to the engine object. + Session = sessionmaker(bind=engine) + + # We create a new session object from the sessionmaker. + session = Session() + + # We delete all the freebies in the database. + session.query(Freebie).delete() + + # We create a fake object that can be used to generate fake data. + fake = Faker() + + # We create a list of swag items that we can randomly select from. + swag_items = [ + "T-shirt", "Hoodie", "Sticker", "Notebook", "Pen", "USB Drive", + "Water Bottle", "Keychain", "Mouse Pad", "Lanyard", "Tote Bag", + "Sunglass", "Stress Ball", "Phone Stand", "Charging Cable", + "Power Bank", "Laptop Sleeve", "Coffee Mug", "Earbud", "Hat", + "Wristband", "Post-it Note", "Screen Cleaner", "Bottle Opener", + "Phone Grip", "Badge", "Button", "Puzzle", "Flashlight", "Umbrella" + ] + + # We create a list to hold all the freebie objects that we create. + freebies = [] + + # We loop 30 times to create 30 freebie objects. + for _ in range(30): + # We create a new freebie object with a random swag item and a + # value between 1 and 3000. + freebie = Freebie(item_name=random.choice(swag_items), + value=random.randint(1, 3000)) + # We append the freebie object to the list of freebies. + freebies.append(freebie) + + # We add the freebies to the session. + session.bulk_save_objects(freebies) + + # We commit the session. + session.commit() + + # We close the session. + session.close() From 0a72d49eed7de82cc5ce72dfa386ccb6ff35f058 Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 17:21:24 +0300 Subject: [PATCH 2/8] feat(models): add association table for companies and devs - Create a new Alembic migration to add the `companies_devs` association table. - Update `models.py` to define the `companies_devs` table and add relationships between `Company` and `Dev`. - Modify `seed.py` to include seeding logic for `Company` and `Dev` models and establish many-to-many relationships through `companies_devs`. This change enhances the database schema to support many-to-many relationships between companies and developers, facilitating more complex queries and data associations. --- lib/freebies.db | Bin 24576 -> 32768 bytes ...create_association_table_companies_devs.py | 34 +++++++++ lib/models.py | 30 +++++++- lib/seed.py | 69 +++++++++++++----- 4 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 lib/migrations/versions/f11961b08200_create_association_table_companies_devs.py diff --git a/lib/freebies.db b/lib/freebies.db index 274aa2e7628b8670ece5901c8e9948b91de69806..033aa02eb7a9496a6eee54beb3d9e871c1da38ea 100644 GIT binary patch delta 1397 zcmZuxOKcle6n(bGW5@QqXUF+FX`d-kVl`9E*v-d90@5U)lCNxwCNa9SCAvjjRO%ud1b1Q~#VI3=M)&j1x%Zr> z+56SAx99yl5$i?>1>ybg@qaOvNpz!2+qWcgi~`{xA`cb?#JBDCDA2r%djcl~I@)|)>T3QKk(!Ex-S2BFxoeBl zxivaJw?;=>2kYxvX^a98d{bjI)G@QXlrQ9_uPqhm_3C;XdUIcTM{3VJk*35~0%GrS zs`(4)JKt8c*VXnocyK?m5RY6})$D{?RHo93@*RCnMo%XsP$qS~O>F za?4b+m!XoC)$v3_bgPTZ(imOj0KLo9sqCbzreT1D|D>o@IW1JcCfhXN=#)E}stgDM z8cyaN4r3W~fmuw=W?*a;ko$cCx{#by2du?tZBfeKw=~n3L08NjuIt3-L&MX|G7Sp&uu078L#ZX^FkRzxk?E$vB@f^%)L`8e zOWWWs-8tzHk*DN$uN> z4`844OnNLG!h|=ZM(mu3&U+DBb&V{7g~88aLuXQ=5FHfJ5OmG+`bLFmMsUA7A)*n; zEU+!`pXPPWx4Dq(91u|w((Y;&$Y@kUx$ge|X+?KC`=GVq_Eum+a6{Yj-;;Vp)C;+* ztW@UW6Wk-BehBB7Rjif7cX4+sUa9Tw>RcQNcZn$OLU6-B9gVk!ESL@l>T-CZv-9jU zo`=y`t7{3mie|&C`bKc{G^(eJ$!XT`dl?Qm!rXU*#qB11MQjcjV|#;_>P^pUqlyNVNSHoJ7BDEg$O5{s36-F&2qdqW4`QJ cPG8*n!b#6@%yZ^S1P;GeqMp!!NBM{R3#X%HnE(I) delta 780 zcmZuuO=uHA6n>kZ$=}XQHk(atW9o{HB50Auh~mk#_=BlNLdmI;Njpgv*WGk8Nog<1 zO*{yC@G9b=^dKJEgLo4`d-ozYB&7V=mg9IoylS8AlMDl6c7#!sh1f4wrE7 zvObtXaAL$49`AO|60_~lYAPdrNN~|@HaRz!d#(6}W*|5wv6^RfMT0r(;kuFbHOs6c z+>TkbIq&n>owSZ1FPRm$E6!TNyyXh69B3&&ex+J6mpn@nbwfii7&8@`CrJd;BZ1!b zw#}8_2A$wNdLvE7^F*GeuBu-Km^IJ@#lUA0MsRmjs2?1DnF{&dEAHmV4s-4) cpa;hXR7(c@x!>elYi(vZ!IOqEp-17yA6^-@NB{r; diff --git a/lib/migrations/versions/f11961b08200_create_association_table_companies_devs.py b/lib/migrations/versions/f11961b08200_create_association_table_companies_devs.py new file mode 100644 index 000000000..b2d2ad935 --- /dev/null +++ b/lib/migrations/versions/f11961b08200_create_association_table_companies_devs.py @@ -0,0 +1,34 @@ +"""Create association table companies_devs + +Revision ID: f11961b08200 +Revises: 34f6305e2835 +Create Date: 2024-09-19 16:44:45.394932 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f11961b08200' +down_revision = '34f6305e2835' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('companies_devs', + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('dev_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['company_id'], ['companies.id'], name=op.f('fk_companies_devs_company_id_companies')), + sa.ForeignKeyConstraint(['dev_id'], ['devs.id'], name=op.f('fk_companies_devs_dev_id_devs')), + sa.PrimaryKeyConstraint('company_id', 'dev_id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('companies_devs') + # ### end Alembic commands ### diff --git a/lib/models.py b/lib/models.py index d211e4718..e5aac5ae9 100644 --- a/lib/models.py +++ b/lib/models.py @@ -1,4 +1,4 @@ -from sqlalchemy import ForeignKey, Column, Integer, String, MetaData +from sqlalchemy import ForeignKey, Column, Integer, String, MetaData, Table from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declarative_base @@ -9,6 +9,16 @@ Base = declarative_base(metadata=metadata) +companies_devs = Table('companies_devs', + Base.metadata, + Column('company_id', + ForeignKey('companies.id'), + primary_key=True), + Column('dev_id', + ForeignKey('devs.id'), + primary_key=True), + extend_existing=True) + class Company(Base): __tablename__ = 'companies' @@ -17,6 +27,12 @@ class Company(Base): name = Column(String()) founding_year = Column(Integer()) + # Python mapping of relationships + freebies = relationship('Freebie', backref="company") + devs = relationship('Dev', + secondary=companies_devs, + back_populates='companies') + def __repr__(self): return f'' @@ -27,6 +43,12 @@ class Dev(Base): id = Column(Integer(), primary_key=True) name = Column(String()) + # Python mapping of relationships + freebies = relationship('Freebie', backref="dev") + companies = relationship('Company', + secondary=companies_devs, + back_populates="devs") + def __repr__(self): return f'' @@ -40,6 +62,10 @@ class Freebie(Base): item_name = Column(String(), nullable=False) value = Column(Integer(), nullable=False) - # Foreign Keys definitions + # Foreign Keys definitions (relationships at the database level) dev_id = Column(Integer(), ForeignKey('devs.id')) company_id = Column(Integer(), ForeignKey('companies.id')) + + def __repr__(self): + return f"Freebie(item-name={self.item_name}, " + \ + f"value={self.value})" diff --git a/lib/seed.py b/lib/seed.py index 6d8f05485..2508a88f6 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -5,6 +5,7 @@ # We import Faker, a Python package that generates fake data. from faker import Faker +from datetime import datetime # We import random to generate random numbers. import random @@ -13,28 +14,65 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -# We import the Freebie class from the models module. -from models import Freebie +# We import the model classes from the models module. +from models import Freebie, Company, Dev if __name__ == '__main__': - # We create an engine object that represents the core interface to + # Create an engine object that represents the core interface to # the database. engine = create_engine('sqlite:///freebies.db') - # We create a sessionmaker object that creates new Session objects + # Create a sessionmaker object that creates new Session objects # bound to the engine object. Session = sessionmaker(bind=engine) - # We create a new session object from the sessionmaker. + # Create a new session object from the sessionmaker. session = Session() - # We delete all the freebies in the database. + # Delete all the freebies in the database. session.query(Freebie).delete() - # We create a fake object that can be used to generate fake data. + # Create a fake object that can be used to generate fake data. fake = Faker() - # We create a list of swag items that we can randomly select from. + # Create a list to hold all the dev objects that we create + devs = [] + + # Create 5 devs + for _ in range(5): + # Create a dev object + dev = Dev(name=fake.unique.name()) + + # Add the dev object to the devs list + devs.append(dev) + + # Add the devs to the session at once + session.bulk_save_objects(devs) + + # Create a list to hold all the Company objects that we create + companies = [] + + # Define the range for a random year + start_date = datetime.strptime('1860-01-01', "%Y-%m-%d") + end_date = datetime.now() + + for _ in range(5): + # Generate a random date + random_date = fake.date_between(start_date=start_date, + end_date=end_date) + company = Company(name=fake.unique.name(), founding_year=random_date) + companies.append(company) + + # Add and commit individually to get IDs back + session.add(company) + session.commit() + + companies.append(company) + + # Create a list to hold all the Freebie objects that we create. + freebies = [] + + # Create a list of swag items that we can randomly select from. swag_items = [ "T-shirt", "Hoodie", "Sticker", "Notebook", "Pen", "USB Drive", "Water Bottle", "Keychain", "Mouse Pad", "Lanyard", "Tote Bag", @@ -44,23 +82,20 @@ "Phone Grip", "Badge", "Button", "Puzzle", "Flashlight", "Umbrella" ] - # We create a list to hold all the freebie objects that we create. - freebies = [] - - # We loop 30 times to create 30 freebie objects. + # Loop 30 times to create 30 freebie objects. for _ in range(30): - # We create a new freebie object with a random swag item and a + # Create a new freebie object with a random swag item and a # value between 1 and 3000. freebie = Freebie(item_name=random.choice(swag_items), value=random.randint(1, 3000)) - # We append the freebie object to the list of freebies. + # Append the freebie object to the list of freebies. freebies.append(freebie) - # We add the freebies to the session. + # Add the freebies to the session. session.bulk_save_objects(freebies) - # We commit the session. + # Commit the session. session.commit() - # We close the session. + # Close the session. session.close() From 0d4399e9195e3434629203151b1c53a1bca18bb6 Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 17:42:03 +0300 Subject: [PATCH 3/8] feat: make company_id non-nullable - Updated freebies.db binary file. - Created migration to make company_id non-nullable. - Updated Freebie model to set company_id as non-nullable. - Modified seed script to delete all Company and Dev entries before seeding data and to assign random company_id to Freebie objects. --- lib/freebies.db | Bin 32768 -> 36864 bytes ...ac3a_make_company_id_to_be_non_nullable.py | 36 ++++++++++++++++++ lib/models.py | 5 ++- lib/seed.py | 7 +++- 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 lib/migrations/versions/b62390c2ac3a_make_company_id_to_be_non_nullable.py diff --git a/lib/freebies.db b/lib/freebies.db index 033aa02eb7a9496a6eee54beb3d9e871c1da38ea..68f06f5109c9158bbc922d6d9b23551070b74492 100644 GIT binary patch delta 1202 zcmZvaPiz}S6vk)&+G~4vym4X^Cr&1bsu00~y$($*PK|LvCT;^xl?#H*+T(ha?QXKW zPLyzPT7(2DpjOKPiHik?!i{1Hgv6B_LgIoDg#%L61E|WShl&$pZ-bJE!)UeNy!XA| zdv6X8*~1=tYeJm_07&$R^f2^9Z$DeiO@ilk-!%CA>0tXI(bg_YF`nmv|hgFg~`!QTlv_%JR9 zM4k%n%ZtHiVkEdHOTjzxMK-q3kH&ip4<9q`-Rm~f&nmyE3FEw8Eae+IZWQs?=nkFzh?UFBunFJW}{6ki=b87 zkIGQ9DrwP)@1vSYWw)LqU)Xz52O9O@}IRNko{!SUbf}g@0a0hO}OYlWF2jj|P z<$>~(@~zTS-cuaqO{JY8rW{KPfKugNW;A1I8BqVzeF>s|HA>lM$;oAn5UepUA#>ff$A0^yM)Qb z##9lUqZb?4AGGfHzC|KQek?hRmqGzu-X^py;m0%#ZBEJYPxz!Ff*c*ot7N~?#-=TN zuF6A&IacDUY&t=cyR_D+Y!0#M_!GF3Z2UB*#zK!P*9@0F6wE*5ladG~sKvVGwN2NT zbQVU3iu6Q@Jb;l?LwQ5Uh$n`!^WT!9Yhp?`yClKjQ~X&G^!YI^9MhWP`I_mYOO8*Z a^_a?rgo9thakiM(ILL(M!xhKg7XAgUKq*lG delta 1274 zcmZuwL1^1n7=D&y*|DSdtk`ZEJMQFlAq$;~WG8Xd!@zOe!J4$eHVuO^_8h<3!itp1 zax&-QY-6-NtZbOEN`;!p?&19FHZquS2<)X=~MklA~1yw7kIsNq!0nKGgE!XUL&Ss0bN|o9T zXNy|8p&6={S97zOuz=2GFPR&4=2|`F-7a<64p7Xi+MKE_q(cIl&R#XwJ;zll&IWTG z-I#@nq3Uz#zUWt1sB2PXg#q;r)fbF{s^`E!I(Sqx+f1pooQ=(nW5d-2-?KTINXJes*oJ!+XuS)rC=GbsQ(%Uu|E)iu|d zFLy@TN8~B_9oF{~`G(vg+k}w{DUx#}j{m_=@nigG690nl;;-;c{1I;9_i!1X$0^K9 z&!i{PJ~+G~_2n}ndW}P9&0l04Rv7+V&YXZmAvP(Z40K&$z4aC~?eNFRw1}nwS*Dw? zf2C?Mw#9_XSV}}$fc?>2h|z9GD#?leX+__Torc!F-`j!-VH@UV@UAp2qH(|$X`{)+ zzwjv$O~A2C-FmknUc<@Zd9}Nf^0)*O$4bTc|XHg?iY;C@UV zc3p<9y3=>s{4|aoXALwlIj$OgALHS9vFmw`9XU)$BANo#HR}08I-X}S?%>p@h-LsS zwW!-P?WR)lKV|OIM0D7_>U2ES^b}t>^ahEDD9xchs(R)+ylVUfd1M&-iqq}Do@pa` zB^G`;_RtTp(91~lMnZg*eq=iy6wz5fn4@f~9xPV4K!hVrR8u=G)AhKUGH;yJ None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('freebies', schema=None) as batch_op: + batch_op.alter_column('company_id', + existing_type=sa.INTEGER(), + nullable=False) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('freebies', schema=None) as batch_op: + batch_op.alter_column('company_id', + existing_type=sa.INTEGER(), + nullable=True) + + # ### end Alembic commands ### diff --git a/lib/models.py b/lib/models.py index e5aac5ae9..1cf2996dc 100644 --- a/lib/models.py +++ b/lib/models.py @@ -64,8 +64,11 @@ class Freebie(Base): # Foreign Keys definitions (relationships at the database level) dev_id = Column(Integer(), ForeignKey('devs.id')) - company_id = Column(Integer(), ForeignKey('companies.id')) + company_id = Column(Integer(), ForeignKey('companies.id'), nullable=False) def __repr__(self): return f"Freebie(item-name={self.item_name}, " + \ f"value={self.value})" + + def print_details(self): + print(f"{self.dev} owns a {self.item_name} from {self.company.name}") diff --git a/lib/seed.py b/lib/seed.py index 2508a88f6..3367721e4 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -31,6 +31,8 @@ # Delete all the freebies in the database. session.query(Freebie).delete() + session.query(Company).delete() + session.query(Dev).delete() # Create a fake object that can be used to generate fake data. fake = Faker() @@ -85,9 +87,10 @@ # Loop 30 times to create 30 freebie objects. for _ in range(30): # Create a new freebie object with a random swag item and a - # value between 1 and 3000. + # value between 1 and 3000, and a random company id. freebie = Freebie(item_name=random.choice(swag_items), - value=random.randint(1, 3000)) + value=random.randint(1, 3000), + company_id=random.randint(1, len(companies))) # Append the freebie object to the list of freebies. freebies.append(freebie) From c2a8198083bf1ae26d17359aa5f122e271520d0c Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 19:54:51 +0300 Subject: [PATCH 4/8] feat(models): add session management and new methods - Import `create_engine` and `sessionmaker` from SQLAlchemy - Create engine and session for database interactions - Add `oldest_company` class method to `Company` model - Add `give_freebie` method to `Company` model with type checks and error handling - Define `FreebieAlreadyGivenError` exception --- lib/models.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/models.py b/lib/models.py index 1cf2996dc..4ba452a39 100644 --- a/lib/models.py +++ b/lib/models.py @@ -1,5 +1,5 @@ -from sqlalchemy import ForeignKey, Column, Integer, String, MetaData, Table -from sqlalchemy.orm import relationship, backref +from sqlalchemy import ForeignKey, Column, Integer, String, MetaData, Table, create_engine +from sqlalchemy.orm import relationship, backref, sessionmaker from sqlalchemy.ext.declarative import declarative_base convention = { @@ -7,7 +7,10 @@ } metadata = MetaData(naming_convention=convention) +engine = create_engine('sqlite:///freebies.db') Base = declarative_base(metadata=metadata) +Session = sessionmaker(bind=engine) +session = Session() companies_devs = Table('companies_devs', Base.metadata, @@ -36,6 +39,20 @@ class Company(Base): def __repr__(self): return f'' + @classmethod + def oldest_company(cls): + return session.query(cls).order_by(cls.founding_year).first() + + def give_freebie(dev, freebie): + if not isinstance(dev, Dev): + raise TypeError('dev argument is not of type Dev.') + if not isinstance(freebie, Freebie): + raise TypeError('freebie argument is not of type freebie.') + if freebie.dev_id: + raise FreebieAlreadyGivenError + else: + dev.freebies.append(freebie) + class Dev(Base): __tablename__ = 'devs' @@ -72,3 +89,7 @@ def __repr__(self): def print_details(self): print(f"{self.dev} owns a {self.item_name} from {self.company.name}") + + +class FreebieAlreadyGivenError(Exception): + pass From a29372c35d3d5dee4913f99f7b379be7cf609df6 Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 20:17:43 +0300 Subject: [PATCH 5/8] feat(models): add methods to give away and check receipt of freebies - Added `received_one` method to check if a `Dev` has received a specific item - Added `give_away` method to transfer a freebie to another `Dev` - Added `FreebieNotMineToGiveError` exception for unauthorized transfer attempts --- lib/models.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/models.py b/lib/models.py index 4ba452a39..dfdbefbb4 100644 --- a/lib/models.py +++ b/lib/models.py @@ -69,6 +69,20 @@ class Dev(Base): def __repr__(self): return f'' + def received_one(self, item_name): + return any(freebie.item_name == item_name for freebie in self.freebies) + + def give_away(self, dev, freebie): + if not isinstance(dev, Dev): + raise TypeError("dev argument must be of type Dev") + if not isinstance(freebie, Freebie): + raise TypeError("freebie argument must be of type Freebie") + if freebie not in self.freebies: + raise FreebieNotMineToGiveError + else: + freebie.dev = dev + session.commit() + class Freebie(Base): # Mapped table name at the database level @@ -93,3 +107,7 @@ def print_details(self): class FreebieAlreadyGivenError(Exception): pass + + +class FreebieNotMineToGiveError(Exception): + pass From 9f428a27cde89c04d896a0a5ae9904ecc2d1094e Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 20:34:50 +0300 Subject: [PATCH 6/8] feat(debug): enhance debug script with session management and queries Added session management using SQLAlchemy's sessionmaker to the `debug.py`. Included queries to: 1. Retrieve and print the company name for the first freebie. 2. Retrieve and print a collection of all freebies for the first company. --- lib/debug.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/debug.py b/lib/debug.py index 4f922eb69..d19a7eac1 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -1,9 +1,23 @@ #!/usr/bin/env python3 from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker -from models import Company, Dev +from models import Company, Dev, Freebie if __name__ == '__main__': engine = create_engine('sqlite:///freebies.db') - import ipdb; ipdb.set_trace() + Session = sessionmaker(bind=engine) + session = Session() + + # 1. Freebie.company - return the Company instance for a Freebie object + freebie = session.query(Freebie).first() + print('1. Company name for the first freebie:', freebie.company) + + # 2. Company.freebies - returns a collection of all freebies for the company + company = session.query(Company).first() + print('2. Collection of all freebies for first company', company.freebies) + + session.close() + import ipdb + ipdb.set_trace() From 93b2d294243244c7481227c763403310651a8164 Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 21:56:36 +0300 Subject: [PATCH 7/8] feat(debug): enhance debug script with error handling and give_freebie tests - Import FreebieAlreadyGivenError and FreebieNotMineToGiveError in debug.py - Add tests for FreebieNotMineToGiveError and give_freebie function in debug.py - Fix give_freebie method in models.py to include self parameter and raise FreebieNotMineToGiveError --- lib/debug.py | 25 ++++++++++++++++++++++--- lib/models.py | 4 +++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/debug.py b/lib/debug.py index d19a7eac1..3d6bf067f 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -3,7 +3,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from models import Company, Dev, Freebie +from models import Company, Dev, Freebie, FreebieAlreadyGivenError, FreebieNotMineToGiveError if __name__ == '__main__': engine = create_engine('sqlite:///freebies.db') @@ -12,11 +12,30 @@ # 1. Freebie.company - return the Company instance for a Freebie object freebie = session.query(Freebie).first() - print('1. Company name for the first freebie:', freebie.company) + print('1. Freebie.company:', freebie.company) # 2. Company.freebies - returns a collection of all freebies for the company company = session.query(Company).first() - print('2. Collection of all freebies for first company', company.freebies) + print('2. Company.freebies: ', company.freebies) + + # 3. Company.give_freebie - associate a freebie with a dev + print('3. Company.give_freebie') + company = session.query(Company).first() + dev = session.query(Dev).first() + first_freebie_not_in_company = session.query(Freebie).filter( + Freebie.id != company.id).first() + # Test for FreebieNotMineToGiveError + first_freebie_not_owned_by_company = session.query(Freebie).filter( + Freebie.company_id != company.id).first() + try: + company.give_freebie(dev, first_freebie_not_owned_by_company) + except FreebieNotMineToGiveError as e: + print(f"3(b). Test for FreebieNotMineToGiveError passed") + # Test for give_freebie + freebie_in_company = company.freebies[0] + company.give_freebie(dev, freebie_in_company) + print('3(c). Test for give_freebie {freebie.dev}: ', + freebie_in_company.dev) session.close() import ipdb diff --git a/lib/models.py b/lib/models.py index dfdbefbb4..5efd21f13 100644 --- a/lib/models.py +++ b/lib/models.py @@ -43,13 +43,15 @@ def __repr__(self): def oldest_company(cls): return session.query(cls).order_by(cls.founding_year).first() - def give_freebie(dev, freebie): + def give_freebie(self, dev, freebie): if not isinstance(dev, Dev): raise TypeError('dev argument is not of type Dev.') if not isinstance(freebie, Freebie): raise TypeError('freebie argument is not of type freebie.') if freebie.dev_id: raise FreebieAlreadyGivenError + if freebie not in self.freebies: + raise FreebieNotMineToGiveError else: dev.freebies.append(freebie) From 3f03b068572f70bfa4cb951c00871e12ba199ca2 Mon Sep 17 00:00:00 2001 From: Silvanos Eric Date: Thu, 19 Sep 2024 22:04:20 +0300 Subject: [PATCH 8/8] docs: add docstrings to models.py methods and improve existing ones Added and improved docstrings for methods in the `Company`, `Dev`, and `Freebie` classes in `models.py` to enhance code readability and provide detailed information about method functionality, parameters, and exceptions. - Added detailed docstrings to `Company.oldest_company` and `Company.give_freebie` - Added detailed docstrings to `Dev.received_one` and `Dev.give_away` - Improved existing docstrings to follow a consistent format - Added type hints and descriptions for parameters and return values - Added exception descriptions for methods that raise exceptions --- lib/models.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/lib/models.py b/lib/models.py index 5efd21f13..f3ee1e0d6 100644 --- a/lib/models.py +++ b/lib/models.py @@ -37,22 +37,52 @@ class Company(Base): back_populates='companies') def __repr__(self): + """Return a string representation of Company object.""" return f'' @classmethod def oldest_company(cls): + """Return the oldest company in the database.""" return session.query(cls).order_by(cls.founding_year).first() def give_freebie(self, dev, freebie): + """Associates the freebie with the dev.""" if not isinstance(dev, Dev): raise TypeError('dev argument is not of type Dev.') if not isinstance(freebie, Freebie): raise TypeError('freebie argument is not of type freebie.') + + @classmethod + def oldest_company(cls): + """ + Return the oldest company in the database. + + The oldest company is determined by the founding year of the company. + """ + return session.query(cls).order_by(cls.founding_year).first() + + def give_freebie(self, dev, freebie): + """ + Associate a freebie with a dev. + + :param dev: the dev to associate the freebie with + :type dev: models.Dev + :param freebie: the freebie to associate with the dev + :type freebie: models.Freebie + :raises TypeError: if dev is not of type models.Dev or freebie is not of type models.Freebie + :raises FreebieAlreadyGivenError: if the freebie has already been given to another dev + :raises FreebieNotMineToGiveError: if the freebie does not belong to the company + """ + if not isinstance(dev, Dev): + raise TypeError('dev argument is not of type models.Dev.') + if not isinstance(freebie, Freebie): + raise TypeError('freebie argument is not of type models.Freebie.') if freebie.dev_id: raise FreebieAlreadyGivenError if freebie not in self.freebies: raise FreebieNotMineToGiveError else: + # Add the freebie to the dev's freebies dev.freebies.append(freebie) @@ -69,20 +99,79 @@ class Dev(Base): back_populates="devs") def __repr__(self): + """ + Return a string representation of the Dev object. + """ return f'' def received_one(self, item_name): + """ + Returns True if any of the freebies associated with the dev has the given + item_name, otherwise returns False. + + :param item_name: the item_name to look for in the freebies + :type item_name: str + :return: whether the item_name was found in the freebies + :rtype: bool + """ return any(freebie.item_name == item_name for freebie in self.freebies) def give_away(self, dev, freebie): + """ + Changes the freebie's dev to be the given dev; your code should only + make the change if the freebie belongs to the dev who's giving it away. + + :param dev: the dev to associate the freebie with + :type dev: models.Dev + :param freebie: the freebie to associate with the dev + :type freebie: models.Freebie + :raises TypeError: if dev is not of type models.Dev or freebie is not of type models.Freebie + :raises FreebieNotMineToGiveError: if the freebie does not belong to the dev + """ if not isinstance(dev, Dev): raise TypeError("dev argument must be of type Dev") if not isinstance(freebie, Freebie): raise TypeError("freebie argument must be of type Freebie") if freebie not in self.freebies: raise FreebieNotMineToGiveError + + def received_one(self, item_name): + """ + Returns True if any of the freebies associated with the dev has the given + item_name, otherwise returns False. + + :param item_name: the item_name to look for in the freebies + :type item_name: str + :return: whether the item_name was found in the freebies + :rtype: bool + """ + return any(freebie.item_name == item_name for freebie in self.freebies) + + def give_away(self, dev, freebie): + """ + Changes the freebie's dev to be the given dev; your code should only + make the change if the freebie belongs to the dev who's giving it away. + + :param dev: the dev to associate the freebie with + :type dev: models.Dev + :param freebie: the freebie to associate with the dev + :type freebie: models.Freebie + :raises TypeError: if dev is not of type models.Dev or freebie is not of type models.Freebie + :raises FreebieNotMineToGiveError: if the freebie does not belong to the dev + """ + # Check the type of the arguments + if not isinstance(dev, Dev): + raise TypeError("dev argument must be of type Dev") + if not isinstance(freebie, Freebie): + raise TypeError("freebie argument must be of type Freebie") + + # Check if the freebie is in the dev's freebies + if freebie not in self.freebies: + raise FreebieNotMineToGiveError else: + # Change the freebie's dev to the given dev freebie.dev = dev + # Commit the changes to the database session.commit() @@ -100,11 +189,19 @@ class Freebie(Base): company_id = Column(Integer(), ForeignKey('companies.id'), nullable=False) def __repr__(self): + """Return a string representation of Freebie object.""" return f"Freebie(item-name={self.item_name}, " + \ f"value={self.value})" def print_details(self): - print(f"{self.dev} owns a {self.item_name} from {self.company.name}") + """ + Prints a nicely formatted string with details about the freebie. + + :return: None + """ + # Print the freebie details + print(f"{self.dev} owns a {self.item_name} " + f"from {self.company.name}") class FreebieAlreadyGivenError(Exception):