whole app
This commit is contained in:
16
Pipfile
Normal file
16
Pipfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
sqlalchemy = "*"
|
||||||
|
mysql-connector-python = "*"
|
||||||
|
flake8 = "*"
|
||||||
|
flake8-html = "*"
|
||||||
|
pytest = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.10"
|
||||||
390
Pipfile.lock
generated
Normal file
390
Pipfile.lock
generated
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "cfa8671212ad20e3d9752509b7d700143b3c1622bb7a13fc892bcfd86b888abd"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.10"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"exceptiongroup": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10",
|
||||||
|
"sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.3.0"
|
||||||
|
},
|
||||||
|
"flake8": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e",
|
||||||
|
"sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==7.3.0"
|
||||||
|
},
|
||||||
|
"flake8-html": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8b870299620cc4a06f73644a1b4d457799abeca1cc914c62ae71ec5bf65c79a5",
|
||||||
|
"sha256:8f126748b1b0edd6cd39e87c6192df56e2f8655b0aa2bb00ffeac8cf27be4325"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.4.3"
|
||||||
|
},
|
||||||
|
"greenlet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:003c930e0e074db83559edc8705f3a2d066d4aa8c2f198aff1e454946efd0f26",
|
||||||
|
"sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36",
|
||||||
|
"sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892",
|
||||||
|
"sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83",
|
||||||
|
"sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688",
|
||||||
|
"sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be",
|
||||||
|
"sha256:22eb5ba839c4b2156f18f76768233fe44b23a31decd9cc0d4cc8141c211fd1b4",
|
||||||
|
"sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d",
|
||||||
|
"sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5",
|
||||||
|
"sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b",
|
||||||
|
"sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb",
|
||||||
|
"sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86",
|
||||||
|
"sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c",
|
||||||
|
"sha256:42efc522c0bd75ffa11a71e09cd8a399d83fafe36db250a87cf1dacfaa15dc64",
|
||||||
|
"sha256:4532f0d25df67f896d137431b13f4cdce89f7e3d4a96387a41290910df4d3a57",
|
||||||
|
"sha256:49c8cfb18fb419b3d08e011228ef8a25882397f3a859b9fe1436946140b6756b",
|
||||||
|
"sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad",
|
||||||
|
"sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95",
|
||||||
|
"sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3",
|
||||||
|
"sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147",
|
||||||
|
"sha256:68671180e3849b963649254a882cd544a3c75bfcd2c527346ad8bb53494444db",
|
||||||
|
"sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb",
|
||||||
|
"sha256:72e77ed69312bab0434d7292316d5afd6896192ac4327d44f3d613ecb85b037c",
|
||||||
|
"sha256:731e154aba8e757aedd0781d4b240f1225b075b4409f1bb83b05ff410582cf00",
|
||||||
|
"sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849",
|
||||||
|
"sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba",
|
||||||
|
"sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac",
|
||||||
|
"sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822",
|
||||||
|
"sha256:7e70ea4384b81ef9e84192e8a77fb87573138aa5d4feee541d8014e452b434da",
|
||||||
|
"sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97",
|
||||||
|
"sha256:8324319cbd7b35b97990090808fdc99c27fe5338f87db50514959f8059999805",
|
||||||
|
"sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34",
|
||||||
|
"sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141",
|
||||||
|
"sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3",
|
||||||
|
"sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0",
|
||||||
|
"sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b",
|
||||||
|
"sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365",
|
||||||
|
"sha256:8c37ef5b3787567d322331d5250e44e42b58c8c713859b8a04c6065f27efbf72",
|
||||||
|
"sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a",
|
||||||
|
"sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc",
|
||||||
|
"sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163",
|
||||||
|
"sha256:96c20252c2f792defe9a115d3287e14811036d51e78b3aaddbee23b69b216302",
|
||||||
|
"sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef",
|
||||||
|
"sha256:a433dbc54e4a37e4fff90ef34f25a8c00aed99b06856f0119dcf09fbafa16392",
|
||||||
|
"sha256:aaa7aae1e7f75eaa3ae400ad98f8644bb81e1dc6ba47ce8a93d3f17274e08322",
|
||||||
|
"sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d",
|
||||||
|
"sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264",
|
||||||
|
"sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b",
|
||||||
|
"sha256:ce539fb52fb774d0802175d37fcff5c723e2c7d249c65916257f0a940cee8904",
|
||||||
|
"sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf",
|
||||||
|
"sha256:d760f9bdfe79bff803bad32b4d8ffb2c1d2ce906313fc10a83976ffb73d64ca7",
|
||||||
|
"sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a",
|
||||||
|
"sha256:efc6dc8a792243c31f2f5674b670b3a95d46fa1c6a912b8e310d6f542e7b0712",
|
||||||
|
"sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==3.2.3"
|
||||||
|
},
|
||||||
|
"iniconfig": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7",
|
||||||
|
"sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==2.1.0"
|
||||||
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d",
|
||||||
|
"sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==3.1.6"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"mccabe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||||
|
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.7.0"
|
||||||
|
},
|
||||||
|
"mysql-connector-python": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0aedee809e1f8dbab6b2732f51ee1619b54a56d15b9070655bc31fb822c1a015",
|
||||||
|
"sha256:1916256ecd039f4673715550d28138416bac5962335e06d36f7434c47feb5232",
|
||||||
|
"sha256:2589af070babdff9c920ee37f929218d80afa704f4e2a99f1ddcb13d19de4450",
|
||||||
|
"sha256:2a5de57814217077a8672063167b616b1034a37b614b93abcb602cc0b8c6fade",
|
||||||
|
"sha256:3853799f4b719357ea25eba05f5f278a158a85a5c8209b3d058947a948bc9262",
|
||||||
|
"sha256:4364d3a37c449f1c0bb9e52fd4eddc620126b9897b6b9f2fd1b3f33dacc16356",
|
||||||
|
"sha256:495798dd34445d749991fb3a2aa87b4205100676939556d8d4aab5d5558e7a1f",
|
||||||
|
"sha256:5508ff6b79d8d46b15791401784a1b5abd10c8e05aec2684c4a50e92c5893cd2",
|
||||||
|
"sha256:55d4a8ace6f97d58d9318d1250d903b0d3b100a6b798442a99c4ac966b974d12",
|
||||||
|
"sha256:66d48ec0ee903a84bcaf5d4d1901ed536fdd90ce6ecae0686f094b4530faf545",
|
||||||
|
"sha256:8ab7719d614cf5463521082fab86afc21ada504b538166090e00eeaa1ff729bc",
|
||||||
|
"sha256:8b16d51447e3603f18478fb5a19b333bfb73fb58f872eb055a105635f53d2345",
|
||||||
|
"sha256:8c79b500f1f9f12761426199d0498309ee5d20c94ed94fc8ae356679667f8181",
|
||||||
|
"sha256:9516a4cdbaee3c9200f0e7d9aafb31057692f45c202cdcb43a3f9b37c94e7c84",
|
||||||
|
"sha256:9c898c5f3e34314ed825f2ffdd52d674e03d59c45d02ac8083a8ec5173c1e0f8",
|
||||||
|
"sha256:9cc8d3c2f45d16b064b0063db857f8a7187b8659253dd32e3f19df1bf1d55ea0",
|
||||||
|
"sha256:ac70a7128f7e690dc0f4376be8366c7e5c8fa47a785232b8abba948576f016ff",
|
||||||
|
"sha256:be0ef15f6023ae2037347498f005a4471f694f8a6b8384c3194895e153120286",
|
||||||
|
"sha256:cb72fcda90b616f0b2d3dae257441e06e8896b2780c3dddc6a65275ec1408d9a",
|
||||||
|
"sha256:d33e2f88e1d4b15844cfed2bb6e90612525ba2c1af2fb10b4a25b2c89a1fe49a",
|
||||||
|
"sha256:d47a0d5b2b9b02f06647d5d7bbb19e237f234d6be91d0e0c935629faacf0797f",
|
||||||
|
"sha256:d87c9e8b5aa9a16cefebe017ee45ddfbad53e668f94d01fe2e055bb8daab9353",
|
||||||
|
"sha256:e24be22a5d96f3535afa5dd331166b02bf72655ea6ed6a2a0eb548c313548788",
|
||||||
|
"sha256:e8b0131006608e533b8eab20078f9e65486068c984ed3efd28413d350d241f44",
|
||||||
|
"sha256:ee1a901c287471013570e29cdf5ca7159898af31cf3a582180eadd41c96b42c9",
|
||||||
|
"sha256:f10fe89397e8da81026d8143e17fc5c12ae5e66e51753a0f49e1db179c4f7113",
|
||||||
|
"sha256:f979e712187796ad57cd0bef76666dd48ed4887104775833c9489ea837144ad8"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==9.3.0"
|
||||||
|
},
|
||||||
|
"packaging": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484",
|
||||||
|
"sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==25.0"
|
||||||
|
},
|
||||||
|
"pluggy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3",
|
||||||
|
"sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==1.6.0"
|
||||||
|
},
|
||||||
|
"pycodestyle": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783",
|
||||||
|
"sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==2.14.0"
|
||||||
|
},
|
||||||
|
"pyflakes": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58",
|
||||||
|
"sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==3.4.0"
|
||||||
|
},
|
||||||
|
"pygments": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887",
|
||||||
|
"sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==2.19.2"
|
||||||
|
},
|
||||||
|
"pytest": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7",
|
||||||
|
"sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==8.4.1"
|
||||||
|
},
|
||||||
|
"sqlalchemy": {
|
||||||
|
"hashes": [
|
||||||
|
"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",
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==2.0.41"
|
||||||
|
},
|
||||||
|
"tomli": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6",
|
||||||
|
"sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd",
|
||||||
|
"sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c",
|
||||||
|
"sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b",
|
||||||
|
"sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8",
|
||||||
|
"sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6",
|
||||||
|
"sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77",
|
||||||
|
"sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff",
|
||||||
|
"sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea",
|
||||||
|
"sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192",
|
||||||
|
"sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249",
|
||||||
|
"sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee",
|
||||||
|
"sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4",
|
||||||
|
"sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98",
|
||||||
|
"sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8",
|
||||||
|
"sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4",
|
||||||
|
"sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281",
|
||||||
|
"sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744",
|
||||||
|
"sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69",
|
||||||
|
"sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13",
|
||||||
|
"sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140",
|
||||||
|
"sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e",
|
||||||
|
"sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e",
|
||||||
|
"sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc",
|
||||||
|
"sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff",
|
||||||
|
"sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec",
|
||||||
|
"sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2",
|
||||||
|
"sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222",
|
||||||
|
"sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106",
|
||||||
|
"sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272",
|
||||||
|
"sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a",
|
||||||
|
"sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==2.2.1"
|
||||||
|
},
|
||||||
|
"typing-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36",
|
||||||
|
"sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==4.14.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
||||||
0
__init__.py
Normal file
0
__init__.py
Normal file
81
authentication.py
Normal file
81
authentication.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
from passlib.hash import argon2
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from models import Collaborator, Credentials
|
||||||
|
import jwt
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordTools:
|
||||||
|
"""
|
||||||
|
Tool used to manage passwords and interact with DB
|
||||||
|
"""
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
def hash(self, password: str) -> str:
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param password:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return argon2.hash(password)
|
||||||
|
|
||||||
|
def get_by_name(self, username: str):
|
||||||
|
"""
|
||||||
|
Get the collaborator's name and return password hash associated if
|
||||||
|
existing
|
||||||
|
:param username: Collaborators.name
|
||||||
|
:return: Credential.password_hash
|
||||||
|
"""
|
||||||
|
if self.db.execute(
|
||||||
|
select(Collaborator).filter_by(name=username)).scalar():
|
||||||
|
sbq = select(Collaborator).where(
|
||||||
|
Collaborator.name == username).subquery()
|
||||||
|
stmt = select(Credentials).join(
|
||||||
|
sbq,
|
||||||
|
Credentials.collaborator_id == sbq.c.id)
|
||||||
|
return self.db.execute(stmt).scalar()
|
||||||
|
return {'message': "Wrong username"}
|
||||||
|
|
||||||
|
def check(self, username: str, password: str) -> bool:
|
||||||
|
# if self.db.scalars(
|
||||||
|
# select(Collaborator).filter_by(name=username)).all():
|
||||||
|
# sbq = select(Collaborator).where(
|
||||||
|
# Collaborator.name == username).subquery()
|
||||||
|
# stmt = select(Credentials).join(
|
||||||
|
# sbq,
|
||||||
|
# Credentials.collaborator_id == sbq.c.id)
|
||||||
|
# result = self.db.scalars(stmt).all()
|
||||||
|
# user_pw = result[0].password_hash
|
||||||
|
user = self.get_by_name(username)
|
||||||
|
if not user:
|
||||||
|
print("Wrong user")
|
||||||
|
return False
|
||||||
|
user_pw = user.password_hash
|
||||||
|
return argon2.verify(password, user_pw)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenTools:
|
||||||
|
def __init__(self, username: str, password: str, team_id: int):
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.team_id = team_id
|
||||||
|
|
||||||
|
def get_token(self, username: str, password: str, team_id: int):
|
||||||
|
# team_id = Collaborator.get_team_by_name(self.username)
|
||||||
|
payload = {'user': username,
|
||||||
|
'password': password,
|
||||||
|
'team_id': team_id,
|
||||||
|
}
|
||||||
|
return jwt.encode(payload, password, algorithm="HS256")
|
||||||
|
|
||||||
|
def check_token(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def store_token(self, token):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuthTools:
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
5
config.py
Normal file
5
config.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
user = "epicevents"
|
||||||
|
password = "m3gapassword"
|
||||||
|
|
||||||
|
user1 = "root"
|
||||||
|
pass1 = "proutproutprout"
|
||||||
66
controllers.py
Normal file
66
controllers.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
from menu import CommercialMenu, ManagementMenu, SupportMenu
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
||||||
|
class App:
|
||||||
|
"""
|
||||||
|
Entry point of the application. Instantiate Tools which are meant to be
|
||||||
|
like kind of repository pattern to interact with data, and Menus then route
|
||||||
|
depending on team... sort of permission management using simple_term_menu
|
||||||
|
"""
|
||||||
|
def __init__(self,
|
||||||
|
db: Session,
|
||||||
|
view,
|
||||||
|
collaborator_tools,
|
||||||
|
customer_tools,
|
||||||
|
contract_tools,
|
||||||
|
event_tools,
|
||||||
|
passwd_tools,
|
||||||
|
tools):
|
||||||
|
self.db = db
|
||||||
|
self.view = view
|
||||||
|
self.passwd_tools = passwd_tools
|
||||||
|
self.collaborator_tools = collaborator_tools
|
||||||
|
self.customer_tools = customer_tools
|
||||||
|
self.contract_tools = contract_tools
|
||||||
|
self.event_tools = event_tools
|
||||||
|
self.tools = tools
|
||||||
|
|
||||||
|
def connect(self) -> tuple | None:
|
||||||
|
"""
|
||||||
|
Check if provided password exists in table credentials and verify
|
||||||
|
if matches with stored hash
|
||||||
|
:return: tuple(team_id:int, user_id:int) | None and print if no match
|
||||||
|
"""
|
||||||
|
username, password = self.view.prompt_connect()
|
||||||
|
if self.passwd_tools.check(username, password):
|
||||||
|
perm = self.collaborator_tools.get_team_by_name(username)
|
||||||
|
user_id = self.collaborator_tools.get_id_by_name(username)
|
||||||
|
return perm, user_id
|
||||||
|
else:
|
||||||
|
print("Connection failed")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
team, user_id = self.connect()
|
||||||
|
if not user_id:
|
||||||
|
exit()
|
||||||
|
if team == 1:
|
||||||
|
CommercialMenu(self.customer_tools,
|
||||||
|
self.contract_tools,
|
||||||
|
self.event_tools,
|
||||||
|
self.tools).launch()
|
||||||
|
|
||||||
|
if team == 2:
|
||||||
|
ManagementMenu(self.collaborator_tools,
|
||||||
|
self.customer_tools,
|
||||||
|
self.contract_tools,
|
||||||
|
self.event_tools,
|
||||||
|
self.tools).launch()
|
||||||
|
|
||||||
|
if team == 3:
|
||||||
|
SupportMenu(self.customer_tools,
|
||||||
|
self.contract_tools,
|
||||||
|
self.event_tools,
|
||||||
|
self.tools,
|
||||||
|
user_id).launch()
|
||||||
11
db.py
Normal file
11
db.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from config import user1, pass1
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
|
||||||
|
DB_URL = "mysql+mysqlconnector://"+user1+":"+pass1+"@localhost:3306/prout"
|
||||||
|
|
||||||
|
|
||||||
|
engine = create_engine(DB_URL, echo=False)
|
||||||
|
SessionLocal = sessionmaker(bind=engine)
|
||||||
|
session = SessionLocal()
|
||||||
73
flake-report/back.svg
Normal file
73
flake-report/back.svg
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="32"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 32 48"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="back.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#3d3d3d"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="47.245066"
|
||||||
|
inkscape:cy="13.218734"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
width="32px"
|
||||||
|
inkscape:window-width="1535"
|
||||||
|
inkscape:window-height="876"
|
||||||
|
inkscape:window-x="65"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1004.3622)">
|
||||||
|
<g
|
||||||
|
id="g5305"
|
||||||
|
transform="translate(0,-3.5)">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5301"
|
||||||
|
d="M 15.577993,1039.1732 4.7040093,1028.3079 15.469253,1017.5512"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5303"
|
||||||
|
d="m 4.921489,1028.3622 26.53252,0"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
64
flake-report/file.svg
Normal file
64
flake-report/file.svg
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="32"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 32 48"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="file.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#3d3d3d"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="24.812518"
|
||||||
|
inkscape:cy="18.901073"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
width="32px"
|
||||||
|
inkscape:window-width="1535"
|
||||||
|
inkscape:window-height="876"
|
||||||
|
inkscape:window-x="65"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1004.3622)">
|
||||||
|
<path
|
||||||
|
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 4.1360619,1007.3622 0,34.375 23.7278761,0 0,-29.9038 -4.471158,-4.4712 -19.2567181,0 z m 18.5469091,0.6084 4.471159,4.4712 -4.471159,0 0,-4.4712 z m -16.6202866,7.8079 19.8746316,0 0,1.8252 -19.8746316,0 0,-1.8252 z m 0,3.2448 19.8746316,0 0,1.8253 -19.8746316,0 0,-1.8253 z m 0,6.0841 19.8746316,0 0,1.8252 -19.8746316,0 0,-1.8252 z m 0,8.2135 19.8746316,0 0,1.8252 -19.8746316,0 0,-1.8252 z"
|
||||||
|
id="path4749"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
30
flake-report/index.html
Normal file
30
flake-report/index.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>flake8 violations</title>
|
||||||
|
<meta http-equiv="Content-Type" value="text/html; charset=UTF-8">
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="masthead" class="sev-4"></div>
|
||||||
|
<div id="page">
|
||||||
|
<h1>flake8 violations</h1>
|
||||||
|
<p id="versions">Generated on 2025-08-19 17:29
|
||||||
|
with Installed plugins: flake8-html: 0.4.3, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0
|
||||||
|
</p>
|
||||||
|
<ul id="index">
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<div id="all-good">
|
||||||
|
<span class="count sev-4">
|
||||||
|
<span class="tick">✓</span>
|
||||||
|
</span>
|
||||||
|
<h2>All good!</h2>
|
||||||
|
<p>No flake8 errors found in 10 files scanned.</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
327
flake-report/styles.css
Normal file
327
flake-report/styles.css
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: white;
|
||||||
|
font-size: 36px;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 img {
|
||||||
|
margin-right: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#versions {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#page {
|
||||||
|
position: relative;
|
||||||
|
max-width: 960px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index {
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index li {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index li + li {
|
||||||
|
border-top: solid silver 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details p {
|
||||||
|
margin-left: 3em;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index a {
|
||||||
|
display: block;
|
||||||
|
padding: 0.8em 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index #all-good {
|
||||||
|
padding: 1.4em 1em 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#all-good .count .tick {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#all-good .count {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#all-good h2,
|
||||||
|
#all-good p {
|
||||||
|
margin-left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index a:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
text-align: center;
|
||||||
|
width: 2.5em;
|
||||||
|
line-height: 2.5em;
|
||||||
|
height: 2.5em;
|
||||||
|
color: white;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sev-1 {
|
||||||
|
background-color: #a00;
|
||||||
|
}
|
||||||
|
.sev-2 {
|
||||||
|
background-color: #b80;
|
||||||
|
}
|
||||||
|
.sev-3 {
|
||||||
|
background-color: #28c;
|
||||||
|
}
|
||||||
|
.sev-4 {
|
||||||
|
background-color: #383;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#doc {
|
||||||
|
background-color: white;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 1em;
|
||||||
|
padding-left: 1.2em;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#doc pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.07em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.violations {
|
||||||
|
position: absolute;
|
||||||
|
margin: 1.2em 0 0 3em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.4);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.violations .count {
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.violations li {
|
||||||
|
padding: 0.1em 0.3em;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-violations::before {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -1em;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code:hover .violations {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt i {
|
||||||
|
color: silver;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
width: 3em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
border-right: solid #eee 1px;
|
||||||
|
padding-right: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.le {
|
||||||
|
background-color: #ffe8e8;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.le:hover {
|
||||||
|
background-color: #fcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index .details {
|
||||||
|
border-top-style: none;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.details {
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index .details li {
|
||||||
|
list-style: none;
|
||||||
|
border-top-style: none;
|
||||||
|
margin: 0.3em 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#srclink {
|
||||||
|
float: right;
|
||||||
|
font-size: 36px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#srclink a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#index .details a {
|
||||||
|
padding: 0;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.le {
|
||||||
|
background-color: #ffe8e8;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.le.sev-1 {
|
||||||
|
background-color: #f88;
|
||||||
|
}
|
||||||
|
.le.sev-2 {
|
||||||
|
background-color: #fda;
|
||||||
|
}
|
||||||
|
.le.sev-3 {
|
||||||
|
background-color: #adf;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 1.2em;
|
||||||
|
vertical-align: -0.35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre { line-height: 125%; }
|
||||||
|
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||||
|
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||||
|
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||||
|
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||||
|
.hll { background-color: #ffffcc }
|
||||||
|
.c { color: #3D7B7B; font-style: italic } /* Comment */
|
||||||
|
.err { border: 1px solid #F00 } /* Error */
|
||||||
|
.k { color: #008000; font-weight: bold } /* Keyword */
|
||||||
|
.o { color: #666 } /* Operator */
|
||||||
|
.ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
|
||||||
|
.cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
|
||||||
|
.cp { color: #9C6500 } /* Comment.Preproc */
|
||||||
|
.cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
|
||||||
|
.c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
|
||||||
|
.cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
|
||||||
|
.gd { color: #A00000 } /* Generic.Deleted */
|
||||||
|
.ge { font-style: italic } /* Generic.Emph */
|
||||||
|
.ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||||
|
.gr { color: #E40000 } /* Generic.Error */
|
||||||
|
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||||
|
.gi { color: #008400 } /* Generic.Inserted */
|
||||||
|
.go { color: #717171 } /* Generic.Output */
|
||||||
|
.gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||||
|
.gs { font-weight: bold } /* Generic.Strong */
|
||||||
|
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||||
|
.gt { color: #04D } /* Generic.Traceback */
|
||||||
|
.kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||||
|
.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||||
|
.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||||
|
.kp { color: #008000 } /* Keyword.Pseudo */
|
||||||
|
.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||||
|
.kt { color: #B00040 } /* Keyword.Type */
|
||||||
|
.m { color: #666 } /* Literal.Number */
|
||||||
|
.s { color: #BA2121 } /* Literal.String */
|
||||||
|
.na { color: #687822 } /* Name.Attribute */
|
||||||
|
.nb { color: #008000 } /* Name.Builtin */
|
||||||
|
.nc { color: #00F; font-weight: bold } /* Name.Class */
|
||||||
|
.no { color: #800 } /* Name.Constant */
|
||||||
|
.nd { color: #A2F } /* Name.Decorator */
|
||||||
|
.ni { color: #717171; font-weight: bold } /* Name.Entity */
|
||||||
|
.ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
|
||||||
|
.nf { color: #00F } /* Name.Function */
|
||||||
|
.nl { color: #767600 } /* Name.Label */
|
||||||
|
.nn { color: #00F; font-weight: bold } /* Name.Namespace */
|
||||||
|
.nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||||
|
.nv { color: #19177C } /* Name.Variable */
|
||||||
|
.ow { color: #A2F; font-weight: bold } /* Operator.Word */
|
||||||
|
.w { color: #BBB } /* Text.Whitespace */
|
||||||
|
.mb { color: #666 } /* Literal.Number.Bin */
|
||||||
|
.mf { color: #666 } /* Literal.Number.Float */
|
||||||
|
.mh { color: #666 } /* Literal.Number.Hex */
|
||||||
|
.mi { color: #666 } /* Literal.Number.Integer */
|
||||||
|
.mo { color: #666 } /* Literal.Number.Oct */
|
||||||
|
.sa { color: #BA2121 } /* Literal.String.Affix */
|
||||||
|
.sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||||
|
.sc { color: #BA2121 } /* Literal.String.Char */
|
||||||
|
.dl { color: #BA2121 } /* Literal.String.Delimiter */
|
||||||
|
.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||||
|
.s2 { color: #BA2121 } /* Literal.String.Double */
|
||||||
|
.se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
|
||||||
|
.sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||||
|
.si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
|
||||||
|
.sx { color: #008000 } /* Literal.String.Other */
|
||||||
|
.sr { color: #A45A77 } /* Literal.String.Regex */
|
||||||
|
.s1 { color: #BA2121 } /* Literal.String.Single */
|
||||||
|
.ss { color: #19177C } /* Literal.String.Symbol */
|
||||||
|
.bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||||
|
.fm { color: #00F } /* Name.Function.Magic */
|
||||||
|
.vc { color: #19177C } /* Name.Variable.Class */
|
||||||
|
.vg { color: #19177C } /* Name.Variable.Global */
|
||||||
|
.vi { color: #19177C } /* Name.Variable.Instance */
|
||||||
|
.vm { color: #19177C } /* Name.Variable.Magic */
|
||||||
|
.il { color: #666 } /* Literal.Number.Integer.Long */
|
||||||
32
main.py
Normal file
32
main.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import models
|
||||||
|
import tools
|
||||||
|
from db import engine, session
|
||||||
|
from controllers import App
|
||||||
|
from views import View
|
||||||
|
from authentication import PasswordTools
|
||||||
|
|
||||||
|
|
||||||
|
models.Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
view = View()
|
||||||
|
collaborator_tools = tools.CollaboratorTools(session)
|
||||||
|
customer_tools = tools.CustomerTools(session)
|
||||||
|
contract_tools = tools.ContractTools(session)
|
||||||
|
event_tools = tools.EventTools(session)
|
||||||
|
passwd_tools = PasswordTools(session)
|
||||||
|
common_tools = tools.Tools(session)
|
||||||
|
with session:
|
||||||
|
App(session,
|
||||||
|
view,
|
||||||
|
collaborator_tools,
|
||||||
|
customer_tools,
|
||||||
|
contract_tools,
|
||||||
|
event_tools,
|
||||||
|
passwd_tools,
|
||||||
|
common_tools).start()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
351
menu.py
Normal file
351
menu.py
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
from simple_term_menu import TerminalMenu
|
||||||
|
from models import Customer, Contract, Collaborator, Event
|
||||||
|
|
||||||
|
|
||||||
|
class Prompt:
|
||||||
|
|
||||||
|
def menu(self, options):
|
||||||
|
terminal_menu = TerminalMenu(options)
|
||||||
|
menu_entry_index = terminal_menu.show()
|
||||||
|
selection = options[menu_entry_index]
|
||||||
|
return selection
|
||||||
|
|
||||||
|
def exec_menu(self, dict_options):
|
||||||
|
selection = self.menu(list(dict_options.keys()))
|
||||||
|
selected_function = dict_options.get(selection)
|
||||||
|
selected_function()
|
||||||
|
|
||||||
|
def return_menu(self, dict_options):
|
||||||
|
selection = self.menu(list(dict_options.keys()))
|
||||||
|
return dict_options.get(selection)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CommercialMenu:
|
||||||
|
"""
|
||||||
|
Main menu for commercial users displaying only specific submenus according
|
||||||
|
to permissions :
|
||||||
|
customers: CRUD
|
||||||
|
contracts: RU
|
||||||
|
events: CR
|
||||||
|
filter :
|
||||||
|
contract: signed
|
||||||
|
"""
|
||||||
|
def __init__(self, customer_tools, contract_tools, event_tools, tools):
|
||||||
|
self.customer_tools = customer_tools
|
||||||
|
self.contract_tools = contract_tools
|
||||||
|
self.event_tools = event_tools
|
||||||
|
self.tools = tools
|
||||||
|
self.prompt = Prompt()
|
||||||
|
|
||||||
|
def launch(self):
|
||||||
|
"""
|
||||||
|
Menu entry point for commercial user; display objects then submenu
|
||||||
|
:return: call submenus
|
||||||
|
"""
|
||||||
|
base = {
|
||||||
|
"Customer": self.customer_menu,
|
||||||
|
"Contract": self.contract_menu,
|
||||||
|
"Event": self.event_menu,
|
||||||
|
"Quit": quit,
|
||||||
|
}
|
||||||
|
while True:
|
||||||
|
self.prompt.exec_menu(base)
|
||||||
|
|
||||||
|
def customers_for_update(self):
|
||||||
|
"""
|
||||||
|
get all customers from db, create a dict options, call menu
|
||||||
|
and get choice (Customer.id)
|
||||||
|
:return: exec the update tool with the chosen id
|
||||||
|
"""
|
||||||
|
options = {}
|
||||||
|
for item in self.tools.list(Customer):
|
||||||
|
options[item[0].name] = item[0].id
|
||||||
|
choice = self.prompt.return_menu(options)
|
||||||
|
self.customer_tools.update(choice)
|
||||||
|
|
||||||
|
def customer_menu(self):
|
||||||
|
"""
|
||||||
|
display the CRUD menu for customer and get choice
|
||||||
|
:return: exec the function associated with chosen item
|
||||||
|
"""
|
||||||
|
customer_options = {
|
||||||
|
"List": self.customer_tools.list,
|
||||||
|
"Create": self.customer_tools.create,
|
||||||
|
"Update": self.customers_for_update,
|
||||||
|
"Delete": self.customer_tools.delete,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(customer_options)
|
||||||
|
|
||||||
|
def contracts_for_update(self):
|
||||||
|
"""
|
||||||
|
get all contracts from db, create a dict options, call menu
|
||||||
|
and get choice (Contract.id)
|
||||||
|
:return: exec the update tool with the chosen id
|
||||||
|
"""
|
||||||
|
options = {}
|
||||||
|
for item in self.tools.list(Contract):
|
||||||
|
options["Contrat "+str(item[0].id)] = item[0].id
|
||||||
|
choice = self.prompt.return_menu(options)
|
||||||
|
self.contract_tools.update(choice)
|
||||||
|
|
||||||
|
def contract_menu(self):
|
||||||
|
"""
|
||||||
|
display the CRUD menu for contracts and get choice
|
||||||
|
:return: exec the function associated with chosen item
|
||||||
|
"""
|
||||||
|
contract_options = {
|
||||||
|
"List": self.contract_list,
|
||||||
|
"Update": self.contracts_for_update,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(contract_options)
|
||||||
|
|
||||||
|
def contract_list(self):
|
||||||
|
"""
|
||||||
|
interface after CRUD menu : list meant to provide filter
|
||||||
|
here on signed attribute
|
||||||
|
:return: exec tool method which apply filter and call view to display
|
||||||
|
"""
|
||||||
|
contract_list_options = {
|
||||||
|
"Signed": self.contract_tools.signed,
|
||||||
|
"Not signed": self.contract_tools.not_signed,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(contract_list_options)
|
||||||
|
|
||||||
|
def event_menu(self):
|
||||||
|
"""
|
||||||
|
display the CRUD menu for event and get choice
|
||||||
|
:return: exec the function associated with chosen item
|
||||||
|
"""
|
||||||
|
event_options = {
|
||||||
|
"List": self.event_tools.list,
|
||||||
|
"Create": self.event_tools.create,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(event_options)
|
||||||
|
|
||||||
|
|
||||||
|
class ManagementMenu:
|
||||||
|
"""
|
||||||
|
Main menu for manager users displaying only specific submenus according
|
||||||
|
to permissions :
|
||||||
|
collaborators: CRUD
|
||||||
|
customers: R
|
||||||
|
contracts: CRU
|
||||||
|
events: RU
|
||||||
|
filter:
|
||||||
|
event: support_id
|
||||||
|
"""
|
||||||
|
def __init__(self, collaborator_tools,
|
||||||
|
customer_tools,
|
||||||
|
contract_tools,
|
||||||
|
event_tools, tools):
|
||||||
|
self.collaborator_tools = collaborator_tools
|
||||||
|
self.customer_tools = customer_tools
|
||||||
|
self.contract_tools = contract_tools
|
||||||
|
self.event_tools = event_tools
|
||||||
|
self.tools = tools
|
||||||
|
self.prompt = Prompt()
|
||||||
|
|
||||||
|
def launch(self):
|
||||||
|
"""
|
||||||
|
Menu entry point for management user; display objects then submenu
|
||||||
|
:return: call submenus
|
||||||
|
"""
|
||||||
|
base = {
|
||||||
|
"Collaborator": self.collaborator_menu,
|
||||||
|
"Customer": self.customer_menu,
|
||||||
|
"Contract": self.contract_menu,
|
||||||
|
"Event": self.event_menu,
|
||||||
|
"Quit": quit,
|
||||||
|
}
|
||||||
|
while True:
|
||||||
|
self.prompt.exec_menu(base)
|
||||||
|
|
||||||
|
def choose_collaborator(self):
|
||||||
|
"""
|
||||||
|
gets all collaborators from db, creates a dict options, calls menu
|
||||||
|
and returns choice (Collaborator.id)
|
||||||
|
:return: chosen Collaborator.id:int
|
||||||
|
"""
|
||||||
|
options = {}
|
||||||
|
for item in self.tools.list(Collaborator):
|
||||||
|
options[item[0].name] = item[0].id
|
||||||
|
return self.prompt.return_menu(options)
|
||||||
|
|
||||||
|
def collaborator_update(self):
|
||||||
|
"""
|
||||||
|
exec choose_collaborator and call tool update with chosen id
|
||||||
|
"""
|
||||||
|
choice = self.choose_collaborator()
|
||||||
|
self.collaborator_tools.update(choice)
|
||||||
|
|
||||||
|
def collaborator_delete(self):
|
||||||
|
"""
|
||||||
|
exec choose_collaborator and call tool delete with chosen id
|
||||||
|
"""
|
||||||
|
choice = self.choose_collaborator()
|
||||||
|
self.collaborator_tools.delete(choice)
|
||||||
|
|
||||||
|
def collaborator_menu(self):
|
||||||
|
"""
|
||||||
|
display the CRUD menu for collaborator and get choice
|
||||||
|
:return: exec the function associated with chosen item
|
||||||
|
"""
|
||||||
|
collaborator_options = {
|
||||||
|
"List": self.collaborator_tools.list,
|
||||||
|
"Create": self.collaborator_tools.create,
|
||||||
|
"Update": self.collaborator_update,
|
||||||
|
"Delete": self.collaborator_delete,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(collaborator_options)
|
||||||
|
|
||||||
|
def customer_menu(self):
|
||||||
|
"""
|
||||||
|
display the CRUD menu for customer and get choice
|
||||||
|
:return: exec the function associated with chosen item
|
||||||
|
"""
|
||||||
|
customer_options = {"List": self.customer_tools.list}
|
||||||
|
self.prompt.exec_menu(customer_options)
|
||||||
|
|
||||||
|
def contracts_for_update(self):
|
||||||
|
(options,
|
||||||
|
customer_options,
|
||||||
|
commercial_options,
|
||||||
|
event_options) = {},{},{},{}
|
||||||
|
commercial = self.collaborator_tools.get_by_team_id(1)
|
||||||
|
for customer in self.tools.list(Customer):
|
||||||
|
customer_options[customer[0].name] = customer[0].id
|
||||||
|
for user in commercial:
|
||||||
|
commercial_options[user[0].name] = user[0].id
|
||||||
|
for event in self.tools.list(Event):
|
||||||
|
event_options[event[0].name] = event[0].id
|
||||||
|
for item in self.tools.list(Contract):
|
||||||
|
options["Contrat "+str(item[0].id)] = item[0].id
|
||||||
|
choice = self.prompt.return_menu(options)
|
||||||
|
self.contract_tools.update(choice, customer_options,
|
||||||
|
commercial_options, event_options)
|
||||||
|
|
||||||
|
def contract_menu(self):
|
||||||
|
"""
|
||||||
|
display the CRUD menu for contract and get choice
|
||||||
|
:return: exec the function associated with chosen item
|
||||||
|
"""
|
||||||
|
contract_options = {"List": self.contract_tools.list,
|
||||||
|
"Create": self.contract_tools.create,
|
||||||
|
"Update": self.contracts_for_update,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(contract_options)
|
||||||
|
|
||||||
|
def event_for_update(self):
|
||||||
|
"""
|
||||||
|
get all events from db, create a dict options, call menu
|
||||||
|
and get choice (Event.id)
|
||||||
|
also gets all support collaborators and creates dict option given
|
||||||
|
to tool update method
|
||||||
|
:return: exec the update tool with the chosen id
|
||||||
|
"""
|
||||||
|
options = {}
|
||||||
|
support_options = {}
|
||||||
|
support = self.collaborator_tools.get_by_team_id(3)
|
||||||
|
for user in support:
|
||||||
|
support_options[user[0].name] = user[0].id
|
||||||
|
for item in self.tools.list(Event):
|
||||||
|
options[item[0].name] = item[0].id
|
||||||
|
choice = self.prompt.return_menu(options)
|
||||||
|
self.event_tools.update(choice, support_options)
|
||||||
|
|
||||||
|
def event_list(self):
|
||||||
|
event_list_options = {
|
||||||
|
"All": self.event_tools.list,
|
||||||
|
"No Support yet": self.event_no_support,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(event_list_options)
|
||||||
|
|
||||||
|
def event_no_support(self):
|
||||||
|
self.event_tools.filter("support_id", "NULL")
|
||||||
|
|
||||||
|
def event_menu(self):
|
||||||
|
event_options = {"List": self.event_list,
|
||||||
|
"Update": self.event_for_update,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(event_options)
|
||||||
|
|
||||||
|
|
||||||
|
class SupportMenu:
|
||||||
|
"""
|
||||||
|
Main menu for support users displaying only specific submenus according
|
||||||
|
to permissions :
|
||||||
|
customers: R
|
||||||
|
contracts: R
|
||||||
|
events: CRU
|
||||||
|
filter:
|
||||||
|
event: owned
|
||||||
|
"""
|
||||||
|
def __init__(self,
|
||||||
|
customer_tools,
|
||||||
|
contract_tools,
|
||||||
|
event_tools,
|
||||||
|
tools,
|
||||||
|
user_id):
|
||||||
|
self.customer_tools = customer_tools
|
||||||
|
self.contract_tools = contract_tools
|
||||||
|
self.event_tools = event_tools
|
||||||
|
self.tools = tools
|
||||||
|
self.user_id = user_id
|
||||||
|
self.prompt = Prompt()
|
||||||
|
|
||||||
|
def launch(self):
|
||||||
|
|
||||||
|
base = {
|
||||||
|
"Customer": self.customer_menu,
|
||||||
|
"Contract": self.contract_menu,
|
||||||
|
"Event": self.event_menu,
|
||||||
|
"Quit": quit,
|
||||||
|
}
|
||||||
|
while True:
|
||||||
|
self.prompt.exec_menu(base)
|
||||||
|
|
||||||
|
def customer_menu(self):
|
||||||
|
customer_options = {"List": self.customer_tools.list}
|
||||||
|
self.prompt.exec_menu(customer_options)
|
||||||
|
|
||||||
|
def contract_menu(self):
|
||||||
|
contract_options = {"List": self.contract_tools.list,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(contract_options)
|
||||||
|
|
||||||
|
def event_menu(self):
|
||||||
|
event_options = {"List": self.event_list,
|
||||||
|
"Create": self.event_tools.create,
|
||||||
|
"Update": self.event_for_update,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(event_options)
|
||||||
|
|
||||||
|
def event_list(self):
|
||||||
|
event_list_options = {
|
||||||
|
"All": self.event_tools.list,
|
||||||
|
"Owned only": self.event_owned,
|
||||||
|
}
|
||||||
|
self.prompt.exec_menu(event_list_options)
|
||||||
|
|
||||||
|
def event_owned(self):
|
||||||
|
self.event_tools.filter_owned(self.user_id)
|
||||||
|
|
||||||
|
def event_for_update(self):
|
||||||
|
options = {}
|
||||||
|
for item in self.tools.list(Event):
|
||||||
|
options[item[0].name] = item[0].id
|
||||||
|
choice = self.prompt.return_menu(options)
|
||||||
|
self.event_tools.update(choice)
|
||||||
|
|
||||||
|
|
||||||
|
def menu(options):
|
||||||
|
terminal_menu = TerminalMenu(options)
|
||||||
|
menu_entry_index = terminal_menu.show()
|
||||||
|
selection = options[menu_entry_index]
|
||||||
|
return selection
|
||||||
|
|
||||||
|
|
||||||
|
def return_menu(dict_options):
|
||||||
|
selection = menu(list(dict_options.keys()))
|
||||||
|
return dict_options.get(selection)
|
||||||
143
models.py
Normal file
143
models.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
from datetime import datetime, date
|
||||||
|
from sqlalchemy import ForeignKey, String, Integer, DateTime, Boolean, Date
|
||||||
|
from sqlalchemy.sql import func
|
||||||
|
from sqlalchemy.orm import (DeclarativeBase, Mapped,
|
||||||
|
mapped_column, relationship)
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Collaborator(Base):
|
||||||
|
__tablename__ = "collaborator"
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(50))
|
||||||
|
email: Mapped[str] = mapped_column(String(30))
|
||||||
|
phone: Mapped[int] = mapped_column(Integer)
|
||||||
|
team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("team.id"))
|
||||||
|
team: Mapped["Team"] = relationship(back_populates="collaborator")
|
||||||
|
customers: Mapped[Optional[list["Customer"]]] = relationship()
|
||||||
|
contracts: Mapped[Optional[list["Contract"]]] = relationship()
|
||||||
|
events: Mapped[Optional[list["Event"]]] = relationship()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (f"Collaborator: (id={self.id!r}, "
|
||||||
|
f"name={self.name!r}, "
|
||||||
|
f"email={self.email!r}, "
|
||||||
|
f"phone={self.phone!r}, "
|
||||||
|
f"team_id={self.team_id!r}, "
|
||||||
|
f"customers={self.customers!r}, "
|
||||||
|
f"contracts={self.contracts!r}, "
|
||||||
|
f"events={self.events!r})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Credentials(Base):
|
||||||
|
__tablename__ = "credentials"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
collaborator_id: Mapped[int] = mapped_column(ForeignKey("collaborator.id"))
|
||||||
|
password_hash: Mapped[str] = mapped_column(String(200))
|
||||||
|
|
||||||
|
|
||||||
|
class Team(Base):
|
||||||
|
__tablename__ = "team"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(10))
|
||||||
|
collaborator: Mapped[list["Collaborator"]] = relationship(
|
||||||
|
back_populates="team")
|
||||||
|
|
||||||
|
|
||||||
|
class Customer(Base):
|
||||||
|
__tablename__ = "customer"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(30))
|
||||||
|
email: Mapped[str] = mapped_column(String(20))
|
||||||
|
phone: Mapped[int] = mapped_column(Integer)
|
||||||
|
company: Mapped[str] = mapped_column(String(40))
|
||||||
|
creation_date: Mapped[date] = mapped_column(Date,
|
||||||
|
server_default=func.now())
|
||||||
|
last_update: Mapped[Optional[datetime]] = mapped_column(DateTime)
|
||||||
|
commercial_id: Mapped[Optional[int]] = mapped_column(
|
||||||
|
ForeignKey("collaborator.id"))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (f"Customer: (id={self.id!r}, "
|
||||||
|
f"name={self.name!r}, "
|
||||||
|
f"email={self.email!r}, "
|
||||||
|
f"phone={self.phone!r}, "
|
||||||
|
f"company={self.company!r}, "
|
||||||
|
f"creation_date={self.creation_date!r}, "
|
||||||
|
f"last_update={self.last_update!r}, "
|
||||||
|
f"commercial_id={self.commercial_id!r})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Contract(Base):
|
||||||
|
__tablename__ = "contract"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
signed: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||||
|
creation_date: Mapped[date] = mapped_column(Date,
|
||||||
|
server_default=func.now())
|
||||||
|
amount: Mapped[int] = mapped_column(Integer)
|
||||||
|
customer_id: Mapped[Optional[int]] = mapped_column(
|
||||||
|
ForeignKey("customer.id"))
|
||||||
|
commercial_id: Mapped[Optional[int]] = mapped_column(
|
||||||
|
ForeignKey("collaborator.id"))
|
||||||
|
event_id: Mapped[Optional[int]] = mapped_column(ForeignKey("event.id"))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (f"Contract: (id={self.id!r}, "
|
||||||
|
f"creation_date={self.creation_date!r}, "
|
||||||
|
f"amount={self.amount!r}, "
|
||||||
|
f"customer_id={self.customer_id!r}, "
|
||||||
|
f"commercial_id={self.commercial_id!r}, "
|
||||||
|
f"event_id={self.event_id!r}, "
|
||||||
|
f"signed={self.signed!r}, "
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Event(Base):
|
||||||
|
__tablename__ = "event"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(200))
|
||||||
|
contract_id: Mapped[Optional[int]] = mapped_column(
|
||||||
|
ForeignKey("contract.id"))
|
||||||
|
customer_id: Mapped[Optional[int]] = mapped_column(
|
||||||
|
ForeignKey("customer.id"))
|
||||||
|
customer_contact: Mapped[str] = mapped_column(String(30))
|
||||||
|
date_start: Mapped[str] = mapped_column(String(30))
|
||||||
|
date_end: Mapped[Optional[str]] = mapped_column(String(30))
|
||||||
|
location: Mapped[Optional[str]] = mapped_column(String(100))
|
||||||
|
attendees: Mapped[Optional[set["Attendee"]]] = relationship(
|
||||||
|
back_populates="event")
|
||||||
|
support_id: Mapped[Optional[int]] = mapped_column(
|
||||||
|
ForeignKey("collaborator.id"))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (f"Event: (id={self.id!r}, "
|
||||||
|
f"name={self.name!r}, "
|
||||||
|
f"contract_id={self.contract_id!r}, "
|
||||||
|
f"customer_id={self.customer_id!r}, "
|
||||||
|
f"customer_contact={self.customer_contact!r}, "
|
||||||
|
f"support_id={self.support_id!r}, "
|
||||||
|
f"date_start={self.date_start!r}, "
|
||||||
|
f"date_end={self.date_end!r}, "
|
||||||
|
f"location={self.location!r}, "
|
||||||
|
f"attendees={self.attendees!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Attendee(Base):
|
||||||
|
__tablename__ = "attendee"
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(50))
|
||||||
|
event_id: Mapped[Optional[int]] = mapped_column(ForeignKey("event.id"))
|
||||||
|
event: Mapped[Optional[set["Event"]]] = relationship(
|
||||||
|
back_populates="attendees")
|
||||||
373
tools.py
Normal file
373
tools.py
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
from menu import return_menu
|
||||||
|
from models import Collaborator, Customer, Contract, Event, Credentials
|
||||||
|
from authentication import PasswordTools
|
||||||
|
from views import View
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import select, update, insert, delete
|
||||||
|
|
||||||
|
|
||||||
|
class Tools:
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
|
self.view = View()
|
||||||
|
|
||||||
|
def list(self, object):
|
||||||
|
while self.db:
|
||||||
|
return self.db.execute(select(object)).all()
|
||||||
|
|
||||||
|
def filter(self, object, filter):
|
||||||
|
item, value = filter
|
||||||
|
stmt = (select(object).where(**{item: value}))
|
||||||
|
print(stmt)
|
||||||
|
# while self.db:
|
||||||
|
# return self.db.execute(select(object).where(**{item: value})).all()
|
||||||
|
|
||||||
|
class CollaboratorTools:
|
||||||
|
"""
|
||||||
|
Interface to manage Collaborator object in DB
|
||||||
|
"""
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
|
self.view = View()
|
||||||
|
self.pw_tools = PasswordTools(self.db)
|
||||||
|
|
||||||
|
def get_id_by_name(self, username):
|
||||||
|
collaborator = self.db.execute(
|
||||||
|
select(Collaborator).where(Collaborator.name == username)).scalar()
|
||||||
|
return collaborator.id
|
||||||
|
|
||||||
|
def get_by_team_id(self, team_id):
|
||||||
|
collaborator = self.db.execute(
|
||||||
|
select(Collaborator).where(Collaborator.team_id == team_id)).all()
|
||||||
|
return collaborator
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
result = self.db.execute(select(Collaborator)).all()
|
||||||
|
self.view.display_results(result)
|
||||||
|
|
||||||
|
def create(self) -> None:
|
||||||
|
"""
|
||||||
|
Create a new collaborator with minimum information
|
||||||
|
:return: None; creates object in DB
|
||||||
|
"""
|
||||||
|
pwd_tools = PasswordTools(self.db)
|
||||||
|
collaborator = self.view.prompt_for_collaborator()
|
||||||
|
print(collaborator)
|
||||||
|
new_collab = Collaborator(
|
||||||
|
name=collaborator['name'],
|
||||||
|
email=collaborator['email'],
|
||||||
|
phone=collaborator['phone'],
|
||||||
|
team_id=collaborator['team_id']
|
||||||
|
)
|
||||||
|
self.db.add(new_collab)
|
||||||
|
self.db.commit()
|
||||||
|
new_collab_pwd = Credentials(collaborator_id=new_collab.id,
|
||||||
|
password_hash=pwd_tools.hash("123456"))
|
||||||
|
self.db.add(new_collab_pwd)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_confirm("collaborator", new_collab.id)
|
||||||
|
|
||||||
|
def update(self, my_id) -> None:
|
||||||
|
"""
|
||||||
|
Update a collaborator; asks for field to change and value
|
||||||
|
:param my_id: id of the collaborator to modify
|
||||||
|
:return: None; Update entry in DB
|
||||||
|
"""
|
||||||
|
pwd_tools = PasswordTools(self.db)
|
||||||
|
collab = self.db.get(Collaborator, my_id)
|
||||||
|
item, value = self.view.prompt_for_collaborator_update()
|
||||||
|
stmt = (update(Collaborator).where(Collaborator.id == my_id).values(
|
||||||
|
**{item: value}))
|
||||||
|
if item == 'password':
|
||||||
|
if not self.db.execute(select(Credentials).where(
|
||||||
|
Credentials.collaborator_id == my_id)).all():
|
||||||
|
stmt = (insert(Credentials).values(
|
||||||
|
collaborator_id=my_id,
|
||||||
|
password_hash=pwd_tools.hash(value)))
|
||||||
|
else:
|
||||||
|
stmt = (update(Credentials).where(
|
||||||
|
Credentials.collaborator_id == my_id).values(
|
||||||
|
password_hash=pwd_tools.hash(value)))
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_change(collab.name, item, value)
|
||||||
|
|
||||||
|
def delete(self, my_id) -> None:
|
||||||
|
"""
|
||||||
|
Delete a collaborator
|
||||||
|
:param my_id: id of the collaborator to delete
|
||||||
|
:return: None; deletes entry in DB
|
||||||
|
"""
|
||||||
|
stmt = delete(Collaborator).where(Collaborator.id == my_id)
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
|
||||||
|
def get_team_by_name(self, username: str) -> int | None:
|
||||||
|
"""
|
||||||
|
Check if username exists, returns team_id to check permission further
|
||||||
|
:param username: string, Collaborator.username
|
||||||
|
:return: None|team_id:int
|
||||||
|
"""
|
||||||
|
ret = self.db.execute(
|
||||||
|
select(Collaborator).where(Collaborator.name == username)).scalar()
|
||||||
|
if ret is None:
|
||||||
|
print({'message': "This username doesn't exist"})
|
||||||
|
return ret
|
||||||
|
else:
|
||||||
|
return ret.team_id
|
||||||
|
|
||||||
|
def list_by_team_id(self, my_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def list_by_contract_id(self, my_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def list_by_event_id(self, my_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerTools:
|
||||||
|
"""
|
||||||
|
Interface to manage Customer object in DB
|
||||||
|
"""
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
|
self.view = View()
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
result = self.db.execute(select(Customer)).all()
|
||||||
|
self.view.display_results(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def create(self) -> None:
|
||||||
|
"""
|
||||||
|
Create a new customer with minimum information
|
||||||
|
:return: None; creates object in DB
|
||||||
|
"""
|
||||||
|
customer = self.view.prompt_for_customer()
|
||||||
|
new_customer = Customer(
|
||||||
|
name=customer['name'],
|
||||||
|
email=customer['email'],
|
||||||
|
phone=customer['phone'],
|
||||||
|
company=customer['company'],
|
||||||
|
)
|
||||||
|
self.db.add(new_customer)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_confirm("customer", new_customer.id)
|
||||||
|
|
||||||
|
def update(self, my_id):
|
||||||
|
"""
|
||||||
|
Update a customer; asks for field to change and value
|
||||||
|
:param my_id: id of the collaborator to modify
|
||||||
|
:return: None; Update entry in DB
|
||||||
|
"""
|
||||||
|
cust = self.db.get(Customer, my_id)
|
||||||
|
item, value = self.view.prompt_for_customer_update()
|
||||||
|
stmt = (update(Customer).where(Customer.id == my_id).values(
|
||||||
|
**{item: value}))
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_change(cust.name, item, value)
|
||||||
|
|
||||||
|
|
||||||
|
def delete(self, my_id) -> None:
|
||||||
|
"""
|
||||||
|
Delete a customer
|
||||||
|
:param my_id: id of the customer to delete
|
||||||
|
:return: None; deletes entry in DB
|
||||||
|
"""
|
||||||
|
stmt = delete(Customer).where(Customer.id == my_id)
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
|
||||||
|
def get_by_commercial_id(self, my_id):
|
||||||
|
"""
|
||||||
|
Check if commercial exists, then returns a list
|
||||||
|
:param my_id: a commercial id
|
||||||
|
:return: a list of Customer
|
||||||
|
"""
|
||||||
|
if self.db.get(Collaborator, my_id):
|
||||||
|
ret = self.db.execute(
|
||||||
|
select(Customer).where(Customer.commercial_id == my_id)).all()
|
||||||
|
if ret is None:
|
||||||
|
print({'message': "No customer found"})
|
||||||
|
return ret
|
||||||
|
else:
|
||||||
|
print({'message': "No commercial with this id"})
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ContractTools:
|
||||||
|
"""
|
||||||
|
Interface to manage Contract object in DB
|
||||||
|
"""
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
|
self.view = View()
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
"""
|
||||||
|
List all contracts from DB
|
||||||
|
:return: a list of Contract objects
|
||||||
|
"""
|
||||||
|
result = self.db.execute(select(Contract)).all()
|
||||||
|
self.view.display_results(result)
|
||||||
|
|
||||||
|
def signed(self):
|
||||||
|
"""
|
||||||
|
List only Contract where signed is True (or 1)
|
||||||
|
:return: a list of Contract objects
|
||||||
|
"""
|
||||||
|
result = self.db.execute(
|
||||||
|
select(Contract).where(Contract.signed == 1))
|
||||||
|
self.view.display_results(result)
|
||||||
|
|
||||||
|
def not_signed(self):
|
||||||
|
"""
|
||||||
|
List only Contract where signed is False (or 0)
|
||||||
|
:return: a list of Contract objects
|
||||||
|
"""
|
||||||
|
result = self.db.execute(
|
||||||
|
select(Contract).where(Contract.signed == 0))
|
||||||
|
self.view.display_results(result)
|
||||||
|
|
||||||
|
def create(self) -> None:
|
||||||
|
"""
|
||||||
|
Create a new contracts with minimum information
|
||||||
|
:return: None; creates object in DB
|
||||||
|
"""
|
||||||
|
contract = self.view.prompt_for_contract()
|
||||||
|
new_contract = Contract(
|
||||||
|
customer=contract['customer'],
|
||||||
|
commercial=contract['commercial'],
|
||||||
|
amount=contract['amount'],
|
||||||
|
)
|
||||||
|
self.db.add(new_contract)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_confirm("contract", new_contract.id)
|
||||||
|
|
||||||
|
def update(self, my_id,
|
||||||
|
customer_options,
|
||||||
|
commercial_options,
|
||||||
|
event_options):
|
||||||
|
"""
|
||||||
|
Update a contract; asks for field to change and value
|
||||||
|
:param my_id: id of the contract to modify
|
||||||
|
:return: None; Update entry in DB
|
||||||
|
"""
|
||||||
|
item, value = self.view.prompt_for_contract_update()
|
||||||
|
if item == 'signed':
|
||||||
|
if value.lower() == "yes":
|
||||||
|
value = 1
|
||||||
|
else:
|
||||||
|
value = 0
|
||||||
|
# stmt = (update(Contract).where(Contract.id == my_id).values(
|
||||||
|
# signed=is_signed))
|
||||||
|
if item == 'customer_id':
|
||||||
|
value = return_menu(customer_options)
|
||||||
|
if item == 'commercial_id':
|
||||||
|
value = return_menu(commercial_options)
|
||||||
|
if item == 'event_id':
|
||||||
|
value = return_menu(event_options)
|
||||||
|
stmt = (update(Contract).where(Contract.id == my_id).values(
|
||||||
|
**{item: value}))
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_change("Contract"+str(my_id), item, value)
|
||||||
|
|
||||||
|
def delete(self, my_id) -> None:
|
||||||
|
"""
|
||||||
|
Delete a contract
|
||||||
|
:param my_id: id of the contract to delete
|
||||||
|
:return: None; deletes entry in DB
|
||||||
|
"""
|
||||||
|
stmt = delete(Contract).where(Contract.id == my_id)
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
class EventTools:
|
||||||
|
"""
|
||||||
|
Interface to manage Event object in DB
|
||||||
|
"""
|
||||||
|
def __init__(self, db: Session):
|
||||||
|
self.db = db
|
||||||
|
self.view = View()
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
"""
|
||||||
|
List all events from DB
|
||||||
|
:return: list of Event objects
|
||||||
|
"""
|
||||||
|
result = self.db.execute(select(Event)).all()
|
||||||
|
self.view.display_results(result)
|
||||||
|
|
||||||
|
def filter(self, field, value):
|
||||||
|
"""
|
||||||
|
Retrieve only event with specified field
|
||||||
|
:param field: Event.field
|
||||||
|
:return: display a list of Event
|
||||||
|
"""
|
||||||
|
result = self.db.execute(
|
||||||
|
select(Event).filter_by(**{field: value})).all()
|
||||||
|
print(field, value, result)
|
||||||
|
self.view.display_results(result)
|
||||||
|
|
||||||
|
def filter_owned(self, user_id):
|
||||||
|
"""
|
||||||
|
List only events where support_id is the id of the user logged in
|
||||||
|
:param user_id: the connected user id
|
||||||
|
:return: list of Event objects
|
||||||
|
"""
|
||||||
|
result = self.db.execute(
|
||||||
|
select(Event).where(Event.support_id == user_id)).all()
|
||||||
|
if not result:
|
||||||
|
self.view.display_error()
|
||||||
|
return None
|
||||||
|
self.view.display_results(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def create(self) -> None:
|
||||||
|
"""
|
||||||
|
Create a new event with minimum information
|
||||||
|
:return: None; creates object in DB
|
||||||
|
"""
|
||||||
|
event = self.view.prompt_for_event()
|
||||||
|
new_event = Event(
|
||||||
|
name=event['name'],
|
||||||
|
contract_id=event['contract_id'],
|
||||||
|
customer_id=event['customer_id'],
|
||||||
|
customer_contact=event['customer_contact'],
|
||||||
|
date_start=event['date_start'],
|
||||||
|
date_end=event['date_end'],
|
||||||
|
location=event['location'],
|
||||||
|
)
|
||||||
|
self.db.add(new_event)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_confirm("event", new_event.id)
|
||||||
|
|
||||||
|
def update(self, my_id, support_options):
|
||||||
|
"""
|
||||||
|
Update an event in DB; asks for field to change and value
|
||||||
|
:param my_id: id of the event to modify
|
||||||
|
:return: None; Update entry in DB
|
||||||
|
"""
|
||||||
|
event = self.db.get(Event, my_id)
|
||||||
|
item, value = self.view.prompt_for_event_update(support_options)
|
||||||
|
if item == 'support_id':
|
||||||
|
value = return_menu(support_options)
|
||||||
|
stmt = (update(Event).where(Event.id == my_id).values(
|
||||||
|
**{item: value}))
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
|
self.view.display_change(event.name, item, value)
|
||||||
|
|
||||||
|
def delete(self, my_id) -> None:
|
||||||
|
"""
|
||||||
|
Delete an event
|
||||||
|
:param my_id: id of the event to delete
|
||||||
|
:return: None; deletes entry in DB
|
||||||
|
"""
|
||||||
|
stmt = delete(Event).where(Event.id == my_id)
|
||||||
|
self.db.execute(stmt)
|
||||||
|
self.db.commit()
|
||||||
139
views.py
Normal file
139
views.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
from getpass import getpass
|
||||||
|
from menu import return_menu
|
||||||
|
|
||||||
|
|
||||||
|
class View:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def prompt_connect(self):
|
||||||
|
print("Please connect")
|
||||||
|
collaborator = input("Username : ")
|
||||||
|
password = getpass("Password : ")
|
||||||
|
return collaborator, password
|
||||||
|
|
||||||
|
def prompt_for_id(self):
|
||||||
|
id = input("What id ? ")
|
||||||
|
return id
|
||||||
|
|
||||||
|
def prompt_for_update(self, options):
|
||||||
|
print("What do you want to update ?")
|
||||||
|
ids = ("support_id", "commercial_id", "customer_id", "event_id")
|
||||||
|
item = return_menu(options)
|
||||||
|
if item == 'password':
|
||||||
|
data = getpass("New password : ")
|
||||||
|
elif item == 'team_id':
|
||||||
|
data = self.prompt_for_collaborator_team()
|
||||||
|
elif item in ids:
|
||||||
|
data = ""
|
||||||
|
else:
|
||||||
|
data = input(f"New {item}'s value : ")
|
||||||
|
return item, data
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_for_collaborator_team(self):
|
||||||
|
options = {"Commercial": 1,
|
||||||
|
"Management": 2,
|
||||||
|
"Support": 3,
|
||||||
|
}
|
||||||
|
return return_menu(options)
|
||||||
|
|
||||||
|
def prompt_for_collaborator(self):
|
||||||
|
collaborator = {}
|
||||||
|
options = {"Commercial": 1,
|
||||||
|
"Management": 2,
|
||||||
|
"Support": 3,
|
||||||
|
}
|
||||||
|
print("Please enter collaborator's information")
|
||||||
|
collaborator['name'] = input("Name ? : ")
|
||||||
|
collaborator['email'] = input("Email ? : ")
|
||||||
|
collaborator['phone'] = input("Phone ? : ")
|
||||||
|
item = return_menu(options)
|
||||||
|
collaborator['team_id'] = item
|
||||||
|
return collaborator
|
||||||
|
|
||||||
|
def prompt_for_collaborator_update(self) -> tuple:
|
||||||
|
options = {"password": "password",
|
||||||
|
"name": "name",
|
||||||
|
"email": "email",
|
||||||
|
"phone": "phone",
|
||||||
|
"team": "team_id",
|
||||||
|
}
|
||||||
|
return self.prompt_for_update(options)
|
||||||
|
|
||||||
|
def prompt_for_customer(self) -> dict:
|
||||||
|
customer = {}
|
||||||
|
print("** New customer **")
|
||||||
|
customer['name'] = input("Name ? : ")
|
||||||
|
customer['email'] = input("Email ? : ")
|
||||||
|
customer['phone'] = input("Phone ? : ")
|
||||||
|
customer['company'] = input("Company name ? : ")
|
||||||
|
return customer
|
||||||
|
|
||||||
|
def prompt_for_customer_update(self) -> tuple:
|
||||||
|
options = {"name": "name",
|
||||||
|
"email": "email",
|
||||||
|
"phone": "phone",
|
||||||
|
"company": "company",
|
||||||
|
}
|
||||||
|
return self.prompt_for_update(options)
|
||||||
|
|
||||||
|
def prompt_for_contract(self) -> dict:
|
||||||
|
contract = {}
|
||||||
|
print("** New contract **")
|
||||||
|
contract['customer'] = input("Customer (id) ? : ")
|
||||||
|
contract['commercial'] = input("Commercial (id) ")
|
||||||
|
contract['amount'] = input("Budget ? : ")
|
||||||
|
return contract
|
||||||
|
|
||||||
|
def prompt_for_contract_update(self) -> tuple:
|
||||||
|
options = {"customer": "customer_id",
|
||||||
|
"commercial": "commercial_id",
|
||||||
|
"event": "event_id",
|
||||||
|
"amount": "amount",
|
||||||
|
"signed": "signed",
|
||||||
|
}
|
||||||
|
return self.prompt_for_update(options)
|
||||||
|
|
||||||
|
def prompt_for_event(self) -> dict:
|
||||||
|
event = {}
|
||||||
|
print("** New Event **")
|
||||||
|
event['name'] = input("Event's name ? : ")
|
||||||
|
event['contract_id'] = input("Contract (id) ? : ")
|
||||||
|
event['customer_id'] = input("Customer (id) ? : ")
|
||||||
|
event['customer_contact'] = input("Customer's contact ? : ")
|
||||||
|
event['date_start'] = input("Start date ? : ")
|
||||||
|
event['date_end'] = input("End date ? : ")
|
||||||
|
event['location'] = input("Location ? : ")
|
||||||
|
# event['attendee'] = input("Attendees ? : ")
|
||||||
|
return event
|
||||||
|
|
||||||
|
def prompt_for_event_update(self, support_options) -> tuple:
|
||||||
|
options = {"name": "name",
|
||||||
|
"contract": "contract_id",
|
||||||
|
"customer": "customer_id",
|
||||||
|
"date_start": "date_start",
|
||||||
|
"date_end": "date_end",
|
||||||
|
"location": "location",
|
||||||
|
"Support Member": "support_id"
|
||||||
|
}
|
||||||
|
return self.prompt_for_update(options)
|
||||||
|
|
||||||
|
def display_quit(self):
|
||||||
|
print("Bye")
|
||||||
|
|
||||||
|
def display_confirm(self, object, id):
|
||||||
|
print(f"New {object} with id:{id} created !")
|
||||||
|
|
||||||
|
def display_change(self, object, item, value):
|
||||||
|
print(f"{item} for {object} updated, set to {value} !")
|
||||||
|
|
||||||
|
def display_results(self, result):
|
||||||
|
for item in result:
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
def display_error(self):
|
||||||
|
print("No object matches this query")
|
||||||
|
|
||||||
|
def display_items(self):
|
||||||
|
print()
|
||||||
Reference in New Issue
Block a user