From 33abc1ed4638c7a2eccbc15ac1c11805fd9316bb Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 15:14:23 +0300 Subject: [PATCH 01/22] Pip --- Pipfile | 10 +- Pipfile.lock | 484 +++++++++++++++++++++++++++++---------------------- lib/debug.py | 2 +- 3 files changed, 281 insertions(+), 215 deletions(-) diff --git a/Pipfile b/Pipfile index 63c79cd98..c8459217a 100644 --- a/Pipfile +++ b/Pipfile @@ -5,12 +5,12 @@ name = "pypi" [packages] alembic = "1.8.1" -importlib-metadata = "6.0.0" -importlib-resources = "5.10.0" -ipdb = "0.13.9" -sqlalchemy = "1.4.42" +importlib-metadata = "*" +importlib-resources = "*" +ipdb = "*" +sqlalchemy = "*" [dev-packages] [requires] -python_full_version = "3.8.13" +python_full_version = "3.12.2" diff --git a/Pipfile.lock b/Pipfile.lock index 3923f4a97..6ddb5cff7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "fa62c636b7ae5acb4993fc7b007a651c55b562aa29962a9a8585ec314b352ae6" + "sha256": "4c91dd3989c6eb2ff779c57681d6f0b0076f241871c26200c6ad27d035f85a3e" }, "pipfile-spec": 6, "requires": { - "python_full_version": "3.8.13" + "python_full_version": "3.12.2" }, "sources": [ { @@ -18,306 +18,372 @@ "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:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", + "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2" ], - "version": "==2.2.1" + "markers": "python_version >= '3.8'", + "version": "==3.0.0" }, - "backcall": { + "colorama": { "hashes": [ - "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", - "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], - "version": "==0.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" }, "decorator": { "hashes": [ - "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", - "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" + "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", + "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a" ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==5.1.1" + "markers": "python_version >= '3.8'", + "version": "==5.2.1" }, "executing": { "hashes": [ - "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc", - "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107" + "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", + "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755" ], - "version": "==1.2.0" + "markers": "python_version >= '3.8'", + "version": "==2.2.0" + }, + "greenlet": { + "hashes": [ + "sha256:00cd814b8959b95a546e47e8d589610534cfb71f19802ea8a2ad99d95d702057", + "sha256:02a98600899ca1ca5d3a2590974c9e3ec259503b2d6ba6527605fcd74e08e207", + "sha256:02f5972ff02c9cf615357c17ab713737cccfd0eaf69b951084a9fd43f39833d3", + "sha256:055916fafad3e3388d27dd68517478933a97edc2fc54ae79d3bec827de2c64c4", + "sha256:0a16fb934fcabfdfacf21d79e6fed81809d8cd97bc1be9d9c89f0e4567143d7b", + "sha256:1592a615b598643dbfd566bac8467f06c8c8ab6e56f069e573832ed1d5d528cc", + "sha256:1919cbdc1c53ef739c94cf2985056bcc0838c1f217b57647cbf4578576c63825", + "sha256:1e4747712c4365ef6765708f948acc9c10350719ca0545e362c24ab973017370", + "sha256:1e76106b6fc55fa3d6fe1c527f95ee65e324a13b62e243f77b48317346559708", + "sha256:1f72667cc341c95184f1c68f957cb2d4fc31eef81646e8e59358a10ce6689457", + "sha256:2593283bf81ca37d27d110956b79e8723f9aa50c4bcdc29d3c0543d4743d2763", + "sha256:2dc5c43bb65ec3669452af0ab10729e8fdc17f87a1f2ad7ec65d4aaaefabf6bf", + "sha256:3091bc45e6b0c73f225374fefa1536cd91b1e987377b12ef5b19129b07d93ebe", + "sha256:354f67445f5bed6604e493a06a9a49ad65675d3d03477d38a4db4a427e9aad0e", + "sha256:3885f85b61798f4192d544aac7b25a04ece5fe2704670b4ab73c2d2c14ab740d", + "sha256:3ab7194ee290302ca15449f601036007873028712e92ca15fc76597a0aeb4c59", + "sha256:3aeca9848d08ce5eb653cf16e15bb25beeab36e53eb71cc32569f5f3afb2a3aa", + "sha256:44671c29da26539a5f142257eaba5110f71887c24d40df3ac87f1117df589e0e", + "sha256:45f9f4853fb4cc46783085261c9ec4706628f3b57de3e68bae03e8f8b3c0de51", + "sha256:4bd139e4943547ce3a56ef4b8b1b9479f9e40bb47e72cc906f0f66b9d0d5cab3", + "sha256:4fefc7aa68b34b9224490dfda2e70ccf2131368493add64b4ef2d372955c207e", + "sha256:6629311595e3fe7304039c67f00d145cd1d38cf723bb5b99cc987b23c1433d61", + "sha256:6fadd183186db360b61cb34e81117a096bff91c072929cd1b529eb20dd46e6c5", + "sha256:71566302219b17ca354eb274dfd29b8da3c268e41b646f330e324e3967546a74", + "sha256:7409796591d879425997a518138889d8d17e63ada7c99edc0d7a1c22007d4907", + "sha256:752f0e79785e11180ebd2e726c8a88109ded3e2301d40abced2543aa5d164275", + "sha256:7791dcb496ec53d60c7f1c78eaa156c21f402dda38542a00afc3e20cae0f480f", + "sha256:782743700ab75716650b5238a4759f840bb2dcf7bff56917e9ffdf9f1f23ec59", + "sha256:7c9896249fbef2c615853b890ee854f22c671560226c9221cfd27c995db97e5c", + "sha256:85f3e248507125bf4af607a26fd6cb8578776197bd4b66e35229cdf5acf1dfbf", + "sha256:89c69e9a10670eb7a66b8cef6354c24671ba241f46152dd3eed447f79c29fb5b", + "sha256:8cb8553ee954536500d88a1a2f58fcb867e45125e600e80f586ade399b3f8819", + "sha256:9ae572c996ae4b5e122331e12bbb971ea49c08cc7c232d1bd43150800a2d6c65", + "sha256:9c7b15fb9b88d9ee07e076f5a683027bc3befd5bb5d25954bb633c385d8b737e", + "sha256:9ea5231428af34226c05f927e16fc7f6fa5e39e3ad3cd24ffa48ba53a47f4240", + "sha256:a31ead8411a027c2c4759113cf2bd473690517494f3d6e4bf67064589afcd3c5", + "sha256:a8fa80665b1a29faf76800173ff5325095f3e66a78e62999929809907aca5659", + "sha256:ad053d34421a2debba45aa3cc39acf454acbcd025b3fc1a9f8a0dee237abd485", + "sha256:b24c7844c0a0afc3ccbeb0b807adeefb7eff2b5599229ecedddcfeb0ef333bec", + "sha256:b50a8c5c162469c3209e5ec92ee4f95c8231b11db6a04db09bbe338176723bb8", + "sha256:ba30e88607fb6990544d84caf3c706c4b48f629e18853fc6a646f82db9629418", + "sha256:bf3fc9145141250907730886b031681dfcc0de1c158f3cc51c092223c0f381ce", + "sha256:c23ea227847c9dbe0b3910f5c0dd95658b607137614eb821e6cbaecd60d81cc6", + "sha256:c3cc1a3ed00ecfea8932477f729a9f616ad7347a5e55d50929efa50a86cb7be7", + "sha256:c49e9f7c6f625507ed83a7485366b46cbe325717c60837f7244fc99ba16ba9d6", + "sha256:d0cb7d47199001de7658c213419358aa8937df767936506db0db7ce1a71f4a2f", + "sha256:d8009ae46259e31bc73dc183e402f548e980c96f33a6ef58cc2e7865db012e13", + "sha256:da956d534a6d1b9841f95ad0f18ace637668f680b1339ca4dcfb2c1837880a0b", + "sha256:dcb9cebbf3f62cb1e5afacae90761ccce0effb3adaa32339a0670fe7805d8068", + "sha256:decb0658ec19e5c1f519faa9a160c0fc85a41a7e6654b3ce1b44b939f8bf1325", + "sha256:df4d1509efd4977e6a844ac96d8be0b9e5aa5d5c77aa27ca9f4d3f92d3fcf330", + "sha256:eeb27bece45c0c2a5842ac4c5a1b5c2ceaefe5711078eed4e8043159fa05c834", + "sha256:efcdfb9df109e8a3b475c016f60438fcd4be68cd13a365d42b35914cdab4bb2b", + "sha256:fd9fb7c941280e2c837b603850efc93c999ae58aae2b40765ed682a6907ebbc5", + "sha256:fe46d4f8e94e637634d54477b0cfabcf93c53f29eedcbdeecaf2af32029b4421" + ], + "markers": "python_version >= '3.9'", + "version": "==3.2.2" }, "importlib-metadata": { "hashes": [ - "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad", - "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d" + "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", + "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" ], "index": "pypi", - "version": "==6.0.0" + "markers": "python_version >= '3.9'", + "version": "==8.7.0" }, "importlib-resources": { "hashes": [ - "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6", - "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484" + "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", + "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec" ], "index": "pypi", - "version": "==5.10.2" + "markers": "python_version >= '3.9'", + "version": "==6.5.2" }, "ipdb": { "hashes": [ - "sha256:c23b6736f01fd4586cc2ecbebdf79a5eb454796853e1cd8f2ed3b7b91d4a3e93", - "sha256:f74c2f741c18b909eaf89f19fde973f745ac721744aa1465888ce45813b63a9c" + "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", + "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726" ], "index": "pypi", - "version": "==0.13.11" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.13.13" }, "ipython": { "hashes": [ - "sha256:da01e6df1501e6e7c32b5084212ddadd4ee2471602e2cf3e0190f4de6b0ea481", - "sha256:f3bf2c08505ad2c3f4ed5c46ae0331a8547d36bf4b21a451e8ae80c0791db95b" + "sha256:62a9373dbc12f28f9feaf4700d052195bf89806279fc8ca11f3f54017d04751b", + "sha256:fef5e33c4a1ae0759e0bba5917c9db4eb8c53fee917b6a526bd973e1ca5159f6" ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==8.8.0" + "markers": "python_version >= '3.11'", + "version": "==9.2.0" + }, + "ipython-pygments-lexers": { + "hashes": [ + "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", + "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c" + ], + "markers": "python_version >= '3.8'", + "version": "==1.1.1" }, "jedi": { "hashes": [ - "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e", - "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612" + "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", + "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9" ], "markers": "python_version >= '3.6'", - "version": "==0.18.2" + "version": "==0.19.2" }, "mako": { "hashes": [ - "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818", - "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34" + "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", + "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59" ], - "markers": "python_version >= '3.7'", - "version": "==1.2.4" + "markers": "python_version >= '3.8'", + "version": "==1.3.10" }, "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:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", + "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", + "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", + "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", + "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", + "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", + "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", + "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", + "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", + "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", + "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", + "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", + "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", + "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", + "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", + "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", + "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", + "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", + "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", + "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", + "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", + "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", + "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", + "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", + "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", + "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", + "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", + "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", + "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", + "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", + "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", + "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", + "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", + "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", + "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", + "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", + "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", + "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", + "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", + "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", + "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", + "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", + "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", + "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", + "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", + "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", + "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", + "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", + "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", + "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", + "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", + "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", + "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", + "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", + "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", + "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", + "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", + "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", + "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", + "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", + "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "markers": "python_version >= '3.9'", + "version": "==3.0.2" }, "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" - }, - "pexpect": { - "hashes": [ - "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", - "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" - ], - "markers": "sys_platform != 'win32'", - "version": "==4.8.0" - }, - "pickleshare": { - "hashes": [ - "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", - "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" - ], - "version": "==0.7.5" + "version": "==0.8.4" }, "prompt-toolkit": { "hashes": [ - "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63", - "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305" - ], - "markers": "python_full_version >= '3.6.2'", - "version": "==3.0.36" - }, - "ptyprocess": { - "hashes": [ - "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", - "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" + "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", + "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed" ], - "version": "==0.7.0" + "markers": "python_version >= '3.8'", + "version": "==3.0.51" }, "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:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" ], - "markers": "python_version >= '3.6'", - "version": "==2.14.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "markers": "python_version >= '3.8'", + "version": "==2.19.1" }, "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:023b3ee6169969beea3bb72312e44d8b7c27c75b347942d943cf49397b7edeb5", + "sha256:03968a349db483936c249f4d9cd14ff2c296adfa1290b660ba6516f973139582", + "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b", + "sha256:087b6b52de812741c27231b5a3586384d60c353fbd0e2f81405a814b5591dc8b", + "sha256:0b3dbf1e7e9bc95f4bac5e2fb6d3fb2f083254c3fdd20a1789af965caf2d2348", + "sha256:118c16cd3f1b00c76d69343e38602006c9cfb9998fa4f798606d28d63f23beda", + "sha256:1936af879e3db023601196a1684d28e12f19ccf93af01bf3280a3262c4b6b4e5", + "sha256:1e3f196a0c59b0cae9a0cd332eb1a4bda4696e863f4f1cf84ab0347992c548c2", + "sha256:23a8825495d8b195c4aa9ff1c430c28f2c821e8c5e2d98089228af887e5d7e29", + "sha256:293cd444d82b18da48c9f71cd7005844dbbd06ca19be1ccf6779154439eec0b8", + "sha256:32f9dc8c44acdee06c8fc6440db9eae8b4af8b01e4b1aee7bdd7241c22edff4f", + "sha256:34ea30ab3ec98355235972dadc497bb659cc75f8292b760394824fab9cf39826", + "sha256:3d3549fc3e40667ec7199033a4e40a2f669898a00a7b18a931d3efb4c7900504", + "sha256:41836fe661cc98abfae476e14ba1906220f92c4e528771a8a3ae6a151242d2ae", + "sha256:4d44522480e0bf34c3d63167b8cfa7289c1c54264c2950cc5fc26e7850967e45", + "sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443", + "sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23", + "sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576", + "sha256:598d9ebc1e796431bbd068e41e4de4dc34312b7aa3292571bb3674a0cb415dd1", + "sha256:5b14e97886199c1f52c14629c11d90c11fbb09e9334fa7bb5f6d068d9ced0ce0", + "sha256:5e22575d169529ac3e0a120cf050ec9daa94b6a9597993d1702884f6954a7d71", + "sha256:60c578c45c949f909a4026b7807044e7e564adf793537fc762b2489d522f3d11", + "sha256:6145afea51ff0af7f2564a05fa95eb46f542919e6523729663a5d285ecb3cf5e", + "sha256:6375cd674fe82d7aa9816d1cb96ec592bac1726c11e0cafbf40eeee9a4516b5f", + "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8", + "sha256:6ab60a5089a8f02009f127806f777fca82581c49e127f08413a66056bd9166dd", + "sha256:725875a63abf7c399d4548e686debb65cdc2549e1825437096a0af1f7e374814", + "sha256:7492967c3386df69f80cf67efd665c0f667cee67032090fe01d7d74b0e19bb08", + "sha256:81965cc20848ab06583506ef54e37cf15c83c7e619df2ad16807c03100745dea", + "sha256:81c24e0c0fde47a9723c81d5806569cddef103aebbf79dbc9fcbb617153dea30", + "sha256:81eedafa609917040d39aa9332e25881a8e7a0862495fcdf2023a9667209deda", + "sha256:81f413674d85cfd0dfcd6512e10e0f33c19c21860342a4890c3a2b59479929f9", + "sha256:8280856dd7c6a68ab3a164b4a4b1c51f7691f6d04af4d4ca23d6ecf2261b7923", + "sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df", + "sha256:8b4af17bda11e907c51d10686eda89049f9ce5669b08fbe71a29747f1e876036", + "sha256:90144d3b0c8b139408da50196c5cad2a6909b51b23df1f0538411cd23ffa45d3", + "sha256:906e6b0d7d452e9a98e5ab8507c0da791856b2380fdee61b765632bb8698026f", + "sha256:90c11ceb9a1f482c752a71f203a81858625d8df5746d787a4786bca4ffdf71c6", + "sha256:911cc493ebd60de5f285bcae0491a60b4f2a9f0f5c270edd1c4dbaef7a38fc04", + "sha256:9a420a91913092d1e20c86a2f5f1fc85c1a8924dbcaf5e0586df8aceb09c9cc2", + "sha256:9f8c9fdd15a55d9465e590a402f42082705d66b05afc3ffd2d2eb3c6ba919560", + "sha256:a104c5694dfd2d864a6f91b0956eb5d5883234119cb40010115fd45a16da5e70", + "sha256:a373a400f3e9bac95ba2a06372c4fd1412a7cee53c37fc6c05f829bf672b8769", + "sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1", + "sha256:a8808d5cf866c781150d36a3c8eb3adccfa41a8105d031bf27e92c251e3969d6", + "sha256:b1f09b6821406ea1f94053f346f28f8215e293344209129a9c0fcc3578598d7b", + "sha256:b2ac41acfc8d965fb0c464eb8f44995770239668956dc4cdf502d1b1ffe0d747", + "sha256:b46fa6eae1cd1c20e6e6f44e19984d438b6b2d8616d21d783d150df714f44078", + "sha256:b50eab9994d64f4a823ff99a0ed28a6903224ddbe7fef56a6dd865eec9243440", + "sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f", + "sha256:c0b0e5e1b5d9f3586601048dd68f392dc0cc99a59bb5faf18aab057ce00d00b2", + "sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d", + "sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc", + "sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a", + "sha256:dd5ec3aa6ae6e4d5b5de9357d2133c07be1aff6405b136dad753a16afb6717dd", + "sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9", + "sha256:ff8e80c4c4932c10493ff97028decfdb622de69cae87e0f127a7ebe32b4069c6" ], "index": "pypi", - "version": "==1.4.46" + "markers": "python_version >= '3.7'", + "version": "==2.0.41" }, "stack-data": { "hashes": [ - "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815", - "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8" + "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", + "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695" ], - "version": "==0.6.2" + "version": "==0.6.3" }, - "tomli": { + "traitlets": { "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", + "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f" ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==2.0.1" + "markers": "python_version >= '3.8'", + "version": "==5.14.3" }, - "traitlets": { + "typing-extensions": { "hashes": [ - "sha256:32500888f5ff7bbf3b9267ea31748fa657aaf34d56d85e60f91dda7dc7f5785b", - "sha256:a1ca5df6414f8b5760f7c5f256e326ee21b581742114545b462b35ffe3f04861" + "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", + "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef" ], - "markers": "python_version >= '3.7'", - "version": "==5.8.1" + "markers": "python_version >= '3.8'", + "version": "==4.13.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:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", + "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931" ], - "markers": "python_version < '3.10'", - "version": "==3.11.0" + "markers": "python_version >= '3.9'", + "version": "==3.21.0" } }, "develop": {} diff --git a/lib/debug.py b/lib/debug.py index 4f922eb69..7dc1f33bb 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -2,7 +2,7 @@ from sqlalchemy import create_engine -from models import Company, Dev +# from models import Company, Dev if __name__ == '__main__': engine = create_engine('sqlite:///freebies.db') From ce4b05abe3fd848f9aa32f810856dfdb814135a5 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 18:33:57 +0545 Subject: [PATCH 02/22] Updated debug.py --- lib/debug.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/debug.py b/lib/debug.py index 7dc1f33bb..2d5812b30 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -2,8 +2,8 @@ from sqlalchemy import create_engine -# from models import Company, Dev +from models import Company, Dev if __name__ == '__main__': - engine = create_engine('sqlite:///freebies.db') + engine = create_engine('sqlite:///lib/freebies.db') import ipdb; ipdb.set_trace() From 58d6c244b92dedf9c624eb051e8b9591dd5aa9a1 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 18:34:08 +0545 Subject: [PATCH 03/22] Updated freebies database --- lib/freebies.db | Bin 20480 -> 24576 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/freebies.db b/lib/freebies.db index 12beb1c963e832db481e7a7493e3029e691ac4dc..d8ed4b2eb4dccdf100d1d1778678052ea377ac89 100644 GIT binary patch delta 466 zcmZozz}Rqrae}lUI|Bm)8xX?)%S0Vxadrm1yq&!KKNwhfrZMpI^6%uU=ab@@wy{xx z$E2B&m0es`ma#RoBrz!`HLWN$H7PT-7)G->2e~?ixGID=I{CONKt(4z^2yX^mZav! z=OyN*Dug)(IeRz;Dfsz^DENi?_~lhTN;O!czpi!Thq6xCd+21cXB*+nHlR{c{Jj~hg2&14(pq~`n{DWLQ z-TgoYXdo15Dg?Q@xdyrVIlBfcK!ibxHSk+t2JuHckU}*WB93AxSPjTLF3rtSe76L7 d3@r@|Obk*?lZ{i8HYt4JM-fh6000{3hqnL# delta 94 zcmZoTz}T>Wae}lUD+2=q2*UvLL>*&MRtCLzSzi7h3@m(74E((OJNfGQq&5o*EaTlQ e#dk}P$285{DB08^Db2(pd6U8yeiVU41_}T{Q53rX From 5c3d58323f68cfd8d21bd6d4c4a635c85fdd77a6 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 18:34:19 +0545 Subject: [PATCH 04/22] Updated models.py --- lib/models.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/lib/models.py b/lib/models.py index 2681bee5a..1cb2b0abb 100644 --- a/lib/models.py +++ b/lib/models.py @@ -16,14 +16,95 @@ class Company(Base): name = Column(String()) founding_year = Column(Integer()) + # Relationship to freebies + freebies = relationship('Freebie', back_populates='company') + def __repr__(self): return f'' + # Aggregate Methods + def give_freebie(self, dev, item_name, value): + """Creates a new Freebie associated with this company and the given dev""" + from sqlalchemy.orm import sessionmaker + from sqlalchemy import create_engine + + # Create new freebie + new_freebie = Freebie( + item_name=item_name, + value=value, + dev=dev, + company=self + ) + return new_freebie + + @classmethod + def oldest_company(cls): + """Returns the Company instance with the earliest founding year""" + from sqlalchemy.orm import sessionmaker + from sqlalchemy import create_engine + + engine = create_engine('sqlite:///lib/freebies.db') + Session = sessionmaker(bind=engine) + session = Session() + + oldest = session.query(cls).order_by(cls.founding_year.asc()).first() + session.close() + return oldest + + @property + def devs(self): + """Returns a collection of all devs who collected freebies from the company""" + return list(set([freebie.dev for freebie in self.freebies])) + + class Dev(Base): __tablename__ = 'devs' id = Column(Integer(), primary_key=True) - name= Column(String()) + name = Column(String()) + + # Relationship to freebies + freebies = relationship('Freebie', back_populates='dev') def __repr__(self): return f'' + + @property + def companies(self): + """Returns a collection of all companies that the Dev has collected freebies from""" + return list(set([freebie.company for freebie in self.freebies])) + + # Aggregate Methods + def received_one(self, item_name): + """Returns True if any of the freebies associated with the dev has that item_name""" + 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 if the freebie belongs to this dev""" + if freebie in self.freebies: + freebie.dev = dev + return True + return False + + +class Freebie(Base): + __tablename__ = 'freebies' + + id = Column(Integer(), primary_key=True) + item_name = Column(String(), nullable=False) + value = Column(Integer(), nullable=False) + + # Foreign Keys + dev_id = Column(Integer(), ForeignKey('devs.id'), nullable=False) + company_id = Column(Integer(), ForeignKey('companies.id'), nullable=False) + + # Relationships + dev = relationship('Dev', back_populates='freebies') + company = relationship('Company', back_populates='freebies') + + def __repr__(self): + return f'' + + def print_details(self): + """Returns a formatted string with freebie details""" + return f"{self.dev.name} owns a {self.item_name} from {self.company.name}" \ No newline at end of file From 8b71d467d5bf280369b7d1a139977599d717e27c Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 18:34:28 +0545 Subject: [PATCH 05/22] Updated seed file --- lib/seed.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/lib/seed.py b/lib/seed.py index b16becbbb..1682db6c8 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -1,3 +1,49 @@ #!/usr/bin/env python3 # Script goes here! + +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from models import Company, Dev, Freebie, Base + +# Create engine and session +engine = create_engine('sqlite:///lib/freebies.db') +Base.metadata.create_all(engine) +Session = sessionmaker(bind=engine) +session = Session() + +# Clear existing data +session.query(Freebie).delete() +session.query(Company).delete() +session.query(Dev).delete() + +# Create companies +google = Company(name="Google", founding_year=1998) +apple = Company(name="Apple", founding_year=1976) +microsoft = Company(name="Microsoft", founding_year=1975) + +# Create devs +alice = Dev(name="Alice") +bob = Dev(name="Bob") +charlie = Dev(name="Charlie") + +# Add to session +session.add_all([google, apple, microsoft, alice, bob, charlie]) +session.commit() + +# Create freebies +freebie1 = Freebie(item_name="T-shirt", value=25, dev=alice, company=google) +freebie2 = Freebie(item_name="Stickers", value=5, dev=alice, company=apple) +freebie3 = Freebie(item_name="Water Bottle", value=15, dev=bob, company=google) +freebie4 = Freebie(item_name="Laptop Bag", value=50, dev=charlie, company=microsoft) +freebie5 = Freebie(item_name="Pen", value=3, dev=bob, company=apple) + +session.add_all([freebie1, freebie2, freebie3, freebie4, freebie5]) +session.commit() + +print("Seed data created successfully!") +print(f"Companies: {session.query(Company).count()}") +print(f"Devs: {session.query(Dev).count()}") +print(f"Freebies: {session.query(Freebie).count()}") + +session.close() \ No newline at end of file From be8985bcae92bc4ca6baceb0bcebc1eadb40d235 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 18:34:47 +0545 Subject: [PATCH 06/22] Added migration for freebies table --- .../190040d5c3eb_create_freebies_table.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/migrations/versions/190040d5c3eb_create_freebies_table.py diff --git a/lib/migrations/versions/190040d5c3eb_create_freebies_table.py b/lib/migrations/versions/190040d5c3eb_create_freebies_table.py new file mode 100644 index 000000000..2659ab9bb --- /dev/null +++ b/lib/migrations/versions/190040d5c3eb_create_freebies_table.py @@ -0,0 +1,40 @@ +"""create freebies table + +Revision ID: 190040d5c3eb +Revises: 5f72c58bf48c +Create Date: 2025-05-24 18:22:16.084542 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '190040d5c3eb' +down_revision = '5f72c58bf48c' +branch_labels = None +depends_on = None + + +# revision identifiers, used by Alembic. + + +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=False), + sa.Column('company_id', sa.Integer(), nullable=False), + 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 ### \ No newline at end of file From c9682c223cda9401c491b37f6eac77240bffcb45 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:10:37 +0545 Subject: [PATCH 07/22] Updated debug.py --- lib/debug.py | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-) diff --git a/lib/debug.py b/lib/debug.py index 2d5812b30..7673f1325 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -1,9 +1,170 @@ #!/usr/bin/env python3 from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from models import Company, Dev, Freebie -from models import Company, Dev +def test_relationships_and_methods(): + """Test all the relationships and methods with the seed data""" + + # Create engine and session + engine = create_engine('sqlite:///lib/freebies.db') + Session = sessionmaker(bind=engine) + session = Session() + + print("=" * 60) + print("TESTING SQLALCHEMY RELATIONSHIPS AND METHODS") + print("=" * 60) + + # Get test data + raila = session.query(Dev).filter_by(name="Raila").first() + ruto = session.query(Dev).filter_by(name="Ruto").first() + rigachi = session.query(Dev).filter_by(name="Rigachi").first() + + odm = session.query(Company).filter_by(name="ODM").first() + uda = session.query(Company).filter_by(name="UDA").first() + dcp = session.query(Company).filter_by(name="DCP").first() + + print("\n1. TESTING BASIC RELATIONSHIPS") + print("-" * 40) + + # Test Freebie relationships + print("Testing Freebie.dev and Freebie.company:") + freebie = session.query(Freebie).filter_by(item_name="ODM T-shirts").first() + print(f" Freebie '{freebie.item_name}' belongs to dev: {freebie.dev.name}") + print(f" Freebie '{freebie.item_name}' belongs to company: {freebie.company.name}") + + # Test Company.freebies + print(f"\nTesting Company.freebies:") + print(f" ODM freebies: {[f.item_name for f in odm.freebies]}") + print(f" UDA freebies: {[f.item_name for f in uda.freebies]}") + print(f" DCP freebies: {[f.item_name for f in dcp.freebies]}") + + # Test Dev.freebies + print(f"\nTesting Dev.freebies:") + print(f" Raila's freebies: {[f.item_name for f in raila.freebies]}") + print(f" Ruto's freebies: {[f.item_name for f in ruto.freebies]}") + print(f" Rigachi's freebies: {[f.item_name for f in rigachi.freebies]}") + + # Test Company.devs (many-to-many through freebies) + print(f"\nTesting Company.devs:") + print(f" ODM devs: {[d.name for d in odm.devs]}") + print(f" UDA devs: {[d.name for d in uda.devs]}") + print(f" DCP devs: {[d.name for d in dcp.devs]}") + + # Test Dev.companies (many-to-many through freebies) + print(f"\nTesting Dev.companies:") + print(f" Raila's companies: {[c.name for c in raila.companies]}") + print(f" Ruto's companies: {[c.name for c in ruto.companies]}") + print(f" Rigachi's companies: {[c.name for c in rigachi.companies]}") + + print("\n2. TESTING FREEBIE METHODS") + print("-" * 40) + + # Test Freebie.print_details() + print("Testing Freebie.print_details():") + for freebie in session.query(Freebie).limit(3): + print(f" {freebie.print_details()}") + + print("\n3. TESTING COMPANY METHODS") + print("-" * 40) + + # Test Company.give_freebie() + print("Testing Company.give_freebie():") + new_freebie = odm.give_freebie(rigachi, "ODM Cap", 50000) + session.add(new_freebie) + session.commit() + print(f" Created new freebie: {new_freebie.print_details()}") + + # Test Company.oldest_company() + print("\nTesting Company.oldest_company():") + oldest = Company.oldest_company() + print(f" Oldest company: {oldest.name} (founded {oldest.founding_year})") + + print("\n4. TESTING DEV METHODS") + print("-" * 40) + + # Test Dev.received_one() + print("Testing Dev.received_one():") + print(f" Raila received 'ODM T-shirts': {raila.received_one('ODM T-shirts')}") + print(f" Raila received 'Laptop': {raila.received_one('Laptop')}") + print(f" Ruto received 'Wheelbarrow': {ruto.received_one('Wheelbarrow')}") + print(f" Ruto received 'ODM T-shirts': {ruto.received_one('ODM T-shirts')}") + + # Test Dev.give_away() + print("\nTesting Dev.give_away():") + wheelbarrow = session.query(Freebie).filter_by(item_name="Wheelbarrow").first() + print(f" Before: Wheelbarrow belongs to {wheelbarrow.dev.name}") + + success = ruto.give_away(rigachi, wheelbarrow) + session.commit() + print(f" Give away successful: {success}") + print(f" After: Wheelbarrow belongs to {wheelbarrow.dev.name}") + + # Test giving away freebie that doesn't belong to dev + odm_tshirt = session.query(Freebie).filter_by(item_name="ODM T-shirts").first() + failed_attempt = ruto.give_away(rigachi, odm_tshirt) + print(f" Ruto trying to give away Raila's T-shirt: {failed_attempt}") + + print("\n5. TESTING AGGREGATE DATA") + print("-" * 40) + + # Show summary statistics + total_companies = session.query(Company).count() + total_devs = session.query(Dev).count() + total_freebies = session.query(Freebie).count() + + print(f"Total companies: {total_companies}") + print(f"Total devs: {total_devs}") + print(f"Total freebies: {total_freebies}") + + # Show value statistics + total_value = sum(f.value for f in session.query(Freebie).all()) + print(f"Total value of all freebies: KSh {total_value:,}") + + # Show most valuable freebie + most_valuable = session.query(Freebie).order_by(Freebie.value.desc()).first() + print(f"Most valuable freebie: {most_valuable.item_name} (KSh {most_valuable.value:,})") + + print("\n6. TESTING EDGE CASES") + print("-" * 40) + + # Test empty relationships + new_company = Company(name="Test Company", founding_year=2024) + session.add(new_company) + session.commit() + print(f"New company freebies: {len(new_company.freebies)}") + print(f"New company devs: {len(new_company.devs)}") + + session.close() + print("\n" + "=" * 60) + print("ALL TESTS COMPLETED SUCCESSFULLY!") + print("=" * 60) if __name__ == '__main__': + # Run the tests first + test_relationships_and_methods() + + # Then start the interactive session + print("\nStarting interactive debug session...") + print("Available objects: Company, Dev, Freebie") + print("Sample usage:") + print(" session = sessionmaker(bind=create_engine('sqlite:///lib/freebies.db'))()") + print(" raila = session.query(Dev).filter_by(name='Raila').first()") + print(" print(raila.freebies)") + print("\n" + "-" * 60) + engine = create_engine('sqlite:///lib/freebies.db') - import ipdb; ipdb.set_trace() + Session = sessionmaker(bind=engine) + session = Session() + + # Make some common objects available in the debug session + raila = session.query(Dev).filter_by(name="Raila").first() + ruto = session.query(Dev).filter_by(name="Ruto").first() + rigachi = session.query(Dev).filter_by(name="Rigachi").first() + + odm = session.query(Company).filter_by(name="ODM").first() + uda = session.query(Company).filter_by(name="UDA").first() + dcp = session.query(Company).filter_by(name="DCP").first() + + import ipdb; ipdb.set_trace() \ No newline at end of file From f190acc63f1dfec7ad6d51b61b13302a54461f98 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:10:45 +0545 Subject: [PATCH 08/22] Updated freebies database --- lib/freebies.db | Bin 24576 -> 24576 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/freebies.db b/lib/freebies.db index d8ed4b2eb4dccdf100d1d1778678052ea377ac89..1cf7dd758bb209c1a97cdc1c07a1103e0df29d70 100644 GIT binary patch delta 303 zcmZoTz}Rqrae_1>-$WT_M!t;+OZ0hI_)jzNf8~G0f1m&KW@PT(Sr~+wT$}^gUve^mn4vC??9VtEK}>%aU-qlK3=9m+{0|xUzwy82 ze+V?_GQTkwGc$v9P-c2!az-XQ6ElNoP-#g%2O~3scu-I2^z%#>&Q^%*@Cbo{^fGlayFglwZy=`2jPN z2n!p7JWw3yG=-4FyzFB3g$m5f;>>Ie>OccLN{dT!Qi~MQO7l{R#U3Xx2{VC|b22)+ hxIqMcyd9WC8QB`$QRMM)r*fOZ1r-1U3r_+~=P-L7I)10SIt$0rD#kWB>pF From 39013677fe11593d431a79696f9eb4d22bca90f3 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:10:52 +0545 Subject: [PATCH 09/22] Updated seed file --- lib/seed.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/seed.py b/lib/seed.py index 1682db6c8..cc1bcd85e 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -18,25 +18,25 @@ session.query(Dev).delete() # Create companies -google = Company(name="Google", founding_year=1998) -apple = Company(name="Apple", founding_year=1976) -microsoft = Company(name="Microsoft", founding_year=1975) +ODM = Company(name="ODM", founding_year=2005) +UDA = Company(name="UDA", founding_year=2022) +DCP = Company(name="DCP", founding_year=2025) # Create devs -alice = Dev(name="Alice") -bob = Dev(name="Bob") -charlie = Dev(name="Charlie") +Raila = Dev(name="Raila") +Ruto = Dev(name="Ruto") +Rigachi = Dev(name="Rigachi") # Add to session -session.add_all([google, apple, microsoft, alice, bob, charlie]) +session.add_all([ODM, UDA, DCP, Raila, Ruto, Rigachi]) session.commit() # Create freebies -freebie1 = Freebie(item_name="T-shirt", value=25, dev=alice, company=google) -freebie2 = Freebie(item_name="Stickers", value=5, dev=alice, company=apple) -freebie3 = Freebie(item_name="Water Bottle", value=15, dev=bob, company=google) -freebie4 = Freebie(item_name="Laptop Bag", value=50, dev=charlie, company=microsoft) -freebie5 = Freebie(item_name="Pen", value=3, dev=bob, company=apple) +freebie1 = Freebie(item_name="ODM T-shirts", value=2500000, dev=Raila, company=ODM) +freebie2 = Freebie(item_name="CDF funds", value=5000000, dev=Raila, company=UDA) +freebie3 = Freebie(item_name="Hustler funds", value=1500000, dev=Ruto, company=ODM) +freebie4 = Freebie(item_name="DCP Tanks", value=500000, dev=Rigachi, company=DCP) +freebie5 = Freebie(item_name="Wheelbarrow", value=300000, dev=Ruto, company=UDA) session.add_all([freebie1, freebie2, freebie3, freebie4, freebie5]) session.commit() From df24cd8aed5da716c08eb44d09d405f7f02d6580 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:11:11 +0545 Subject: [PATCH 10/22] Added migrations cache --- ...0d5c3eb_create_freebies_table.cpython-312.pyc | Bin 0 -> 1931 bytes ...58bf48c_create_companies_devs.cpython-312.pyc | Bin 0 -> 1738 bytes .../7a71dbf71c64_create_db.cpython-312.pyc | Bin 0 -> 692 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/migrations/versions/__pycache__/190040d5c3eb_create_freebies_table.cpython-312.pyc create mode 100644 lib/migrations/versions/__pycache__/5f72c58bf48c_create_companies_devs.cpython-312.pyc create mode 100644 lib/migrations/versions/__pycache__/7a71dbf71c64_create_db.cpython-312.pyc diff --git a/lib/migrations/versions/__pycache__/190040d5c3eb_create_freebies_table.cpython-312.pyc b/lib/migrations/versions/__pycache__/190040d5c3eb_create_freebies_table.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9e765c8eb998550004d7734c0d2cb2420870584 GIT binary patch literal 1931 zcmb_dO>7%Q6y9C0cV{>2D6Jv_r6fKOQBxc_t_`jU6(XUiX`3P{4p@fOdS~oxyMN5C zQ*F4U5)$G>ZdH^>2?B|r^x$hHqzVa%140Ul5!JwrUbq#RUV377*NfdMq7rnZdGqGI zeczim@4eX{G8vhnL%BV>e3xgK-@{EZxIWXHqs$EkGBpOWkTY3}t8r||r_5B1hkPv! z(=`F6Y9gHg0w{dU*Ce`%bd`ftPFi(!j5Hr*n;4;nfxN7*HB2PS@1PaKGaNg6ZlRi; zJzXf23b3r7LXB91ylS@Ge5I(DD~)EUqRaC!%mT%#W{ZVlIbSH}i>2&rrCKajXJ4Kv zR7&MiQI`J*51(TR=eSEm8Ac|`FrwZMGFrwcP0`ugp|Rg!d^S;DN$w&M&=sutMlHAT< zg`mYksl^WEcXXglI4JM+^M7;Xko+8mD^gtLvACIk#SLrlkpa2HVo!=yEE6!f(Ioq5<5^sT~5MbO%o+2I5 z5ia}Kuv=vOtb>u!vd^Pn-myI&YliI;wn@g{#)hTg;337=OfS5P2hKvZe%WdI*EEc> zZ`v)xMks`MIC`Pmot1ekIiYx&OJ2 zD1aF^AY$94rV!!d)csR*dOuRK#@ywFPcB`*w7&QokUx_?mDZo%;E;SlqnODR;r?MEcT~+*k7F z@)t^n;=V?uzksQqz|_w1ozc$Ze&O}+@b~_={{98~;4P;M+`$J=@5sCF?VkUpaU0%( z`}6gKGw*l72LnBS0H*IBk%5iZ;$Phbj zTo3peDvzAuXDLS{=v=eo%blY8X*#{IGvmv>c>Ut~i$C+vMFu&J2pIWoY%k?Th^VU2 z(N&d*nu)B2q4!;RADWt}FC#0!M?(hy_y0E}6S`6d+XmKbeOaZwgG`Ue5V^>Pp6b|9 znsIoXM^5_y%vla>o9GNaLABvq;T0*lmttA=PflR@`=bmy@hdZN4=~EHHE?HaaxHUb WY@CwJku|xejIhFAlN>u41@t!?ouZ-u literal 0 HcmV?d00001 diff --git a/lib/migrations/versions/__pycache__/5f72c58bf48c_create_companies_devs.cpython-312.pyc b/lib/migrations/versions/__pycache__/5f72c58bf48c_create_companies_devs.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..077c3fc5038a1697e7da78d3810ba2291b119535 GIT binary patch literal 1738 zcmd5+&1(}u6yJ}@?q*Fyu_`L)s!#};(xgpWqj<1Us2^3-gM`4c*_pJfyR&h2Q%UjA zf}nU(6sjnARqMh3!;@H1MsIrYrlekaa%PilS1Hz`1M}XSdHa6vW8a%M*{m+GHG8)( z-46ue3qQn5>It1=Ojr=0P!*sEC0F#Msw8q*cI7I7pqhfIssiPz%C-hIsN4e8G~+7c zdRxw=XRq4?Ss^lP-)mafLBU}IqM1O~FQOSIaC~e`RLVxFFLN1qd^-(D8|aGaWUcx#-SLd;^S2%mex3w5_)azK9&}H0+e5Y zMedcYgvEM@sf0fIab2sJt|BV`jHo6M{}$u=h?&^)8i&$iuWF0E>WCZFQA@Fu*Db|r1Zn>^icM%2H^3Y|PZTKw=9Xw^WkwqxrlaW&i zZwZ>|4vrZ{22x;AW!!gL9;WI94$%}MRJjxqW=#7o5XZAf`wVK2`#1=R<>2sz(BaXn z_?2?)lHUk#SOgg-@sxuR;#Re0JDm1$zB!a{5QORu$}>az8X`5RJB~>)g8O%Hn{Fuu04&sHrdRbX{7gB(=2yut{v;d z!uGVu+8Vh5)gfvk3lt6)!tGOCzSPObs4RkRLF->gX-X|^Si>Ql;T6Jh%y-yj58)D-Gp?BmW ztFw~YZ|A-}i)SuQmNPe=?c3;eC!dQwzC9j|C(dZ%xSs8L{>bw^@6>ar(}|1F zcWH76xl&dOe*Cyvda(Z|sDkY@0oR}fH*j$Sm?1w&!9zOt!}D_4T*i#pZ|Ewcgs_aR z!m4f7C3UKavt)!M8F3mGrymFl^DGIAY{lY6G*gEcgvqYz@Ue-g>pQ~9Y1MLLQwcdH zOkVmkd3I007TjtkGX?vx?uTMi;{) zZbT6-(*7UziGl^ny#ZfPDoyJgh@px zuA`Ffn_}|(f9yS&XGMuOIeEpTGx3{&dMw~hg%JKwbg11w0dVjY4DL*D{9 Date: Sat, 24 May 2025 19:11:17 +0545 Subject: [PATCH 11/22] Added migrations temporary cache files --- lib/migrations/__pycache__/env.cpython-312.pyc | Bin 0 -> 2640 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/migrations/__pycache__/env.cpython-312.pyc diff --git a/lib/migrations/__pycache__/env.cpython-312.pyc b/lib/migrations/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..961a307b287c68ed2fa9e8ee35d557ddac426ebf GIT binary patch literal 2640 zcmZuzO=ufO6rREh3ym>|)$T}IWwoo$ zj%>>$;6e{gDU_aaXb-s-=h#zk$+Z^?JIGm_Ln$fr=3)nf>7j3SwO-rxESh=q=FNMv z@6GqEeov(o1a0NJ>B?_1LVxl}(8P|g`!5KOk&bl1L^WYv5V#yMBQC2H3STA-Ecwr#>{95jq9vssX(8Py?L5Sfp0`0)hq{srA*#Gv;3Z<3Qy9-Qv2)+3*A1)e1@t{(jS}y)-j*eTb=#U? zm@E!JQ8i2+q4Op;Rnvs?+rjat$THCyjGWJ7FFdIk%!^hw zxJQRkFTBllUPMQuMx9~1!Rigxak`RrzA{x)tB_iAszJ>;SE;L1tvRe|5;tn8HKOMw zSA-=u!&F)(tWYCN)m5gtDbrwts%D{RSi0jTbwf3Y#tL?wIkWk=8}sw-X1rahVeu8H z7Ueh2O&nH*@|)rI6bZnfz^H0Ds>bcyG;LUgu*i9Vc7dhCaf}aceROT%bGyWrRLH%b zVZvW|q27dwS(EkmCQFo%qCqCXr*@yv1=A=lgzgrIwK!F8y8Xwa6gt<4W+4m*yrV(4 ziuTT;uAa7nTMAwuo!(I1UA?(2b+x5zOUiB~Guvk;w)zLR`p2H!TF+j5lZZxRdugQf zv=d{k#MnmS+&@T&j&94Dwmj03M>b`=FM?v9YrUldho|9LB70*(d^QoiaVgH@9=1cI zufXdA(mP~^5bIzDq3R)~4|g41Jq71$9WS~N;=OtbqMt_AMBnE_^Z->;q5nV%kbDI_ z6j&zEo<`9R1lAj@>*7->@C;lL?mH7$gku=tnB^(t9asXZMEMi4%E@^%dXiJ*A9(<-z1 zB<0jjdH&rlj#l{0cnIwEL0T{@BhXQP%UpT3VVZX!jQ4@!=k!obd$=Edktr`S?Sl>u z*3~%zr!zVLI)`6cj47|Pn>`6YFPRj>+Ew^DKS2lhOQXKrww!LugDrXR=d10Z%dMfy z8}gN{%q~Uu^Z$og`AS?Zj{^F}#r&*+H>r?#TrYc>^tZVC${XU$3vv zefdU=B$PcFrL*nScq=u&kvb1JOystceeL8}D>=5AJO>a>Wv-I@LwD|0Lb|x2nentlQc`iCLm3V$3&gJRo%(XbL5GAZZt=W9Hn>Ot-5MFu8 zPch{HbCs~5eo4G!$AMRCMMLvb>{Y3|QyBhFqQGg|jq$IZ=}=A-t{jlGUkgrUUOwH7 z;RFO%;2VBHyRe45LLA5+fU04V59vFg Date: Sat, 24 May 2025 19:11:26 +0545 Subject: [PATCH 12/22] Added Python cache files --- lib/__pycache__/models.cpython-312.pyc | Bin 0 -> 5702 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/__pycache__/models.cpython-312.pyc diff --git a/lib/__pycache__/models.cpython-312.pyc b/lib/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aef1d80bcf1ac63a30486f9cc3fcdb5219dcd29d GIT binary patch literal 5702 zcmd5=T}&L;6`q;>oBd;VVGS5;m;^g!6N9l5)v-gc3kx>2j9ufl($>*r*tx*s*gfmSi^CD!;W2C<;KRZp|QZJoXTrlnosa) zAt9t42}jzQaHd@e7ms6t=1zMOp0t<{S>K^~)4qfc+D_G#;uC&>BQD~9;J(jW>m~v~ z@sYp-PlXZ`xYL{}UguOV3Ep><`Dz%|2ULF*HN>a^pa!d`;ap8D^e>zkbD6$@EG4P2 z%vq8%-F;bYDxEPsgBgR25o)@J4Vub~nc_KOC~?J5VuBf5Uz!5INt6V}*`Gf<6=tR-pFSDq^ z1IK;A>LlFdQI9=Z5w*B;US-4!BR*6bsLEc)uX?U?i2$l55hOLL_c~{K+B(o|ZZIr$3c#4|t%8A%b-WAWsGp$3I6Pi7~l zKodkymW_pd4;lpiHlBqF43eMWCfLCC>Mj`L+m2x_<`~L*yUXX0p71@eyc6|=|2ok| zbzEF}ax#A6@^E(4m{ur}`ZHsx3?XP5mnU<^csA29`C`YYb2 z^v=ngDatY>lT?=T;qq>sHl-eR(Dy)9;8r6|w_kea_?_doUs(KZF?6UX9@@ZzCvD|o z#|=OBiQUk^PXxaLpPO}pAMvU~1HYREe>1$ujAcfcam;`q9ysrp*O_(CxZ&en`FI~C zC__n+GGtmR3tLijJ)2CyfGSO=jB&{rPw7%w0g{qYfk31&F!YS1lB*O=00b7$iLQf* zrWN>p=m(Q*ahWiA#hj)$Wso#8dee1P(WZ#$!0Az#4z5_Ezo98wa-5`domrZus6o@W zMWhFSKO6x(Z)@$tHC$tUk`BM=Azg{TY-6-C8DUAnw~(T zN=_PK78;J}dSi-EaKtQC36)23X3*Y8#(hkOq9;w)Xo~8F=}Kx@oy0t*BdO_C zcICEA+4KxsmTr2=CU3g4nhIy%ZEQ8x03bNSx_rHDZrka2Vx2%$;J$Ek-Yzh?Pa`eG z))S@3D|7C(+LpzEmD(5PJZrUkZ^h<3pF|oL_RsIXt(PKgk0XbdBZrrUA9wUDcl12& z=w0sUEp^08k^aY#v&)gQrO3HC_o^6rGx)RMjnHan&s=b~wTiGcv({_SSm2BbrW#0P z1*`~ddPj+KZ}LfDj7tiyL2@w5&+ronP&>-Ja3kylo>|_uEc<^Ls5~2)=3;#QjBP6l z;Ekq{B*r``J1QxfhFGIZ)8kot2;j-qKQyvwW?*GO1Kc>Z6zI~S6T=j3K86|h1co1` z$EG5HNs@&QPTVCQu+@aUZ^5wsJX8e^a3CCgtM6g__ve0h?!(63rH0!L?=;_Se!sD| z6zwa8;zcq36?8UGRCnr*1%_g^2>uvTWZCq|GQ@-_NCspxAj@w|DO!0XC|j}L>QbCe zF}pH_luA*e6fuc*VTH<~uVU4L)eBIWen>ua9WsOQtV)j~MVzGBNkWYrJq6u+98)^` zMv9)#Due|+3)OXQHQHJ@_o+8D@44k&G*-Na3%zUp$ozA++DrcCLVPXSbm!RJWgiK1{8IP8H64?&MwVpNj!k^ZGu{9r;Y~yXrR_&|1gVdK2fa`AiU8{X7!O zP3tUpj3oRj#kQ}F9|7rqmcw{=WGX(EsrU)YRcy3M1TkYt)L@>H2(kRc5r^P(3Qlr~ zA`G(7W5pbd*4w}yorJj$#-MMOcDTA>d5JdS%6qYDhRRAeEG40C^8sM0WD5PrVP^{U zH;skdeD3Cz#Yi#KRutR*f1P^h>zvy1R{(zSUd3Q8jDi_gFymkdu+zco&J@Kn z;K>8@+b}1I>)HjalqvfZG=9ogqvaew%B%c!5wNm)MtNquk^yg_UrfkX#B+&G5h(>& zm&qaIVUlGd)KgM2f_TW}!~|E#t#%pai?IC$3hU?mS&j$uoZ()BxXrM-e6s4lp9xpm zIqnzy0%R&T_Bgp5_Z!DFALEDa@hsVjIZUCmi=KoztgoXiB&oox_KCN9jAY2QN!pWd zL(x-`B#>^YayCOcyR~dm(e$3q3eyFct79_vW`VeM1g7@aEdYlb+SZo&@XkXF;g0Bau-)H7wWZqg ze~kWl|DX1+)SfSe&KJe=mLK6q*#%c;3h#mjh);O>rQ#|eGbC?sG8kRcBTtzQ~|uV(=OQ{b4RDji0( zlr>TgXkM1sD$`2Na)wIxBC;at6u3|PwKvs;^n7~R-?rMcf9~{+v#WIttDz_p#!xIw zdx4MkVHL*;p=c{e(E;pkMX4PD3cZebBfW&J?_)KH6(-FV*qy}|R%oaC_n^Xv)bw=` z$&Nx~CDd(2p#$sga)cSQg3UU_ok9L{pWwQ{Z-_3}9&1P3Ckekwu``y9e*@{ii94zj zGWW9QCwASRw~gW*dfTm>-Ki^2AB?wbgzRBtH*e)pWE(F&nFLi)_AR>SPEWDv`K4!yp~FS-u;tVh z#{@o1uGsPN&_GOl+B?D1P#oS4@KpgZX-C0V1sJidscQ7;>%8s47l{0kt?)Ps7hlD| zd$-P>o~+wZIS7E~8%j#k^P-fUhU=6f<=s}^3lWjsg;|Pt5cXwv>Yb7^gYdGFF=Tt0 zJ+=(Dku{jEAB74~p|*aZeZGC6W4_~N>!P?6{cX#=mSX$MkKB)p_h#Omd3^H1^2rOu z9(by_SQIaov7+`HV+A|ub9h`-gy{wolT)hcvn(tJZ7bDf-^OA*%Xfc>Gj`wy-b0xI zC!nGK7%Ie%mR)O)6m2g*Ka4k?ff9%%r#i^A*N1-3^q$WCl`qto`{8Fsc z`qHDRm0*8iU@g*c$8*>F%M(kPQroM4ajis#3f}=MUV!O;OvDbM=P;sSjq&lT}V&FZtL>VLlu~U&Y;Ustvh(0|Coz^z{Nl2x<2BL z0gSr5Mc?UlhfpIFeDJY>=;FkR|7bz{)YGuoxZ-)f;M{a}@csOpy2;`H=IeYjzh~~i TCI^3;-BErRhQ8qNpNZi=lSB+~ literal 0 HcmV?d00001 From eeaf522f0ade1113d4309766a7a046cd3e4a754f Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:58:05 +0545 Subject: [PATCH 13/22] Updated compiled Python model file --- lib/__pycache__/models.cpython-312.pyc | Bin 5702 -> 5718 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/__pycache__/models.cpython-312.pyc b/lib/__pycache__/models.cpython-312.pyc index aef1d80bcf1ac63a30486f9cc3fcdb5219dcd29d..ba4bd826e369b4980a831f4c2f0073b9e4b1c6c9 100644 GIT binary patch delta 46 zcmX@6b4`cqG%qg~0}wn|wvp>M6QloRHfA$^ul&rs;`o%*oYa!k7(Kns*38_(08q~j A`2YX_ delta 30 kcmcbnb4-WpG%qg~0}xEvwUO&L6QliPHfFQUKFnOg0E^lPB>(^b From 2239bdcf291c468a662e8d63266dc19cad422737 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:58:13 +0545 Subject: [PATCH 14/22] Updated debug script --- lib/debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/debug.py b/lib/debug.py index 7673f1325..7ffc80b18 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -130,7 +130,7 @@ def test_relationships_and_methods(): print("-" * 40) # Test empty relationships - new_company = Company(name="Test Company", founding_year=2024) + new_company = Company(name="PNU", founding_year=2007) session.add(new_company) session.commit() print(f"New company freebies: {len(new_company.freebies)}") From fdef47155e362184f3d5acfa11f6e0e6a50331ee Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:58:21 +0545 Subject: [PATCH 15/22] Updated freebies database --- lib/freebies.db | Bin 24576 -> 24576 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/freebies.db b/lib/freebies.db index 1cf7dd758bb209c1a97cdc1c07a1103e0df29d70..f7144be33e0b9bb097003ba54671741f04f16a26 100644 GIT binary patch delta 111 zcmV-#0FeKHzyW~30gxL35s@520THoap)U~+%K!uqzyJ^R59$x!55Nz=vk?%k4-W|i z1OO8PP)=0`*8l(j6tmAiH4qX94`%=m=?~NoyAP!gjSqScXR{FyQ4bRl2L=Ee0|5z7 RL`@(=VQ>J$Py>^CUxx1h9!LNH delta 76 zcmZoTz}Rqrae_1>-$WT_M!t;+3-$RK7#LXiPc!g;<$uM0pa1k`L4n=;EJ7?S44co} gn+gc9@mDeMzvREdzngz4e=mO(P(F=+a#egA09;}g=>Px# From b965fd2e7ccfba1832ab0d5db2619c000554e7de Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:58:50 +0545 Subject: [PATCH 16/22] Added freebies database --- freebies.db | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 freebies.db diff --git a/freebies.db b/freebies.db new file mode 100644 index 000000000..e69de29bb From ce0735d282c0566f290f10c7cd3c2e15ee874869 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 19:58:59 +0545 Subject: [PATCH 17/22] Added joins deletion scripts --- lib/Joins_delete/delete_company.py | 57 ++++++++++ lib/Joins_delete/sqlite_joins.py | 169 +++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 lib/Joins_delete/delete_company.py create mode 100644 lib/Joins_delete/sqlite_joins.py diff --git a/lib/Joins_delete/delete_company.py b/lib/Joins_delete/delete_company.py new file mode 100644 index 000000000..8fef33e48 --- /dev/null +++ b/lib/Joins_delete/delete_company.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +import sqlite3 +import os + +def delete_company(): + """Delete company with id=4 and name='Test Company' using SQLite3""" + + # Path to database (go up one level to lib folder) + db_path = os.path.join(os.path.dirname(__file__), '..', 'freebies.db') + + try: + # Connect to database + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("Checking for company with ID=4 and name='Test Company'...") + + # Check if company exists + cursor.execute("SELECT * FROM companies WHERE id = 4 AND name = 'Test Company'") + company = cursor.fetchone() + + if not company: + print(" Company with ID=4 and name 'Test Company' not found") + return + + print(f" Found company: ID={company[0]}, Name='{company[1]}', Founded={company[2]}") + + # Check for related freebies + cursor.execute("SELECT COUNT(*) FROM freebies WHERE company_id = 4") + freebie_count = cursor.fetchone()[0] + + if freebie_count > 0: + print(f" Company has {freebie_count} related freebies. Deleting them first...") + cursor.execute("DELETE FROM freebies WHERE company_id = 4") + print(f" Deleted {cursor.rowcount} freebies") + + # Delete the company + cursor.execute("DELETE FROM companies WHERE id = 4 AND name = 'Test Company'") + + if cursor.rowcount > 0: + conn.commit() + print(" Successfully deleted 'Test Company' with ID=4") + else: + print(" No company was deleted") + + except sqlite3.Error as e: + print(f" Database error: {e}") + conn.rollback() + except Exception as e: + print(f" Error: {e}") + finally: + if 'conn' in locals(): + conn.close() + +if __name__ == "__main__": + delete_company() \ No newline at end of file diff --git a/lib/Joins_delete/sqlite_joins.py b/lib/Joins_delete/sqlite_joins.py new file mode 100644 index 000000000..0dbe2da01 --- /dev/null +++ b/lib/Joins_delete/sqlite_joins.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 + +import sqlite3 +import os + +def connect_to_db(): + """Create connection to the SQLite database in lib folder""" + try: + # Get path to lib folder where freebies.db is located + db_path = os.path.join(os.path.dirname(__file__), '..', 'freebies.db') + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row # Access columns by name + return conn + except sqlite3.Error as e: + print(f"Error connecting to database: {e}") + return None + +def essential_joins(): + """Demonstrate the 4 most important SQL join patterns""" + conn = connect_to_db() + if not conn: + return + + cursor = conn.cursor() + + print("ESSENTIAL SQL JOIN PATTERNS") + print("=" * 50) + + try: + # 1. INNER JOIN - Most common join + print("\n1. INNER JOIN:") + print("Shows freebies with dev and company names") + print("-" * 40) + + query = """ + SELECT + f.item_name, + f.value, + d.name as dev_name, + c.name as company_name + FROM freebies f + INNER JOIN devs d ON f.dev_id = d.id + INNER JOIN companies c ON f.company_id = c.id + ORDER BY f.value DESC + """ + + cursor.execute(query) + results = cursor.fetchall() + + for row in results: + print(f"{row['dev_name']} got {row['item_name']} (KSh {row['value']:,}) from {row['company_name']}") + + # 2. LEFT JOIN - Include all records from left table + print("\n\n2. LEFT JOIN:") + print("Shows all devs, even those without freebies") + print("-" * 40) + + query = """ + SELECT + d.name as dev_name, + COUNT(f.id) as freebie_count, + COALESCE(SUM(f.value), 0) as total_value + FROM devs d + LEFT JOIN freebies f ON d.id = f.dev_id + GROUP BY d.id, d.name + ORDER BY total_value DESC + """ + + cursor.execute(query) + results = cursor.fetchall() + + for row in results: + print(f"{row['dev_name']}: {row['freebie_count']} freebies, Total: KSh {row['total_value']:,}") + + # 3. GROUP BY with JOIN - Aggregation across tables + print("\n\n3. GROUP BY with JOIN:") + print("Company performance summary") + print("-" * 40) + + query = """ + SELECT + c.name as company_name, + COUNT(f.id) as freebies_given, + COALESCE(SUM(f.value), 0) as total_spent, + COUNT(DISTINCT f.dev_id) as devs_reached + FROM companies c + LEFT JOIN freebies f ON c.id = f.company_id + GROUP BY c.id, c.name + ORDER BY total_spent DESC + """ + + cursor.execute(query) + results = cursor.fetchall() + + for row in results: + print(f"{row['company_name']}: {row['freebies_given']} freebies, KSh {row['total_spent']:,}, {row['devs_reached']} devs") + + # 4. WHERE with JOIN - Filtered joins + print("\n\n4. WHERE with JOIN:") + print("High-value freebies (> KSh 1M)") + print("-" * 40) + + query = """ + SELECT + d.name as dev_name, + f.item_name, + f.value, + c.name as company_name + FROM freebies f + INNER JOIN devs d ON f.dev_id = d.id + INNER JOIN companies c ON f.company_id = c.id + WHERE f.value > 1000000 + ORDER BY f.value DESC + """ + + cursor.execute(query) + results = cursor.fetchall() + + if results: + for row in results: + print(f"{row['dev_name']} got {row['item_name']} (KSh {row['value']:,}) from {row['company_name']}") + else: + print("No high-value freebies found") + + except sqlite3.Error as e: + print(f"Error executing queries: {e}") + + finally: + conn.close() + +def quick_stats(): + """Show quick database statistics""" + conn = connect_to_db() + if not conn: + return + + cursor = conn.cursor() + + print("\n\nQUICK DATABASE STATS") + print("=" * 30) + + try: + # Count records + cursor.execute("SELECT COUNT(*) FROM companies") + companies = cursor.fetchone()[0] + + cursor.execute("SELECT COUNT(*) FROM devs") + devs = cursor.fetchone()[0] + + cursor.execute("SELECT COUNT(*) FROM freebies") + freebies = cursor.fetchone()[0] + + cursor.execute("SELECT SUM(value) FROM freebies") + total_value = cursor.fetchone()[0] or 0 + + print(f"Companies: {companies}") + print(f"Developers: {devs}") + print(f"Freebies: {freebies}") + print(f"Total Value: KSh {total_value:,}") + + except sqlite3.Error as e: + print(f"Error getting stats: {e}") + + finally: + conn.close() + +if __name__ == "__main__": + quick_stats() + essential_joins() \ No newline at end of file From 5d05a7be32685c30ca8dc47f0983ad51d690e10c Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 20:11:07 +0545 Subject: [PATCH 18/22] updates --- README.md | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ freebies.db | 0 2 files changed, 220 insertions(+) delete mode 100644 freebies.db diff --git a/README.md b/README.md index b598b7784..3b572a6c0 100644 --- a/README.md +++ b/README.md @@ -180,3 +180,223 @@ data). - `Dev.give_away(dev, freebie)` accepts a `Dev` instance and a `Freebie` instance, 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 + +## Setup Instructions + +### 1. Install Dependencies + +```bash +# Navigate to project directory +cd *** + +# Install required packages +pipenv install +pipenv shell + +# Or using pip +pip install sqlalchemy alembic +``` + +### 2. Database Setup + +```bash +# Navigate to lib directory +cd lib +# Run database migration +alembic revision -m "create freebies table" +alembic upgrade head + +# Create sample data +python seed.py +``` + +## Running the Application + +### 1. Test Relationships and Methods + +```bash +# From lib directory +python debug.py +``` + +**Expected Output:** +- All relationship tests passed +- All method implementations working +- Interactive ipdb session starts + +### 2. SQL Join Demonstrations + +```bash +# From project root directory +python lib/Joins_delete/sqlite_joins.py +``` + +**Shows:** +- INNER JOIN examples +- LEFT JOIN patterns +- GROUP BY aggregations +- WHERE filtering + +### 3. Database Management + +```bash +# Delete specific company +python lib/Joins_delete/delete_company.py + +# Check database status +python -c " +import sqlite3 +conn = sqlite3.connect('lib/freebies.db') +cursor = conn.cursor() +cursor.execute('SELECT COUNT(*) FROM companies') +print(f'Companies: {cursor.fetchone()[0]}') +cursor.execute('SELECT COUNT(*) FROM devs') +print(f'Devs: {cursor.fetchone()[0]}') +cursor.execute('SELECT COUNT(*) FROM freebies') +print(f'Freebies: {cursor.fetchone()[0]}') +conn.close() +" +``` + +## Key Features + +### Models and Relationships + +- **Company**: Has many freebies, connects to devs through freebies +- **Dev**: Has many freebies, connects to companies through freebies +- **Freebie**: Belongs to both a dev and a company + +### Available Methods + +#### Freebie Methods +```python +freebie.print_details() # Returns formatted string +``` + +#### Company Methods +```python +company.give_freebie(dev, item_name, value) # Creates new freebie +Company.oldest_company() # Class method returns oldest company +``` + +#### Dev Methods +```python +dev.received_one(item_name) # Returns True/False +dev.give_away(other_dev, freebie) # Transfers freebie ownership +``` + +## Sample Data + +The seed script creates: + +| Company | Founded | Freebies Given | +|---------|---------|----------------| +| ODM | 2005 | 2 | +| UDA | 2022 | 2 | +| DCP | 2025 | 1 | + +| Developer | Freebies Received | Total Value | +|-----------|-------------------|-------------| +| Raila | 2 | KSh 7,500,000 | +| Ruto | 2 | KSh 1,800,000 | +| Rigachi | 1 | KSh 500,000 | + +## Testing Commands + +### Quick Database Check +```bash +# From lib directory +sqlite3 freebies.db "SELECT * FROM companies;" +sqlite3 freebies.db "SELECT * FROM devs;" +sqlite3 freebies.db "SELECT * FROM freebies;" +``` + +### Test Specific Relationships +```python +# In debug.py ipdb session +raila = session.query(Dev).filter_by(name="Raila").first() +print(raila.freebies) # Show Raila's freebies +print(raila.companies) # Show companies Raila got freebies from + +odm = session.query(Company).filter_by(name="ODM").first() +print(odm.freebies) # Show ODM's freebies +print(odm.devs) # Show devs who got freebies from ODM +``` + +### Test Methods +```python +# Test freebie details +freebie = session.query(Freebie).first() +print(freebie.print_details()) + +# Test company methods +odm.give_freebie(rigachi, "ODM Cap", 50000) +oldest = Company.oldest_company() + +# Test dev methods +raila.received_one("ODM T-shirts") # Should return True +ruto.give_away(rigachi, wheelbarrow_freebie) +``` + +## Troubleshooting + +### Common Issues + +1. **ModuleNotFoundError: No module named 'models'** + ```bash + # Make sure you're in the lib directory + cd lib + python debug.py + ``` + +2. **sqlite3.OperationalError: no such table: freebies** + ```bash + # Run the migration first + alembic upgrade head + ``` + +3. **No data in database** + ```bash + # Create sample data + python seed.py + ``` + +### Reset Database +```bash +# From lib directory +rm freebies.db +alembic upgrade head +python seed.py +``` + +## SQL Join Examples + +The project demonstrates these essential join patterns: + +1. **INNER JOIN** - Match records in both tables +2. **LEFT JOIN** - Include all records from left table +3. **GROUP BY** - Aggregate data across tables +4. **WHERE** - Filter joined results + +Run `python lib/Joins_delete/sqlite_joins.py` to see examples. + +## Assignment Deliverables + + **Migrations**: Created freebies table with foreign keys + **Relationships**: All models have correct SQLAlchemy relationships + **Freebie methods**: `print_details()` implemented + **Company methods**: `give_freebie()` and `oldest_company()` working + **Dev methods**: `received_one()` and `give_away()` functional + **Testing**: Comprehensive test suite in debug.py + +## Next Steps + +1. Run all tests: `python debug.py` +2. Explore joins: `python lib/Joins_delete/sqlite_joins.py` +3. Test edge cases in the interactive session +4. Add more complex queries and relationships + +--- + +**Note**: This project uses a political theme for sample data (ODM, UDA, DCP parties with Raila, Ruto, Rigathi as developers) for educational purposes. +``` diff --git a/freebies.db b/freebies.db deleted file mode 100644 index e69de29bb..000000000 From 6694734f13a9c8d070742f6523c7e154500ad401 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 20:14:38 +0545 Subject: [PATCH 19/22] updates --- README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/README.md b/README.md index 3b572a6c0..2d340010a 100644 --- a/README.md +++ b/README.md @@ -243,19 +243,6 @@ python lib/Joins_delete/sqlite_joins.py # Delete specific company python lib/Joins_delete/delete_company.py -# Check database status -python -c " -import sqlite3 -conn = sqlite3.connect('lib/freebies.db') -cursor = conn.cursor() -cursor.execute('SELECT COUNT(*) FROM companies') -print(f'Companies: {cursor.fetchone()[0]}') -cursor.execute('SELECT COUNT(*) FROM devs') -print(f'Devs: {cursor.fetchone()[0]}') -cursor.execute('SELECT COUNT(*) FROM freebies') -print(f'Freebies: {cursor.fetchone()[0]}') -conn.close() -" ``` ## Key Features @@ -399,4 +386,4 @@ Run `python lib/Joins_delete/sqlite_joins.py` to see examples. --- **Note**: This project uses a political theme for sample data (ODM, UDA, DCP parties with Raila, Ruto, Rigathi as developers) for educational purposes. -``` + From e57f42347f499227ea3394efd89b4b45199d642b Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Sat, 24 May 2025 20:38:17 +0545 Subject: [PATCH 20/22] Final --- lib/debug.py | 281 +++++++++++++++++++++++++----------------------- lib/freebies.db | Bin 24576 -> 24576 bytes lib/models.py | 61 +++++++++-- lib/seed.py | 112 ++++++++++++------- 4 files changed, 271 insertions(+), 183 deletions(-) diff --git a/lib/debug.py b/lib/debug.py index 7ffc80b18..3c910c804 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -3,12 +3,14 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from models import Company, Dev, Freebie +import os def test_relationships_and_methods(): """Test all the relationships and methods with the seed data""" - # Create engine and session - engine = create_engine('sqlite:///lib/freebies.db') + # Fix the database path - use absolute path to freebies.db in current directory + db_path = os.path.join(os.path.dirname(__file__), 'freebies.db') + engine = create_engine(f'sqlite:///{db_path}') Session = sessionmaker(bind=engine) session = Session() @@ -16,132 +18,136 @@ def test_relationships_and_methods(): print("TESTING SQLALCHEMY RELATIONSHIPS AND METHODS") print("=" * 60) - # Get test data - raila = session.query(Dev).filter_by(name="Raila").first() - ruto = session.query(Dev).filter_by(name="Ruto").first() - rigachi = session.query(Dev).filter_by(name="Rigachi").first() - - odm = session.query(Company).filter_by(name="ODM").first() - uda = session.query(Company).filter_by(name="UDA").first() - dcp = session.query(Company).filter_by(name="DCP").first() - - print("\n1. TESTING BASIC RELATIONSHIPS") - print("-" * 40) - - # Test Freebie relationships - print("Testing Freebie.dev and Freebie.company:") - freebie = session.query(Freebie).filter_by(item_name="ODM T-shirts").first() - print(f" Freebie '{freebie.item_name}' belongs to dev: {freebie.dev.name}") - print(f" Freebie '{freebie.item_name}' belongs to company: {freebie.company.name}") - - # Test Company.freebies - print(f"\nTesting Company.freebies:") - print(f" ODM freebies: {[f.item_name for f in odm.freebies]}") - print(f" UDA freebies: {[f.item_name for f in uda.freebies]}") - print(f" DCP freebies: {[f.item_name for f in dcp.freebies]}") - - # Test Dev.freebies - print(f"\nTesting Dev.freebies:") - print(f" Raila's freebies: {[f.item_name for f in raila.freebies]}") - print(f" Ruto's freebies: {[f.item_name for f in ruto.freebies]}") - print(f" Rigachi's freebies: {[f.item_name for f in rigachi.freebies]}") - - # Test Company.devs (many-to-many through freebies) - print(f"\nTesting Company.devs:") - print(f" ODM devs: {[d.name for d in odm.devs]}") - print(f" UDA devs: {[d.name for d in uda.devs]}") - print(f" DCP devs: {[d.name for d in dcp.devs]}") - - # Test Dev.companies (many-to-many through freebies) - print(f"\nTesting Dev.companies:") - print(f" Raila's companies: {[c.name for c in raila.companies]}") - print(f" Ruto's companies: {[c.name for c in ruto.companies]}") - print(f" Rigachi's companies: {[c.name for c in rigachi.companies]}") - - print("\n2. TESTING FREEBIE METHODS") - print("-" * 40) - - # Test Freebie.print_details() - print("Testing Freebie.print_details():") - for freebie in session.query(Freebie).limit(3): - print(f" {freebie.print_details()}") - - print("\n3. TESTING COMPANY METHODS") - print("-" * 40) - - # Test Company.give_freebie() - print("Testing Company.give_freebie():") - new_freebie = odm.give_freebie(rigachi, "ODM Cap", 50000) - session.add(new_freebie) - session.commit() - print(f" Created new freebie: {new_freebie.print_details()}") - - # Test Company.oldest_company() - print("\nTesting Company.oldest_company():") - oldest = Company.oldest_company() - print(f" Oldest company: {oldest.name} (founded {oldest.founding_year})") - - print("\n4. TESTING DEV METHODS") - print("-" * 40) - - # Test Dev.received_one() - print("Testing Dev.received_one():") - print(f" Raila received 'ODM T-shirts': {raila.received_one('ODM T-shirts')}") - print(f" Raila received 'Laptop': {raila.received_one('Laptop')}") - print(f" Ruto received 'Wheelbarrow': {ruto.received_one('Wheelbarrow')}") - print(f" Ruto received 'ODM T-shirts': {ruto.received_one('ODM T-shirts')}") - - # Test Dev.give_away() - print("\nTesting Dev.give_away():") - wheelbarrow = session.query(Freebie).filter_by(item_name="Wheelbarrow").first() - print(f" Before: Wheelbarrow belongs to {wheelbarrow.dev.name}") - - success = ruto.give_away(rigachi, wheelbarrow) - session.commit() - print(f" Give away successful: {success}") - print(f" After: Wheelbarrow belongs to {wheelbarrow.dev.name}") - - # Test giving away freebie that doesn't belong to dev - odm_tshirt = session.query(Freebie).filter_by(item_name="ODM T-shirts").first() - failed_attempt = ruto.give_away(rigachi, odm_tshirt) - print(f" Ruto trying to give away Raila's T-shirt: {failed_attempt}") - - print("\n5. TESTING AGGREGATE DATA") - print("-" * 40) - - # Show summary statistics - total_companies = session.query(Company).count() - total_devs = session.query(Dev).count() - total_freebies = session.query(Freebie).count() - - print(f"Total companies: {total_companies}") - print(f"Total devs: {total_devs}") - print(f"Total freebies: {total_freebies}") - - # Show value statistics - total_value = sum(f.value for f in session.query(Freebie).all()) - print(f"Total value of all freebies: KSh {total_value:,}") - - # Show most valuable freebie - most_valuable = session.query(Freebie).order_by(Freebie.value.desc()).first() - print(f"Most valuable freebie: {most_valuable.item_name} (KSh {most_valuable.value:,})") - - print("\n6. TESTING EDGE CASES") - print("-" * 40) - - # Test empty relationships - new_company = Company(name="PNU", founding_year=2007) - session.add(new_company) - session.commit() - print(f"New company freebies: {len(new_company.freebies)}") - print(f"New company devs: {len(new_company.devs)}") - - session.close() - print("\n" + "=" * 60) - print("ALL TESTS COMPLETED SUCCESSFULLY!") - print("=" * 60) + try: + # Check if we have data first + company_count = session.query(Company).count() + dev_count = session.query(Dev).count() + freebie_count = session.query(Freebie).count() + + if company_count == 0 or dev_count == 0 or freebie_count == 0: + print(" No data found in database!") + print("Please run: python seed.py first") + session.close() + return + + print(f"Database contains: {company_count} companies, {dev_count} devs, {freebie_count} freebies") + + # Get test data + raila = session.query(Dev).filter_by(name="Raila").first() + ruto = session.query(Dev).filter_by(name="Ruto").first() + rigachi = session.query(Dev).filter_by(name="Rigachi").first() + + odm = session.query(Company).filter_by(name="ODM").first() + uda = session.query(Company).filter_by(name="UDA").first() + dcp = session.query(Company).filter_by(name="DCP").first() + + if not all([raila, ruto, rigachi, odm, uda, dcp]): + print(" Expected data not found! Please run: python seed.py") + session.close() + return + + print("\n1. TESTING BASIC RELATIONSHIPS") + print("-" * 40) + + # Test Freebie relationships + print("Testing Freebie.dev and Freebie.company:") + freebie = session.query(Freebie).filter_by(item_name="ODM T-shirts").first() + if freebie: + print(f" Freebie '{freebie.item_name}' belongs to dev: {freebie.dev.name}") + print(f" Freebie '{freebie.item_name}' belongs to company: {freebie.company.name}") + + # Test Company.freebies + print(f"\nTesting Company.freebies:") + print(f" ODM freebies: {[f.item_name for f in odm.freebies]}") + print(f" UDA freebies: {[f.item_name for f in uda.freebies]}") + print(f" DCP freebies: {[f.item_name for f in dcp.freebies]}") + + # Test Dev.freebies + print(f"\nTesting Dev.freebies:") + print(f" Raila's freebies: {[f.item_name for f in raila.freebies]}") + print(f" Ruto's freebies: {[f.item_name for f in ruto.freebies]}") + print(f" Rigachi's freebies: {[f.item_name for f in rigachi.freebies]}") + + # Test Company.devs (many-to-many through freebies) + print(f"\nTesting Company.devs:") + print(f" ODM devs: {[d.name for d in odm.devs]}") + print(f" UDA devs: {[d.name for d in uda.devs]}") + print(f" DCP devs: {[d.name for d in dcp.devs]}") + + # Test Dev.companies (many-to-many through freebies) + print(f"\nTesting Dev.companies:") + print(f" Raila's companies: {[c.name for c in raila.companies]}") + print(f" Ruto's companies: {[c.name for c in ruto.companies]}") + print(f" Rigachi's companies: {[c.name for c in rigachi.companies]}") + + print("\n2. TESTING FREEBIE METHODS") + print("-" * 40) + + # Test Freebie.print_details() + print("Testing Freebie.print_details():") + for freebie in session.query(Freebie).limit(3): + print(f" {freebie.print_details()}") + + print("\n3. TESTING COMPANY METHODS") + print("-" * 40) + + # Test Company.oldest_company() + print("Testing Company.oldest_company():") + oldest = Company.oldest_company() + if oldest: + print(f" Oldest company: {oldest.name} (founded {oldest.founding_year})") + + print("\n4. TESTING DEV METHODS") + print("-" * 40) + + # Test Dev.received_one() + print("Testing Dev.received_one():") + print(f" Raila received 'ODM T-shirts': {raila.received_one('ODM T-shirts')}") + print(f" Raila received 'Laptop': {raila.received_one('Laptop')}") + print(f" Ruto received 'Wheelbarrow': {ruto.received_one('Wheelbarrow')}") + print(f" Ruto received 'ODM T-shirts': {ruto.received_one('ODM T-shirts')}") + + print("\n5. TESTING AGGREGATE DATA") + print("-" * 40) + + # Show summary statistics + total_companies = session.query(Company).count() + total_devs = session.query(Dev).count() + total_freebies = session.query(Freebie).count() + + print(f"Total companies: {total_companies}") + print(f"Total devs: {total_devs}") + print(f"Total freebies: {total_freebies}") + + # Show value statistics + total_value = sum(f.value for f in session.query(Freebie).all()) + print(f"Total value of all freebies: KSh {total_value:,}") + + # Show most valuable freebie + most_valuable = session.query(Freebie).order_by(Freebie.value.desc()).first() + if most_valuable: + print(f"Most valuable freebie: {most_valuable.item_name} (KSh {most_valuable.value:,})") + + print("\n" + "=" * 60) + print("ALL TESTS COMPLETED SUCCESSFULLY!") + print("=" * 60) + + except Exception as e: + print(f" Error during testing: {e}") + finally: + session.close() if __name__ == '__main__': + # Check if database exists + db_path = os.path.join(os.path.dirname(__file__), 'freebies.db') + + if not os.path.exists(db_path): + print(" Database file 'freebies.db' not found!") + print("Please run the following commands first:") + print("1. alembic upgrade head") + print("2. python seed.py") + exit(1) + # Run the tests first test_relationships_and_methods() @@ -149,22 +155,27 @@ def test_relationships_and_methods(): print("\nStarting interactive debug session...") print("Available objects: Company, Dev, Freebie") print("Sample usage:") - print(" session = sessionmaker(bind=create_engine('sqlite:///lib/freebies.db'))()") print(" raila = session.query(Dev).filter_by(name='Raila').first()") print(" print(raila.freebies)") print("\n" + "-" * 60) - engine = create_engine('sqlite:///lib/freebies.db') + engine = create_engine(f'sqlite:///{db_path}') Session = sessionmaker(bind=engine) session = Session() # Make some common objects available in the debug session - raila = session.query(Dev).filter_by(name="Raila").first() - ruto = session.query(Dev).filter_by(name="Ruto").first() - rigachi = session.query(Dev).filter_by(name="Rigachi").first() - - odm = session.query(Company).filter_by(name="ODM").first() - uda = session.query(Company).filter_by(name="UDA").first() - dcp = session.query(Company).filter_by(name="DCP").first() + try: + raila = session.query(Dev).filter_by(name="Raila").first() + ruto = session.query(Dev).filter_by(name="Ruto").first() + rigachi = session.query(Dev).filter_by(name="Rigachi").first() + + odm = session.query(Company).filter_by(name="ODM").first() + uda = session.query(Company).filter_by(name="UDA").first() + dcp = session.query(Company).filter_by(name="DCP").first() + + print("Pre-loaded objects: raila, ruto, rigachi, odm, uda, dcp, session") + + except Exception as e: + print(f"Warning: Could not pre-load objects: {e}") import ipdb; ipdb.set_trace() \ No newline at end of file diff --git a/lib/freebies.db b/lib/freebies.db index f7144be33e0b9bb097003ba54671741f04f16a26..d3024a550d8bc03ca0db0a4feba96df9d91220b3 100644 GIT binary patch delta 109 zcmZoTz}Rqrae_3X_(U0JM)8da3-#F;7#NuO?{8)|_$x2O#$V3B|C0X-|8D-J{Js3; z{OOwo1p@ger^gHEim Date: Sat, 24 May 2025 20:40:02 +0545 Subject: [PATCH 21/22] Final --- README.md | 81 ------------------------------------------------------- 1 file changed, 81 deletions(-) diff --git a/README.md b/README.md index 2d340010a..b14eb87b3 100644 --- a/README.md +++ b/README.md @@ -245,93 +245,12 @@ python lib/Joins_delete/delete_company.py ``` -## Key Features - -### Models and Relationships - -- **Company**: Has many freebies, connects to devs through freebies -- **Dev**: Has many freebies, connects to companies through freebies -- **Freebie**: Belongs to both a dev and a company - -### Available Methods - -#### Freebie Methods -```python -freebie.print_details() # Returns formatted string -``` - -#### Company Methods -```python -company.give_freebie(dev, item_name, value) # Creates new freebie -Company.oldest_company() # Class method returns oldest company -``` - -#### Dev Methods -```python -dev.received_one(item_name) # Returns True/False -dev.give_away(other_dev, freebie) # Transfers freebie ownership -``` - -## Sample Data - -The seed script creates: - -| Company | Founded | Freebies Given | -|---------|---------|----------------| -| ODM | 2005 | 2 | -| UDA | 2022 | 2 | -| DCP | 2025 | 1 | - -| Developer | Freebies Received | Total Value | -|-----------|-------------------|-------------| -| Raila | 2 | KSh 7,500,000 | -| Ruto | 2 | KSh 1,800,000 | -| Rigachi | 1 | KSh 500,000 | - -## Testing Commands - -### Quick Database Check -```bash -# From lib directory -sqlite3 freebies.db "SELECT * FROM companies;" -sqlite3 freebies.db "SELECT * FROM devs;" -sqlite3 freebies.db "SELECT * FROM freebies;" -``` - -### Test Specific Relationships -```python -# In debug.py ipdb session -raila = session.query(Dev).filter_by(name="Raila").first() -print(raila.freebies) # Show Raila's freebies -print(raila.companies) # Show companies Raila got freebies from - -odm = session.query(Company).filter_by(name="ODM").first() -print(odm.freebies) # Show ODM's freebies -print(odm.devs) # Show devs who got freebies from ODM -``` - -### Test Methods -```python -# Test freebie details -freebie = session.query(Freebie).first() -print(freebie.print_details()) - -# Test company methods -odm.give_freebie(rigachi, "ODM Cap", 50000) -oldest = Company.oldest_company() - -# Test dev methods -raila.received_one("ODM T-shirts") # Should return True -ruto.give_away(rigachi, wheelbarrow_freebie) -``` - ## Troubleshooting ### Common Issues 1. **ModuleNotFoundError: No module named 'models'** ```bash - # Make sure you're in the lib directory cd lib python debug.py ``` From f3b8a448791f3c80862b6d40aaba16fa546751b6 Mon Sep 17 00:00:00 2001 From: Michael Ouma Date: Mon, 26 May 2025 13:33:51 +0300 Subject: [PATCH 22/22] delete --- lib/Joins_delete/sqlite_joins.py | 169 ------------------ .../delete_company.py | 0 2 files changed, 169 deletions(-) delete mode 100644 lib/Joins_delete/sqlite_joins.py rename lib/{Joins_delete => delete}/delete_company.py (100%) diff --git a/lib/Joins_delete/sqlite_joins.py b/lib/Joins_delete/sqlite_joins.py deleted file mode 100644 index 0dbe2da01..000000000 --- a/lib/Joins_delete/sqlite_joins.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python3 - -import sqlite3 -import os - -def connect_to_db(): - """Create connection to the SQLite database in lib folder""" - try: - # Get path to lib folder where freebies.db is located - db_path = os.path.join(os.path.dirname(__file__), '..', 'freebies.db') - conn = sqlite3.connect(db_path) - conn.row_factory = sqlite3.Row # Access columns by name - return conn - except sqlite3.Error as e: - print(f"Error connecting to database: {e}") - return None - -def essential_joins(): - """Demonstrate the 4 most important SQL join patterns""" - conn = connect_to_db() - if not conn: - return - - cursor = conn.cursor() - - print("ESSENTIAL SQL JOIN PATTERNS") - print("=" * 50) - - try: - # 1. INNER JOIN - Most common join - print("\n1. INNER JOIN:") - print("Shows freebies with dev and company names") - print("-" * 40) - - query = """ - SELECT - f.item_name, - f.value, - d.name as dev_name, - c.name as company_name - FROM freebies f - INNER JOIN devs d ON f.dev_id = d.id - INNER JOIN companies c ON f.company_id = c.id - ORDER BY f.value DESC - """ - - cursor.execute(query) - results = cursor.fetchall() - - for row in results: - print(f"{row['dev_name']} got {row['item_name']} (KSh {row['value']:,}) from {row['company_name']}") - - # 2. LEFT JOIN - Include all records from left table - print("\n\n2. LEFT JOIN:") - print("Shows all devs, even those without freebies") - print("-" * 40) - - query = """ - SELECT - d.name as dev_name, - COUNT(f.id) as freebie_count, - COALESCE(SUM(f.value), 0) as total_value - FROM devs d - LEFT JOIN freebies f ON d.id = f.dev_id - GROUP BY d.id, d.name - ORDER BY total_value DESC - """ - - cursor.execute(query) - results = cursor.fetchall() - - for row in results: - print(f"{row['dev_name']}: {row['freebie_count']} freebies, Total: KSh {row['total_value']:,}") - - # 3. GROUP BY with JOIN - Aggregation across tables - print("\n\n3. GROUP BY with JOIN:") - print("Company performance summary") - print("-" * 40) - - query = """ - SELECT - c.name as company_name, - COUNT(f.id) as freebies_given, - COALESCE(SUM(f.value), 0) as total_spent, - COUNT(DISTINCT f.dev_id) as devs_reached - FROM companies c - LEFT JOIN freebies f ON c.id = f.company_id - GROUP BY c.id, c.name - ORDER BY total_spent DESC - """ - - cursor.execute(query) - results = cursor.fetchall() - - for row in results: - print(f"{row['company_name']}: {row['freebies_given']} freebies, KSh {row['total_spent']:,}, {row['devs_reached']} devs") - - # 4. WHERE with JOIN - Filtered joins - print("\n\n4. WHERE with JOIN:") - print("High-value freebies (> KSh 1M)") - print("-" * 40) - - query = """ - SELECT - d.name as dev_name, - f.item_name, - f.value, - c.name as company_name - FROM freebies f - INNER JOIN devs d ON f.dev_id = d.id - INNER JOIN companies c ON f.company_id = c.id - WHERE f.value > 1000000 - ORDER BY f.value DESC - """ - - cursor.execute(query) - results = cursor.fetchall() - - if results: - for row in results: - print(f"{row['dev_name']} got {row['item_name']} (KSh {row['value']:,}) from {row['company_name']}") - else: - print("No high-value freebies found") - - except sqlite3.Error as e: - print(f"Error executing queries: {e}") - - finally: - conn.close() - -def quick_stats(): - """Show quick database statistics""" - conn = connect_to_db() - if not conn: - return - - cursor = conn.cursor() - - print("\n\nQUICK DATABASE STATS") - print("=" * 30) - - try: - # Count records - cursor.execute("SELECT COUNT(*) FROM companies") - companies = cursor.fetchone()[0] - - cursor.execute("SELECT COUNT(*) FROM devs") - devs = cursor.fetchone()[0] - - cursor.execute("SELECT COUNT(*) FROM freebies") - freebies = cursor.fetchone()[0] - - cursor.execute("SELECT SUM(value) FROM freebies") - total_value = cursor.fetchone()[0] or 0 - - print(f"Companies: {companies}") - print(f"Developers: {devs}") - print(f"Freebies: {freebies}") - print(f"Total Value: KSh {total_value:,}") - - except sqlite3.Error as e: - print(f"Error getting stats: {e}") - - finally: - conn.close() - -if __name__ == "__main__": - quick_stats() - essential_joins() \ No newline at end of file diff --git a/lib/Joins_delete/delete_company.py b/lib/delete/delete_company.py similarity index 100% rename from lib/Joins_delete/delete_company.py rename to lib/delete/delete_company.py