From 880e6745d0c23a045897ccdb6d373ce1145a2034 Mon Sep 17 00:00:00 2001 From: rxhma-sys Date: Tue, 3 Jun 2025 20:46:18 +0300 Subject: [PATCH] Working Freebie Tracker with models, logic, and seed data --- .canvas | 12 +- .github/workflows/canvas-sync-ruby-update.yml | 62 +- .gitignore | 102 +-- CONTRIBUTING.md | 76 +- LICENSE.md | 46 +- Pipfile | 32 +- Pipfile.lock | 712 ++++++++++-------- README.md | 364 ++++----- alembic.ini | 36 + app.db | Bin 0 -> 12288 bytes freebies.db | Bin 0 -> 16384 bytes lib/_init_.py | 0 lib/alembic.ini | 210 +++--- lib/debug.py | 45 +- .../5f72c58bf48c_create_companies_devs.py | 39 - .../versions/7a71dbf71c64_create_db.py | 24 - lib/models.py | 93 ++- lib/seed.py | 34 +- main.py | 45 ++ {lib/migrations => migrations}/README | 0 {lib/migrations => migrations}/env.py | 40 +- {lib/migrations => migrations}/script.py.mako | 0 22 files changed, 1060 insertions(+), 912 deletions(-) create mode 100644 alembic.ini create mode 100644 app.db create mode 100644 freebies.db create mode 100644 lib/_init_.py delete mode 100644 lib/migrations/versions/5f72c58bf48c_create_companies_devs.py delete mode 100644 lib/migrations/versions/7a71dbf71c64_create_db.py create mode 100644 main.py rename {lib/migrations => migrations}/README (100%) rename {lib/migrations => migrations}/env.py (52%) rename {lib/migrations => migrations}/script.py.mako (100%) diff --git a/.canvas b/.canvas index f2f2b2541..24c0de792 100644 --- a/.canvas +++ b/.canvas @@ -1,6 +1,6 @@ ---- -:lessons: -- :id: 215796 - :course_id: 5935 - :canvas_url: https://learning.flatironschool.com/courses/5935/assignments/215796 - :type: assignment +--- +:lessons: +- :id: 215796 + :course_id: 5935 + :canvas_url: https://learning.flatironschool.com/courses/5935/assignments/215796 + :type: assignment diff --git a/.github/workflows/canvas-sync-ruby-update.yml b/.github/workflows/canvas-sync-ruby-update.yml index f8818dc0c..dda725b6a 100644 --- a/.github/workflows/canvas-sync-ruby-update.yml +++ b/.github/workflows/canvas-sync-ruby-update.yml @@ -1,31 +1,31 @@ -name: Sync with Canvas Ruby v2.7 - -on: - push: - branches: [master, main] - paths: - - 'README.md' - -jobs: - sync: - name: Sync with Canvas - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7 - - - name: Install github-to-canvas - run: gem install github-to-canvas - - # Secret stored in learn-co-curriculum Settings/Secrets - - name: Sync from .canvas file - run: github-to-canvas -a -lr - env: - CANVAS_API_KEY: ${{ secrets.CANVAS_API_KEY }} - CANVAS_API_PATH: ${{ secrets.CANVAS_API_PATH }} +name: Sync with Canvas Ruby v2.7 + +on: + push: + branches: [master, main] + paths: + - 'README.md' + +jobs: + sync: + name: Sync with Canvas + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + + - name: Install github-to-canvas + run: gem install github-to-canvas + + # Secret stored in learn-co-curriculum Settings/Secrets + - name: Sync from .canvas file + run: github-to-canvas -a -lr + env: + CANVAS_API_KEY: ${{ secrets.CANVAS_API_KEY }} + CANVAS_API_PATH: ${{ secrets.CANVAS_API_PATH }} diff --git a/.gitignore b/.gitignore index 1fcadf3de..02af8a838 100644 --- a/.gitignore +++ b/.gitignore @@ -1,51 +1,51 @@ -# Created by https://www.gitignore.io/api/node - -.DS_Store - -### Node ### -# Logs -logs -*.log -npm-debug.log* - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules -jspm_packages - -# Optional npm cache directory -.npm - -# Optional REPL history -.node_repl_history - -# Learn-specific .results.json -.results.json - -# Ignore bundler config. -/.bundle - -# Ignore the default SQLite database. -/db/*.sqlite3 -/db/*.sqlite3-journal - -# Ignore all logfiles and tempfiles. -/log/* -!/log/.keep -/tmp - -# Virtual env metadata -.venv - -# pytest cache -.pytest_cache +# Created by https://www.gitignore.io/api/node + +.DS_Store + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Learn-specific .results.json +.results.json + +# Ignore bundler config. +/.bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +# Ignore all logfiles and tempfiles. +/log/* +!/log/.keep +/tmp + +# Virtual env metadata +.venv + +# pytest cache +.pytest_cache diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33ecf8a38..0c9ab1612 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,38 +1,38 @@ -# Contributing to Learn.co Curriculum - -We're really exited that you're about to contribute to the [open -curriculum](https://learn.co/content-license) on [Learn.co](https://learn.co). -If this is your first time contributing, please continue reading to learn how to -make the most meaningful and useful impact possible. - -## Raising an Issue to Encourage a Contribution - -If you notice a problem with the curriculum that you believe needs improvement -but you're unable to make the change yourself, you should raise a Github issue -containing a clear description of the problem. Include relevant snippets of the -content and/or screenshots if applicable. Curriculum owners regularly review -issue lists and your issue will be prioritized and addressed as appropriate. - -## Submitting a Pull Request to Suggest an Improvement - -If you see an opportunity for improvement and can make the change yourself go -ahead and use a typical git workflow to make it happen: - -- Fork this curriculum repository -- Make the change on your fork, with descriptive commits in the standard format -- Open a Pull Request against this repo - -A curriculum owner will review your change and approve or comment on it in due -course. - -## Why Contribute? - -Curriculum on Learn is publicly and freely available under Learn's -[Educational Content License](https://learn.co/content-license). By embracing an -open-source contribution model, our goal is for the curriculum on Learn to -become, in time, the best educational content the world has ever seen. - -We need help from the community of Learners to maintain and improve the -educational content. Everything from fixing typos, to correcting out-dated -information, to improving exposition, to adding better examples, to fixing -tests—all contributions to making the curriculum more effective are welcome. +# Contributing to Learn.co Curriculum + +We're really exited that you're about to contribute to the [open +curriculum](https://learn.co/content-license) on [Learn.co](https://learn.co). +If this is your first time contributing, please continue reading to learn how to +make the most meaningful and useful impact possible. + +## Raising an Issue to Encourage a Contribution + +If you notice a problem with the curriculum that you believe needs improvement +but you're unable to make the change yourself, you should raise a Github issue +containing a clear description of the problem. Include relevant snippets of the +content and/or screenshots if applicable. Curriculum owners regularly review +issue lists and your issue will be prioritized and addressed as appropriate. + +## Submitting a Pull Request to Suggest an Improvement + +If you see an opportunity for improvement and can make the change yourself go +ahead and use a typical git workflow to make it happen: + +- Fork this curriculum repository +- Make the change on your fork, with descriptive commits in the standard format +- Open a Pull Request against this repo + +A curriculum owner will review your change and approve or comment on it in due +course. + +## Why Contribute? + +Curriculum on Learn is publicly and freely available under Learn's +[Educational Content License](https://learn.co/content-license). By embracing an +open-source contribution model, our goal is for the curriculum on Learn to +become, in time, the best educational content the world has ever seen. + +We need help from the community of Learners to maintain and improve the +educational content. Everything from fixing typos, to correcting out-dated +information, to improving exposition, to adding better examples, to fixing +tests—all contributions to making the curriculum more effective are welcome. diff --git a/LICENSE.md b/LICENSE.md index 4694fa644..acc1ac809 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,23 +1,23 @@ -# Learn.co Educational Content License - -Copyright (c) 2021 Flatiron School, Inc - -The Flatiron School, Inc. owns this Educational Content. However, the Flatiron -School supports the development and availability of educational materials in the -public domain. Therefore, the Flatiron School grants Users of the Flatiron -Educational Content set forth in this repository certain rights to reuse, build -upon and share such Educational Content subject to the terms of the Educational -Content License set forth [here](http://learn.co/content-license) -(http://learn.co/content-license). You must read carefully the terms and -conditions contained in the Educational Content License as such terms govern -access to and use of the Educational Content. - -Flatiron School is willing to allow you access to and use of the Educational -Content only on the condition that you accept all of the terms and conditions -contained in the Educational Content License set forth -[here](http://learn.co/content-license) (http://learn.co/content-license). By -accessing and/or using the Educational Content, you are agreeing to all of the -terms and conditions contained in the Educational Content License. If you do not -agree to any or all of the terms of the Educational Content License, you are -prohibited from accessing, reviewing or using in any way the Educational -Content. +# Learn.co Educational Content License + +Copyright (c) 2021 Flatiron School, Inc + +The Flatiron School, Inc. owns this Educational Content. However, the Flatiron +School supports the development and availability of educational materials in the +public domain. Therefore, the Flatiron School grants Users of the Flatiron +Educational Content set forth in this repository certain rights to reuse, build +upon and share such Educational Content subject to the terms of the Educational +Content License set forth [here](http://learn.co/content-license) +(http://learn.co/content-license). You must read carefully the terms and +conditions contained in the Educational Content License as such terms govern +access to and use of the Educational Content. + +Flatiron School is willing to allow you access to and use of the Educational +Content only on the condition that you accept all of the terms and conditions +contained in the Educational Content License set forth +[here](http://learn.co/content-license) (http://learn.co/content-license). By +accessing and/or using the Educational Content, you are agreeing to all of the +terms and conditions contained in the Educational Content License. If you do not +agree to any or all of the terms of the Educational Content License, you are +prohibited from accessing, reviewing or using in any way the Educational +Content. diff --git a/Pipfile b/Pipfile index 63c79cd98..17557bb63 100644 --- a/Pipfile +++ b/Pipfile @@ -1,16 +1,16 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -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" - -[dev-packages] - -[requires] -python_full_version = "3.8.13" +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +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" + +[dev-packages] + +[requires] +python_full_version = "3.12.3" diff --git a/Pipfile.lock b/Pipfile.lock index 3923f4a97..a5e9ca21b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,324 +1,388 @@ -{ - "_meta": { - "hash": { - "sha256": "fa62c636b7ae5acb4993fc7b007a651c55b562aa29962a9a8585ec314b352ae6" - }, - "pipfile-spec": 6, - "requires": { - "python_full_version": "3.8.13" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "alembic": { - "hashes": [ - "sha256:6880dec4f28dd7bd999d2ed13fbe7c9d4337700a44d11a524c0ce0c59aaf0dbd", - "sha256:e8a6ff9f3b1887e1fed68bfb8fb9a000d8f61c21bdcc85b67bb9f87fcbc4fce3" - ], - "index": "pypi", - "version": "==1.9.2" - }, - "appnope": { - "hashes": [ - "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24", - "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e" - ], - "markers": "sys_platform == 'darwin'", - "version": "==0.1.3" - }, - "asttokens": { - "hashes": [ - "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3", - "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c" - ], - "version": "==2.2.1" - }, - "backcall": { - "hashes": [ - "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", - "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" - ], - "version": "==0.2.0" - }, - "decorator": { - "hashes": [ - "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", - "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" - ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==5.1.1" - }, - "executing": { - "hashes": [ - "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc", - "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107" - ], - "version": "==1.2.0" - }, - "importlib-metadata": { - "hashes": [ - "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad", - "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d" - ], - "index": "pypi", - "version": "==6.0.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6", - "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484" - ], - "index": "pypi", - "version": "==5.10.2" - }, - "ipdb": { - "hashes": [ - "sha256:c23b6736f01fd4586cc2ecbebdf79a5eb454796853e1cd8f2ed3b7b91d4a3e93", - "sha256:f74c2f741c18b909eaf89f19fde973f745ac721744aa1465888ce45813b63a9c" - ], - "index": "pypi", - "version": "==0.13.11" - }, - "ipython": { - "hashes": [ - "sha256:da01e6df1501e6e7c32b5084212ddadd4ee2471602e2cf3e0190f4de6b0ea481", - "sha256:f3bf2c08505ad2c3f4ed5c46ae0331a8547d36bf4b21a451e8ae80c0791db95b" - ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==8.8.0" - }, - "jedi": { - "hashes": [ - "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e", - "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612" - ], - "markers": "python_version >= '3.6'", - "version": "==0.18.2" - }, - "mako": { - "hashes": [ - "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818", - "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34" - ], - "markers": "python_version >= '3.7'", - "version": "==1.2.4" - }, - "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" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "matplotlib-inline": { - "hashes": [ - "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", - "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304" - ], - "markers": "python_version >= '3.5'", - "version": "==0.1.6" - }, - "parso": { - "hashes": [ - "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", - "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75" - ], - "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" - }, - "prompt-toolkit": { - "hashes": [ - "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63", - "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305" - ], - "markers": "python_full_version >= '3.6.2'", - "version": "==3.0.36" - }, - "ptyprocess": { - "hashes": [ - "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", - "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" - ], - "version": "==0.7.0" - }, - "pure-eval": { - "hashes": [ - "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350", - "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3" - ], - "version": "==0.2.2" - }, - "pygments": { - "hashes": [ - "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297", - "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717" - ], - "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" - }, - "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" - ], - "index": "pypi", - "version": "==1.4.46" - }, - "stack-data": { - "hashes": [ - "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815", - "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8" - ], - "version": "==0.6.2" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==2.0.1" - }, - "traitlets": { - "hashes": [ - "sha256:32500888f5ff7bbf3b9267ea31748fa657aaf34d56d85e60f91dda7dc7f5785b", - "sha256:a1ca5df6414f8b5760f7c5f256e326ee21b581742114545b462b35ffe3f04861" - ], - "markers": "python_version >= '3.7'", - "version": "==5.8.1" - }, - "wcwidth": { - "hashes": [ - "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e", - "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0" - ], - "version": "==0.2.6" - }, - "zipp": { - "hashes": [ - "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa", - "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766" - ], - "markers": "python_version < '3.10'", - "version": "==3.11.0" - } - }, - "develop": {} -} +{ + "_meta": { + "hash": { + "sha256": "e138f1470cbfd7e837d1cdbd7bdd33022bd34f2b5a85c2e9f861cee471f56aba" + }, + "pipfile-spec": 6, + "requires": { + "python_full_version": "3.12.3" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "alembic": { + "hashes": [ + "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4", + "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.8.1" + }, + "asttokens": { + "hashes": [ + "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", + "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.0" + }, + "decorator": { + "hashes": [ + "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", + "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a" + ], + "markers": "python_version >= '3.7'", + "version": "==5.2.1" + }, + "executing": { + "hashes": [ + "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", + "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755" + ], + "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' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", + "version": "==3.2.2" + }, + "importlib-metadata": { + "hashes": [ + "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad", + "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==6.0.0" + }, + "importlib-resources": { + "hashes": [ + "sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668", + "sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.10.0" + }, + "ipdb": { + "hashes": [ + "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5" + ], + "index": "pypi", + "markers": "python_version >= '2.7'", + "version": "==0.13.9" + }, + "ipython": { + "hashes": [ + "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04", + "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8" + ], + "markers": "python_version >= '3.7'", + "version": "==9.3.0" + }, + "ipython-pygments-lexers": { + "hashes": [ + "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", + "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c" + ], + "markers": "python_version >= '3.8'", + "version": "==1.1.1" + }, + "jedi": { + "hashes": [ + "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", + "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9" + ], + "markers": "python_version >= '3.6'", + "version": "==0.19.2" + }, + "mako": { + "hashes": [ + "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", + "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59" + ], + "markers": "python_version >= '3.8'", + "version": "==1.3.10" + }, + "markupsafe": { + "hashes": [ + "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.9'", + "version": "==3.0.2" + }, + "matplotlib-inline": { + "hashes": [ + "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", + "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca" + ], + "markers": "python_version >= '3.8'", + "version": "==0.1.7" + }, + "parso": { + "hashes": [ + "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", + "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d" + ], + "markers": "python_version >= '3.6'", + "version": "==0.8.4" + }, + "pexpect": { + "hashes": [ + "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", + "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f" + ], + "markers": "sys_platform != 'win32' and sys_platform != 'emscripten'", + "version": "==4.9.0" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", + "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.51" + }, + "ptyprocess": { + "hashes": [ + "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", + "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" + ], + "version": "==0.7.0" + }, + "pure-eval": { + "hashes": [ + "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", + "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42" + ], + "version": "==0.2.3" + }, + "pygments": { + "hashes": [ + "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" + ], + "markers": "python_version >= '3.8'", + "version": "==2.19.1" + }, + "setuptools": { + "hashes": [ + "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", + "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" + ], + "markers": "python_version >= '3.9'", + "version": "==80.9.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:04f2598c70ea4a29b12d429a80fad3a5202d56dce19dd4916cc46a965a5ca2e9", + "sha256:0501f74dd2745ec38f44c3a3900fb38b9db1ce21586b691482a19134062bf049", + "sha256:0ee377eb5c878f7cefd633ab23c09e99d97c449dd999df639600f49b74725b80", + "sha256:11b2ec26c5d2eefbc3e6dca4ec3d3d95028be62320b96d687b6e740424f83b7d", + "sha256:15d878929c30e41fb3d757a5853b680a561974a0168cd33a750be4ab93181628", + "sha256:177e41914c476ed1e1b77fd05966ea88c094053e17a85303c4ce007f88eff363", + "sha256:1811a0b19a08af7750c0b69e38dec3d46e47c4ec1d74b6184d69f12e1c99a5e0", + "sha256:1d0c23ecf7b3bc81e29459c34a3f4c68ca538de01254e24718a7926810dc39a6", + "sha256:22459fc1718785d8a86171bbe7f01b5c9d7297301ac150f508d06e62a2b4e8d2", + "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124", + "sha256:2e56dfed0cc3e57b2f5c35719d64f4682ef26836b81067ee6cfad062290fd9e2", + "sha256:2fd49af453e590884d9cdad3586415922a8e9bb669d874ee1dc55d2bc425aacd", + "sha256:3ab7c158f98de6cb4f1faab2d12973b330c2878d0c6b689a8ca424c02d66e1b3", + "sha256:4948b6c5f4e56693bbeff52f574279e4ff972ea3353f45967a14c30fb7ae2beb", + "sha256:4e1c5f8182b4f89628d782a183d44db51b5af84abd6ce17ebb9804355c88a7b5", + "sha256:5ce6929417d5dce5ad1d3f147db81735a4a0573b8fb36e3f95500a06eaddd93e", + "sha256:5ede1495174e69e273fad68ad45b6d25c135c1ce67723e40f6cf536cb515e20b", + "sha256:5f966b64c852592469a7eb759615bbd351571340b8b344f1d3fa2478b5a4c934", + "sha256:6045b3089195bc008aee5c273ec3ba9a93f6a55bc1b288841bd4cfac729b6516", + "sha256:6c9d004eb78c71dd4d3ce625b80c96a827d2e67af9c0d32b1c1e75992a7916cc", + "sha256:6e39e97102f8e26c6c8550cb368c724028c575ec8bc71afbbf8faaffe2b2092a", + "sha256:723e3b9374c1ce1b53564c863d1a6b2f1dc4e97b1c178d9b643b191d8b1be738", + "sha256:876eb185911c8b95342b50a8c4435e1c625944b698a5b4a978ad2ffe74502908", + "sha256:9256563506e040daddccaa948d055e006e971771768df3bb01feeb4386c242b0", + "sha256:934472bb7d8666727746a75670a1f8d91a9cae8c464bba79da30a0f6faccd9e1", + "sha256:97ff50cd85bb907c2a14afb50157d0d5486a4b4639976b4a3346f34b6d1b5272", + "sha256:9b01d9cd2f9096f688c71a3d0f33f3cd0af8549014e66a7a7dee6fc214a7277d", + "sha256:9e3a65ce9ed250b2f096f7b559fe3ee92e6605fab3099b661f0397a9ac7c8d95", + "sha256:a7dd5b7b34a8ba8d181402d824b87c5cee8963cb2e23aa03dbfe8b1f1e417cde", + "sha256:a85723c00a636eed863adb11f1e8aaa36ad1c10089537823b4540948a8429798", + "sha256:b42c59ffd2d625b28cdb2ae4cde8488543d428cba17ff672a543062f7caee525", + "sha256:bd448b262544b47a2766c34c0364de830f7fb0772d9959c1c42ad61d91ab6565", + "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88", + "sha256:df76e9c60879fdc785a34a82bf1e8691716ffac32e7790d31a98d7dec6e81545", + "sha256:e12c6949bae10f1012ab5c0ea52ab8db99adcb8c7b717938252137cdf694c775", + "sha256:e4ef8cb3c5b326f839bfeb6af5f406ba02ad69a78c7aac0fbeeba994ad9bb48a", + "sha256:e7e740453f0149437c101ea4fdc7eea2689938c5760d7dcc436c863a12f1f565", + "sha256:effc89e606165ca55f04f3f24b86d3e1c605e534bf1a96e4e077ce1b027d0b71", + "sha256:f0f574465b78f29f533976c06b913e54ab4980b9931b69aa9d306afff13a9471", + "sha256:fa5b7eb2051e857bf83bade0641628efe5a88de189390725d3e6033a1fff4257", + "sha256:fdb94a3d1ba77ff2ef11912192c066f01e68416f554c194d769391638c8ad09a" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.4.42" + }, + "stack-data": { + "hashes": [ + "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", + "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695" + ], + "version": "==0.6.3" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '3.7'", + "version": "==0.10.2" + }, + "traitlets": { + "hashes": [ + "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", + "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f" + ], + "markers": "python_version >= '3.8'", + "version": "==5.14.3" + }, + "wcwidth": { + "hashes": [ + "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", + "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5" + ], + "version": "==0.2.13" + }, + "zipp": { + "hashes": [ + "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", + "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343" + ], + "markers": "python_version >= '3.9'", + "version": "==3.22.0" + } + }, + "develop": {} +} diff --git a/README.md b/README.md index b598b7784..efbe77331 100644 --- a/README.md +++ b/README.md @@ -1,182 +1,182 @@ -# Phase 3 Mock Code Challenge: Freebie Tracker - -## Learning Goals - -- Write SQLAlchemy migrations. -- Connect between tables using SQLAlchemy relationships. -- Use SQLAlchemy to run CRUD statements in the database. - -*** - -## Key Vocab - -- **Schema**: the blueprint of a database. Describes how data relates to other - data in tables, columns, and relationships between them. -- **Persist**: save a schema in a database. -- **Engine**: a Python object that translates SQL to Python and vice-versa. -- **Session**: a Python object that uses an engine to allow us to - programmatically interact with a database. -- **Transaction**: a strategy for executing database statements such that - the group succeeds or fails as a unit. -- **Migration**: the process of moving data from one or more databases to one - or more target databases. - -*** - -## Introduction - -For this assignment, we'll be working with a freebie domain. - -As developers, when you attend hackathons, you'll realize they hand out a lot of -free items (informally called _freebies_, or swag)! Let's make an app for -developers that keeps track of all the freebies they obtain. - -We have three models: `Company`, `Dev`, and `Freebie` - -For our purposes, a `Company` has many `Freebie`s, a `Dev` has many `Freebie`s, -and a `Freebie` belongs to a `Dev` and to a `Company`. - -`Company` - `Dev` is a many to many relationship. - -**Note**: You should draw your domain on paper or on a whiteboard _before you -start coding_. Remember to identify a single source of truth for your data. - -## Instructions - -To get started, run `pipenv install && pipenv shell` while inside of this -directory. - -Build out all of the methods listed in the deliverables. The methods are listed -in a suggested order, but you can feel free to tackle the ones you think are -easiest. Be careful: some of the later methods rely on earlier ones. - -**Remember!** This mock code challenge does not have tests. You cannot run -`pytest` and you cannot run `learn test`. You'll need to create your own sample -instances so that you can try out your code on your own. Make sure your -relationships and methods work in the console before submitting. - -We've provided you with a tool that you can use to test your code. To use it, -run `python debug.py` from the command line. This will start an `ipdb` session -with your classes defined. You can test out the methods that you write here. You -are also encouraged to use the `seed.py` file to create sample data to test your -models and associations. - -Writing error-free code is more important than completing all of the -deliverables listed- prioritize writing methods that work over writing more -methods that don't work. You should test your code in the console as you write. - -Similarly, messy code that works is better than clean code that doesn't. First, -prioritize getting things working. Then, if there is time at the end, refactor -your code to adhere to best practices. - -**Before you submit!** Save and run your code to verify that it works as you -expect. If you have any methods that are not working yet, feel free to leave -comments describing your progress. - -*** - -## What You Already Have - -The starter code has migrations and models for the initial `Company` and `Dev` -models, and seed data for some `Company`s and `Dev`s. The schema currently looks -like this: - -### companies Table - -| Column | Type | -| ------------- | ------- | -| name | String | -| founding_year | Integer | - -### devs Table - -| Column | Type | -| ------ | ------ | -| name | String | - -You will need to create the migration for the `freebies` table using the -attributes specified in the deliverables below. - -*** - -## Deliverables - -Write the following methods in the classes in the files provided. Feel free to -build out any helper methods if needed. - -Remember: SQLAlchemy gives your classes access to a lot of methods already! -Keep in mind what methods SQLAlchemy gives you access to on each of your -classes when you're approaching the deliverables below. - -### Migrations - -Before working on the rest of the deliverables, you will need to create a -migration for the `freebies` table. - -- A `Freebie` belongs to a `Dev`, and a `Freebie` also belongs to a `Company`. - In your migration, create any columns your `freebies` table will need to - establish these relationships using the right foreign keys. -- The `freebies` table should also have: - - An `item_name` column that stores a string. - - A `value` column that stores an integer. - -After creating the `freebies` table using a migration, use the `seed.py` file to -create instances of your `Freebie` class so you can test your code. - -**After you've set up your `freebies` table**, work on building out the following -deliverables. - -### Relationship Attributes and Methods - -Use SQLAlchemy's `ForeignKey`, `relationship()`, and `backref()` objects to -build relationships between your three models. - -**Note**: The plural of "freebie" is "freebies" and the singular of "freebies" -is "freebie". - -#### Freebie - -- `Freebie.dev` returns the `Dev` instance for this Freebie. -- `Freebie.company` returns the `Company` instance for this Freebie. - -#### Company - -- `Company.freebies` returns a collection of all the freebies for the Company. -- `Company.devs` returns a collection of all the devs who collected freebies - from the company. - -#### Dev - -- `Dev.freebies` returns a collection of all the freebies that the Dev has collected. -- `Dev.companies`returns a collection of all the companies that the Dev has collected - freebies from. - -Use `python debug.py` and check that these methods work before proceeding. For -example, you should be able to retrieve a dev from the database by its -attributes and view their companies with `dev.companies` (based on your seed -data). - -### Aggregate Methods - -#### Freebie - -- `Freebie.print_details()`should return a string formatted as follows: - `{dev name} owns a {freebie item_name} from {company name}`. - -#### Company - -- `Company.give_freebie(dev, item_name, value)` takes a `dev` (an instance of - the `Dev` class), an `item_name` (string), and a `value` as arguments, and - creates a new `Freebie` instance associated with this company and the given - dev. -- Class method `Company.oldest_company()`returns the `Company` instance with - the earliest founding year. - -#### Dev - -- `Dev.received_one(item_name)` accepts an `item_name` (string) and returns - `True` if any of the freebies associated with the dev has that `item_name`, - otherwise returns `False`. -- `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 +# Phase 3 Mock Code Challenge: Freebie Tracker + +## Learning Goals + +- Write SQLAlchemy migrations. +- Connect between tables using SQLAlchemy relationships. +- Use SQLAlchemy to run CRUD statements in the database. + +*** + +## Key Vocab + +- **Schema**: the blueprint of a database. Describes how data relates to other + data in tables, columns, and relationships between them. +- **Persist**: save a schema in a database. +- **Engine**: a Python object that translates SQL to Python and vice-versa. +- **Session**: a Python object that uses an engine to allow us to + programmatically interact with a database. +- **Transaction**: a strategy for executing database statements such that + the group succeeds or fails as a unit. +- **Migration**: the process of moving data from one or more databases to one + or more target databases. + +*** + +## Introduction + +For this assignment, we'll be working with a freebie domain. + +As developers, when you attend hackathons, you'll realize they hand out a lot of +free items (informally called _freebies_, or swag)! Let's make an app for +developers that keeps track of all the freebies they obtain. + +We have three models: `Company`, `Dev`, and `Freebie` + +For our purposes, a `Company` has many `Freebie`s, a `Dev` has many `Freebie`s, +and a `Freebie` belongs to a `Dev` and to a `Company`. + +`Company` - `Dev` is a many to many relationship. + +**Note**: You should draw your domain on paper or on a whiteboard _before you +start coding_. Remember to identify a single source of truth for your data. + +## Instructions + +To get started, run `pipenv install && pipenv shell` while inside of this +directory. + +Build out all of the methods listed in the deliverables. The methods are listed +in a suggested order, but you can feel free to tackle the ones you think are +easiest. Be careful: some of the later methods rely on earlier ones. + +**Remember!** This mock code challenge does not have tests. You cannot run +`pytest` and you cannot run `learn test`. You'll need to create your own sample +instances so that you can try out your code on your own. Make sure your +relationships and methods work in the console before submitting. + +We've provided you with a tool that you can use to test your code. To use it, +run `python debug.py` from the command line. This will start an `ipdb` session +with your classes defined. You can test out the methods that you write here. You +are also encouraged to use the `seed.py` file to create sample data to test your +models and associations. + +Writing error-free code is more important than completing all of the +deliverables listed- prioritize writing methods that work over writing more +methods that don't work. You should test your code in the console as you write. + +Similarly, messy code that works is better than clean code that doesn't. First, +prioritize getting things working. Then, if there is time at the end, refactor +your code to adhere to best practices. + +**Before you submit!** Save and run your code to verify that it works as you +expect. If you have any methods that are not working yet, feel free to leave +comments describing your progress. + +*** + +## What You Already Have + +The starter code has migrations and models for the initial `Company` and `Dev` +models, and seed data for some `Company`s and `Dev`s. The schema currently looks +like this: + +### companies Table + +| Column | Type | +| ------------- | ------- | +| name | String | +| founding_year | Integer | + +### devs Table + +| Column | Type | +| ------ | ------ | +| name | String | + +You will need to create the migration for the `freebies` table using the +attributes specified in the deliverables below. + +*** + +## Deliverables + +Write the following methods in the classes in the files provided. Feel free to +build out any helper methods if needed. + +Remember: SQLAlchemy gives your classes access to a lot of methods already! +Keep in mind what methods SQLAlchemy gives you access to on each of your +classes when you're approaching the deliverables below. + +### Migrations + +Before working on the rest of the deliverables, you will need to create a +migration for the `freebies` table. + +- A `Freebie` belongs to a `Dev`, and a `Freebie` also belongs to a `Company`. + In your migration, create any columns your `freebies` table will need to + establish these relationships using the right foreign keys. +- The `freebies` table should also have: + - An `item_name` column that stores a string. + - A `value` column that stores an integer. + +After creating the `freebies` table using a migration, use the `seed.py` file to +create instances of your `Freebie` class so you can test your code. + +**After you've set up your `freebies` table**, work on building out the following +deliverables. + +### Relationship Attributes and Methods + +Use SQLAlchemy's `ForeignKey`, `relationship()`, and `backref()` objects to +build relationships between your three models. + +**Note**: The plural of "freebie" is "freebies" and the singular of "freebies" +is "freebie". + +#### Freebie + +- `Freebie.dev` returns the `Dev` instance for this Freebie. +- `Freebie.company` returns the `Company` instance for this Freebie. + +#### Company + +- `Company.freebies` returns a collection of all the freebies for the Company. +- `Company.devs` returns a collection of all the devs who collected freebies + from the company. + +#### Dev + +- `Dev.freebies` returns a collection of all the freebies that the Dev has collected. +- `Dev.companies`returns a collection of all the companies that the Dev has collected + freebies from. + +Use `python debug.py` and check that these methods work before proceeding. For +example, you should be able to retrieve a dev from the database by its +attributes and view their companies with `dev.companies` (based on your seed +data). + +### Aggregate Methods + +#### Freebie + +- `Freebie.print_details()`should return a string formatted as follows: + `{dev name} owns a {freebie item_name} from {company name}`. + +#### Company + +- `Company.give_freebie(dev, item_name, value)` takes a `dev` (an instance of + the `Dev` class), an `item_name` (string), and a `value` as arguments, and + creates a new `Freebie` instance associated with this company and the given + dev. +- Class method `Company.oldest_company()`returns the `Company` instance with + the earliest founding year. + +#### Dev + +- `Dev.received_one(item_name)` accepts an `item_name` (string) and returns + `True` if any of the freebies associated with the dev has that `item_name`, + otherwise returns `False`. +- `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 diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 000000000..6d89215ac --- /dev/null +++ b/alembic.ini @@ -0,0 +1,36 @@ +[alembic] +script_location = migrations +sqlalchemy.url = sqlite:///app.db + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s diff --git a/app.db b/app.db new file mode 100644 index 0000000000000000000000000000000000000000..aa73729d84185a6c6e30386b3f7d5b2846669bf4 GIT binary patch literal 12288 zcmeI#KTE?v7zXgWDry2D-MYSGLPXHcf@cU=jH$gwq0^Dp2*Lc(HZ5Is^CS7q9L*IA zlBJVNd0w~+PmViszfA9V6lQfw&(*w0YU;Bt&uMp<={@p3Ec)0a{ip>n(k%is5kOB%gU#8y?D!;E?y!Dq!l1q5t34--~-e00Izz00bZa0SG_< L0uX=z1U3RccL-Qe literal 0 HcmV?d00001 diff --git a/freebies.db b/freebies.db new file mode 100644 index 0000000000000000000000000000000000000000..4b0d46cabd0ad11afabb7a53677de0eaf8916308 GIT binary patch literal 16384 zcmeI&L2uJA7zSY5Da}&Ug^5i)Ox1jBCEx%D4qO&0uT*r|$O6)Ksa)#Ph$cZA?IeO6zQpJaH63Uc6B6kR-WS4V7 zh^2E$=OP)pa`m1T4L6h)c`*4@)g6rLXJQBIzjT2F0SG_<0uX=z1Rwwb2tWV=|1I$0 zp<&kQb^7sfD*IzKOqA*e>a-|JEl)VU;J)*;D|iv*jfxcvc*pfcTX@_(@VWc4+r7ti zI8|XUlA+?S9Iy4vS!|ui@tL}I-0L`eC!_P;lE)6zWLeXp*V%WxWByzm^F}aeW~hSa0>(LHT?O19T|wTWOA&S=-v8(qvq zsnb%TzqeCqPB*Dp+u5Ph_p|Bd3!him!o>6N4X3np_F;S$4T9)R?_9~GIH*kY-(_X* z=FI*^>>v7q1OW&@00Izz00bZa0SG_<0uX?}iV9R&iEOhIrFdIK=0%lexBGFTzYj?B zYySR!N$j8Y<%(_)%Z301AOHafKmY;|fB*y_009U<;EKQ+Gf3@3juN>>b None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('companies', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(), nullable=True), - sa.Column('founding_year', sa.Integer(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('devs', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('devs') - op.drop_table('companies') - # ### end Alembic commands ### diff --git a/lib/migrations/versions/7a71dbf71c64_create_db.py b/lib/migrations/versions/7a71dbf71c64_create_db.py deleted file mode 100644 index 23e0a655b..000000000 --- a/lib/migrations/versions/7a71dbf71c64_create_db.py +++ /dev/null @@ -1,24 +0,0 @@ -"""create db - -Revision ID: 7a71dbf71c64 -Revises: -Create Date: 2023-03-15 15:05:55.516631 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '7a71dbf71c64' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade() -> None: - pass - - -def downgrade() -> None: - pass diff --git a/lib/models.py b/lib/models.py index 2681bee5a..f10555cba 100644 --- a/lib/models.py +++ b/lib/models.py @@ -1,29 +1,64 @@ -from sqlalchemy import ForeignKey, Column, Integer, String, MetaData -from sqlalchemy.orm import relationship, backref -from sqlalchemy.ext.declarative import declarative_base - -convention = { - "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", -} -metadata = MetaData(naming_convention=convention) - -Base = declarative_base(metadata=metadata) - -class Company(Base): - __tablename__ = 'companies' - - id = Column(Integer(), primary_key=True) - name = Column(String()) - founding_year = Column(Integer()) - - def __repr__(self): - return f'' - -class Dev(Base): - __tablename__ = 'devs' - - id = Column(Integer(), primary_key=True) - name= Column(String()) - - def __repr__(self): - return f'' +# lib/models.py + +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship, declarative_base + +Base = declarative_base() + +class Company(Base): + __tablename__ = 'companies' + + id = Column(Integer, primary_key=True) + name = Column(String, nullable=False) + founding_year = Column(Integer) + + freebies = relationship("Freebie", back_populates="company") + devs = relationship("Dev", secondary="freebies", back_populates="companies", viewonly=True) + + def give_freebie(self, dev, item_name, value): + return Freebie(item_name=item_name, value=value, company=self, dev=dev) + + @classmethod + def oldest_company(cls, session): + return session.query(cls).order_by(cls.founding_year).first() + + +class Dev(Base): + __tablename__ = 'devs' + + id = Column(Integer, primary_key=True) + name = Column(String, nullable=False) + + freebies = relationship("Freebie", back_populates="dev") + companies = relationship("Company", secondary="freebies", back_populates="devs", viewonly=True) + + def received_one(self, item_name): + #Check if this dev has received a freebie with the givvenitem name + return any(freebie.item_name == item_name for freebie in self.freebies) + + def give_away(self, other_dev, freebie): + + # Returns True if successful, otherwise returns False. + + if freebie in self.freebies: + freebie.dev = other_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) + + company_id = Column(Integer, ForeignKey('companies.id'), nullable=False) + dev_id = Column(Integer, ForeignKey('devs.id'), nullable=False) + + company = relationship("Company", back_populates="freebies") + dev = relationship("Dev", back_populates="freebies") + + def print_details(self): + """Return a string with formatted freebie details.""" + return f"{self.dev.name} owns a {self.item_name} from {self.company.name}." diff --git a/lib/seed.py b/lib/seed.py index b16becbbb..2e7a8876d 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -1,3 +1,31 @@ -#!/usr/bin/env python3 - -# Script goes here! +#!/usr/bin/env python3 + +# Script goes here! +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from models import Base, Company, Dev, Freebie + +engine = create_engine('sqlite:///freebie_tracker.db') +Session = sessionmaker(bind=engine) +session = Session() +session.query(Freebie).delete() +session.query(Company).delete() +session.query(Dev).delete() +session.commit() + +c1 = Company(name="TechCity", founding_year=2005) +c2 = Company(name="FreebieCo", founding_year=1995) + +d1 = Dev(name="Eugene") +d2 = Dev(name="Kelly") + +session.add_all([c1, c2, d1, d2]) +session.commit() + +# Create freebies +f1 = Freebie(item_name="Sticker Pack", value=5, company=c1, dev=d1) +f2 = Freebie(item_name="T-Shirt", value=20, company=c1, dev=d2) +f3 = Freebie(item_name="Mug", value=10, company=c2, dev=d1) + +session.add_all([f1, f2, f3]) +session.commit() diff --git a/main.py b/main.py new file mode 100644 index 000000000..8dabb6180 --- /dev/null +++ b/main.py @@ -0,0 +1,45 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from lib.models import Base, Company, Dev, Freebie + +# Connect to the database +engine = create_engine("sqlite:///freebies.db") +Base.metadata.create_all(engine) +Session = sessionmaker(bind=engine) +session = Session() + +session.query(Freebie).delete() +session.query(Dev).delete() +session.query(Company).delete() +session.commit() + +company1 = Company(name="Moringa", founding_year=2015) +company2 = Company(name="Zee Gen", founding_year=2019) + +dev1 = Dev(name="Rahma") +dev2 = Dev(name="Zahra") + +# Create freebies +freebie1 = company1.give_freebie(dev1, "T-shirt", 20) +freebie2 = company2.give_freebie(dev2, "Notebook", 10) +freebie3 = company1.give_freebie(dev2, "Pen", 5) + +session.add_all([company1, company2, dev1, dev2, freebie1, freebie2, freebie3]) +session.commit() + +print("🔹 Freebie Details:") +for freebie in session.query(Freebie).all(): + print(freebie.print_details()) + +print("\n🔹 Has Rahma received a T-shirt?") +print(dev1.received_one("T-shirt")) + +print("\n🔹 Giving away the 'Pen' from Sarah to Rahma:") +success = dev2.give_away(dev1, freebie3) +print("Give away successful:", success) +print(freebie3.print_details()) + +print("\n🔹 Oldest Company:") +oldest = Company.oldest_company(session) +print(f"{oldest.name} founded in {oldest.founding_year}") diff --git a/lib/migrations/README b/migrations/README similarity index 100% rename from lib/migrations/README rename to migrations/README diff --git a/lib/migrations/env.py b/migrations/env.py similarity index 52% rename from lib/migrations/env.py rename to migrations/env.py index c7aab9656..318e0ecb5 100644 --- a/lib/migrations/env.py +++ b/migrations/env.py @@ -1,3 +1,5 @@ +import sys +import os from logging.config import fileConfig from sqlalchemy import engine_from_config @@ -5,40 +7,20 @@ from alembic import context -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from lib.models import Base + + config = context.config -# Interpret the config file for Python logging. -# This line sets up loggers basically. if config.config_file_name is not None: fileConfig(config.config_file_name) -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -from models import Base target_metadata = Base.metadata -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - def run_migrations_offline() -> None: - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ url = config.get_main_option("sqlalchemy.url") context.configure( url=url, @@ -52,12 +34,6 @@ def run_migrations_offline() -> None: def run_migrations_online() -> None: - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ connectable = engine_from_config( config.get_section(config.config_ini_section), prefix="sqlalchemy.", @@ -66,7 +42,7 @@ def run_migrations_online() -> None: with connectable.connect() as connection: context.configure( - connection=connection, target_metadata=target_metadata, render_as_batch=True, + connection=connection, target_metadata=target_metadata ) with context.begin_transaction(): diff --git a/lib/migrations/script.py.mako b/migrations/script.py.mako similarity index 100% rename from lib/migrations/script.py.mako rename to migrations/script.py.mako