From c8d8754282c21eebbe3500236653282d4ac2dbfb Mon Sep 17 00:00:00 2001 From: DotNaos Date: Sun, 18 May 2025 19:31:58 +0200 Subject: [PATCH] Zoo Task finished --- .../Zoo_Task_CRUD_Operations.ipynb | 181 +++++++++++------- code/pyproject.toml | 1 + code/uv.lock | 57 ++++++ 3 files changed, 171 insertions(+), 68 deletions(-) diff --git a/code/online-part-2a/Zoo_Task_CRUD_Operations.ipynb b/code/online-part-2a/Zoo_Task_CRUD_Operations.ipynb index d135b35..5aee70e 100644 --- a/code/online-part-2a/Zoo_Task_CRUD_Operations.ipynb +++ b/code/online-part-2a/Zoo_Task_CRUD_Operations.ipynb @@ -13,36 +13,34 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "import psycopg2\n", - "import psycopg2.extras" + "import psycopg2.extras\n", + "import os\n" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "# Verbindung aufbauen\n", - "# TODO: hier müssen Ihre Verbindungsdaten eingetragen werden\n", - "# TODO: ggf. müssen Sie auch den Namen der Datenbank anpassen oder eine leere Datenbank 'zoo' anlegen\n", - "\n", "conn = psycopg2.connect(\n", - " host=\"localhost\",\n", - " port=5432,\n", - " dbname=\"postgres\",\n", - " user=\"postgres\",\n", - " password=\"postgres\",\n", + " host=os.getenv('PGHOST', 'localhost'),\n", + " port=int(os.getenv('PGPORT', 5432)),\n", + " dbname=os.getenv('PGDATABASE', 'zoo'),\n", + " user=os.getenv('PGUSER', 'postgres'),\n", + " password=os.getenv('PGPASSWORD', 'postgres'),\n", ")\n" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -75,66 +73,74 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ - "# TODO: Implementieren Sie hier die CRUD-Operationen für Zookeeper\n", - "\n", - "# TODO: Tierpleger anlegen, liefert die ID des neuen Tierpflegers zurück\n", + "# CRUD-Operationen für Zookeeper\n", "def create_zookeeper(name, email, specialty):\n", - " pass\n", + " with conn.cursor() as cur:\n", + " cur.execute(\"INSERT INTO zookeepers (name, email, specialty) VALUES (%s, %s, %s) RETURNING id\", (name, email, specialty))\n", + " new_id = cur.fetchone()[0]\n", + " conn.commit()\n", + " return new_id\n", "\n", - "# TODO: Tierpfleger nach ID lesen, Tupel zurückgeben\n", "def read_zookeeper(id):\n", - " pass\n", + " with conn.cursor() as cur:\n", + " cur.execute(\"SELECT id, name, email, specialty FROM zookeepers WHERE id = %s\", (id,))\n", + " row = cur.fetchone()\n", + " return row\n", "\n", - "# TODO: Alle Tierpfleger lesen, liefert eine Liste von Tupeln zurück (nach ID sortiert)\n", "def read_all_zookeepers():\n", - " pass\n", + " with conn.cursor() as cur:\n", + " cur.execute(\"SELECT id, name, email, specialty FROM zookeepers ORDER BY id\")\n", + " rows = cur.fetchall()\n", + " return rows\n", "\n", - "# TODO: Tierpfleger aktualisieren\n", "def update_zookeeper(id, name, email, specialty):\n", - " pass\n", + " with conn.cursor() as cur:\n", + " cur.execute(\"UPDATE zookeepers SET name = %s, email = %s, specialty = %s WHERE id = %s\", (name, email, specialty, id))\n", + " conn.commit()\n", "\n", - "# TODO: Tierpfleger per ID löschen\n", "def delete_zookeeper(id):\n", - " pass" + " with conn.cursor() as cur:\n", + " cur.execute(\"DELETE FROM zookeepers WHERE id = %s\", (id,))\n", + " conn.commit()\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "# Tests - diese sollten alle erfolgreich durchlaufen werden und nicht verändert werden\n", "\n", - "# john = (\"John Doe\", \"john@example.com\", \"Elephants\")\n", - "# jane = (\"Jane Doe\", \"jane@example.com\", \"Giraffes\")\n", + "john = (\"John Doe\", \"john@example.com\", \"Elephants\")\n", + "jane = (\"Jane Doe\", \"jane@example.com\", \"Giraffes\")\n", "\n", - "# id = create_zookeeper(*john)\n", - "# id2 = create_zookeeper(*jane)\n", + "id = create_zookeeper(*john)\n", + "id2 = create_zookeeper(*jane)\n", "\n", - "# assert read_zookeeper(id) == (id, *john)\n", - "# assert read_zookeeper(id2) == (id2, *jane)\n", + "assert read_zookeeper(id) == (id, *john)\n", + "assert read_zookeeper(id2) == (id2, *jane)\n", "\n", - "# john = (\"John Smith\", \"john2@example.com\", \"Zebras\")\n", - "# update_zookeeper(id, *john)\n", - "# assert read_zookeeper(id) == (id, *john)\n", + "john = (\"John Smith\", \"john2@example.com\", \"Zebras\")\n", + "update_zookeeper(id, *john)\n", + "assert read_zookeeper(id) == (id, *john)\n", "\n", - "# all_zookeepers = read_all_zookeepers()\n", - "# assert len(all_zookeepers) == 2\n", - "# assert all_zookeepers[0] == (id, *john)\n", - "# assert all_zookeepers[1] == (id2, *jane)\n", + "all_zookeepers = read_all_zookeepers()\n", + "assert len(all_zookeepers) == 2\n", + "assert all_zookeepers[0] == (id, *john)\n", + "assert all_zookeepers[1] == (id2, *jane)\n", "\n", - "# delete_zookeeper(id)\n", - "# delete_zookeeper(id2)\n", - "# assert read_zookeeper(id) == None\n", - "# assert read_zookeeper(id2) == None\n", + "delete_zookeeper(id)\n", + "delete_zookeeper(id2)\n", + "assert read_zookeeper(id) == None\n", + "assert read_zookeeper(id2) == None\n", "\n", - "# all_zookeepers = read_all_zookeepers()\n", - "# assert len(all_zookeepers) == 0" + "all_zookeepers = read_all_zookeepers()\n", + "assert len(all_zookeepers) == 0" ] }, { @@ -152,11 +158,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "## Nutzung von SQLAlchemy\n", + "import os\n", "from sqlalchemy import create_engine, Column, Integer, String, ForeignKey\n", "from sqlalchemy.orm import Session, relationship, declarative_base\n", "\n", @@ -178,8 +185,8 @@ " zookeeper_id = Column(Integer, ForeignKey('zookeepers.id'))\n", " zookeeper = relationship(\"Zookeeper\", back_populates=\"animals\")\n", "\n", - "# TODO: Verbingung zur Datenbank herstellen\n", - "engine = create_engine('postgresql://IHR_POSTGRES_USER:IHR_POSTGRES_PASSWORD/zoo')" + "# Verbindung zur Datenbank herstellen\n", + "engine = create_engine(os.getenv('DATABASE_URL', 'postgresql://postgres:postgres@localhost:5432/zoo'))" ] }, { @@ -203,56 +210,94 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ - "# TODO: zwei Zookeeper anlegen\n", + "# Zwei Zookeeper anlegen\n", "with Session(engine) as session:\n", - " john = ...\n", - " jane = ...\n", - "\n", - " session.add(...)\n", - "\n", + " john = Zookeeper(name='John Doe', email='john@example.com', specialty='Elephants')\n", + " jane = Zookeeper(name='Jane Doe', email='jane@example.com', specialty='Giraffes')\n", + " session.add_all([john, jane])\n", " session.commit()\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 John Doe john@example.com Elephants\n", + "4 Jane Doe jane@example.com Giraffes\n" + ] + } + ], "source": [ - "# TODO: Alle Zookeeper ausgeben\n", + "# Alle Zookeeper ausgeben\n", "with Session(engine) as session:\n", - " ..." + " for zk in session.query(Zookeeper).order_by(Zookeeper.id).all():\n", + " print(zk.id, zk.name, zk.email, zk.specialty)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ - "# TODO: 3 Elefanten anlegen, die John betreut\n", + "# 3 Elefanten anlegen, die John betreut\n", "with Session(engine) as session:\n", - " ...\n", + " john = session.query(Zookeeper).filter_by(name='John Doe').one()\n", + " elephants = [\n", + " Animal(name='Babar', species='Elephant', zookeeper=john),\n", + " Animal(name='Dumbo', species='Elephant', zookeeper=john),\n", + " Animal(name='Hathi', species='Elephant', zookeeper=john),\n", + " ]\n", + " session.add_all(elephants)\n", + " session.commit()\n", "\n", - "# TODO: 2 Giraffen anlegen, die Jane betreut\n", + "# 2 Giraffen anlegen, die Jane betreut\n", "with Session(engine) as session:\n", - " ..." + " jane = session.query(Zookeeper).filter_by(name='Jane Doe').one()\n", + " giraffes = [\n", + " Animal(name='Melman', species='Giraffe', zookeeper=jane),\n", + " Animal(name='Gloria', species='Giraffe', zookeeper=jane),\n", + " ]\n", + " session.add_all(giraffes)\n", + " session.commit()\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "----------------------------------------\n", + "| Babar | Elephant | John Doe |\n", + "| Dumbo | Elephant | John Doe |\n", + "| Hathi | Elephant | John Doe |\n", + "| Melman | Giraffe | Jane Doe |\n", + "| Gloria | Giraffe | Jane Doe |\n", + "----------------------------------------\n" + ] + } + ], "source": [ "# Liste aller Tiere mit ihren Pflegern ausgeben:\n", "with Session(engine) as session:\n", - " ...\n", - "\n", + " animals = session.query(Animal).order_by(Animal.id).all()\n", + " print('-' * 40)\n", + " for animal in animals:\n", + " print(f\"| {animal.name:<10} | {animal.species:<10} | {animal.zookeeper.name:<10} |\")\n", + " print('-' * 40)\n", "# Beispielausgabe:\n", "#\n", "# ----------------------------------------\n", diff --git a/code/pyproject.toml b/code/pyproject.toml index 6c5c86b..f60ff27 100644 --- a/code/pyproject.toml +++ b/code/pyproject.toml @@ -9,4 +9,5 @@ dependencies = [ "pandas>=2.2.3", "psycopg2>=2.9.10", "pymongo>=4.12.1", + "sqlalchemy>=2.0.41", ] diff --git a/code/uv.lock b/code/uv.lock index 1537fef..33edc3c 100644 --- a/code/uv.lock +++ b/code/uv.lock @@ -53,6 +53,7 @@ dependencies = [ { name = "pandas" }, { name = "psycopg2" }, { name = "pymongo" }, + { name = "sqlalchemy" }, ] [package.metadata] @@ -63,6 +64,7 @@ requires-dist = [ { name = "pandas", specifier = ">=2.2.3" }, { name = "psycopg2", specifier = ">=2.9.10" }, { name = "pymongo", specifier = ">=4.12.1" }, + { name = "sqlalchemy", specifier = ">=2.0.41" }, ] [[package]] @@ -183,6 +185,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9b/1f/4417c26e26a1feab85a27e927f7a73d8aabc84544be8ba108ce4aa90eb1e/fonttools-4.58.0-py3-none-any.whl", hash = "sha256:c96c36880be2268be409df7b08c5b5dacac1827083461a6bc2cb07b8cbcec1d7", size = 1111440 }, ] +[[package]] +name = "greenlet" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/c1/a82edae11d46c0d83481aacaa1e578fea21d94a1ef400afd734d47ad95ad/greenlet-3.2.2.tar.gz", hash = "sha256:ad053d34421a2debba45aa3cc39acf454acbcd025b3fc1a9f8a0dee237abd485", size = 185797 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/30/97b49779fff8601af20972a62cc4af0c497c1504dfbb3e93be218e093f21/greenlet-3.2.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:3ab7194ee290302ca15449f601036007873028712e92ca15fc76597a0aeb4c59", size = 269150 }, + { url = "https://files.pythonhosted.org/packages/21/30/877245def4220f684bc2e01df1c2e782c164e84b32e07373992f14a2d107/greenlet-3.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc5c43bb65ec3669452af0ab10729e8fdc17f87a1f2ad7ec65d4aaaefabf6bf", size = 637381 }, + { url = "https://files.pythonhosted.org/packages/8e/16/adf937908e1f913856b5371c1d8bdaef5f58f251d714085abeea73ecc471/greenlet-3.2.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:decb0658ec19e5c1f519faa9a160c0fc85a41a7e6654b3ce1b44b939f8bf1325", size = 651427 }, + { url = "https://files.pythonhosted.org/packages/ad/49/6d79f58fa695b618654adac64e56aff2eeb13344dc28259af8f505662bb1/greenlet-3.2.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fadd183186db360b61cb34e81117a096bff91c072929cd1b529eb20dd46e6c5", size = 645795 }, + { url = "https://files.pythonhosted.org/packages/5a/e6/28ed5cb929c6b2f001e96b1d0698c622976cd8f1e41fe7ebc047fa7c6dd4/greenlet-3.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1919cbdc1c53ef739c94cf2985056bcc0838c1f217b57647cbf4578576c63825", size = 648398 }, + { url = "https://files.pythonhosted.org/packages/9d/70/b200194e25ae86bc57077f695b6cc47ee3118becf54130c5514456cf8dac/greenlet-3.2.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3885f85b61798f4192d544aac7b25a04ece5fe2704670b4ab73c2d2c14ab740d", size = 606795 }, + { url = "https://files.pythonhosted.org/packages/f8/c8/ba1def67513a941154ed8f9477ae6e5a03f645be6b507d3930f72ed508d3/greenlet-3.2.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:85f3e248507125bf4af607a26fd6cb8578776197bd4b66e35229cdf5acf1dfbf", size = 1117976 }, + { url = "https://files.pythonhosted.org/packages/c3/30/d0e88c1cfcc1b3331d63c2b54a0a3a4a950ef202fb8b92e772ca714a9221/greenlet-3.2.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1e76106b6fc55fa3d6fe1c527f95ee65e324a13b62e243f77b48317346559708", size = 1145509 }, + { url = "https://files.pythonhosted.org/packages/90/2e/59d6491834b6e289051b252cf4776d16da51c7c6ca6a87ff97e3a50aa0cd/greenlet-3.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:fe46d4f8e94e637634d54477b0cfabcf93c53f29eedcbdeecaf2af32029b4421", size = 296023 }, + { url = "https://files.pythonhosted.org/packages/65/66/8a73aace5a5335a1cba56d0da71b7bd93e450f17d372c5b7c5fa547557e9/greenlet-3.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba30e88607fb6990544d84caf3c706c4b48f629e18853fc6a646f82db9629418", size = 629911 }, + { url = "https://files.pythonhosted.org/packages/48/08/c8b8ebac4e0c95dcc68ec99198842e7db53eda4ab3fb0a4e785690883991/greenlet-3.2.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:055916fafad3e3388d27dd68517478933a97edc2fc54ae79d3bec827de2c64c4", size = 635251 }, + { url = "https://files.pythonhosted.org/packages/37/26/7db30868f73e86b9125264d2959acabea132b444b88185ba5c462cb8e571/greenlet-3.2.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2593283bf81ca37d27d110956b79e8723f9aa50c4bcdc29d3c0543d4743d2763", size = 632620 }, + { url = "https://files.pythonhosted.org/packages/10/ec/718a3bd56249e729016b0b69bee4adea0dfccf6ca43d147ef3b21edbca16/greenlet-3.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89c69e9a10670eb7a66b8cef6354c24671ba241f46152dd3eed447f79c29fb5b", size = 628851 }, + { url = "https://files.pythonhosted.org/packages/9b/9d/d1c79286a76bc62ccdc1387291464af16a4204ea717f24e77b0acd623b99/greenlet-3.2.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a98600899ca1ca5d3a2590974c9e3ec259503b2d6ba6527605fcd74e08e207", size = 593718 }, + { url = "https://files.pythonhosted.org/packages/cd/41/96ba2bf948f67b245784cd294b84e3d17933597dffd3acdb367a210d1949/greenlet-3.2.2-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b50a8c5c162469c3209e5ec92ee4f95c8231b11db6a04db09bbe338176723bb8", size = 1105752 }, + { url = "https://files.pythonhosted.org/packages/68/3b/3b97f9d33c1f2eb081759da62bd6162159db260f602f048bc2f36b4c453e/greenlet-3.2.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:45f9f4853fb4cc46783085261c9ec4706628f3b57de3e68bae03e8f8b3c0de51", size = 1125170 }, + { url = "https://files.pythonhosted.org/packages/31/df/b7d17d66c8d0f578d2885a3d8f565e9e4725eacc9d3fdc946d0031c055c4/greenlet-3.2.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:9ea5231428af34226c05f927e16fc7f6fa5e39e3ad3cd24ffa48ba53a47f4240", size = 269899 }, +] + [[package]] name = "ipykernel" version = "6.29.5" @@ -674,6 +701,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] +[[package]] +name = "sqlalchemy" +version = "2.0.41" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/66/45b165c595ec89aa7dcc2c1cd222ab269bc753f1fc7a1e68f8481bd957bf/sqlalchemy-2.0.41.tar.gz", hash = "sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9", size = 9689424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/ad/2e1c6d4f235a97eeef52d0200d8ddda16f6c4dd70ae5ad88c46963440480/sqlalchemy-2.0.41-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443", size = 2115491 }, + { url = "https://files.pythonhosted.org/packages/cf/8d/be490e5db8400dacc89056f78a52d44b04fbf75e8439569d5b879623a53b/sqlalchemy-2.0.41-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc", size = 2102827 }, + { url = "https://files.pythonhosted.org/packages/a0/72/c97ad430f0b0e78efaf2791342e13ffeafcbb3c06242f01a3bb8fe44f65d/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1", size = 3225224 }, + { url = "https://files.pythonhosted.org/packages/5e/51/5ba9ea3246ea068630acf35a6ba0d181e99f1af1afd17e159eac7e8bc2b8/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a", size = 3230045 }, + { url = "https://files.pythonhosted.org/packages/78/2f/8c14443b2acea700c62f9b4a8bad9e49fc1b65cfb260edead71fd38e9f19/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d", size = 3159357 }, + { url = "https://files.pythonhosted.org/packages/fc/b2/43eacbf6ccc5276d76cea18cb7c3d73e294d6fb21f9ff8b4eef9b42bbfd5/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23", size = 3197511 }, + { url = "https://files.pythonhosted.org/packages/fa/2e/677c17c5d6a004c3c45334ab1dbe7b7deb834430b282b8a0f75ae220c8eb/sqlalchemy-2.0.41-cp313-cp313-win32.whl", hash = "sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f", size = 2082420 }, + { url = "https://files.pythonhosted.org/packages/e9/61/e8c1b9b6307c57157d328dd8b8348ddc4c47ffdf1279365a13b2b98b8049/sqlalchemy-2.0.41-cp313-cp313-win_amd64.whl", hash = "sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df", size = 2108329 }, + { url = "https://files.pythonhosted.org/packages/1c/fc/9ba22f01b5cdacc8f5ed0d22304718d2c758fce3fd49a5372b886a86f37c/sqlalchemy-2.0.41-py3-none-any.whl", hash = "sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576", size = 1911224 }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -715,6 +763,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, ] +[[package]] +name = "typing-extensions" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, +] + [[package]] name = "tzdata" version = "2025.2"