{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Schema Conversion: JSON Schema ↔ Pydantic\n",
    "\n",
    "Convert between JSON Schema and Pydantic models: generate models from schemas (`from_dict`, `from_file`, `from_json`) and export schemas from models (`to_dict`, `to_file`, `to_json`).\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/optophi/pycharter/blob/main/docs/notebooks/06_schema_conversion.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. JSON Schema → Pydantic Model\n",
    "\n",
    "Generate a Pydantic model from a JSON Schema dict."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pycharter import from_dict\n",
    "\n",
    "schema = {\n",
    "    \"type\": \"object\",\n",
    "    \"version\": \"1.0.0\",\n",
    "    \"properties\": {\n",
    "        \"id\": {\"type\": \"string\"},\n",
    "        \"name\": {\"type\": \"string\", \"minLength\": 1},\n",
    "        \"email\": {\"type\": \"string\", \"format\": \"email\"},\n",
    "        \"age\": {\"type\": \"integer\", \"minimum\": 0, \"maximum\": 150},\n",
    "        \"tags\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}},\n",
    "    },\n",
    "    \"required\": [\"id\", \"name\", \"email\"],\n",
    "}\n",
    "\n",
    "User = from_dict(schema, \"User\")\n",
    "user = User(id=\"user-123\", name=\"Alice\", email=\"alice@example.com\", age=30, tags=[\"admin\", \"active\"])\n",
    "print(\"Model:\", User.__name__)\n",
    "print(\"Instance:\", user.name, user.age)\n",
    "print(\"Fields:\", list(User.model_fields.keys()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. From Various Sources\n",
    "\n",
    "Generate models from dict, JSON string, or file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pycharter import from_dict, from_json\n",
    "\n",
    "Model1 = from_dict({\"type\": \"object\", \"version\": \"1.0.0\", \"properties\": {\"id\": {\"type\": \"string\"}}}, \"FromDict\")\n",
    "print(\"from_dict:\", Model1.__name__)\n",
    "\n",
    "json_str = '{\"type\": \"object\", \"version\": \"1.0.0\", \"properties\": {\"name\": {\"type\": \"string\"}}}'\n",
    "Model2 = from_json(json_str, \"FromJson\")\n",
    "print(\"from_json:\", Model2.__name__)\n",
    "\n",
    "# from_file('schema.yaml', 'MyModel') or from_file('schema.json', 'MyModel')\n",
    "print(\"from_file: use for YAML/JSON files\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Pydantic Model → JSON Schema\n",
    "\n",
    "Convert a Pydantic model to a JSON Schema dict or string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List, Optional\n",
    "from pydantic import BaseModel, Field\n",
    "from pycharter import to_dict, to_json\n",
    "\n",
    "class Product(BaseModel):\n",
    "    product_id: str\n",
    "    name: str = Field(..., min_length=1)\n",
    "    price: float = Field(..., ge=0)\n",
    "    in_stock: bool = True\n",
    "    tags: Optional[List[str]] = None\n",
    "\n",
    "schema = to_dict(Product)\n",
    "print(\"Schema type:\", schema.get(\"type\"))\n",
    "print(\"Properties:\", list(schema.get(\"properties\", {}).keys()))\n",
    "print(\"Required:\", schema.get(\"required\"))\n",
    "\n",
    "json_str = to_json(Product)\n",
    "print(\"to_json length:\", len(json_str))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Nested Objects\n",
    "\n",
    "Handle nested objects and arrays of objects in conversion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "schema = {\n",
    "    \"type\": \"object\",\n",
    "    \"version\": \"1.0.0\",\n",
    "    \"properties\": {\n",
    "        \"id\": {\"type\": \"string\"},\n",
    "        \"address\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\"street\": {\"type\": \"string\"}, \"city\": {\"type\": \"string\"}, \"zip\": {\"type\": \"string\"}},\n",
    "            \"required\": [\"city\"],\n",
    "        },\n",
    "        \"contacts\": {\n",
    "            \"type\": \"array\",\n",
    "            \"items\": {\"type\": \"object\", \"properties\": {\"type\": {\"type\": \"string\"}, \"value\": {\"type\": \"string\"}}},\n",
    "        },\n",
    "    },\n",
    "}\n",
    "\n",
    "Person = from_dict(schema, \"Person\")\n",
    "person = Person(\n",
    "    id=\"p-1\",\n",
    "    address={\"street\": \"123 Main\", \"city\": \"NYC\", \"zip\": \"10001\"},\n",
    "    contacts=[{\"type\": \"email\", \"value\": \"a@b.com\"}],\n",
    ")\n",
    "print(\"Nested address:\", person.address)\n",
    "print(\"Contacts:\", person.contacts)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Round-Trip: Pydantic → Schema → Pydantic\n",
    "\n",
    "Convert a model to schema and back to a new model; both accept the same data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Original(BaseModel):\n",
    "    name: str = Field(..., min_length=1)\n",
    "    value: int = Field(..., ge=0)\n",
    "\n",
    "schema = to_dict(Original)\n",
    "schema[\"version\"] = \"1.0.0\"  # pycharter expects version for round-trip\n",
    "Regenerated = from_dict(schema, \"Regenerated\")\n",
    "\n",
    "data = {\"name\": \"test\", \"value\": 42}\n",
    "orig = Original(**data)\n",
    "regen = Regenerated(**data)\n",
    "print(\"Original:\", orig.name)\n",
    "print(\"Regenerated:\", regen.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Complex Types (format, enum)\n",
    "\n",
    "Supported formats: `uuid`, `email`, `date-time`, `uri`; plus `enum` for restricted values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "schema = {\n",
    "    \"type\": \"object\",\n",
    "    \"version\": \"1.0.0\",\n",
    "    \"properties\": {\n",
    "        \"id\": {\"type\": \"string\", \"format\": \"uuid\"},\n",
    "        \"email\": {\"type\": \"string\", \"format\": \"email\"},\n",
    "        \"created_at\": {\"type\": \"string\", \"format\": \"date-time\"},\n",
    "        \"website\": {\"type\": \"string\", \"format\": \"uri\"},\n",
    "        \"status\": {\"type\": \"string\", \"enum\": [\"active\", \"inactive\", \"pending\"]},\n",
    "    },\n",
    "}\n",
    "\n",
    "Record = from_dict(schema, \"Record\")\n",
    "print(\"Record model fields:\", list(Record.model_fields.keys()))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
