Reupload (purge)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,5 @@
|
||||
prenom = "Alice"
|
||||
nom = "Dupont"
|
||||
age = 30
|
||||
|
||||
print(prenom)
|
||||
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "bases"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"requests>=2.32.5",
|
||||
]
|
||||
Generated
+113
@@ -0,0 +1,113 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "bases"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "requests" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "requests", specifier = ">=2.32.5" }]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.1.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.11"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.6.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"Title": "LSDA",
|
||||
"Author": "Tolkien"
|
||||
},
|
||||
{
|
||||
"Title": "1984",
|
||||
"Author": "Orwell"
|
||||
},
|
||||
{
|
||||
"Title": "La voie des rois",
|
||||
"Author": "Brendan Sanderson"
|
||||
},
|
||||
{
|
||||
"Title": "Justici\u00e8re",
|
||||
"Author": "Brendan Sanderson"
|
||||
},
|
||||
{
|
||||
"Title": "Jus d'abricot",
|
||||
"Author": "COCKTAILS"
|
||||
},
|
||||
{
|
||||
"Title": "Jugement et trahison",
|
||||
"Author": "Alan O'Reilly"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,92 @@
|
||||
import json
|
||||
|
||||
class Book:
|
||||
def __init__(self, title: str, author: str):
|
||||
self.title = title
|
||||
self.author = author
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.title} de {self.author}"
|
||||
|
||||
def __eq__(self, other: "Book"):
|
||||
"""Pour comparer un objet de classe à un autre au lieu de l'ID par défaut"""
|
||||
return self.title == other.title and self.author == other.author
|
||||
|
||||
def __gt__(self, other: "Book"):
|
||||
return self.title > other.title
|
||||
|
||||
def __lt__(self, other: "Book"):
|
||||
return self.title.lower() < other.title.lower()
|
||||
|
||||
def serialized(self):
|
||||
"""Converti la classe en dictionnaire JSONable"""
|
||||
return {"Title": self.title, "Author": self.author}
|
||||
|
||||
@classmethod
|
||||
def deserialized(cls, dictionnary):
|
||||
"""Lis un JSON pour le retranscrire en classe Book"""
|
||||
return cls(title= dictionnary["Title"], author= dictionnary["Author"])
|
||||
|
||||
class Library:
|
||||
"""Gère un contenu de livres"""
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
self.books = self.load()
|
||||
|
||||
def add_new_book(self, book: Book):
|
||||
"""Ajoute un nouveau livre"""
|
||||
if book in self.books:
|
||||
return f"{book.title.capitalize()} y est déjà."
|
||||
self.books.append(book)
|
||||
self.save()
|
||||
return f"{book.title.capitalize()} a été ajouté."
|
||||
|
||||
def take_out_book(self, book_name: str):
|
||||
"""Retire un livre déjà présent"""
|
||||
for book in self.books:
|
||||
if book.title.lower() == book_name.lower():
|
||||
self.books.remove(book)
|
||||
self.save()
|
||||
return f"{book.title.capitalize()} a été retiré."
|
||||
return f"{book.title.capitalize()} n'y est pas."
|
||||
|
||||
def list_books_in_list(self):
|
||||
"""Liste les livres présents"""
|
||||
return self.books
|
||||
|
||||
def sort_books_by_name(self):
|
||||
"""Tri les livres par ordre alphabétique"""
|
||||
self.books.sort()
|
||||
|
||||
def display_number_of_books(self) -> int:
|
||||
"""Donne le nombre de livres présents"""
|
||||
return len(self.books)
|
||||
|
||||
def search_book_by_word(self, word: str):
|
||||
"""Cherche un livre par mot de départ"""
|
||||
results = []
|
||||
for book in self.books:
|
||||
if word.lower() in book.title.lower():
|
||||
results.append(book)
|
||||
return results if len(results) > 0 else "Pas de résultat."
|
||||
|
||||
def search_book_by_letter(self, letter: str):
|
||||
"""Cherche un livre par lettre de départ"""
|
||||
results = []
|
||||
for book in self.books:
|
||||
if book.title.lower().startswith(letter.lower()):
|
||||
results.append(book)
|
||||
return results if len(results) > 0 else "Pas de résultat."
|
||||
|
||||
# Sauvegarde des livres dans un JSON pour persistance des données
|
||||
|
||||
def save(self):
|
||||
"""Inscris dans un fichier JSON le contenu de la bibliothèque"""
|
||||
book_dict = [book.serialized() for book in self.books]
|
||||
with open("books.json", "w", encoding= "utf-8") as json_file: # a pour append, w pour write
|
||||
json.dump(book_dict, json_file, indent= 4)
|
||||
|
||||
def load(self) -> list[Book]:
|
||||
"""Récupère le contenu d'un fichier JSON"""
|
||||
with open("books.json", encoding= "utf-8") as json_file:
|
||||
return json.load(json_file, object_hook= Book.deserialized)
|
||||
@@ -0,0 +1,167 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>250</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Bibliothèque - Python</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
<width>480</width>
|
||||
<height>210</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_add_book">
|
||||
<attribute name="title">
|
||||
<string>Ajouter Livre</string>
|
||||
</attribute>
|
||||
<widget class="QLineEdit" name="title_input">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>140</x>
|
||||
<y>40</y>
|
||||
<width>320</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="title_lb">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>40</y>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Titre</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="author_input">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>140</x>
|
||||
<y>80</y>
|
||||
<width>320</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="author_lb">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>80</y>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auteur</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="save_btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>140</x>
|
||||
<y>120</y>
|
||||
<width>111</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enregistrer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_search">
|
||||
<attribute name="title">
|
||||
<string>Recherche</string>
|
||||
</attribute>
|
||||
<widget class="QListView" name="results_view">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>60</y>
|
||||
<width>430</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="search_lb">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
<width>181</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Recherche (par mot ou lettre)</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="search_input">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>210</x>
|
||||
<y>20</y>
|
||||
<width>240</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_delete">
|
||||
<attribute name="title">
|
||||
<string>Supprimer</string>
|
||||
</attribute>
|
||||
<widget class="QPushButton" name="delete_btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>50</y>
|
||||
<width>100</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Supprimer</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="title_list">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>60</y>
|
||||
<width>320</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,15 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gui.ui_library import Ui_Library
|
||||
|
||||
class SignalManagement(QObject):
|
||||
change_list_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, ui):
|
||||
super().__init__()
|
||||
self.ui = ui
|
||||
|
||||
def connect_signals(self):
|
||||
self.change_list_signal.connect(self.ui.refresh_title_list)
|
||||
@@ -0,0 +1,77 @@
|
||||
# Form implementation generated from reading ui file '.\gui\library.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.10.2
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(520, 250)
|
||||
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.tabWidget = QtWidgets.QTabWidget(parent=self.centralwidget)
|
||||
self.tabWidget.setGeometry(QtCore.QRect(20, 20, 480, 210))
|
||||
self.tabWidget.setElideMode(QtCore.Qt.TextElideMode.ElideNone)
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.tab_add_book = QtWidgets.QWidget()
|
||||
self.tab_add_book.setObjectName("tab_add_book")
|
||||
self.title_input = QtWidgets.QLineEdit(parent=self.tab_add_book)
|
||||
self.title_input.setGeometry(QtCore.QRect(140, 40, 320, 20))
|
||||
self.title_input.setObjectName("title_input")
|
||||
self.title_lb = QtWidgets.QLabel(parent=self.tab_add_book)
|
||||
self.title_lb.setGeometry(QtCore.QRect(20, 40, 100, 20))
|
||||
self.title_lb.setObjectName("title_lb")
|
||||
self.author_input = QtWidgets.QLineEdit(parent=self.tab_add_book)
|
||||
self.author_input.setGeometry(QtCore.QRect(140, 80, 320, 20))
|
||||
self.author_input.setObjectName("author_input")
|
||||
self.author_lb = QtWidgets.QLabel(parent=self.tab_add_book)
|
||||
self.author_lb.setGeometry(QtCore.QRect(20, 80, 100, 20))
|
||||
self.author_lb.setObjectName("author_lb")
|
||||
self.save_btn = QtWidgets.QPushButton(parent=self.tab_add_book)
|
||||
self.save_btn.setGeometry(QtCore.QRect(140, 120, 111, 41))
|
||||
self.save_btn.setObjectName("save_btn")
|
||||
self.tabWidget.addTab(self.tab_add_book, "")
|
||||
self.tab_search = QtWidgets.QWidget()
|
||||
self.tab_search.setObjectName("tab_search")
|
||||
self.results_view = QtWidgets.QListView(parent=self.tab_search)
|
||||
self.results_view.setGeometry(QtCore.QRect(20, 60, 430, 100))
|
||||
self.results_view.setObjectName("results_view")
|
||||
self.search_lb = QtWidgets.QLabel(parent=self.tab_search)
|
||||
self.search_lb.setGeometry(QtCore.QRect(20, 20, 181, 16))
|
||||
self.search_lb.setObjectName("search_lb")
|
||||
self.search_input = QtWidgets.QLineEdit(parent=self.tab_search)
|
||||
self.search_input.setGeometry(QtCore.QRect(210, 20, 240, 20))
|
||||
self.search_input.setObjectName("search_input")
|
||||
self.tabWidget.addTab(self.tab_search, "")
|
||||
self.tab_delete = QtWidgets.QWidget()
|
||||
self.tab_delete.setObjectName("tab_delete")
|
||||
self.delete_btn = QtWidgets.QPushButton(parent=self.tab_delete)
|
||||
self.delete_btn.setGeometry(QtCore.QRect(360, 50, 100, 40))
|
||||
self.delete_btn.setObjectName("delete_btn")
|
||||
self.title_list = QtWidgets.QComboBox(parent=self.tab_delete)
|
||||
self.title_list.setGeometry(QtCore.QRect(20, 60, 320, 20))
|
||||
self.title_list.setObjectName("title_list")
|
||||
self.tabWidget.addTab(self.tab_delete, "")
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "Bibliothèque - Python"))
|
||||
self.title_lb.setText(_translate("MainWindow", "Titre"))
|
||||
self.author_lb.setText(_translate("MainWindow", "Auteur"))
|
||||
self.save_btn.setText(_translate("MainWindow", "Enregistrer"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_add_book), _translate("MainWindow", "Ajouter Livre"))
|
||||
self.search_lb.setText(_translate("MainWindow", "Recherche (par mot ou lettre)"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_search), _translate("MainWindow", "Recherche"))
|
||||
self.delete_btn.setText(_translate("MainWindow", "Supprimer"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_delete), _translate("MainWindow", "Supprimer"))
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from logic import actions
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gui.ui_library import Ui_Library
|
||||
|
||||
class Ui_Initialization:
|
||||
def __init__(self, ui: Ui_Library):
|
||||
self.ui = ui
|
||||
|
||||
def connect_button(self):
|
||||
self.ui.save_btn.clicked.connect(self.ui.record_book)
|
||||
self.ui.delete_btn.clicked.connect(self.ui.delete_book)
|
||||
self.ui.change_list_signal.connect(self.ui.refresh_title_list)
|
||||
|
||||
self.ui.search_input.textChanged.connect(self.ui.search_book)
|
||||
|
||||
def fulfill_list(self):
|
||||
self.ui.title_list.addItems(actions.list_titles())
|
||||
@@ -0,0 +1,59 @@
|
||||
from logic import actions
|
||||
|
||||
from gui.signal_mgt import SignalManagement
|
||||
from gui.ui_init import Ui_Initialization
|
||||
from gui.ui_base import Ui_MainWindow
|
||||
|
||||
from PyQt6.QtWidgets import QMessageBox
|
||||
from PyQt6.QtGui import QStandardItemModel, QStandardItem
|
||||
|
||||
class Ui_Library(Ui_MainWindow):
|
||||
##### Méthode de mise à jour de la liste des titres avec QTSignal
|
||||
|
||||
def setupUi(self, MainWindow):
|
||||
super().setupUi(MainWindow)
|
||||
ui_init = Ui_Initialization(self)
|
||||
ui_init.connect_button()
|
||||
ui_init.fulfill_list()
|
||||
|
||||
self.signal_mgt = SignalManagement(self)
|
||||
self.signal_mgt.connect_signals()
|
||||
|
||||
def record_book(self):
|
||||
title = self.title_input.text()
|
||||
author = self.author_input.text()
|
||||
message = actions.register_book(title, author)
|
||||
QMessageBox.information(None, "Nouvel enregistrement", message)
|
||||
# self.refresh_title_list()
|
||||
self.signal_mgt.change_list_signal.emit()
|
||||
|
||||
def delete_book(self):
|
||||
title = self.title_list.currentText()
|
||||
message = actions.delete_book(title)
|
||||
QMessageBox.information(None, "Suppression confirmée", message)
|
||||
# self.refresh_title_list()
|
||||
self.signal_mgt.change_list_signal.emit()
|
||||
|
||||
def refresh_title_list(self):
|
||||
self.title_list.clear()
|
||||
self.title_list.addItems(actions.list_titles())
|
||||
|
||||
def search_book(self):
|
||||
word = self.search_input.text()
|
||||
if len(word) == 1:
|
||||
results = actions.search_by_letter(word)
|
||||
self.display_list_view(results)
|
||||
return
|
||||
if len(word) > 1:
|
||||
results = actions.search_by_word(word)
|
||||
self.display_list_view(results)
|
||||
return
|
||||
|
||||
def display_list_view(self, results):
|
||||
self.model = QStandardItemModel()
|
||||
self.results_view.setModel(self.model)
|
||||
if isinstance(results, str):
|
||||
self.model.appendRow(QStandardItem(results))
|
||||
else:
|
||||
for book in results:
|
||||
self.model.appendRow(QStandardItem(str(book)))
|
||||
@@ -0,0 +1,19 @@
|
||||
from classes import Book, Library
|
||||
|
||||
library = Library("Romanesque")
|
||||
|
||||
def register_book(title: str, author: str):
|
||||
book = Book(title, author)
|
||||
return library.add_new_book(book)
|
||||
|
||||
def list_titles() -> list[str]:
|
||||
return [book.title for book in library.list_books_in_list()]
|
||||
|
||||
def delete_book(title: str):
|
||||
return library.take_out_book(title)
|
||||
|
||||
def search_by_letter(letter: str):
|
||||
return library.search_book_by_letter(letter)
|
||||
|
||||
def search_by_word(word: str):
|
||||
return library.search_book_by_word(word)
|
||||
@@ -0,0 +1,12 @@
|
||||
import sys
|
||||
from PyQt6.QtWidgets import QApplication, QMainWindow
|
||||
from gui.ui_library import Ui_Library
|
||||
|
||||
app = QApplication([])
|
||||
MainWindow = QMainWindow()
|
||||
gui = Ui_Library()
|
||||
gui.setupUi(MainWindow)
|
||||
|
||||
MainWindow.show()
|
||||
|
||||
sys.exit(app.exec())
|
||||
@@ -0,0 +1,16 @@
|
||||
from classes import Book, Library
|
||||
|
||||
library = Library("Romans")
|
||||
|
||||
for i in range(3):
|
||||
new_book_title = input ("Quel livre ajouter ? > ")
|
||||
new_book_author = input ("Qui l'a écrit ? > ")
|
||||
new_book = Book(new_book_title, new_book_author)
|
||||
new_book_added = library.add_new_book(new_book)
|
||||
print(new_book_added)
|
||||
|
||||
print(library.list_books_in_list())
|
||||
|
||||
library.sort_books_by_name()
|
||||
|
||||
print(library.list_books_in_list())
|
||||
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "bibliotheque"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"pyqt6>=6.10.2",
|
||||
]
|
||||
Generated
+67
@@ -0,0 +1,67 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "bibliotheque"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "pyqt6" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "pyqt6", specifier = ">=6.10.2" }]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6"
|
||||
version = "6.10.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyqt6-qt6" },
|
||||
{ name = "pyqt6-sip" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/96/03/e756f52e8b0d7bb5527baf8c46d59af0746391943bdb8655acba22ee4168/pyqt6-6.10.2.tar.gz", hash = "sha256:6c0db5d8cbb9a3e7e2b5b51d0ff3f283121fa27b864db6d2f35b663c9be5cc83", size = 1085573, upload-time = "2026-01-08T16:40:00.244Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/3f/f073a980969aa485ef288eb2e3b94c223ba9c7ac9941543f19b51659b98d/pyqt6-6.10.2-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:37ae7c1183fe4dd0c6aefd2006a35731245de1cb6f817bb9e414a3e4848dfd6d", size = 60244482, upload-time = "2026-01-08T16:38:50.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/3e/9a015651ec71cea2e2f960c37edeb21623ba96a74956c0827def837f7c6b/pyqt6-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:78e1b3d5763e4cbc84485aef600e0aba5e1932fd263b716f92cd1a40dfa5e924", size = 37899440, upload-time = "2026-01-08T16:39:09.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/74/a88fec2b99700270ca5d7dc7d650236a4990ed6fc88e055ca0fc8a339ee3/pyqt6-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:bbc3af541bbecd27301bfe69fe445aa1611a9b490bd3de77306b12df632f7ec6", size = 40748467, upload-time = "2026-01-08T16:39:29.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/34/be7a55529607b21db00a49ca53cb07c3092d2a5a95ea19bb95cfa0346904/pyqt6-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:bd328cb70bc382c48861cd5f0a11b2b8ae6f5692d5a2d6679ba52785dced327b", size = 26015391, upload-time = "2026-01-08T16:39:42.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/de/d9c88f976602b7884fec4ad54a4575d48e23e4f390e5357ea83917358846/pyqt6-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:7901ba1df024b7ee9fdacfb2b7661aeb3749ae8b0bef65428077de3e0450eabb", size = 26208415, upload-time = "2026-01-08T16:39:57.751Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6-qt6"
|
||||
version = "6.10.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/1b/137184632cad83a210e7955226744a77945260ca2e75892fe36299d26ada/pyqt6_qt6-6.10.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:4bb2798a95f624b462b70c4f185422235b714b01e55abab32af1740f147948e2", size = 68472463, upload-time = "2025-11-27T14:20:51.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/df/ca795ac3d04243ad63499cfedcf92d8b5f6e3585a2a26c09f34cb58c8e44/pyqt6_qt6-6.10.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0921cc522512cb40dbab673806bc1676924819550e0aec8e3f3fe6907387c5b7", size = 62296168, upload-time = "2025-11-27T14:21:21.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/7e/9867361252e2a4717dba95c64a0f3a793603f4a52cb9a46abbb041e960f5/pyqt6_qt6-6.10.1-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:04069aea421703b1269c8a1bcf017e36463af284a044239a4ebda3bde0a629fb", size = 83829262, upload-time = "2025-11-27T14:22:00.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/7b/18f4eb2273a92283fe4d87aa740a400eb14a4e41b8f990aaf563e9767db6/pyqt6_qt6-6.10.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:5b9be39e0120e32d0b42cdb844e3ae110ddadd39629c991e511902c06f155aff", size = 82877396, upload-time = "2025-11-27T14:22:36.994Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/5c/648c515d57bc82909d0597befb03bbc2f7a570f323dba3ad38629669efcb/pyqt6_qt6-6.10.1-py3-none-win_amd64.whl", hash = "sha256:df564d3dc2863b1fde22b39bea9f56ceb2a3ed7d6f0b76d3f96c2d3bc5d71516", size = 76670151, upload-time = "2025-11-27T14:23:11.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/13/2d2a9c0559bfa53effea5e2c1ed7aebb430186ce0b64cfba235231a049d9/pyqt6_qt6-6.10.1-py3-none-win_arm64.whl", hash = "sha256:48282e0f99682daf4f1e220cfe9f41255e003af38f7728a30d40c76e55c89816", size = 58276316, upload-time = "2025-11-27T14:23:38.744Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6-sip"
|
||||
version = "13.11.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e3/7d/d2916048e2e3960f68cb4e93907639844f7b8ff95897dcc98553776ccdfc/pyqt6_sip-13.11.0.tar.gz", hash = "sha256:d463af37738bda1856c9ef513e5620a37b7a005e9d589c986c3304db4a8a14d3", size = 92509, upload-time = "2026-01-13T16:01:32.16Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/53/a6/0e4d8fa7d6deb750bd0fdf89024e39c71fb127efb5eeedfab6830ad6679a/pyqt6_sip-13.11.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6b3267cd93b7f4da6fdf9a6a26f3baed8faae06e5cdd76235f2acc2116c40a54", size = 112367, upload-time = "2026-01-13T16:01:09.08Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/e6/25dc20a03c46000e8b93aaf79347227926b67959283e5aab797daa7f64d8/pyqt6_sip-13.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c30248d9bbe54c46a78e5d549da50295ecd6584b965597f751e272f000fb8527", size = 301150, upload-time = "2026-01-13T16:01:12.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/9f/e850cd350aade789660cafba38c00777e686040c06b8cd0b45339b80fcba/pyqt6_sip-13.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c367b53a91e575ef66c1375f899713bdaf0a8b2c64b95ac226e9644854a4984", size = 323303, upload-time = "2026-01-13T16:01:10.736Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/26/5261d62108f7579407230f8c1d4dda43c18b5600ce70bf3becb2f997d5cc/pyqt6_sip-13.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:077958105c2ea2f62be2f1a7611ff8bd44cb52fb5ea8fc8c59ea949144acb7b5", size = 53461, upload-time = "2026-01-13T16:01:13.875Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/80/6c88b97eda309d6babb7292200bf51165dc06d0204d891b7bf1fb17a8ed0/pyqt6_sip-13.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:52812471619d3d3750b940d7d124cd0954107656924921ac177e098ba36362fb", size = 48650, upload-time = "2026-01-13T16:01:14.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/a0/46abcae4fce175a326185460a02c13ab81332bca7dd55c1e853ba6aee71e/pyqt6_sip-13.11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:929716eebde1a64ffdb6b1715db6a22aefd5634d6df84858c7deb5e85be84fdf", size = 112353, upload-time = "2026-01-13T16:01:16.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/38/27c3aa3f153fcd83a0765fedf8e44a1136f189a322bcc9c494c5b3793cd7/pyqt6_sip-13.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75144e8a0bcf9d1a9069011890401748af353749f1de1b6a314b880781edf9d", size = 301497, upload-time = "2026-01-13T16:01:20.531Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/ac/1053ffce45e4174f0a8174557b88537aa82bf96ba03c7dd208c59de36f69/pyqt6_sip-13.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8082b5f57ffad5dddf5efcf0ef5eaf94841395aa4e7c374c79ef24cf49b0f0ce", size = 323498, upload-time = "2026-01-13T16:01:17.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/d3/447b30d1f00cc50ad9e5c53b2e920068606b16857da83f8036b390c79fad/pyqt6_sip-13.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d49b5bf3d8d36cd7db93ddc54cd09dbba96a3fd926e445ef75499b41e47b5a3", size = 53469, upload-time = "2026-01-13T16:01:21.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/67/77e6fafcabd01c0a11166ab7464509896f137929f82c4f2e03aea1bf41b3/pyqt6_sip-13.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:293eac1b53c66c54b03266cc30015ec77454af679043a4f188b9bb80a9656996", size = 48643, upload-time = "2026-01-13T16:01:22.669Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/28/a5178c8e005bafbf9c0fd507f45a3eef619ab582811414a0a461ee75994f/pyqt6_sip-13.11.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:4dc9c4df24af0571423c3e85b5c008bad42ed48558eef80fbc3e5d30274c5abb", size = 112431, upload-time = "2026-01-13T16:01:23.832Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/3c/02770b02b5a05779e26bd02c202c2fd32aa38e225d01f14c06908e33738c/pyqt6_sip-13.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c974d5a193f32e55e746e9b63138503163ac63500dbb1fd67233d8a8d71369bd", size = 301236, upload-time = "2026-01-13T16:01:28.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/47/5af493a698cc520581ca1000b4ab09b8182992053ffe2478062dde5e4671/pyqt6_sip-13.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4284540ffccd8349763ddce3518264dde62f20556720d4061b9c895e09011ca0", size = 323919, upload-time = "2026-01-13T16:01:25.122Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/2d/64b26e21183a7ff180105871dd5983a8da539d8768921728268dc6d0a73d/pyqt6_sip-13.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:9bd81cb351640abc803ea2fe7262b5adea28615c9b96fd103d1b6f3459937211", size = 55078, upload-time = "2026-01-13T16:01:29.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/36/23f699fa8b1c3fcc312ecd12661a1df6057d92e16d4def2399b59cf7bf22/pyqt6_sip-13.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:cd95ec98f8edb15bcea832b8657809f69d758bc4151cc6fd7790c0181949e45f", size = 49465, upload-time = "2026-01-13T16:01:31.174Z" },
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,91 @@
|
||||
import json
|
||||
|
||||
class Contact:
|
||||
def __init__(self, name: str, phone: str, email: str):
|
||||
self.name = name
|
||||
self.phone = phone
|
||||
self.email = email
|
||||
|
||||
def __iter__(self):
|
||||
yield self.name
|
||||
yield self.phone
|
||||
yield self.email
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.name} - {self.phone} - {self.email}"
|
||||
|
||||
def __eq__(self, other: "Contact"):
|
||||
return self.name == other.name\
|
||||
and self.phone == other.phone\
|
||||
and self.email == other.email
|
||||
|
||||
def serialized(self):
|
||||
"""Converti la classe en dictionnaire JSONable"""
|
||||
return {"Name": self.name, "Phone": self.phone, "Email": self.email}
|
||||
|
||||
@classmethod
|
||||
def deserialized(cls, dictionnary):
|
||||
"""Lis un JSON pour le retranscrire en classe Contact"""
|
||||
return cls(name= dictionnary["Name"], phone= dictionnary["Phone"], email= dictionnary["Email"])
|
||||
|
||||
class Repertory:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
self.contacts: list[Contact] = self.load()
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.contacts)
|
||||
|
||||
def __getitem__(self, index: int) -> Contact:
|
||||
return self.contacts[index]
|
||||
|
||||
def add_contact(self, contact: Contact):
|
||||
if contact in self.contacts:
|
||||
return f"{contact.name} est déjà dans le répertoire."
|
||||
self.contacts.append(contact)
|
||||
self.save()
|
||||
return f"Nouveau contact :\nNom : {contact.name}\nNuméro : {contact.phone}\nCourriel : {contact.email}"
|
||||
|
||||
def delete_contact(self, contact_info: str):
|
||||
for contact in self.contacts:
|
||||
if contact.name.lower() == contact_info.lower()\
|
||||
or contact.phone.lower() == contact_info.lower()\
|
||||
or contact.email.lower() == contact_info.lower():
|
||||
self.contacts.remove(contact)
|
||||
self.save()
|
||||
return f"{contact.name} a été supprimé du répertoire."
|
||||
return f"{contact.name} n'est pas dans le répertoire."
|
||||
|
||||
def search_contact(self, research_str: str):
|
||||
results = []
|
||||
for contact in self.contacts:
|
||||
if research_str.lower() in contact.name.lower()\
|
||||
or research_str.lower() in contact.phone.lower()\
|
||||
or research_str.lower() in contact.email.lower():
|
||||
results.append(contact)
|
||||
return results if len(results) > 0 else "Pas de résultat."
|
||||
|
||||
def display_contacts(self):
|
||||
results = []
|
||||
for idx, contact in enumerate(self.contacts):
|
||||
results.append(
|
||||
f"----- Contact n°{idx + 1} -----\n"
|
||||
f"Nom : {contact.name}\n"
|
||||
f"Numéro : {contact.phone}\n"
|
||||
f"Courriel : {contact.email}\n"
|
||||
" "
|
||||
)
|
||||
return "".join(results) if results else "Aucun contact dans le répertoire."
|
||||
|
||||
# Sauvegarde des livres dans un JSON pour persistance des données
|
||||
|
||||
def save(self):
|
||||
"""Inscris dans un fichier JSON le contenu de la bibliothèque"""
|
||||
contact_dict = [contact.serialized() for contact in self.contacts]
|
||||
with open("contacts.json", "w") as json_file: # a pour append, w pour write
|
||||
json.dump(contact_dict, json_file, indent= 4)
|
||||
|
||||
def load(self) -> list[Contact]:
|
||||
"""Récupère le contenu d'un fichier JSON"""
|
||||
with open("contacts.json") as json_file:
|
||||
return json.load(json_file, object_hook= Contact.deserialized)
|
||||
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{
|
||||
"Name": "Alberto",
|
||||
"Phone": "0612345678",
|
||||
"Email": "alberto@courriel.fr"
|
||||
},
|
||||
{
|
||||
"Name": "Alice",
|
||||
"Phone": "0123456789",
|
||||
"Email": "alice@gmail.com"
|
||||
},
|
||||
{
|
||||
"Name": "Alberto",
|
||||
"Phone": "0482764563",
|
||||
"Email": "kfjsbdfg@sdfgj.fr"
|
||||
},
|
||||
{
|
||||
"Name": "Giorgio",
|
||||
"Phone": "857398375493",
|
||||
"Email": "giorgio.armani@armagnac.it"
|
||||
},
|
||||
{
|
||||
"Name": "Fran\u00e7ois LE GUERAND",
|
||||
"Phone": "0145892367",
|
||||
"Email": "fr-le-g@gmail.fr"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
from classes import Contact,Repertory
|
||||
|
||||
repertory = Repertory("Repertoire")
|
||||
|
||||
for i in range(1):
|
||||
new_contact_name = input("Nom > ")
|
||||
new_contact_phone = input("Numéro > ")
|
||||
new_contact_email = input("Courriel > ")
|
||||
|
||||
new_contact = Contact(new_contact_name, new_contact_phone, new_contact_email)
|
||||
new_contact_added = repertory.add_contact(new_contact)
|
||||
|
||||
print(new_contact_added)
|
||||
|
||||
search_criteria = input("Critères de recherche > ")
|
||||
print(repertory.search_contact(search_criteria))
|
||||
|
||||
print(repertory.display_contacts())
|
||||
@@ -0,0 +1,7 @@
|
||||
[project]
|
||||
name = "contacts"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "contacts"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,33 @@
|
||||
class Fraction:
|
||||
def __init__(self, numerateur, denominateur):
|
||||
if denominateur == 0:
|
||||
raise ValueError("Le denominateur ne peut etre nul")
|
||||
if not isinstance(denominateur, int) or not isinstance(numerateur, int):
|
||||
raise TypeError("Le numerateur/denominateur doit etre un entier")
|
||||
self.numerateur = numerateur
|
||||
self.denominateur = denominateur
|
||||
|
||||
@property
|
||||
def quotient(self):
|
||||
return self.numerateur / self.denominateur
|
||||
|
||||
print("Debut programme")
|
||||
try:
|
||||
tiers = Fraction(1, '0')
|
||||
print(tiers.quotient)
|
||||
except (ValueError, TypeError) as msg :
|
||||
print(msg)
|
||||
|
||||
liste = [1,2,3,4]
|
||||
dico = {'nom': "John"}
|
||||
|
||||
try:
|
||||
print(dico['prenom'])
|
||||
print(liste[7])
|
||||
except LookupError as msg:
|
||||
if isinstance(msg, KeyError):
|
||||
print("La cle n'existe pas")
|
||||
else:
|
||||
print(msg)
|
||||
|
||||
print("Fin du programme")
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
def addition(*args): # args => tuple (2,3,4,5,6,6,6,7,7,8,9)
|
||||
return sum(args)
|
||||
|
||||
print(addition(2,3,4,5))
|
||||
|
||||
livres = [
|
||||
{"titre": "1984", "auteur": "Orwell"},
|
||||
{"titre": "1983", "auteur": "Orwella"},
|
||||
{"titre": "1985", "auteur": "Orwell"}
|
||||
]
|
||||
|
||||
|
||||
def display_infos(**kwargs): # kwargs => dict {"Titre": "Scandale", "Auteur": "Marlène Schiappa"}
|
||||
resultats = []
|
||||
for livre in livres:
|
||||
if "auteur" in kwargs and kwargs["auteur"].lower() in livre["auteur"]:
|
||||
resultats.append(livre)
|
||||
if "titre" in kwargs and kwargs["titre"].lower() in livre["titre"]:
|
||||
resultats.append(livre)
|
||||
return resultats
|
||||
|
||||
infos: dict[str | int, str | int] = {"age": 23, "nom": "Albatar", "is_admin" : False}
|
||||
|
||||
class Criteres(TypedDict):
|
||||
auteur: NotRequired[str]
|
||||
titre: NotRequired[str]
|
||||
|
||||
print(CONSTANTES["db_name"])
|
||||
@@ -0,0 +1,42 @@
|
||||
from datetime import date, time, datetime, timedelta
|
||||
import pytz
|
||||
|
||||
date_2026 = date(2026, 1, 10)
|
||||
print(date_2026)
|
||||
|
||||
today = date.today()
|
||||
print(today)
|
||||
|
||||
hour = time()
|
||||
print(hour)
|
||||
|
||||
hour = time(12, 34, 23)
|
||||
print(hour)
|
||||
|
||||
hour_now = datetime.now().time()
|
||||
print(hour_now)
|
||||
day_now = datetime.now().date()
|
||||
print(day_now)
|
||||
|
||||
timezone_paris = pytz.timezone("Europe/Paris")
|
||||
timezone_moscou = pytz.timezone("Europe/Moscow")
|
||||
timezone_pyongyang = pytz.timezone("Asia/Pyongyang")
|
||||
|
||||
heure_actuelle_paris = datetime.now(tz= timezone_paris)
|
||||
heure_actuelle_moscou = datetime.now(tz= timezone_moscou)
|
||||
heure_actuelle_pyongyang = datetime.now(tz= timezone_pyongyang)
|
||||
|
||||
print(heure_actuelle_paris)
|
||||
print(heure_actuelle_moscou)
|
||||
print(heure_actuelle_pyongyang)
|
||||
|
||||
date_15_jours = date.today() + timedelta(days= 15)
|
||||
print(date_15_jours)
|
||||
|
||||
debut_voyage = datetime(2026, 1, 16, 12, 7, 18, tzinfo= timezone_paris)
|
||||
fin_voyage = datetime(2026, 12, 25, 13, 56, 23, tzinfo= timezone_pyongyang)
|
||||
print(debut_voyage - fin_voyage)
|
||||
|
||||
detail_voyage = debut_voyage - fin_voyage
|
||||
print(detail_voyage.days)
|
||||
print(detail_voyage.seconds)
|
||||
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "divers"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"pytz>=2025.2",
|
||||
]
|
||||
Generated
+23
@@ -0,0 +1,23 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "divers"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "pytz" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "pytz", specifier = ">=2025.2" }]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2025.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
An isogram is a word that has no repeating letters, consecutive or non-consecutive. Implement a function that determines whether a string that contains only letters is an isogram. Assume the empty string is an isogram. Ignore letter case.
|
||||
|
||||
Example: (Input --> Output)
|
||||
|
||||
"Dermatoglyphics" --> true
|
||||
"aba" --> false
|
||||
"moOse" --> false (ignore letter case)
|
||||
"""
|
||||
|
||||
def is_isogram(string):
|
||||
string = string.lower()
|
||||
for letter in string:
|
||||
if string.count(letter) > 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Solution la plus opti
|
||||
|
||||
def is_isogram(string):
|
||||
return len(string) == len(set(string.lower()))
|
||||
@@ -0,0 +1,72 @@
|
||||
destinations = {
|
||||
"Pyongyang": 5,
|
||||
"Havana": 2,
|
||||
"Caracas": 7
|
||||
}
|
||||
|
||||
destinations["Moscow"] = 0
|
||||
destinations["Ho-Chi-Minh Town"] = 0
|
||||
|
||||
def add_destination(destinations: dict, town: str, number: int):
|
||||
if town not in destinations:
|
||||
destinations[town] = 0
|
||||
|
||||
def add_reservation(destinations: dict, town: str, number: int):
|
||||
if town in destinations:
|
||||
destinations[town] += number
|
||||
else:
|
||||
destinations[town] = number
|
||||
|
||||
def remove_reservation(destinations: dict, town: str):
|
||||
if town in destinations:
|
||||
new_destinations = dict(destinations)
|
||||
del new_destinations[town]
|
||||
return new_destinations
|
||||
|
||||
def show_destinations_available(destinations: dict):
|
||||
for town in destinations:
|
||||
if destinations[town] != 0:
|
||||
print(town + " : " + str(destinations[town]))
|
||||
|
||||
def show_destinations_by_reservations(destinations: dict):
|
||||
print(sorted(destinations)) # ["Pyongyang", "Havana", "Caracas"]
|
||||
print(sorted(destinations.keys())) # ["Pyongyang", "Havana", "Caracas"]
|
||||
print(sorted(destinations.values)) # [5, 2, 7]
|
||||
print(sorted(destinations.items)) # [("Pyongyang", 5), ("Havana", 2), ("Caracas", 7)]
|
||||
|
||||
def return_second_tuple_element(tuple):
|
||||
return tuple[1]
|
||||
|
||||
print(sorted(destinations.items(), key= return_second_tuple_element)) # [('Moscow', 0), ('Ho-Chi-Minh Town', 0), ('Havana', 2), ('Pyongyang', 5), ('Caracas', 7)]
|
||||
print(sorted(destinations.items(), key= lambda tuple : tuple[1])) # [('Moscow', 0), ('Ho-Chi-Minh Town', 0), ('Havana', 2), ('Pyongyang', 5), ('Caracas', 7)]
|
||||
|
||||
#show_destinations_available(destinations)
|
||||
|
||||
# Données déstructurées
|
||||
|
||||
dc_infos = ("Dale", "Cooper", "32")
|
||||
dc_prenom = dc_infos[0]
|
||||
dc_nom = dc_infos[1]
|
||||
dc_age = dc_infos[2]
|
||||
|
||||
dc_prenom, dc_nom, dc_age = dc_infos
|
||||
|
||||
def generator():
|
||||
for i in range(10):
|
||||
yield i
|
||||
|
||||
def square_generator(n):
|
||||
for i in range(n):
|
||||
yield i ** 2
|
||||
|
||||
print(generator())
|
||||
squares = square_generator(10)
|
||||
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
@@ -0,0 +1,28 @@
|
||||
import sys
|
||||
|
||||
input_string = input("Ecris un truc > ")
|
||||
|
||||
if len(input_string) == 0:
|
||||
print("Tu aurais pu participer.")
|
||||
sys.exit()
|
||||
|
||||
def return_string_infos(input_string: str):
|
||||
print("Tu as tapé : " + "\"" + input_string + "\"")
|
||||
print("Elle fait exactement " + str(len(input_string)) + " caractères.")
|
||||
print("EN MAJUSCULES, ELLE DONNE : " + "\"" + input_string.upper() + "\"")
|
||||
print("en minuscules, elle donne : " + "\"" + input_string.lower() + "\"")
|
||||
print("A l'envers, elle donne : " + "\"" + input_string[::-1] + "\"")
|
||||
|
||||
def replace_word_in_string(input_string: str, word_to_replace: str, word_replacement: str) -> str:
|
||||
print(f"Remplaçant \"{word_to_replace}\" par \"{word_replacement}\", cela donne : {input_string.replace(word_to_replace, word_replacement)}")
|
||||
|
||||
def count_occurences_in_string(input_string: str, occurence: str):
|
||||
print(f"L'occurence \"{occurence}\" apparaît {input_string.count(occurence)} fois.")
|
||||
|
||||
def strip_of_spaces(input_string: str):
|
||||
print(f"Sans les espaces de début et de fin, cela donne : {input_string.strip()}")
|
||||
|
||||
return_string_infos(input_string)
|
||||
replace_word_in_string(input_string, "vie", "mort")
|
||||
count_occurences_in_string(input_string, "e")
|
||||
strip_of_spaces(input_string)
|
||||
@@ -0,0 +1 @@
|
||||
from Semaine_04.Exercice_01.exo_01 import
|
||||
@@ -0,0 +1,34 @@
|
||||
word_to_find = "parapluie"
|
||||
word_hidden = "_" * len(word_to_find)
|
||||
list_word_hidden = list(word_hidden)
|
||||
attempts = 10
|
||||
|
||||
already_chosen_letter = []
|
||||
|
||||
print(word_hidden)
|
||||
|
||||
while word_hidden != word_to_find and attempts > 0:
|
||||
chosen_letter = input("Saisir une lettre > ").strip().lower()[0] # on ne prend VRAIMENT que la première lettre
|
||||
|
||||
if chosen_letter in already_chosen_letter:
|
||||
print(f"Vous avez déjà saisie la lettre {chosen_letter.upper()}.")
|
||||
attempts -= 1
|
||||
continue
|
||||
|
||||
already_chosen_letter.append(chosen_letter)
|
||||
|
||||
if chosen_letter not in word_to_find:
|
||||
attempts -= 1
|
||||
continue
|
||||
|
||||
for index, letter in enumerate(word_to_find):
|
||||
if letter == chosen_letter:
|
||||
list_word_hidden[index] = chosen_letter
|
||||
|
||||
word_hidden = "".join(list_word_hidden)
|
||||
print(f"Il reste {attempts} tentatives. \n {word_hidden} \n")
|
||||
|
||||
if word_hidden != word_to_find:
|
||||
print(f"Perdu ! Le mot était {word_to_find.upper()}.")
|
||||
else:
|
||||
print("Gagné !")
|
||||
@@ -0,0 +1,7 @@
|
||||
[project]
|
||||
name = "exercice-01"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "exercice-01"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,6 @@
|
||||
from math import pi
|
||||
|
||||
class Point:
|
||||
def __init__(self, x: float, y: float):
|
||||
self.x = x
|
||||
self.y = y
|
||||
@@ -0,0 +1 @@
|
||||
Nom,Prénom,Âge
|
||||
@@ -0,0 +1,57 @@
|
||||
# Reussir dans la POO c'est se rapprocher au plus pret de la realite
|
||||
from typing import Self
|
||||
|
||||
|
||||
class Fraction:
|
||||
def __init__(self, numerateur, denominateur):
|
||||
# Attributs
|
||||
self.numerateur = numerateur # tiers.numerateur = 1
|
||||
self.denominateur = denominateur
|
||||
self.simplifier()
|
||||
|
||||
def quotient(self):
|
||||
return self.numerateur / self.denominateur
|
||||
|
||||
@classmethod # decorateur
|
||||
def from_str(cls, num: str, den: str):
|
||||
return cls(numerateur= int(num), denominateur= int(den))
|
||||
|
||||
@staticmethod # Pas de 'self' => Helpers
|
||||
def pgcd(a, b):
|
||||
if b == 0: return a
|
||||
return Fraction.pgcd(a= b, b= a % b)
|
||||
|
||||
def simplifier(self):
|
||||
a = self.numerateur
|
||||
b = self.denominateur
|
||||
pgcd = Fraction.pgcd(a, b)
|
||||
|
||||
self.numerateur = self.numerateur // pgcd
|
||||
self.denominateur //= pgcd
|
||||
|
||||
def __add__(self, fraction):
|
||||
num = (self.numerateur * fraction.denominateur) + (self.denominateur * fraction.numerateur)
|
||||
den = self.denominateur * fraction.denominateur
|
||||
return Fraction(numerateur= num, denominateur= den)
|
||||
|
||||
def __mul__(self, facteur: int | Self):
|
||||
if isinstance(facteur, int):
|
||||
return Fraction(
|
||||
numerateur= self.numerateur * facteur,
|
||||
denominateur= self.denominateur
|
||||
)
|
||||
return Fraction(
|
||||
numerateur= self.numerateur * facteur.numerateur,
|
||||
denominateur= self.denominateur * facteur.denominateur
|
||||
)
|
||||
|
||||
def __rmul__(self, nombre: int):
|
||||
return self.__mul__(nombre)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Fraction({self.numerateur}/{self.denominateur})"
|
||||
|
||||
# le PGCD de 146 et 248
|
||||
fraction = Fraction.from_str('2', '5')
|
||||
print(fraction * 2)
|
||||
print(2 * fraction)
|
||||
@@ -0,0 +1,42 @@
|
||||
import os
|
||||
from tkinter.ttk import Separator
|
||||
import pandas
|
||||
import csv
|
||||
|
||||
def addition(a: int, b: int):
|
||||
somme = a + b
|
||||
return somme
|
||||
|
||||
def create_folder(path: str, name:str):
|
||||
chemin_complet = os.path.join(path, name)
|
||||
if not os.path.isdir(chemin_complet):
|
||||
os.mkdir(chemin_complet)
|
||||
|
||||
def create_empty_csv_file(
|
||||
path: str,
|
||||
filename: str,
|
||||
/, # ce qui est avant le / est du positionnage
|
||||
*, # ce qui est après le * est du nommage
|
||||
colonnes: list,
|
||||
separateur: str = ","
|
||||
):
|
||||
tableau = pandas.DataFrame(columns= colonnes)
|
||||
chemin_complet = os.path.join(path, filename)
|
||||
tableau.to_csv(
|
||||
path_or_buf= chemin_complet,
|
||||
sep= separateur,
|
||||
index= False
|
||||
)
|
||||
|
||||
def write_to_csv(
|
||||
path: str,
|
||||
filename: str,
|
||||
colonnes: list,
|
||||
donnees: list[dict]
|
||||
):
|
||||
if not filename.endswith(".csv"):
|
||||
filename += ".csv"
|
||||
chemin_complet = os.path.join(path, filename)
|
||||
fichier_csv = open(chemin_complet, "a")
|
||||
writer = csv.DictWriter(fichier_csv, colonnes, delimiter= separateur, lineterminator= "\n")
|
||||
writer.writerows(donnees)
|
||||
@@ -0,0 +1,31 @@
|
||||
from functions import addition, create_folder, create_empty_csv_file, write_to_csv
|
||||
|
||||
print(addition(3,7))
|
||||
|
||||
create_folder(".", "data")
|
||||
|
||||
# Ou bien, si on veut changer l'ordre des arguments
|
||||
|
||||
create_folder(
|
||||
name= "data",
|
||||
path= "."
|
||||
)
|
||||
|
||||
create_empty_csv_file(
|
||||
".",
|
||||
"csv_test",
|
||||
colonnes= ["Nom", "Prénom", "Âge"]
|
||||
)
|
||||
|
||||
data = [
|
||||
{"id" = 1, "Prénom": "Dale", "Nom": "Cooper", "Âge": 35},
|
||||
{"id" = 2, "Prénom": "Dale", "Nom": "Cooper", "Âge": 35},
|
||||
{"id" = 3, "Prénom": "Dougie", "Nom": "Jones", "Âge": 35},
|
||||
]
|
||||
|
||||
write_to_csv(
|
||||
path= "data",
|
||||
filename= "donnees",
|
||||
colonnes= ["Nom", "Prénom", "Âge"],
|
||||
donnees= data[]
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Heritage / Composition
|
||||
# Heritage: relation "EST UN"
|
||||
|
||||
class Person:
|
||||
def __init__(self, nom, prenom, age):
|
||||
self.nom = nom
|
||||
self.prenom = prenom
|
||||
self.age = age
|
||||
|
||||
def dire_bonjour(self):
|
||||
return "Bonjour tout le monde"
|
||||
|
||||
class Student(Person): # Heritage
|
||||
def __init__(self, nom, prenom, age, domaine):
|
||||
super().__init__(nom, prenom, age)
|
||||
self.domaine = domaine
|
||||
|
||||
# override
|
||||
def dire_bonjour(self):
|
||||
return super().dire_bonjour() + "! Ca va?"
|
||||
|
||||
alice = Student('Smith', 'Alice', 19, 'Finance')
|
||||
print(alice.dire_bonjour())
|
||||
print(alice.__dict__)
|
||||
print(isinstance(alice, Student))
|
||||
print(isinstance(alice, Person))
|
||||
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "fonctions"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"pandas>=2.3.3",
|
||||
]
|
||||
@@ -0,0 +1,47 @@
|
||||
# Composition: traduit une relation "A UN" "POSSED UN" "DEPEND DE"
|
||||
from math import pi
|
||||
from composition import Point
|
||||
|
||||
class Forme: # Abstraction
|
||||
def __init__(self, centre: Point):
|
||||
self.centre = centre
|
||||
|
||||
def aire(self):
|
||||
raise NotImplementedError("Attention aire doit etre redefini")
|
||||
|
||||
def perimetre(self):
|
||||
raise NotImplementedError("Attention perimetre doit etre redefini")
|
||||
|
||||
class Cercle:
|
||||
def __init__(self, centre: Point, rayon: float):
|
||||
self.centre = centre
|
||||
self.rayon = rayon
|
||||
|
||||
@property
|
||||
def aire(self):
|
||||
return pi * self.rayon ** 2
|
||||
|
||||
@property
|
||||
def perimetre(self):
|
||||
return 2 * pi * self.rayon
|
||||
|
||||
class Rectangle(Forme):
|
||||
def __init__(self, centre: Point, longeur: float, largeur: float):
|
||||
super().__init__(centre)
|
||||
self.longueur = longeur
|
||||
self.largeur = largeur
|
||||
|
||||
def aire(self):
|
||||
return self.longueur * self.largeur
|
||||
|
||||
def perimetre(self):
|
||||
return 2 * (self.longueur + self.largeur)
|
||||
|
||||
cercle1 = Cercle(centre= Point(2, 2), rayon= 5)
|
||||
cercle2 = Cercle(centre= Point(-2, 2), rayon= 6)
|
||||
rect1 = Rectangle(centre= Point(0, 0), longeur= 12, largeur= 5)
|
||||
rect2 = Rectangle(centre= Point(1, 1), longeur= 10, largeur= 6)
|
||||
|
||||
formes = [cercle1, cercle2, rect1, rect2]
|
||||
for forme in formes:
|
||||
print(forme.aire())
|
||||
Generated
+161
@@ -0,0 +1,161 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "fonctions"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "pandas" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "pandas", specifier = ">=2.3.3" }]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/7f/ec53e32bf10c813604edf07a3682616bd931d026fcde7b6d13195dfb684a/numpy-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d3703409aac693fa82c0aee023a1ae06a6e9d065dba10f5e8e80f642f1e9d0a2", size = 16656888, upload-time = "2026-01-10T06:42:40.913Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/e0/1f9585d7dae8f14864e948fd7fa86c6cb72dee2676ca2748e63b1c5acfe0/numpy-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7211b95ca365519d3596a1d8688a95874cc94219d417504d9ecb2df99fa7bfa8", size = 12373956, upload-time = "2026-01-10T06:42:43.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/43/9762e88909ff2326f5e7536fa8cb3c49fb03a7d92705f23e6e7f553d9cb3/numpy-2.4.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5adf01965456a664fc727ed69cc71848f28d063217c63e1a0e200a118d5eec9a", size = 5202567, upload-time = "2026-01-10T06:42:45.107Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/ee/34b7930eb61e79feb4478800a4b95b46566969d837546aa7c034c742ef98/numpy-2.4.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:26f0bcd9c79a00e339565b303badc74d3ea2bd6d52191eeca5f95936cad107d0", size = 6549459, upload-time = "2026-01-10T06:42:48.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/e3/5f115fae982565771be994867c89bcd8d7208dbfe9469185497d70de5ddf/numpy-2.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0093e85df2960d7e4049664b26afc58b03236e967fb942354deef3208857a04c", size = 14404859, upload-time = "2026-01-10T06:42:49.947Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/7d/9c8a781c88933725445a859cac5d01b5871588a15969ee6aeb618ba99eee/numpy-2.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad270f438cbdd402c364980317fb6b117d9ec5e226fff5b4148dd9aa9fc6e02", size = 16371419, upload-time = "2026-01-10T06:42:52.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/d2/8aa084818554543f17cf4162c42f162acbd3bb42688aefdba6628a859f77/numpy-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:297c72b1b98100c2e8f873d5d35fb551fce7040ade83d67dd51d38c8d42a2162", size = 16182131, upload-time = "2026-01-10T06:42:54.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/db/0425216684297c58a8df35f3284ef56ec4a043e6d283f8a59c53562caf1b/numpy-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf6470d91d34bf669f61d515499859fa7a4c2f7c36434afb70e82df7217933f9", size = 18295342, upload-time = "2026-01-10T06:42:56.991Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/4c/14cb9d86240bd8c386c881bafbe43f001284b7cce3bc01623ac9475da163/numpy-2.4.1-cp312-cp312-win32.whl", hash = "sha256:b6bcf39112e956594b3331316d90c90c90fb961e39696bda97b89462f5f3943f", size = 5959015, upload-time = "2026-01-10T06:42:59.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/cf/52a703dbeb0c65807540d29699fef5fda073434ff61846a564d5c296420f/numpy-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:e1a27bb1b2dee45a2a53f5ca6ff2d1a7f135287883a1689e930d44d1ff296c87", size = 12310730, upload-time = "2026-01-10T06:43:01.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/80/a828b2d0ade5e74a9fe0f4e0a17c30fdc26232ad2bc8c9f8b3197cf7cf18/numpy-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:0e6e8f9d9ecf95399982019c01223dc130542960a12edfa8edd1122dfa66a8a8", size = 10312166, upload-time = "2026-01-10T06:43:03.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/68/732d4b7811c00775f3bd522a21e8dd5a23f77eb11acdeb663e4a4ebf0ef4/numpy-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d797454e37570cfd61143b73b8debd623c3c0952959adb817dd310a483d58a1b", size = 16652495, upload-time = "2026-01-10T06:43:06.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/ca/857722353421a27f1465652b2c66813eeeccea9d76d5f7b74b99f298e60e/numpy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c55962006156aeef1629b953fd359064aa47e4d82cfc8e67f0918f7da3344f", size = 12368657, upload-time = "2026-01-10T06:43:09.094Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/0d/2377c917513449cc6240031a79d30eb9a163d32a91e79e0da47c43f2c0c8/numpy-2.4.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:71abbea030f2cfc3092a0ff9f8c8fdefdc5e0bf7d9d9c99663538bb0ecdac0b9", size = 5197256, upload-time = "2026-01-10T06:43:13.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/39/569452228de3f5de9064ac75137082c6214be1f5c532016549a7923ab4b5/numpy-2.4.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b55aa56165b17aaf15520beb9cbd33c9039810e0d9643dd4379e44294c7303e", size = 6545212, upload-time = "2026-01-10T06:43:15.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/a4/77333f4d1e4dac4395385482557aeecf4826e6ff517e32ca48e1dafbe42a/numpy-2.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0faba4a331195bfa96f93dd9dfaa10b2c7aa8cda3a02b7fd635e588fe821bf5", size = 14402871, upload-time = "2026-01-10T06:43:17.324Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/87/d341e519956273b39d8d47969dd1eaa1af740615394fe67d06f1efa68773/numpy-2.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e3087f53e2b4428766b54932644d148613c5a595150533ae7f00dab2f319a8", size = 16359305, upload-time = "2026-01-10T06:43:19.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/91/789132c6666288eaa20ae8066bb99eba1939362e8f1a534949a215246e97/numpy-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:49e792ec351315e16da54b543db06ca8a86985ab682602d90c60ef4ff4db2a9c", size = 16181909, upload-time = "2026-01-10T06:43:21.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/b8/090b8bd27b82a844bb22ff8fdf7935cb1980b48d6e439ae116f53cdc2143/numpy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e9e06c4c2379db47f3f6fc7a8652e7498251789bf8ff5bd43bf478ef314ca2", size = 18284380, upload-time = "2026-01-10T06:43:23.957Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/78/722b62bd31842ff029412271556a1a27a98f45359dea78b1548a3a9996aa/numpy-2.4.1-cp313-cp313-win32.whl", hash = "sha256:3d1a100e48cb266090a031397863ff8a30050ceefd798f686ff92c67a486753d", size = 5957089, upload-time = "2026-01-10T06:43:27.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/a6/cf32198b0b6e18d4fbfa9a21a992a7fca535b9bb2b0cdd217d4a3445b5ca/numpy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:92a0e65272fd60bfa0d9278e0484c2f52fe03b97aedc02b357f33fe752c52ffb", size = 12307230, upload-time = "2026-01-10T06:43:29.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/6c/534d692bfb7d0afe30611320c5fb713659dcb5104d7cc182aff2aea092f5/numpy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:20d4649c773f66cc2fc36f663e091f57c3b7655f936a4c681b4250855d1da8f5", size = 10313125, upload-time = "2026-01-10T06:43:31.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/a1/354583ac5c4caa566de6ddfbc42744409b515039e085fab6e0ff942e0df5/numpy-2.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f93bc6892fe7b0663e5ffa83b61aab510aacffd58c16e012bb9352d489d90cb7", size = 12496156, upload-time = "2026-01-10T06:43:34.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/b0/42807c6e8cce58c00127b1dc24d365305189991f2a7917aa694a109c8d7d/numpy-2.4.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:178de8f87948163d98a4c9ab5bee4ce6519ca918926ec8df195af582de28544d", size = 5324663, upload-time = "2026-01-10T06:43:36.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/55/7a621694010d92375ed82f312b2f28017694ed784775269115323e37f5e2/numpy-2.4.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:98b35775e03ab7f868908b524fc0a84d38932d8daf7b7e1c3c3a1b6c7a2c9f15", size = 6645224, upload-time = "2026-01-10T06:43:37.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/96/9fa8635ed9d7c847d87e30c834f7109fac5e88549d79ef3324ab5c20919f/numpy-2.4.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941c2a93313d030f219f3a71fd3d91a728b82979a5e8034eb2e60d394a2b83f9", size = 14462352, upload-time = "2026-01-10T06:43:39.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d1/8cf62d8bb2062da4fb82dd5d49e47c923f9c0738032f054e0a75342faba7/numpy-2.4.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529050522e983e00a6c1c6b67411083630de8b57f65e853d7b03d9281b8694d2", size = 16407279, upload-time = "2026-01-10T06:43:41.93Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/1c/95c86e17c6b0b31ce6ef219da00f71113b220bcb14938c8d9a05cee0ff53/numpy-2.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2302dc0224c1cbc49bb94f7064f3f923a971bfae45c33870dcbff63a2a550505", size = 16248316, upload-time = "2026-01-10T06:43:44.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/b4/e7f5ff8697274c9d0fa82398b6a372a27e5cef069b37df6355ccb1f1db1a/numpy-2.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9171a42fcad32dcf3fa86f0a4faa5e9f8facefdb276f54b8b390d90447cff4e2", size = 18329884, upload-time = "2026-01-10T06:43:46.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/a4/b073f3e9d77f9aec8debe8ca7f9f6a09e888ad1ba7488f0c3b36a94c03ac/numpy-2.4.1-cp313-cp313t-win32.whl", hash = "sha256:382ad67d99ef49024f11d1ce5dcb5ad8432446e4246a4b014418ba3a1175a1f4", size = 6081138, upload-time = "2026-01-10T06:43:48.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/16/af42337b53844e67752a092481ab869c0523bc95c4e5c98e4dac4e9581ac/numpy-2.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:62fea415f83ad8fdb6c20840578e5fbaf5ddd65e0ec6c3c47eda0f69da172510", size = 12447478, upload-time = "2026-01-10T06:43:50.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/f8/fa85b2eac68ec631d0b631abc448552cb17d39afd17ec53dcbcc3537681a/numpy-2.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a7870e8c5fc11aef57d6fea4b4085e537a3a60ad2cdd14322ed531fdca68d261", size = 10382981, upload-time = "2026-01-10T06:43:52.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/a7/ef08d25698e0e4b4efbad8d55251d20fe2a15f6d9aa7c9b30cd03c165e6f/numpy-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3869ea1ee1a1edc16c29bbe3a2f2a4e515cc3a44d43903ad41e0cacdbaf733dc", size = 16652046, upload-time = "2026-01-10T06:43:54.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/39/e378b3e3ca13477e5ac70293ec027c438d1927f18637e396fe90b1addd72/numpy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e867df947d427cdd7a60e3e271729090b0f0df80f5f10ab7dd436f40811699c3", size = 12378858, upload-time = "2026-01-10T06:43:57.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/74/7ec6154f0006910ed1fdbb7591cf4432307033102b8a22041599935f8969/numpy-2.4.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e3bd2cb07841166420d2fa7146c96ce00cb3410664cbc1a6be028e456c4ee220", size = 5207417, upload-time = "2026-01-10T06:43:59.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/b7/053ac11820d84e42f8feea5cb81cc4fcd1091499b45b1ed8c7415b1bf831/numpy-2.4.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:f0a90aba7d521e6954670550e561a4cb925713bd944445dbe9e729b71f6cabee", size = 6542643, upload-time = "2026-01-10T06:44:01.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/c4/2e7908915c0e32ca636b92e4e4a3bdec4cb1e7eb0f8aedf1ed3c68a0d8cd/numpy-2.4.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d558123217a83b2d1ba316b986e9248a1ed1971ad495963d555ccd75dcb1556", size = 14418963, upload-time = "2026-01-10T06:44:04.047Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/c0/3ed5083d94e7ffd7c404e54619c088e11f2e1939a9544f5397f4adb1b8ba/numpy-2.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f44de05659b67d20499cbc96d49f2650769afcb398b79b324bb6e297bfe3844", size = 16363811, upload-time = "2026-01-10T06:44:06.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/68/42b66f1852bf525050a67315a4fb94586ab7e9eaa541b1bef530fab0c5dd/numpy-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:69e7419c9012c4aaf695109564e3387f1259f001b4326dfa55907b098af082d3", size = 16197643, upload-time = "2026-01-10T06:44:08.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/40/e8714fc933d85f82c6bfc7b998a0649ad9769a32f3494ba86598aaf18a48/numpy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd257026eb1b34352e749d7cc1678b5eeec3e329ad8c9965a797e08ccba205", size = 18289601, upload-time = "2026-01-10T06:44:10.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/9a/0d44b468cad50315127e884802351723daca7cf1c98d102929468c81d439/numpy-2.4.1-cp314-cp314-win32.whl", hash = "sha256:727c6c3275ddefa0dc078524a85e064c057b4f4e71ca5ca29a19163c607be745", size = 6005722, upload-time = "2026-01-10T06:44:13.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/bb/c6513edcce5a831810e2dddc0d3452ce84d208af92405a0c2e58fd8e7881/numpy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d5d7999df434a038d75a748275cd6c0094b0ecdb0837342b332a82defc4dc4d", size = 12438590, upload-time = "2026-01-10T06:44:15.006Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/da/a598d5cb260780cf4d255102deba35c1d072dc028c4547832f45dd3323a8/numpy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:ce9ce141a505053b3c7bce3216071f3bf5c182b8b28930f14cd24d43932cd2df", size = 10596180, upload-time = "2026-01-10T06:44:17.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/bc/ea3f2c96fcb382311827231f911723aeff596364eb6e1b6d1d91128aa29b/numpy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e53170557d37ae404bf8d542ca5b7c629d6efa1117dac6a83e394142ea0a43f", size = 12498774, upload-time = "2026-01-10T06:44:19.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/ab/ef9d939fe4a812648c7a712610b2ca6140b0853c5efea361301006c02ae5/numpy-2.4.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:a73044b752f5d34d4232f25f18160a1cc418ea4507f5f11e299d8ac36875f8a0", size = 5327274, upload-time = "2026-01-10T06:44:23.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/31/d381368e2a95c3b08b8cf7faac6004849e960f4a042d920337f71cef0cae/numpy-2.4.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:fb1461c99de4d040666ca0444057b06541e5642f800b71c56e6ea92d6a853a0c", size = 6648306, upload-time = "2026-01-10T06:44:25.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/e5/0989b44ade47430be6323d05c23207636d67d7362a1796ccbccac6773dd2/numpy-2.4.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423797bdab2eeefbe608d7c1ec7b2b4fd3c58d51460f1ee26c7500a1d9c9ee93", size = 14464653, upload-time = "2026-01-10T06:44:26.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/a7/cfbe475c35371cae1358e61f20c5f075badc18c4797ab4354140e1d283cf/numpy-2.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52b5f61bdb323b566b528899cc7db2ba5d1015bda7ea811a8bcf3c89c331fa42", size = 16405144, upload-time = "2026-01-10T06:44:29.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/a3/0c63fe66b534888fa5177cc7cef061541064dbe2b4b60dcc60ffaf0d2157/numpy-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42d7dd5fa36d16d52a84f821eb96031836fd405ee6955dd732f2023724d0aa01", size = 16247425, upload-time = "2026-01-10T06:44:31.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/2b/55d980cfa2c93bd40ff4c290bf824d792bd41d2fe3487b07707559071760/numpy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b6b5e28bbd47b7532698e5db2fe1db693d84b58c254e4389d99a27bb9b8f6b", size = 18330053, upload-time = "2026-01-10T06:44:34.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/12/8b5fc6b9c487a09a7957188e0943c9ff08432c65e34567cabc1623b03a51/numpy-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:5de60946f14ebe15e713a6f22850c2372fa72f4ff9a432ab44aa90edcadaa65a", size = 6152482, upload-time = "2026-01-10T06:44:36.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/a5/9f8ca5856b8940492fc24fbe13c1bc34d65ddf4079097cf9e53164d094e1/numpy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f085da926c0d491ffff3096f91078cc97ea67e7e6b65e490bc8dcda65663be2", size = 12627117, upload-time = "2026-01-10T06:44:38.828Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/0d/eca3d962f9eef265f01a8e0d20085c6dd1f443cbffc11b6dede81fd82356/numpy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6436cffb4f2bf26c974344439439c95e152c9a527013f26b3577be6c2ca64295", size = 10667121, upload-time = "2026-01-10T06:44:41.644Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pandas"
|
||||
version = "2.3.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy" },
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "pytz" },
|
||||
{ name = "tzdata" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2025.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,533 @@
|
||||
from datetime import date
|
||||
from typing import Iterator, Callable
|
||||
|
||||
|
||||
class Service:
|
||||
"""
|
||||
Un service deploye.
|
||||
"""
|
||||
|
||||
__slots__ = ('_nom', '_port', '_protocole', '_critique')
|
||||
|
||||
def __init__(self, nom: str, port: int, protocole: str, *, critique: bool = False):
|
||||
self._nom = nom
|
||||
self._port = port
|
||||
self._protocole = protocole
|
||||
self._critique = critique
|
||||
|
||||
def est_critique(self) -> bool:
|
||||
"""Ce service est-il critique pour le SI?"""
|
||||
return self._critique
|
||||
|
||||
def ecoute_sur(self, port: int) -> bool:
|
||||
"""Ce service ecoute-t-il sur ce port?"""
|
||||
return self._port == port
|
||||
|
||||
def utilise_protocole(self, protocole: str) -> bool:
|
||||
"""Ce service utilise-t-il ce protocole?"""
|
||||
return self._protocole.lower() == protocole.lower()
|
||||
|
||||
def __eq__(self, autre: object) -> bool:
|
||||
if not isinstance(autre, Service):
|
||||
return NotImplemented
|
||||
return self._nom == autre._nom and self._port == autre._port
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Service({self._nom!r}, {self._port}, {self._protocole!r}, critique={self._critique})"
|
||||
|
||||
|
||||
class Services:
|
||||
"""
|
||||
Une collection de services. Encapsule la logique de gestion.
|
||||
Cet objet sait filtrer, compter, itérer sur ses services.
|
||||
"""
|
||||
|
||||
__slots__ = ('_services',)
|
||||
|
||||
def __init__(self, *services: Service):
|
||||
self._services: tuple[Service, ...] = services
|
||||
|
||||
def avec(self, service: Service) -> 'Services':
|
||||
"""Retourne une nouvelle collection incluant ce service."""
|
||||
if service in self._services:
|
||||
return self
|
||||
return Services(*self._services, service)
|
||||
|
||||
def sans(self, service: Service) -> 'Services':
|
||||
"""Retourne une nouvelle collection sans ce service."""
|
||||
return Services(*(s for s in self._services if s != service))
|
||||
|
||||
def critiques(self) -> 'Services':
|
||||
"""Retourne uniquement les services critiques."""
|
||||
return Services(*(s for s in self._services if s.est_critique()))
|
||||
|
||||
def sur_port(self, port: int) -> 'Services':
|
||||
"""Retourne les services écoutant sur ce port."""
|
||||
return Services(*(s for s in self._services if s.ecoute_sur(port)))
|
||||
|
||||
def contient(self, service: Service) -> bool:
|
||||
"""Cette collection contient-elle ce service?"""
|
||||
return service in self._services
|
||||
|
||||
def est_vide(self) -> bool:
|
||||
"""Cette collection est-elle vide?"""
|
||||
return len(self._services) == 0
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._services)
|
||||
|
||||
def __iter__(self) -> Iterator[Service]:
|
||||
return iter(self._services)
|
||||
|
||||
def __contains__(self, service: Service) -> bool:
|
||||
return service in self._services
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Services({', '.join(repr(s) for s in self._services)})"
|
||||
|
||||
|
||||
class Serveur:
|
||||
"""
|
||||
Un serveur hebergeant des services.
|
||||
"""
|
||||
|
||||
def __init__(self, nom: str, ip: str, os: str, mise_en_service: date):
|
||||
self._nom = nom
|
||||
self._ip = ip
|
||||
self._os = os
|
||||
self._mise_en_service = mise_en_service
|
||||
self._services = Services()
|
||||
|
||||
def heberge(self, service: Service) -> None:
|
||||
"""Ajoute un service a ce serveur."""
|
||||
self._services = self._services.avec(service)
|
||||
|
||||
def retire(self, service: Service) -> None:
|
||||
"""Retire un service de ce serveur."""
|
||||
self._services = self._services.sans(service)
|
||||
|
||||
def services_critiques(self) -> Services:
|
||||
"""Retourne les services critiques heberges."""
|
||||
return self._services.critiques()
|
||||
|
||||
def nombre_services(self) -> int:
|
||||
"""Combien de services ce serveur heberge-t-il?"""
|
||||
return len(self._services)
|
||||
|
||||
def repond_au_nom(self, nom: str) -> bool:
|
||||
"""Ce serveur repond-il a ce nom?"""
|
||||
return self._nom == nom
|
||||
|
||||
def a_pour_ip(self, ip: str) -> bool:
|
||||
"""Ce serveur a-t-il cette IP?"""
|
||||
return self._ip == ip
|
||||
|
||||
def fonctionne_sous(self, os: str) -> bool:
|
||||
"""Ce serveur fonctionne-t-il sous cet OS?"""
|
||||
return self._os.lower() == os.lower()
|
||||
|
||||
def en_service_depuis(self, depuis: date) -> bool:
|
||||
"""Ce serveur est-il en service depuis au moins cette date?"""
|
||||
return self._mise_en_service <= depuis
|
||||
|
||||
def fiche(self) -> str:
|
||||
"""Retourne une fiche descriptive complete du serveur."""
|
||||
lignes = [
|
||||
f"Serveur: {self._nom}",
|
||||
f" IP: {self._ip}",
|
||||
f" OS: {self._os}",
|
||||
f" En service depuis: {self._mise_en_service}",
|
||||
f" Services ({len(self._services)}):",
|
||||
]
|
||||
for service in self._services:
|
||||
lignes.append(f" - {service}")
|
||||
return "\n".join(lignes)
|
||||
|
||||
def __eq__(self, autre: object) -> bool:
|
||||
if not isinstance(autre, Serveur):
|
||||
return NotImplemented
|
||||
return self._nom == autre._nom and self._ip == autre._ip
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Nombre de services heberges."""
|
||||
return len(self._services)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Serveur({self._nom!r}, {self._ip!r}, {self._os!r}, {self._mise_en_service!r})"
|
||||
|
||||
|
||||
class ServeurPhysique(Serveur):
|
||||
"""
|
||||
Un serveur physique dans un rack.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
nom: str,
|
||||
ip: str,
|
||||
os: str,
|
||||
mise_en_service: date,
|
||||
*,
|
||||
rack: str,
|
||||
consommation_kw: float,
|
||||
sous_garantie: bool = True
|
||||
):
|
||||
super().__init__(nom, ip, os, mise_en_service)
|
||||
self._rack = rack
|
||||
self._consommation_kw = consommation_kw
|
||||
self._sous_garantie = sous_garantie
|
||||
|
||||
def est_sous_garantie(self) -> bool:
|
||||
"""Ce serveur est-il encore sous garantie?"""
|
||||
return self._sous_garantie
|
||||
|
||||
def consomme_plus_que(self, seuil_kw: float) -> bool:
|
||||
"""Ce serveur consomme-t-il plus que le seuil?"""
|
||||
return self._consommation_kw > seuil_kw
|
||||
|
||||
def dans_rack(self, rack: str) -> bool:
|
||||
"""Ce serveur est-il dans ce rack?"""
|
||||
return self._rack == rack
|
||||
|
||||
def fiche(self) -> str:
|
||||
"""Retourne une fiche descriptive complete du serveur."""
|
||||
base = super().fiche()
|
||||
garantie = "Oui" if self._sous_garantie else "Non"
|
||||
return "\n".join([
|
||||
base,
|
||||
f" Rack: {self._rack}",
|
||||
f" Consommation: {self._consommation_kw} kW",
|
||||
f" Sous garantie: {garantie}",
|
||||
])
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"ServeurPhysique({self._nom!r}, rack={self._rack!r})"
|
||||
|
||||
|
||||
class ServeurVirtuel(Serveur):
|
||||
"""
|
||||
Un serveur virtuel (VM).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
nom: str,
|
||||
ip: str,
|
||||
os: str,
|
||||
mise_en_service: date,
|
||||
*,
|
||||
hyperviseur: str,
|
||||
allocation: str
|
||||
):
|
||||
super().__init__(nom, ip, os, mise_en_service)
|
||||
self._hyperviseur = hyperviseur
|
||||
self._allocation = allocation
|
||||
|
||||
def tourne_sur_hyperviseur(self, hyperviseur: str) -> bool:
|
||||
"""Cette VM tourne-t-elle sur cet hyperviseur?"""
|
||||
return self._hyperviseur.lower() == hyperviseur.lower()
|
||||
|
||||
def fiche(self) -> str:
|
||||
"""Retourne une fiche descriptive complete du serveur."""
|
||||
base = super().fiche()
|
||||
return "\n".join([
|
||||
base,
|
||||
f" Hyperviseur: {self._hyperviseur}",
|
||||
f" Allocation: {self._allocation}",
|
||||
])
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"ServeurVirtuel({self._nom!r}, hyperviseur={self._hyperviseur!r})"
|
||||
|
||||
|
||||
class Technicien:
|
||||
"""
|
||||
Un technicien.
|
||||
"""
|
||||
|
||||
__slots__ = ('_identifiant', '_nom', '_specialite')
|
||||
|
||||
def __init__(self, identifiant: str, nom: str, specialite: str):
|
||||
self._identifiant = identifiant
|
||||
self._nom = nom
|
||||
self._specialite = specialite
|
||||
|
||||
def est_specialise_en(self, domaine: str) -> bool:
|
||||
"""Ce technicien est-il specialise dans ce domaine?"""
|
||||
return self._specialite.lower() == domaine.lower()
|
||||
|
||||
def __eq__(self, autre: object) -> bool:
|
||||
if not isinstance(autre, Technicien):
|
||||
return NotImplemented
|
||||
return self._identifiant == autre._identifiant
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Technicien({self._identifiant!r}, {self._nom!r}, {self._specialite!r})"
|
||||
|
||||
|
||||
class Maintenance:
|
||||
"""
|
||||
Une intervention de maintenance sur un serveur.
|
||||
"""
|
||||
|
||||
__slots__ = ('_identifiant', '_date', '_type', '_serveur', '_technicien')
|
||||
|
||||
PREVENTIVE = "preventive"
|
||||
CORRECTIVE = "corrective"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
identifiant: str,
|
||||
date_intervention: date,
|
||||
type_maintenance: str,
|
||||
serveur: Serveur,
|
||||
technicien: Technicien
|
||||
):
|
||||
if type_maintenance not in (self.PREVENTIVE, self.CORRECTIVE):
|
||||
raise ValueError(f"Type de maintenance invalide: {type_maintenance}")
|
||||
self._identifiant = identifiant
|
||||
self._date = date_intervention
|
||||
self._type = type_maintenance
|
||||
self._serveur = serveur
|
||||
self._technicien = technicien
|
||||
|
||||
def concerne_serveur(self, serveur: Serveur) -> bool:
|
||||
"""Cette maintenance concerne-t-elle ce serveur?"""
|
||||
return self._serveur == serveur
|
||||
|
||||
def effectuee_par(self, technicien: Technicien) -> bool:
|
||||
"""Cette maintenance a-t-elle ete effectuee par ce technicien?"""
|
||||
return self._technicien == technicien
|
||||
|
||||
def est_preventive(self) -> bool:
|
||||
"""Est-ce une maintenance preventive?"""
|
||||
return self._type == self.PREVENTIVE
|
||||
|
||||
def est_corrective(self) -> bool:
|
||||
"""Est-ce une maintenance corrective?"""
|
||||
return self._type == self.CORRECTIVE
|
||||
|
||||
def a_eu_lieu_apres(self, depuis: date) -> bool:
|
||||
"""Cette maintenance a-t-elle eu lieu après cette date?"""
|
||||
return self._date >= depuis
|
||||
|
||||
def rapport(self) -> str:
|
||||
"""Retourne un rapport formater de la maintenance."""
|
||||
sep = "=" * 50
|
||||
return "\n".join([
|
||||
sep,
|
||||
f"MAINTENANCE {self._identifiant}",
|
||||
sep,
|
||||
f"Date: {self._date}",
|
||||
f"Type: {self._type.upper()}",
|
||||
f"Serveur: {self._serveur}",
|
||||
f"Technicien: {self._technicien}",
|
||||
sep,
|
||||
])
|
||||
|
||||
def __eq__(self, autre: object) -> bool:
|
||||
if not isinstance(autre, Maintenance):
|
||||
return NotImplemented
|
||||
return self._identifiant == autre._identifiant
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Maintenance({self._identifiant!r}, {self._date!r}, {self._type!r})"
|
||||
|
||||
|
||||
class Datacenter:
|
||||
"""
|
||||
Un datacenter regroupant des serveurs.
|
||||
"""
|
||||
|
||||
def __init__(self, nom: str, localisation: str):
|
||||
self._nom = nom
|
||||
self._localisation = localisation
|
||||
self._serveurs: list[Serveur] = []
|
||||
self._maintenances: list[Maintenance] = []
|
||||
|
||||
def accueille(self, serveur: Serveur) -> None:
|
||||
"""Accueille un nouveau serveur dans le datacenter."""
|
||||
if serveur not in self._serveurs:
|
||||
self._serveurs.append(serveur)
|
||||
|
||||
def retire_serveur(self, serveur: Serveur) -> None:
|
||||
"""Retire un serveur du datacenter."""
|
||||
if serveur in self._serveurs:
|
||||
self._serveurs.remove(serveur)
|
||||
|
||||
def enregistre(self, maintenance: Maintenance) -> None:
|
||||
"""Enregistre une maintenance effectuee."""
|
||||
self._maintenances.append(maintenance)
|
||||
|
||||
def services_critiques(self) -> Services:
|
||||
"""Tous les services critiques heberges dans ce datacenter."""
|
||||
critiques = []
|
||||
for serveur in self._serveurs:
|
||||
for service in serveur.services_critiques():
|
||||
critiques.append(service)
|
||||
return Services(*critiques)
|
||||
|
||||
def total_services(self) -> int:
|
||||
"""Nombre total de services heberges."""
|
||||
return sum(serveur.nombre_services() for serveur in self._serveurs)
|
||||
|
||||
def serveurs_maintenus_par(self, technicien: Technicien) -> list[Serveur]:
|
||||
"""Liste des serveurs ayant ete maintenus par ce technicien."""
|
||||
serveurs_trouves = []
|
||||
for maintenance in self._maintenances:
|
||||
if maintenance.effectuee_par(technicien):
|
||||
for serveur in self._serveurs:
|
||||
if maintenance.concerne_serveur(serveur) and serveur not in serveurs_trouves:
|
||||
serveurs_trouves.append(serveur)
|
||||
return serveurs_trouves
|
||||
|
||||
def pour_chaque_serveur(self, action: Callable[[Serveur], None]) -> None:
|
||||
"""Applique une action a chaque serveur"""
|
||||
for serveur in self._serveurs:
|
||||
action(serveur)
|
||||
|
||||
def pour_chaque_serveur_physique(self, action: Callable[[ServeurPhysique], None]) -> None:
|
||||
"""Applique une action a chaque serveur physique."""
|
||||
for serveur in self._serveurs:
|
||||
if isinstance(serveur, ServeurPhysique):
|
||||
action(serveur)
|
||||
|
||||
def inventaire(self) -> str:
|
||||
"""Retourne l'inventaire complet du datacenter."""
|
||||
sep = "#" * 60
|
||||
lignes = [
|
||||
"",
|
||||
sep,
|
||||
f"DATACENTER: {self._nom}",
|
||||
f"Localisation: {self._localisation}",
|
||||
sep,
|
||||
"",
|
||||
f"Nombre de serveurs: {len(self._serveurs)}",
|
||||
f"Total services: {self.total_services()}",
|
||||
f"Services critiques: {len(self.services_critiques())}",
|
||||
"",
|
||||
]
|
||||
for serveur in self._serveurs:
|
||||
lignes.append(serveur.fiche())
|
||||
lignes.append("")
|
||||
return "\n".join(lignes)
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Nombre de serveurs dans le datacenter."""
|
||||
return len(self._serveurs)
|
||||
|
||||
def __contains__(self, serveur: Serveur) -> bool:
|
||||
"""Ce serveur est-il dans le datacenter?"""
|
||||
return serveur in self._serveurs
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Datacenter({self._nom!r}, {self._localisation!r})"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Creation de services
|
||||
nginx = Service("nginx", 80, "HTTP", critique=True)
|
||||
api = Service("api-backend", 8080, "HTTP", critique=True)
|
||||
ssh = Service("sshd", 22, "TCP")
|
||||
postgres = Service("postgresql", 5432, "TCP", critique=True)
|
||||
redis = Service("redis", 6379, "TCP")
|
||||
|
||||
# Creation de serveurs
|
||||
srv_web = ServeurPhysique(
|
||||
"srv-web-01",
|
||||
"192.168.1.10",
|
||||
"Debian 12",
|
||||
date(2023, 6, 15),
|
||||
rack="R01-U12",
|
||||
consommation_kw=0.8,
|
||||
sous_garantie=True
|
||||
)
|
||||
srv_web.heberge(nginx)
|
||||
srv_web.heberge(ssh)
|
||||
|
||||
srv_api = ServeurVirtuel(
|
||||
"srv-api-01",
|
||||
"192.168.1.20",
|
||||
"Ubuntu 22.04",
|
||||
date(2024, 1, 10),
|
||||
hyperviseur="Proxmox",
|
||||
allocation="4 vCPU / 8 Go RAM"
|
||||
)
|
||||
srv_api.heberge(api)
|
||||
srv_api.heberge(redis)
|
||||
|
||||
srv_db = ServeurPhysique(
|
||||
"srv-db-01",
|
||||
"192.168.1.30",
|
||||
"Rocky Linux 9",
|
||||
date(2022, 3, 1),
|
||||
rack="R02-U08",
|
||||
consommation_kw=1.2,
|
||||
sous_garantie=False
|
||||
)
|
||||
srv_db.heberge(postgres)
|
||||
srv_db.heberge(ssh)
|
||||
|
||||
# Creation du datacenter
|
||||
dc_paris = Datacenter("DC-Paris-01", "Paris, France")
|
||||
dc_paris.accueille(srv_web)
|
||||
dc_paris.accueille(srv_api)
|
||||
dc_paris.accueille(srv_db)
|
||||
|
||||
# Creation de techniciens
|
||||
alice = Technicien("T001", "Alice Martin", "Linux")
|
||||
bob = Technicien("T002", "Bob Dupont", "Virtualisation")
|
||||
|
||||
# Creation de maintenances
|
||||
maint_1 = Maintenance(
|
||||
"M2024-001",
|
||||
date(2024, 11, 15),
|
||||
Maintenance.PREVENTIVE,
|
||||
srv_web,
|
||||
alice
|
||||
)
|
||||
|
||||
maint_2 = Maintenance(
|
||||
"M2024-002",
|
||||
date(2024, 12, 3),
|
||||
Maintenance.CORRECTIVE,
|
||||
srv_db,
|
||||
alice
|
||||
)
|
||||
|
||||
maint_3 = Maintenance(
|
||||
"M2025-001",
|
||||
date(2025, 1, 8),
|
||||
Maintenance.PREVENTIVE,
|
||||
srv_api,
|
||||
bob
|
||||
)
|
||||
|
||||
dc_paris.enregistre(maint_1)
|
||||
dc_paris.enregistre(maint_2)
|
||||
dc_paris.enregistre(maint_3)
|
||||
|
||||
# Inventaire complet
|
||||
print(dc_paris.inventaire())
|
||||
|
||||
# Services critiques
|
||||
print("--- SERVICES CRITIQUES ---")
|
||||
print(dc_paris.services_critiques())
|
||||
|
||||
# Serveurs maintenus par Alice
|
||||
print("\n--- SERVEURS MAINTENUS PAR ALICE ---")
|
||||
for srv in dc_paris.serveurs_maintenus_par(alice):
|
||||
print(f" - {srv}")
|
||||
|
||||
# Rapport de maintenance
|
||||
print("\n--- RAPPORT MAINTENANCE ---")
|
||||
print(maint_1.rapport())
|
||||
|
||||
print("\n--- SERVEURS HORS GARANTIE ---")
|
||||
dc_paris.pour_chaque_serveur_physique(
|
||||
lambda s: print(f" ! {s}") if not s.est_sous_garantie() else None
|
||||
)
|
||||
|
||||
print("\n--- TEST ÉGALITÉ ---")
|
||||
nginx_copie = Service("nginx", 80, "HTTPS") # Meme nom/port, protocole different
|
||||
print(f"nginx == nginx_copie: {nginx == nginx_copie}") # True (egalite sur nom+port)
|
||||
@@ -0,0 +1,28 @@
|
||||
def persistance(num: int):
|
||||
"""
|
||||
A partir d'un entier positif, renvoie la persistance multiplicative,
|
||||
à savoir le nombre de fois qu'on peut multiplier les chiffres de l'entier
|
||||
entre eux jusqu'à un résultat d'un seul chiffre.
|
||||
|
||||
Exemples :
|
||||
4 -> 0
|
||||
39 -> 3 (3*9=27, 2*7=14, 1*4=4)
|
||||
"""
|
||||
if num < 0:
|
||||
return "Merci d'utiliser un entier positif."
|
||||
|
||||
steps = 0
|
||||
start = num
|
||||
|
||||
while len(str(num)) > 1:
|
||||
base = 1
|
||||
|
||||
for n in str(num):
|
||||
base *= int(n)
|
||||
|
||||
steps += 1
|
||||
num = base
|
||||
|
||||
if steps == 0:
|
||||
return f"Partant de {start}, pas besoin de multiplication."
|
||||
return f"Partant de {start}, la persistance multiplicative est de {steps} étapes."
|
||||
@@ -0,0 +1,22 @@
|
||||
def numeral_square(num: int):
|
||||
"""
|
||||
A partir d'un entier positif, renvoie la racine numérique,
|
||||
à savoir la somme récursive de tous les chiffres d'un nombre.
|
||||
|
||||
Exemples :
|
||||
16 -> 1+6=7
|
||||
1567 -> 1+5+6+7=19, 1+9=10, 1+0=1
|
||||
"""
|
||||
if num < 0:
|
||||
return "Merci d'utiliser un entier positif."
|
||||
start = num
|
||||
|
||||
while len(str(num)) > 1:
|
||||
base = 0
|
||||
|
||||
for n in str(num):
|
||||
base += int(n)
|
||||
|
||||
num = base
|
||||
|
||||
return f"La racine numérique de {start} donne {base}."
|
||||
@@ -0,0 +1,18 @@
|
||||
def gen_pyramid(num: int) -> list[str]:
|
||||
"""
|
||||
A partir d'un entier, renvoie sous forme de tableau une
|
||||
pyramide composée d'autant d'étages avec le caractère *
|
||||
"""
|
||||
if num == 0:
|
||||
return ["Merci d'utiliser un entier non nul."]
|
||||
if num < 0:
|
||||
num = +abs(num)
|
||||
|
||||
pyramid = []
|
||||
|
||||
for n in range(num):
|
||||
white_spaces = ' ' * (num - n - 1)
|
||||
bloc_spaces = '*' * (2 * n + 1)
|
||||
pyramid.append(f"{white_spaces}{bloc_spaces}{white_spaces}")
|
||||
|
||||
return pyramid
|
||||
@@ -0,0 +1,45 @@
|
||||
from datetime import date, time, datetime, timedelta
|
||||
from pprint import pprint
|
||||
|
||||
from exo_01 import persistance
|
||||
from exo_02 import numeral_square
|
||||
from exo_03 import gen_pyramid
|
||||
from prog_or_obj import Service,\
|
||||
ServeurPhysique,\
|
||||
ServeurVirtuel,\
|
||||
Technicien,\
|
||||
Maintenance,\
|
||||
Datacenter
|
||||
|
||||
# Exercice 1
|
||||
print("\n----- Exercice [1] -----\n")
|
||||
exo_1_test_list = [-7, 4, 39, 999]
|
||||
for test in exo_1_test_list:
|
||||
print(persistance(test))
|
||||
|
||||
# Exercice 2
|
||||
print("\n----- Exercice [2] -----\n")
|
||||
exo_2_test_list = [-7, 16, 942, 132189, 493193]
|
||||
for test in exo_2_test_list:
|
||||
print(numeral_square(test))
|
||||
|
||||
# Exercice 3
|
||||
print("\n----- Exercice [3] -----\n")
|
||||
exo_3_test_list = [-7, 0, 6, 12]
|
||||
for test in exo_3_test_list:
|
||||
pprint(gen_pyramid(test), width= 30)
|
||||
|
||||
# Exercice de Programmation Orientée Objet - Centre de données
|
||||
|
||||
today = date.today()
|
||||
two_days_ago = date.today() - timedelta(days= 2)
|
||||
five_days_ago = date.today() - timedelta(days= 5)
|
||||
|
||||
service_01 = Service("HTTP", 80, "TCP", True)
|
||||
service_02 = Service("HTTPS", "443", "TCP", True)
|
||||
service_03 = Service("PostGresql", 5432, "TCP", False)
|
||||
service_04 = Service("Counter-Strike LAN", 36729, "UDP", False)
|
||||
|
||||
server_physical_01 = ServeurPhysique("Thorium", "10.10.19.94", "Debian 13", today, "HP version 4.2", 45, True)
|
||||
server_physical_02 = ServeurPhysique("Iridium", "10.10.18.94", "Arch Linux 4", two_days_ago, "HP version 4.2", 37, True)
|
||||
server_physical_03 = ServeurPhysique("Polonium", "10.10.17.94", "Apple Server mdr", five_days_ago, "HP version 3.3", 445, False)
|
||||
@@ -0,0 +1,255 @@
|
||||
from datetime import date
|
||||
import json
|
||||
|
||||
class Service:
|
||||
"""
|
||||
Application déployée sur un serveur.
|
||||
"""
|
||||
def __init__(self, name: str, port: str | int, protocol: str, is_critical: bool):
|
||||
self.name = name
|
||||
self.port = int(port)
|
||||
self.protocol = protocol
|
||||
self.is_critical = is_critical
|
||||
|
||||
def __eq__(self, other: "Service"):
|
||||
return self.name == other.name and self.port == other.port
|
||||
|
||||
def serialized_services(self):
|
||||
"""Converti la classe \"Services\" en dictionnaire JSONable"""
|
||||
return {
|
||||
"Name": self.name,
|
||||
"Port": self.port,
|
||||
"Protocol": self.protocol,
|
||||
"Is_Critical": self.is_critical,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def deserialized_services(cls, dictionnary):
|
||||
"""Lis un JSON pour le retranscrire en classe \"Services\""""
|
||||
return cls(
|
||||
name= dictionnary["Name"],
|
||||
port= dictionnary["Port"],
|
||||
protocol= dictionnary["Protocol"],
|
||||
is_critical= dictionnary["Is_Critical"],
|
||||
)
|
||||
|
||||
class Serveur:
|
||||
"""
|
||||
Serveur, classe maîtresse. NE PAS UTILISER DIRECTEMENT.
|
||||
Préférez les classes héritées :
|
||||
- ServeurPhysique
|
||||
- ServeurVirtuel
|
||||
"""
|
||||
def __init__(self, name: str, ip_address: str, os_name: str, commission_date: date):
|
||||
self.name = name
|
||||
self.ip_address = ip_address
|
||||
self.os_name = os_name
|
||||
self.commission_date = commission_date
|
||||
self.services = self.load_services()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"\
|
||||
[Serveur] > {self.name}\n\
|
||||
[Addresse IP] > {self.ip_address}\n\
|
||||
[Système d'Exploitation] > {self.os_name}\n\
|
||||
[Date de mise en prod] > {self.commission_date}"
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.services)
|
||||
|
||||
def list_services(self):
|
||||
return self.services
|
||||
|
||||
def add_service(self, service: Service):
|
||||
"""Ajout d'un service à la liste des services disponibles"""
|
||||
if service in self.services:
|
||||
return f"Le service [{service.name.capitalize()}] est déjà présent."
|
||||
self.services.append(service)
|
||||
self.save_services()
|
||||
return f"{service.name.capitalize()} a été ajouté."
|
||||
|
||||
def remove_service(self, service_str: str):
|
||||
"""Retire un service de la liste des services disponibles"""
|
||||
for service in self.services:
|
||||
if service.name.lower() == service_str.lower()\
|
||||
or str(service.port) == service_str.lower()\
|
||||
or service.protocol.lower() == service_str.lower():
|
||||
self.services.remove(service)
|
||||
self.save_services()
|
||||
return f"{service.name.capitalize} a été retiré."
|
||||
return f"{service.name.capitalize()} n'est pas dans la liste."
|
||||
|
||||
def serialized_servers(self):
|
||||
"""Converti la classe \"Services\" en dictionnaire JSONable"""
|
||||
return {
|
||||
"Name": self.name,
|
||||
"IP_Address": self.ip_address,
|
||||
"OS_Name": self.os_name,
|
||||
"Commission_Date": self.commission_date,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def deserialized_servers(cls, dictionnary):
|
||||
"""Lis un JSON pour le retranscrire en classe \"Services\""""
|
||||
return cls(
|
||||
name= dictionnary["Name"],
|
||||
ip_address= dictionnary["IP_Address"],
|
||||
os_name= dictionnary["OS_Name"],
|
||||
commission_date= dictionnary["Commission_Date"],
|
||||
)
|
||||
|
||||
def save_services(self):
|
||||
"""
|
||||
Inscris dans un fichier JSON le contenu des services disponibles
|
||||
"""
|
||||
services_dict = [services.serialized_services() for services in self.services]
|
||||
with open("services.json", "w", encoding= "utf-8") as json_file:
|
||||
json.dump(services_dict, json_file, indent= 4)
|
||||
|
||||
def load_services(self) -> list[Service]:
|
||||
"""
|
||||
Récupère le contenu d'un fichier JSON contenant les services
|
||||
"""
|
||||
with open("services.json", encoding= "utf-8") as json_file:
|
||||
return json.load(json_file, object_hook= Service.deserialized_services)
|
||||
|
||||
class ServeurPhysique(Serveur):
|
||||
def __init__(self, name: str, ip_address: str, os_name: str, commission_date: date, rack: str, electric_cons: int, warranty: bool):
|
||||
super().__init__(name, ip_address, os_name, commission_date)
|
||||
self.rack = rack
|
||||
self.electric_cons = electric_cons
|
||||
warranty_bools = ("Non", "Oui")
|
||||
self.warranty = warranty_bools[warranty]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"\
|
||||
{super().__str__()}\n\
|
||||
[Châssis] > {self.rack}\n\
|
||||
[Consommation en kWh] > {self.electric_cons}\n\
|
||||
[Sous garantie] > {self.warranty}"
|
||||
|
||||
def __len__(self) -> int:
|
||||
return super().__len__()
|
||||
|
||||
class ServeurVirtuel(Serveur):
|
||||
def __init__(self, name: str, ip_address: str, os_name: str, commission_date: date, hypervisor: str, vcpus: int, giga_ram: float):
|
||||
super().__init__(name, ip_address, os_name, commission_date)
|
||||
self.hypervisor = hypervisor
|
||||
self.vcpus = vcpus
|
||||
self.giga_ram = giga_ram
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"\
|
||||
{super().__str__()}\n\
|
||||
[Hyperviseur] > {self.hypervisor}\n\
|
||||
[Coeurs virtuels] > {self.vcpus}\n\
|
||||
[Mémoire vive (Go)] > {self.giga_ram}"
|
||||
|
||||
def __len__(self) -> int:
|
||||
return super().__len__()
|
||||
|
||||
class Maintenance:
|
||||
def __init__(self, id: int, commission_date: date, type: str, affected_technician: "Technicien", associated_server: "ServeurPhysique | ServeurVirtuel"):
|
||||
self.id = id
|
||||
self.commission_date = commission_date
|
||||
self.type = type
|
||||
self.affected_technician = affected_technician
|
||||
self.associated_server = associated_server
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"\
|
||||
[ID] > {self.id}\n\
|
||||
[Serveur associé] > {self.associated_server}\n\
|
||||
[Date de mise en prod] > {self.commission_date}\n\
|
||||
[Type de maintenance] > {self.type}\n\
|
||||
[Technicien affecté] > {self.affected_technician}"
|
||||
|
||||
# J'ai le sentiment que ce n'est PAS ce qui est demandé. Mais il est 16h25 et je n'ai plus d'idée sur comment le faire
|
||||
def link_to_server(self, server: ServeurPhysique | ServeurVirtuel):
|
||||
return f"La maintenance est liée au serveur {server.name.upper()}, addresse IP {server.ip_address}."
|
||||
|
||||
def serialized_maintenances(self):
|
||||
"""Converti la classe \"Maintenance\" en dictionnaire JSONable"""
|
||||
return {
|
||||
"ID": self.id,
|
||||
"Associated_Server": self.associated_server,
|
||||
"Commission_Date": self.commission_date,
|
||||
"Type": self.type,
|
||||
"Affected_Technician": self.affected_technician,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def deserialized_maintenances(cls, dictionnary):
|
||||
"""Lis un JSON pour le retranscrire en classe \"Maintenance\""""
|
||||
return cls(
|
||||
id= dictionnary["ID"],
|
||||
associated_server= dictionnary["Associated_Server"],
|
||||
commission_date= dictionnary["Commission_Date"],
|
||||
type= dictionnary["Type"],
|
||||
affected_technician= dictionnary["Affected_Technician"],
|
||||
)
|
||||
|
||||
class Technicien:
|
||||
def __init__(self, fullname: str, speciality: str, id: int):
|
||||
self.name = fullname
|
||||
self.speciality = speciality
|
||||
self.id = id
|
||||
self.maintenances = self.load_maintenances()
|
||||
|
||||
def list_affected_jobs(self):
|
||||
return self.maintenances
|
||||
|
||||
def affect_maintenance(self):
|
||||
pass
|
||||
|
||||
def save_maintenances(self):
|
||||
"""
|
||||
Inscris dans un fichier JSON le contenu des maintenances disponibles
|
||||
"""
|
||||
maintenances_dict = [maintenances.serialized_maintenances() for maintenances in self.maintenances]
|
||||
with open("maintenances.json", "w", encoding= "utf-8") as json_file:
|
||||
json.dump(maintenances_dict, json_file, indent= 4)
|
||||
|
||||
def load_maintenances(self) -> list[Maintenance]:
|
||||
"""
|
||||
Récupère le contenu d'un fichier JSON contenant les maintenances
|
||||
"""
|
||||
with open("maintenances.json", encoding= "utf-8") as json_file:
|
||||
return json.load(json_file, object_hook= Maintenance.deserialized_maintenances)
|
||||
|
||||
class Datacenter:
|
||||
def __init__(self, name: str, location: str, id: int):
|
||||
self.name = name
|
||||
self.location = location
|
||||
self.id = id
|
||||
self.servers = self.load_servers()
|
||||
|
||||
def list_servers(self):
|
||||
return self.servers
|
||||
|
||||
def list_critical_services(self):
|
||||
for server in self.servers:
|
||||
server.list_services()
|
||||
# Là j'ai cherché à récupérer un genre de 'if server.list_services().is_critical'
|
||||
# pour avoir le "True/False" et renvoyer le résultat en cas de "True" mais j'ai craqué
|
||||
|
||||
def list_services_number(self):
|
||||
services = 0
|
||||
for server in self.servers:
|
||||
services += len(server)
|
||||
return services
|
||||
|
||||
def save_servers(self):
|
||||
"""
|
||||
Inscris dans un fichier JSON le contenu des serveurs disponibles
|
||||
"""
|
||||
servers_dict = [servers.serialized_servers() for servers in self.servers]
|
||||
with open("servers.json", "w", encoding= "utf-8") as json_file:
|
||||
json.dump(servers_dict, json_file, indent= 4)
|
||||
|
||||
def load_servers(self) -> list[Serveur]:
|
||||
"""
|
||||
Récupère le contenu d'un fichier JSON contenant les serveurs
|
||||
"""
|
||||
with open("servers.json", encoding= "utf-8") as json_file:
|
||||
return json.load(json_file, object_hook= Serveur.deserialized_servers)
|
||||
@@ -0,0 +1,66 @@
|
||||
# Les bases de Python
|
||||
|
||||
## UV (NPM de Python)
|
||||
|
||||
*Je suis sur Windows, les commandes divergent sur UNIX(like)
|
||||
|
||||
Pour installer UV, remplaçant PIP et consorts (évidemment codé en RUST) :
|
||||
|
||||
https://docs.astral.sh/uv/#projects
|
||||
|
||||
Dans le dossier de travail :
|
||||
|
||||
`uv init --python 3.12`
|
||||
|
||||
Et dans le dossier initialisé :
|
||||
|
||||
```sh
|
||||
.venv\Scripts\activate
|
||||
uv run main.py
|
||||
|
||||
# On peut aussi activer le .venv en l'initialisation plus avant en intervertissant les commandes, donc :
|
||||
|
||||
uv run main.py
|
||||
.venv\Scripts\activate
|
||||
```
|
||||
|
||||
## Quelques bases oubliées
|
||||
|
||||
### Fonctions
|
||||
|
||||
Une fonction appelle un résultat.
|
||||
|
||||
```py
|
||||
fonction(variable)
|
||||
```
|
||||
|
||||
Définir une fonction :
|
||||
|
||||
```py
|
||||
def fonction():
|
||||
return true
|
||||
```
|
||||
|
||||
On peut forcer le type des variables d'une fonction :
|
||||
|
||||
```py
|
||||
def fonction(a: int, b: int):
|
||||
somme = a + b
|
||||
return somme
|
||||
```
|
||||
|
||||
Et même une valeur par défaut
|
||||
|
||||
```py
|
||||
def fonction(a: int, b: int = 5):
|
||||
somme = a + b
|
||||
return somme
|
||||
```
|
||||
|
||||
### Méthodes
|
||||
|
||||
Une méthode suit une variable.
|
||||
|
||||
```py
|
||||
variable.methode("Valeur")
|
||||
```
|
||||
@@ -0,0 +1,136 @@
|
||||
## Fonctions LAMBDA
|
||||
|
||||
|
||||
|
||||
## Méthodes et fonctions avancées
|
||||
|
||||
### Données déstructurées
|
||||
|
||||
dc_infos = ("Dale", "Cooper", "32")
|
||||
dc_prenom = dc_infos[0]
|
||||
dc_nom = dc_infos[1]
|
||||
dc_age = dc_infos[2]
|
||||
|
||||
dc_prenom, dc_nom, dc_age = dc_infos
|
||||
|
||||
### Surcharge de méthode
|
||||
|
||||
```py
|
||||
range(10) # [0,1,2,3,4,5,6,7,8,9]
|
||||
range(2, 10) # [2,3,4,5,6,7,8,9]
|
||||
range(2, 10, 2) # [2,4,6,8]
|
||||
```
|
||||
|
||||
### A la place de `return` => `yield`
|
||||
|
||||
```py
|
||||
def generator():
|
||||
for i in range(10):
|
||||
return i
|
||||
```
|
||||
|
||||
Renverra [0], `return` étant un arrêt.
|
||||
|
||||
Alors que :
|
||||
|
||||
```py
|
||||
def generator():
|
||||
for i in range(10):
|
||||
yield i
|
||||
```
|
||||
|
||||
Va renvoyer : `<function generator at 0x000002584950E340>`
|
||||
|
||||
### Générateurs
|
||||
|
||||
La fonction `next` permet d'économiser des ressources en mettant en mémoire la fonction et sa position. Par exemple :
|
||||
|
||||
```py
|
||||
def square_generator(n):
|
||||
for i in range(n):
|
||||
yield i ** 2
|
||||
```
|
||||
|
||||
Renverra `<function square_generator at 0x000001351A12E3E0>`. Maintenant, on utilise :
|
||||
|
||||
```py
|
||||
squares = square_generator(10)
|
||||
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
print(next(squares))
|
||||
```
|
||||
|
||||
Renverra :
|
||||
|
||||
```
|
||||
0
|
||||
1
|
||||
4
|
||||
9
|
||||
16
|
||||
25
|
||||
36
|
||||
49
|
||||
```
|
||||
|
||||
### Return, Yield et Continue
|
||||
|
||||
```py
|
||||
for square in generator:
|
||||
if square > 50: # Si le carré est supérieur à 50, la boucle cesse
|
||||
break
|
||||
if square % 2 == 0: # Si le carré est pair, on le zappe
|
||||
continue
|
||||
print(square)
|
||||
```
|
||||
|
||||
## Format String
|
||||
|
||||
Le classique :
|
||||
|
||||
```py
|
||||
print("La lettre " + variable_str + " apparaît " + variable_int + " fois.")
|
||||
```
|
||||
|
||||
Se remplace bien par :
|
||||
|
||||
```py
|
||||
print(f"La lettre {variable_str} apparaît {variable_int} fois.")
|
||||
```
|
||||
|
||||
Le `%s` c'est un peu démodé face au f-string, franchement.
|
||||
|
||||
## Listes de compréhension
|
||||
|
||||
Pour peupler une liste avec des formules, on peut faire du standard :
|
||||
|
||||
```py
|
||||
liste = []
|
||||
|
||||
for i in range(5)
|
||||
liste.append(i)
|
||||
|
||||
print(liste) # [0, 1, 2, 3, 4]
|
||||
```
|
||||
|
||||
Ou, une liste compréhensive, contenant l'algo de base amenant le résultat :
|
||||
|
||||
```py
|
||||
liste = [i for i in range(5)]
|
||||
|
||||
print(liste) # [0, 1, 2, 3, 4]
|
||||
```
|
||||
|
||||
Et même inclure des conditions :
|
||||
|
||||
```py
|
||||
liste = [i for i in range(5) if i % 2 == 0]
|
||||
|
||||
print(liste) # [0, 2, 4]
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
## Sérialisation, Dé-sérialisation
|
||||
|
||||
Sérialisation => On prend des objets, on transforme en JSON
|
||||
Dé-sérialisation => On prend un JSON, on transforme en objets
|
||||
|
||||
### Exemple de sérialisation
|
||||
|
||||
On prend une liste classique
|
||||
|
||||
```py
|
||||
livres = [{"titre": "1984", "auteur": "Orwell"}, {"titre": "LSDA", "auteur": "Tolkien"}]
|
||||
```
|
||||
|
||||
On peut donc traiter un fichier JSON avec la fonction `open` et le sérialiser avec `dump` :
|
||||
|
||||
```py
|
||||
import json
|
||||
|
||||
livres = [{"titre": "1984", "auteur": "Orwell"}, {"titre": "LSDA", "auteur": "Tolkien"}]
|
||||
|
||||
# file=/path/ , w-rite
|
||||
fichier_json = open("livres.json", "w")
|
||||
json.dump(livres, fichier_json)
|
||||
fichier_json.close()
|
||||
```
|
||||
|
||||
Ou, avec un `Context-Manager` au lieu d'ouvrir et de fermer :
|
||||
|
||||
```py
|
||||
import json
|
||||
|
||||
livres = [{"titre": "1984", "auteur": "Orwell"}, {"titre": "LSDA", "auteur": "Tolkien"}]
|
||||
|
||||
with open("livres.json", "w") as fichier_json:
|
||||
json.dump(livres, fichier_json, indent= 4)
|
||||
```
|
||||
|
||||
### Exemple de dé-sérialisation
|
||||
|
||||
```py
|
||||
import json
|
||||
|
||||
livres = [{"titre": "1984", "auteur": "Orwell"}, {"titre": "LSDA", "auteur": "Tolkien"}]
|
||||
|
||||
with open("livres.json", "w") as fichier_json:
|
||||
objet = json.load(fichier_json)
|
||||
|
||||
print(objet)
|
||||
|
||||
objet.append({"titre": "Alice aux pays", "auteur": "Caroll"})
|
||||
|
||||
with open("livres.json", "w") as fichier_json:
|
||||
json.dump(objet, fichier_json, indent= 4)
|
||||
```
|
||||
|
||||
## Les classes
|
||||
|
||||
### Méthodes "Dunder"
|
||||
|
||||
- __init__
|
||||
- __repr__
|
||||
@@ -0,0 +1,14 @@
|
||||
## Front-End facile et rapide
|
||||
|
||||
### QT Designer
|
||||
|
||||
Créer une interface
|
||||
|
||||
### PyQt6
|
||||
|
||||
```py
|
||||
uv add pyqt6
|
||||
.\.venv\Scripts\activate
|
||||
# Ensuite on récupère le fichier .ui créé par QT Designer et on le convertie n .py
|
||||
pyuic6 .\gui\library.ui -o .\gui\ui_library.py
|
||||
```
|
||||
@@ -0,0 +1,26 @@
|
||||
## TYPE_CHECKING
|
||||
|
||||
En cas d'import circulaire de dépendance, un conflit peut apparaitre :
|
||||
|
||||
```py
|
||||
# dans main.py :
|
||||
|
||||
from classes.py import dependance_01
|
||||
```
|
||||
|
||||
```py
|
||||
# dans classes.py :
|
||||
|
||||
from main.py import dependance_02
|
||||
```
|
||||
|
||||
La lecture des fichiers par Python peut conduire à une boucle où chacun fichier lit l'autre et bloque sur un import. On utilise alors dans la dépendance secondaire :
|
||||
|
||||
```py
|
||||
# dans classes.py :
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from main.py import dependance_02
|
||||
```
|
||||
|
||||
Pour sortir de la boucle.
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -0,0 +1,59 @@
|
||||
import fractions
|
||||
|
||||
|
||||
class Fraction:
|
||||
numerator = 1
|
||||
denominator = 1
|
||||
|
||||
def __init__(self, numerator, denominator):
|
||||
self.numerator = numerator
|
||||
self.denominator = denominator
|
||||
self.simplifier()
|
||||
|
||||
def __mul__(self, factor: int | Self):
|
||||
if isinstance(factor, int):
|
||||
return Fraction(
|
||||
numerator = self.numerator * factor,
|
||||
denominator = self.denominator
|
||||
)
|
||||
return Fraction(
|
||||
numerator = self.numerator * factor.numerator,
|
||||
denominator = self.denominator * factor.denominator
|
||||
)
|
||||
|
||||
def __rmul__(self, number: int):
|
||||
return self.__mul__(number)
|
||||
|
||||
def quotient(self):
|
||||
"""[Fraction] Chiffre sur Chiffre"""
|
||||
return self.numerator / self.denominator
|
||||
|
||||
def plus(self, fraction):
|
||||
"""[Fraction] Addition de deux fractions"""
|
||||
num = (self.numerator * self.denominator) + (self.denominator + fraction.numerator)
|
||||
den = self.denominator * fraction.denominator
|
||||
return Fraction(numerator= num, denominator= den)
|
||||
|
||||
@staticmethod
|
||||
def pgcd( a, b):
|
||||
"""Calcul du PGCD de deux entiers naturels"""
|
||||
if b == 0:return a
|
||||
return Fraction.pgcd(a= b, b= a % b)
|
||||
|
||||
def simplifier(self):
|
||||
a = self.numerator
|
||||
b = self.denominator
|
||||
pgcd = Fraction.pgcd(a, b)
|
||||
|
||||
self.numerator = self.numerator // pgcd # // force la division à ne PAS donner de float
|
||||
self.denominator //= pgcd # C'est la même chose que dessus
|
||||
|
||||
def __repr__(self):
|
||||
return f"Fraction({self.numerator}/{self.denominator})"
|
||||
|
||||
tiers = Fraction(numerator= 1, denominator= 3)
|
||||
quart = Fraction(1, 4)
|
||||
|
||||
fraction = tiers.plus(quart)
|
||||
print(fraction)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from pprint import pprint
|
||||
|
||||
prenom = "John" # crée une instance (obj) de type 'str' et l'initialise avec la valeur 'John'
|
||||
nom = str('Doe') # crée une instance (obj) de type 'str' et l'initialise avec la valeur 'Doe'
|
||||
adress = str() # crée une instance (obj) de type 'str'
|
||||
|
||||
# list, dict, tuple, set, bool => types primitives
|
||||
|
||||
class Fraction: # pour se differencier des types primitifs
|
||||
numerateur = 1
|
||||
denominateur = 1
|
||||
|
||||
def quotient(self): # self = tiers /quart
|
||||
return self.numerateur / self.denominateur
|
||||
|
||||
tiers = Fraction() # crée une instance (obj) de type 'Fraction'
|
||||
tiers.numerateur = 1
|
||||
tiers.denominateur = 3
|
||||
quart = Fraction()
|
||||
quart.numerateur = 1
|
||||
quart.denominateur = 4
|
||||
|
||||
print(tiers.__dict__)
|
||||
print(quart.__dict__)
|
||||
|
||||
# Mapping proxy
|
||||
# pprint(Fraction.__dict__)
|
||||
# pprint(tiers.__dict__)
|
||||
# print(tiers.denominateur)
|
||||
|
||||
# 2 points de vue: Fraction (classe) et tiers (objet)
|
||||
print(Fraction.quotient) # <function Fraction.dire_bonjour at 0x100eec720>
|
||||
print(tiers.quotient) # <bound method Fraction.dire_bonjour of <__main__.Fraction object at 0x1028937d0>>
|
||||
# <bound method Fraction.dire_bonjour of tiers>
|
||||
|
||||
print(tiers.quotient()) # TypeError: Fraction.dire_bonjour() takes 0 positional arguments but 1 was given
|
||||
# Les methodes lies aux objets ont besoin de savoir QUI les a appeler
|
||||
print(quart.quotient())
|
||||
@@ -0,0 +1,7 @@
|
||||
[project]
|
||||
name = "progorobj"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "progorobj"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user