{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d3c30378",
   "metadata": {},
   "source": [
    "# 22-05 · Flask: trasy, wzory i kształty\n",
    "\n",
    "Praktyka do sekcji [„Pierwsza aplikacja webowa na Flask” `projects/flask/todo-app/app.py`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4d697a7",
   "metadata": {},
   "source": [
    "## O kliencie testowym w tym laptopie\n",
    "\n",
    "Zazwyczaj aplikacja Flask-uruchamiana jest za pomocą polecenia `python app.py`i czeka na żądania przeglądarki w nieskończoność (`app.run()`). W automatycznie wykonywanym notebooku nie ma przeglądarki i nie ma sensu czekać wiecznie, dlatego używamy `app.test_client()`. Konstruuje żądanie w procesie i przekazuje je aplikacji przez interfejs testowy Flask/Werkzeug, bez otwierania portu sieciowego lub uruchamiania prawdziwego serwera — ta sama logika przetwarzania żądań co w rzeczywistej aplikacji, ale bez sieci (sekcja 22.33 strony omawia tę różnicę bardziej szczegółowo)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f49a328",
   "metadata": {},
   "source": [
    "## Praktyczny przykład — składamy małą aplikację"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d74c4157",
   "metadata": {},
   "outputs": [],
   "source": [
    "from flask import Flask, redirect, render_template_string, request, url_for\n",
    "\n",
    "app = Flask(__name__)\n",
    "zadachi = [\"Выучить основы Python\", \"Собрать сайт на Flask\"]\n",
    "\n",
    "SHABLON_GLAVNOJ = \"\"\"\n",
    "<h1>Мой список задач</h1>\n",
    "<ul>\n",
    "{% for zadacha in zadachi %}\n",
    "  <li>{{ zadacha }}</li>\n",
    "{% endfor %}\n",
    "</ul>\n",
    "\"\"\"\n",
    "\n",
    "SHABLON_PRIVET = \"<h1>Привет, {{ imya }}!</h1>\"\n",
    "\n",
    "\n",
    "@app.route(\"/\")\n",
    "def glavnaya():\n",
    "    return render_template_string(SHABLON_GLAVNOJ, zadachi=zadachi)\n",
    "\n",
    "\n",
    "@app.route(\"/privet/<imya>\")\n",
    "def privet(imya):\n",
    "    return render_template_string(SHABLON_PRIVET, imya=imya)\n",
    "\n",
    "\n",
    "@app.route(\"/dobavit\", methods=[\"POST\"])\n",
    "def dobavit():\n",
    "    novaya_zadacha = request.form.get(\"zadacha\", \"\").strip()\n",
    "    if novaya_zadacha:\n",
    "        zadachi.append(novaya_zadacha)\n",
    "    return redirect(url_for(\"glavnaya\"))\n",
    "\n",
    "\n",
    "client = app.test_client()\n",
    "print(\"Приложение и тестовый клиент готовы.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f525b211",
   "metadata": {},
   "source": [
    "## Eksperyment 1 - Strona główna (GET /)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75757778",
   "metadata": {},
   "outputs": [],
   "source": [
    "otvet = client.get(\"/\")\n",
    "telo = otvet.get_data(as_text=True)\n",
    "\n",
    "print(\"Код ответа:\", otvet.status_code)\n",
    "print(telo)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c2fbdb3",
   "metadata": {},
   "source": [
    "## Sprawdzenie wyniku"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79c2cb94",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert otvet.status_code == 200\n",
    "assert \"Выучить основы Python\" in telo\n",
    "assert \"Собрать сайт на Flask\" in telo\n",
    "print(\"Верно: главная страница отдаёт список задач с кодом 200.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87f33b0a",
   "metadata": {},
   "source": [
    "## Eksperyment 2 - Dynamiczna trasa /privet/<imya>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ecbbef3",
   "metadata": {},
   "outputs": [],
   "source": [
    "otvet2 = client.get(\"/privet/Ада\")\n",
    "telo2 = otvet2.get_data(as_text=True)\n",
    "\n",
    "print(telo2)\n",
    "assert \"Привет, Ада!\" in telo2\n",
    "print(\"Верно: значение из адреса подставилось в шаблон.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5428266a",
   "metadata": {},
   "source": [
    "## Eksperyment 3 — wysyłanie formularza (POST /dobavit)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c4bc2ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "kolichestvo_do = len(zadachi)\n",
    "\n",
    "otvet3 = client.post(\"/dobavit\", data={\"zadacha\": \"Прочитать книгу\"})\n",
    "print(\"Код ответа:\", otvet3.status_code)   # 302 — редирект на главную\n",
    "print(\"Задач было:\", kolichestvo_do, \"-> стало:\", len(zadachi))\n",
    "\n",
    "assert otvet3.status_code == 302\n",
    "assert len(zadachi) == kolichestvo_do + 1\n",
    "assert \"Прочитать книгу\" in zadachi\n",
    "print(\"Верно: POST-запрос добавил новую задачу и вернул редирект.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc1ab4f4",
   "metadata": {},
   "source": [
    "## Zadanie niezależne od zadania ★★\n",
    "\n",
    "Złóż formularz z pustym zadaniem (`{\"zadacha\": \"   \"}`) i upewnić się, że zadanie lista się nie zmieniło, tak jak teraz `app.py`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e2117b31",
   "metadata": {},
   "outputs": [],
   "source": [
    "kolichestvo_do2 = len(zadachi)\n",
    "client.post(\"/dobavit\", data={\"zadacha\": \"   \"})\n",
    "\n",
    "assert len(zadachi) == kolichestvo_do2\n",
    "print(\"Верно: пустая (только пробелы) задача не была добавлена.\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
