{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-23-08-00",
   "metadata": {},
   "source": [
    "# 23-08 · Операции с Path\n",
    "\n",
    "Практика к разделу [«pathlib: работаем с путями и каталогами»](../../site/chapters/glava-23/23-07-pathlib.html). Настоящий файл — `projects/python/safesort/src/safesort/models.py`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-01",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Потренироваться в операциях над `Path` — оператор `/`, `.name`, `.suffix`, `.parent` — и собрать неизменяемую модель `FileInfo`, на которой строится вся модель данных SafeSort."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-02",
   "metadata": {},
   "source": [
    "## Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-08-03",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "koren = Path(\"/home/anna/Downloads\")\n",
    "fajl = koren / \"otchet.pdf\"\n",
    "\n",
    "print(fajl.name)\n",
    "print(fajl.suffix)\n",
    "print(fajl.parent)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-04",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-08-05",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert fajl.name == \"otchet.pdf\"\n",
    "assert fajl.suffix == \".pdf\"\n",
    "assert fajl.parent == koren\n",
    "assert (koren / \"podkatalog\" / \"vlozhennyj.txt\").parent.name == \"podkatalog\"\n",
    "print(\"Верно: оператор / и атрибуты name/suffix/parent работают как ожидается.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-06",
   "metadata": {},
   "source": [
    "## FileInfo — неизменяемая модель данных"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-08-07",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass, FrozenInstanceError\n",
    "\n",
    "\n",
    "@dataclass(frozen=True)\n",
    "class FileInfo:\n",
    "    path: Path\n",
    "    size: int\n",
    "    extension: str\n",
    "\n",
    "\n",
    "primer = FileInfo(path=fajl, size=204800, extension=fajl.suffix.lower())\n",
    "print(primer)\n",
    "\n",
    "try:\n",
    "    primer.size = 0\n",
    "except FrozenInstanceError as oshibka:\n",
    "    print(\"Изменить поле нельзя:\", oshibka)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-08",
   "metadata": {},
   "source": [
    "## Starter\n",
    "\n",
    "Заполните отмеченное место. Неизменённый starter не проходит tests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "task-23-08",
   "metadata": {
    "tags": [
     "exercise",
     "starter"
    ]
   },
   "outputs": [],
   "source": [
    "def opisat_fajl(path: Path, size: int) -> FileInfo:\n",
    "    # TODO: construct and return FileInfo.\n",
    "    raise NotImplementedError\n",
    "\n",
    "\n",
    "zadanie_info = opisat_fajl(koren / \"photo.JPG\", 2048)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-10",
   "metadata": {},
   "source": [
    "## Task\n",
    "\n",
    "Напишите `opisat_fajl(path, size)`, создающую `FileInfo` с lower-case suffix."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-11",
   "metadata": {},
   "source": [
    "## Tests\n",
    "\n",
    "Запустите после task cell: есть основной пример и хотя бы один крайний случай."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tests-23-08",
   "metadata": {
    "tags": [
     "exercise-tests"
    ]
   },
   "outputs": [],
   "source": [
    "assert zadanie_info.extension == \".jpg\"\n",
    "assert zadanie_info.size == 2048\n",
    "assert zadanie_info.path.name == \"photo.JPG\"\n",
    "assert opisat_fajl(Path(\"README\"), 10).extension == \"\"\n",
    "print(\"Tests passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-13",
   "metadata": {},
   "source": [
    "## Hint\n",
    "\n",
    "Расширение находится в `path.suffix`; примените `.lower()`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-08-14",
   "metadata": {},
   "source": [
    "## Solution\n",
    "\n",
    "<details><summary>Показать решение после собственной попытки</summary>\n",
    "\n",
    "```python\n",
    "def opisat_fajl(path: Path, size: int) -> FileInfo:\n",
    "    return FileInfo(path=path, size=size, extension=path.suffix.lower())\n",
    "\n",
    "\n",
    "zadanie_info = opisat_fajl(koren / \"photo.JPG\", 2048)\n",
    "```\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "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
}
