{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Getting Airport and Geographic Information Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully saved 20 documents to ../data/examples/airports_and_geographic_information/airports_and_geographic_information.json\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from pymongo import MongoClient\n",
    "from bson.json_util import dumps\n",
    "\n",
    "# 1. Setup Connection\n",
    "client = MongoClient(\"mongodb+srv://ioc_de_ete:RjL7xac3g2U6RULZ@ioc-de-ete.oijg8.mongodb.net/?authMechanism=SCRAM-SHA-1&tls=true\") # Replace with your connection string\n",
    "db = client['external']\n",
    "collection = db['replica_nlo_airports_and_geographic_information_serve']\n",
    "\n",
    "# 2. Query the data (Get first 20)\n",
    "# We use list() to pull the data from the cursor immediately\n",
    "cursor = collection.find().limit(20)\n",
    "documents_list = list(cursor)\n",
    "\n",
    "# 3. Save to JSON file\n",
    "output_filename = '../data/examples/airports_and_geographic_information/airports_and_geographic_information.json'\n",
    "\n",
    "# OPTION A: Using bson.json_util (Recommended)\n",
    "# This handles ObjectId and Datetime fields automatically\n",
    "with open(output_filename, 'w') as json_file:\n",
    "    json_file.write(dumps(documents_list, indent=4))\n",
    "\n",
    "print(f\"Successfully saved {len(documents_list)} documents to {output_filename}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Complete PyCharter Workflow: Airports and Geographic Information\n",
    "\n",
    "This notebook demonstrates the complete workflow for Airports and Geographic Information:\n",
    "1. Convert Pydantic model to JSON Schema\n",
    "2. Parse contract YAML and decompose into components\n",
    "3. Store all components in PostgreSQL metadata store\n",
    "4. Extract from store and reconstruct contract\n",
    "5. Generate Pydantic model from reconstructed contract\n",
    "\n",
    "## Prerequisites\n",
    "\n",
    "Before running this notebook, ensure you have:\n",
    "1. Initialized the database: `pycharter db init postgresql://user:pass@localhost/dbname`\n",
    "2. (Optional) Seeded initial data: `pycharter db seed data/seed postgresql://user:pass@localhost/dbname`\n",
    "3. PostgreSQL running and accessible"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✓ Loaded 20 records from JSON file\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "import os\n",
    "from typing import Optional, Dict, Any, List\n",
    "from datetime import datetime\n",
    "import sys\n",
    "\n",
    "from pydantic import BaseModel\n",
    "\n",
    "from pycharter import (\n",
    "    # Service 1: Contract Parser\n",
    "    parse_contract_file,\n",
    "    ContractMetadata,\n",
    "    # Service 2: Metadata Store\n",
    "    PostgresMetadataStore,\n",
    "    # Service 3: Pydantic Generator\n",
    "    from_dict,\n",
    "    # Service 4: JSON Schema Converter\n",
    "    to_dict,\n",
    "    # Service 5: Runtime Validator\n",
    "    get_model_from_contract,\n",
    "    # Service 6: Contract Builder\n",
    "    build_contract_from_store,\n",
    ")\n",
    "\n",
    "# Setup paths\n",
    "ROOT_DIR = os.path.join('..')\n",
    "DATA_DIR = os.path.join(ROOT_DIR, 'data')\n",
    "EXAMPLES_DIR = os.path.join(DATA_DIR, 'examples')\n",
    "AIRPORTS_DIR = os.path.join(EXAMPLES_DIR, 'airports_and_geographic_information')\n",
    "AIRPORTS_CONTRACT = os.path.join(AIRPORTS_DIR, 'airports_and_geographic_information_contract.yaml')\n",
    "AIRPORTS_JSON = os.path.join(AIRPORTS_DIR, 'airports_and_geographic_information.json')\n",
    "\n",
    "# Add paths for importing airports_and_geographic_information models\n",
    "sys.path.append(ROOT_DIR)\n",
    "sys.path.append(AIRPORTS_DIR)\n",
    "\n",
    "# Import AirportsAndGeographicInformation model\n",
    "from airport_and_geographic_information_models import AirportsAndGeographicInformation\n",
    "\n",
    "# Helper function to convert MongoDB-style JSON to Python objects\n",
    "def convert_mongodb_json(obj):\n",
    "    \"\"\"Convert MongoDB-style JSON ($oid, $date) to Python objects.\"\"\"\n",
    "    if isinstance(obj, dict):\n",
    "        if '$oid' in obj:\n",
    "            return obj['$oid']  # Return as string, or convert to ObjectId if needed\n",
    "        elif '$date' in obj:\n",
    "            # Convert ISO date string to datetime\n",
    "            date_str = obj['$date']\n",
    "            if isinstance(date_str, str):\n",
    "                # Handle ISO format: \"2014-01-01T00:00:00Z\"\n",
    "                try:\n",
    "                    return datetime.fromisoformat(date_str.replace('Z', '+00:00'))\n",
    "                except:\n",
    "                    return date_str\n",
    "            return date_str\n",
    "        else:\n",
    "            return {k: convert_mongodb_json(v) for k, v in obj.items()}\n",
    "    elif isinstance(obj, list):\n",
    "        return [convert_mongodb_json(item) for item in obj]\n",
    "    else:\n",
    "        return obj\n",
    "\n",
    "# Load sample data from JSON file\n",
    "json_data = None\n",
    "if os.path.exists(AIRPORTS_JSON):\n",
    "    with open(AIRPORTS_JSON, 'r') as f:\n",
    "        raw_data = json.load(f)\n",
    "        # Convert MongoDB-style JSON to Python objects\n",
    "        json_data = convert_mongodb_json(raw_data)\n",
    "    print(f\"✓ Loaded {len(json_data)} records from JSON file\")\n",
    "else:\n",
    "    print(\"⚠ JSON file not found, will use sample data from model\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "======================================================================\n",
      "Step 1: Convert Pydantic Model to JSON Schema\n",
      "======================================================================\n",
      "✓ Converted AirportsAndGeographicInformation model to JSON Schema\n",
      "  Schema title: AirportsAndGeographicInformation\n",
      "  Schema version: 1.0.0\n",
      "  Number of properties: 28\n"
     ]
    }
   ],
   "source": [
    "# Step 1: Convert Pydantic Model to JSON Schema\n",
    "print(\"=\" * 70)\n",
    "print(\"Step 1: Convert Pydantic Model to JSON Schema\")\n",
    "print(\"=\" * 70)\n",
    "\n",
    "# Convert AirportsAndGeographicInformation Pydantic model to JSON Schema\n",
    "airports_schema = to_dict(AirportsAndGeographicInformation)\n",
    "print(f\"✓ Converted AirportsAndGeographicInformation model to JSON Schema\")\n",
    "print(f\"  Schema title: {airports_schema.get('title')}\")\n",
    "print(f\"  Schema version: {airports_schema.get('version', 'N/A')}\")\n",
    "print(f\"  Number of properties: {len(airports_schema.get('properties', {}))}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "======================================================================\n",
      "Step 2: Parse Contract YAML and Decompose into Components\n",
      "======================================================================\n",
      "✓ Parsed contract file: ../data/examples/airports_and_geographic_information/airports_and_geographic_information_contract.yaml\n",
      "  Schema version: 1.0.0\n",
      "  Metadata version: 1.0.0\n",
      "  Ownership: operations-team / data-engineering\n",
      "  Coercion rules: 24 fields\n",
      "  Validation rules: 13 fields\n",
      "  Governance rules: Yes\n"
     ]
    }
   ],
   "source": [
    "# Step 2: Parse Contract YAML and Decompose into Components\n",
    "print(\"\\n\" + \"=\" * 70)\n",
    "print(\"Step 2: Parse Contract YAML and Decompose into Components\")\n",
    "print(\"=\" * 70)\n",
    "\n",
    "# Parse the contract file\n",
    "contract_metadata = parse_contract_file(AIRPORTS_CONTRACT)\n",
    "print(f\"✓ Parsed contract file: {AIRPORTS_CONTRACT}\")\n",
    "\n",
    "# Extract components\n",
    "schema = contract_metadata.schema\n",
    "metadata = contract_metadata.metadata\n",
    "ownership = contract_metadata.ownership\n",
    "governance_rules = contract_metadata.governance_rules\n",
    "versions = contract_metadata.versions\n",
    "\n",
    "print(f\"  Schema version: {versions.get('schema', 'N/A')}\")\n",
    "print(f\"  Metadata version: {versions.get('metadata', 'N/A')}\")\n",
    "print(f\"  Ownership: {ownership.get('owner', 'N/A')} / {ownership.get('team', 'N/A')}\")\n",
    "\n",
    "# Extract coercion and validation rules from schema properties\n",
    "# (They're embedded in the schema properties in the contract YAML)\n",
    "coercion_rules = {}\n",
    "validation_rules = {}\n",
    "\n",
    "if \"properties\" in schema:\n",
    "    for field_name, field_schema in schema[\"properties\"].items():\n",
    "        if isinstance(field_schema, dict):\n",
    "            # Extract coercion rule\n",
    "            if \"coercion\" in field_schema:\n",
    "                coercion_rules[field_name] = field_schema[\"coercion\"]\n",
    "            \n",
    "            # Extract validation rules\n",
    "            if \"validations\" in field_schema:\n",
    "                validation_rules[field_name] = field_schema[\"validations\"]\n",
    "\n",
    "print(f\"  Coercion rules: {len(coercion_rules)} fields\")\n",
    "print(f\"  Validation rules: {len(validation_rules)} fields\")\n",
    "print(f\"  Governance rules: {'Yes' if governance_rules else 'No'}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "======================================================================\n",
      "Step 3: Store All Components in PostgreSQL Metadata Store\n",
      "======================================================================\n",
      "✓ Connected to PostgreSQL metadata store\n",
      "✓ Stored schema (ID: cac91aac-7a68-47b2-a468-be4adddf56c6, version: 1.0.0)\n",
      "✓ Stored metadata\n",
      "✓ Stored metadata with ownership and governance rules\n",
      "✓ Stored coercion rules (version: 1.0.0)\n",
      "✓ Stored validation rules (version: 1.0.0)\n",
      "\n",
      "✓ All components stored successfully (Schema ID: cac91aac-7a68-47b2-a468-be4adddf56c6)\n"
     ]
    }
   ],
   "source": [
    "# Step 3: Store All Components in PostgreSQL Metadata Store\n",
    "print(\"\\n\" + \"=\" * 70)\n",
    "print(\"Step 3: Store All Components in PostgreSQL Metadata Store\")\n",
    "print(\"=\" * 70)\n",
    "\n",
    "# Connect to PostgreSQL\n",
    "connection_string = \"postgresql://postgres:1234567890@localhost:5432/postgres\"\n",
    "store = PostgresMetadataStore(connection_string=connection_string)\n",
    "store.connect()\n",
    "print(\"✓ Connected to PostgreSQL metadata store\")\n",
    "\n",
    "# Store schema (use the schema from the contract, which has version info)\n",
    "schema_version = versions.get(\"schema\", schema.get(\"version\", \"1.0.0\"))\n",
    "schema_id = store.store_schema(\n",
    "    schema_name=\"airports_and_geographic_information\",\n",
    "    schema=schema,\n",
    "    version=schema_version\n",
    ")\n",
    "print(f\"✓ Stored schema (ID: {schema_id}, version: {schema_version})\")\n",
    "\n",
    "# Merge ownership and governance rules into metadata before storing\n",
    "# Ownership and governance are part of metadata, not separate entities\n",
    "metadata_dict = metadata.copy() if metadata else {}\n",
    "if ownership:\n",
    "    metadata_dict[\"business_owners\"] = [ownership.get(\"owner\", \"unknown\")] if ownership.get(\"owner\") else []\n",
    "if governance_rules:\n",
    "    metadata_dict[\"governance_rules\"] = governance_rules\n",
    "\n",
    "# Store metadata once with all information (ownership and governance included)\n",
    "# Note: The new API uses schema_id (not resource_id/resource_type)\n",
    "if metadata_dict:\n",
    "    store.store_metadata(\n",
    "        schema_id=schema_id,\n",
    "        metadata=metadata_dict,\n",
    "        version=schema_version\n",
    "    )\n",
    "    print(f\"✓ Stored metadata (including ownership and governance rules)\")\n",
    "\n",
    "# Store coercion rules\n",
    "if coercion_rules:\n",
    "    coercion_version = versions.get(\"coercion_rules\", \"1.0.0\")\n",
    "    store.store_coercion_rules(\n",
    "        schema_id=schema_id,\n",
    "        coercion_rules=coercion_rules,\n",
    "        version=coercion_version\n",
    "    )\n",
    "    print(f\"✓ Stored coercion rules (version: {coercion_version})\")\n",
    "\n",
    "# Store validation rules\n",
    "if validation_rules:\n",
    "    validation_version = versions.get(\"validation_rules\", \"1.0.0\")\n",
    "    store.store_validation_rules(\n",
    "        schema_id=schema_id,\n",
    "        validation_rules=validation_rules,\n",
    "        version=validation_version\n",
    "    )\n",
    "    print(f\"✓ Stored validation rules (version: {validation_version})\")\n",
    "\n",
    "print(f\"\\n✓ All components stored successfully (Schema ID: {schema_id})\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "======================================================================\n",
      "Step 4: Extract from Metadata Store and Reconstruct Contract\n",
      "======================================================================\n",
      "✓ Reconstructed contract from metadata store\n",
      "  Contract keys: ['schema', 'metadata', 'versions']\n",
      "  Schema title: Airports and Geographic Information\n",
      "  Schema version: 1.0.0\n",
      "  Metadata present: Yes\n"
     ]
    }
   ],
   "source": [
    "# Step 4: Extract from Metadata Store and Reconstruct Contract\n",
    "print(\"\\n\" + \"=\" * 70)\n",
    "print(\"Step 4: Extract from Metadata Store and Reconstruct Contract\")\n",
    "print(\"=\" * 70)\n",
    "\n",
    "# Reconstruct the contract from the metadata store\n",
    "reconstructed_contract = build_contract_from_store(\n",
    "    store=store,\n",
    "    schema_id=schema_id,\n",
    "    version=None,  # Use latest version\n",
    "    include_metadata=True,\n",
    "    include_ownership=True,\n",
    "    include_governance=True\n",
    ")\n",
    "print(f\"✓ Reconstructed contract from metadata store\")\n",
    "print(f\"  Contract keys: {list(reconstructed_contract.keys())}\")\n",
    "\n",
    "# Show what was reconstructed\n",
    "if \"schema\" in reconstructed_contract:\n",
    "    print(f\"  Schema title: {reconstructed_contract['schema'].get('title')}\")\n",
    "    print(f\"  Schema version: {reconstructed_contract['schema'].get('version')}\")\n",
    "if \"metadata\" in reconstructed_contract:\n",
    "    print(f\"  Metadata present: Yes\")\n",
    "if \"ownership\" in reconstructed_contract:\n",
    "    print(f\"  Ownership: {reconstructed_contract['ownership'].get('owner', 'N/A')}\")\n",
    "if \"governance_rules\" in reconstructed_contract:\n",
    "    print(f\"  Governance rules present: Yes\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "======================================================================\n",
      "Step 5: Create Pydantic Model from Reconstructed Contract\n",
      "======================================================================\n",
      "✓ Generated Pydantic model: AirportsAndGeographicInformation\n",
      "\n",
      "Testing model with sample data:\n",
      "⚠ Validation issues found:\n",
      "  - {'type': 'missing', 'loc': ('metadata',), 'msg': 'Field required', 'input': {'iata_ap_code': 'QCU', 'valid_since': '1970-01-01T00:00:00+00:00', 'valid_until': '2035-12-31T00:00:00+00:00', 'icao_code': '', 'ap_name': 'AKUNNAAQ', 'country_code': 'GL', 'dst_zone_code': 'GL01', 'area': '', 'category': 'B', 'time_zone': 'GL01', 'coord_longitude': 'W05233', 'coord_latitude': 'N06873', 'external_reference': None, 'record_id': 15458, 'last_update': '2022-03-02T10:38:20+00:00', 'ap_name_nls': 'AKUNNAAQ', 'runways': [], 'subtype_details': [{'aircraft_owner': '*', 'subfleet_type': '*', 'effective_date': datetime.datetime(2014, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), 'discontinue_date': None, 'standard_taxitime_in': '0010', 'standard_taxitime_out': '0015'}], 'country': 'GREENLAND', 'area_rules': {'airport': [], 'country': [], 'city': []}, 'diff_utc_lst': -180, 'diff_lst_dst': 60}, 'url': 'https://errors.pydantic.dev/2.12/v/missing'}\n",
      "  - {'type': 'string_type', 'loc': ('valid_since',), 'msg': 'Input should be a valid string', 'input': datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), 'url': 'https://errors.pydantic.dev/2.12/v/string_type'}\n",
      "  - {'type': 'string_type', 'loc': ('valid_until',), 'msg': 'Input should be a valid string', 'input': datetime.datetime(2035, 12, 31, 0, 0, tzinfo=datetime.timezone.utc), 'url': 'https://errors.pydantic.dev/2.12/v/string_type'}\n",
      "  - {'type': 'string_type', 'loc': ('last_update',), 'msg': 'Input should be a valid string', 'input': datetime.datetime(2022, 3, 2, 10, 38, 20, tzinfo=datetime.timezone.utc), 'url': 'https://errors.pydantic.dev/2.12/v/string_type'}\n",
      "  - {'type': 'string_type', 'loc': ('diff_utc_lst',), 'msg': 'Input should be a valid string', 'input': -180, 'url': 'https://errors.pydantic.dev/2.12/v/string_type'}\n",
      "  ... and 1 more errors\n",
      "\n",
      "✓ Disconnected from metadata store\n"
     ]
    }
   ],
   "source": [
    "# Step 5: Create Pydantic Model from Reconstructed Contract\n",
    "print(\"\\n\" + \"=\" * 70)\n",
    "print(\"Step 5: Create Pydantic Model from Reconstructed Contract\")\n",
    "print(\"=\" * 70)\n",
    "\n",
    "# Generate Pydantic model from the reconstructed contract\n",
    "# This will merge coercion and validation rules into the schema\n",
    "AirportsModel = get_model_from_contract(\n",
    "    contract=reconstructed_contract,\n",
    "    model_name=\"AirportsAndGeographicInformation\"\n",
    ")\n",
    "print(f\"✓ Generated Pydantic model: {AirportsModel.__name__}\")\n",
    "\n",
    "# Test the model with sample data (if available)\n",
    "if json_data and len(json_data) > 0:\n",
    "    print(\"\\nTesting model with sample data:\")\n",
    "    sample_data = json_data[0].copy()\n",
    "    \n",
    "    try:\n",
    "        # Clean up the data - remove MongoDB _id field and handle datetime objects\n",
    "        cleaned_data = {}\n",
    "        for key, value in sample_data.items():\n",
    "            # Skip MongoDB _id field\n",
    "            if key == '_id':\n",
    "                continue\n",
    "            \n",
    "            # Convert datetime objects to ISO format strings\n",
    "            if isinstance(value, datetime):\n",
    "                cleaned_data[key] = value.isoformat()\n",
    "            elif value is None:\n",
    "                cleaned_data[key] = None\n",
    "            else:\n",
    "                cleaned_data[key] = value\n",
    "        \n",
    "        # Use the runtime validator to validate and coerce the data\n",
    "        from pycharter import validate_with_contract\n",
    "        \n",
    "        validation_result = validate_with_contract(\n",
    "            data=cleaned_data,\n",
    "            contract=reconstructed_contract\n",
    "        )\n",
    "        \n",
    "        if validation_result.is_valid:\n",
    "            airports_instance = AirportsModel(**validation_result.validated_data)\n",
    "            print(f\"✓ Successfully created AirportsAndGeographicInformation instance\")\n",
    "            print(f\"  IATA AP Code: {airports_instance.iata_ap_code}\")\n",
    "            print(f\"  AP Name: {airports_instance.ap_name}\")\n",
    "            print(f\"  Country Code: {airports_instance.country_code}\")\n",
    "        else:\n",
    "            print(f\"⚠ Validation issues found:\")\n",
    "            for error in validation_result.errors[:5]:  # Show first 5 errors\n",
    "                print(f\"  - {error}\")\n",
    "            if len(validation_result.errors) > 5:\n",
    "                print(f\"  ... and {len(validation_result.errors) - 5} more errors\")\n",
    "    except Exception as e:\n",
    "        print(f\"✗ Error creating instance: {e}\")\n",
    "        print(f\"  Error details: {type(e).__name__}\")\n",
    "        import traceback\n",
    "        traceback.print_exc()\n",
    "else:\n",
    "    print(\"\\n⚠ No sample data available for testing\")\n",
    "\n",
    "# Disconnect from store\n",
    "store.disconnect()\n",
    "print(\"\\n✓ Disconnected from metadata store\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'$defs': {'AirportsAndGeographicInformationCity_infoItem': {'properties': {},\n",
       "   'title': 'AirportsAndGeographicInformationCity_infoItem',\n",
       "   'type': 'object'},\n",
       "  'AirportsAndGeographicInformationMetadata': {'properties': {},\n",
       "   'title': 'AirportsAndGeographicInformationMetadata',\n",
       "   'type': 'object'},\n",
       "  'AirportsAndGeographicInformationRunwaysItem': {'properties': {},\n",
       "   'title': 'AirportsAndGeographicInformationRunwaysItem',\n",
       "   'type': 'object'},\n",
       "  'AirportsAndGeographicInformationSubtype_detailsItem': {'properties': {},\n",
       "   'title': 'AirportsAndGeographicInformationSubtype_detailsItem',\n",
       "   'type': 'object'}},\n",
       " 'properties': {'metadata': {'$ref': '#/$defs/AirportsAndGeographicInformationMetadata'},\n",
       "  'iata_ap_code': {'description': 'IATA airport code',\n",
       "   'maxLength': 3,\n",
       "   'title': 'IATA AP Code',\n",
       "   'type': 'string'},\n",
       "  'valid_since': {'description': 'Valid from date',\n",
       "   'format': 'date-time',\n",
       "   'title': 'Valid Since',\n",
       "   'type': 'string'},\n",
       "  'valid_until': {'description': 'Valid until date',\n",
       "   'format': 'date-time',\n",
       "   'title': 'Valid Until',\n",
       "   'type': 'string'},\n",
       "  'icao_code': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'ICAO airport code',\n",
       "   'title': 'ICAO Code'},\n",
       "  'ap_name': {'description': 'Airport name',\n",
       "   'maxLength': 30,\n",
       "   'title': 'AP Name',\n",
       "   'type': 'string'},\n",
       "  'country_code': {'description': 'Country code',\n",
       "   'maxLength': 4,\n",
       "   'title': 'Country Code',\n",
       "   'type': 'string'},\n",
       "  'dst_zone_code': {'description': 'Daylight saving time zone code',\n",
       "   'maxLength': 4,\n",
       "   'title': 'DST Zone Code',\n",
       "   'type': 'string'},\n",
       "  'area': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Area code',\n",
       "   'title': 'Area'},\n",
       "  'category': {'description': 'Airport category',\n",
       "   'maxLength': 1,\n",
       "   'title': 'Category',\n",
       "   'type': 'string'},\n",
       "  'time_zone': {'description': 'Time zone code',\n",
       "   'maxLength': 4,\n",
       "   'title': 'Time Zone',\n",
       "   'type': 'string'},\n",
       "  'coord_longitude': {'description': 'Longitude coordinate',\n",
       "   'maxLength': 10,\n",
       "   'title': 'Coord Longitude',\n",
       "   'type': 'string'},\n",
       "  'coord_latitude': {'description': 'Latitude coordinate',\n",
       "   'maxLength': 9,\n",
       "   'title': 'Coord Latitude',\n",
       "   'type': 'string'},\n",
       "  'external_reference': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'External reference',\n",
       "   'title': 'External Reference'},\n",
       "  'record_id': {'anyOf': [{'type': 'integer'}, {'type': 'null'}],\n",
       "   'default': 0,\n",
       "   'description': 'Record ID',\n",
       "   'title': 'Record Id'},\n",
       "  'last_update': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Last update timestamp',\n",
       "   'title': 'Last Update'},\n",
       "  'last_update_user_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Last update user ID',\n",
       "   'title': 'Last Update User Id'},\n",
       "  'ap_name_nls': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Airport name in native language',\n",
       "   'title': 'AP Name NLS'},\n",
       "  'runways': {'anyOf': [{'items': {'$ref': '#/$defs/AirportsAndGeographicInformationRunwaysItem'},\n",
       "     'type': 'array'},\n",
       "    {'type': 'null'}],\n",
       "   'default': [],\n",
       "   'description': 'List of runways at this airport',\n",
       "   'title': 'Runways'},\n",
       "  'subtype_details': {'anyOf': [{'items': {'$ref': '#/$defs/AirportsAndGeographicInformationSubtype_detailsItem'},\n",
       "     'type': 'array'},\n",
       "    {'type': 'null'}],\n",
       "   'default': [],\n",
       "   'description': 'Subtype-specific airport details',\n",
       "   'title': 'Subtype Details'},\n",
       "  'city_info': {'anyOf': [{'items': {'$ref': '#/$defs/AirportsAndGeographicInformationCity_infoItem'},\n",
       "     'type': 'array'},\n",
       "    {'type': 'null'}],\n",
       "   'default': [],\n",
       "   'description': 'City information',\n",
       "   'title': 'City Info'},\n",
       "  'iata_city_code': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'IATA city code (from city_info)',\n",
       "   'title': 'IATA City Code'},\n",
       "  'city': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'City name (from city_info)',\n",
       "   'title': 'City'},\n",
       "  'state_designator': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'State designator (from city_info)',\n",
       "   'title': 'State Designator'},\n",
       "  'country': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Country name',\n",
       "   'title': 'Country'},\n",
       "  'diff_utc_lst': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Deviation to UTC (signed Minutes)',\n",
       "   'title': 'Diff UTC LST'},\n",
       "  'diff_lst_dst': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Deviation from Local Standard Time (signed Minutes)',\n",
       "   'title': 'Diff LST DST'},\n",
       "  'area_rules': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "   'default': None,\n",
       "   'description': 'Area rules categorized by airport, country, and city',\n",
       "   'title': 'Area Rules'}},\n",
       " 'required': ['metadata',\n",
       "  'iata_ap_code',\n",
       "  'valid_since',\n",
       "  'valid_until',\n",
       "  'ap_name',\n",
       "  'country_code',\n",
       "  'dst_zone_code',\n",
       "  'category',\n",
       "  'time_zone',\n",
       "  'coord_longitude',\n",
       "  'coord_latitude'],\n",
       " 'title': 'AirportsAndGeographicInformation',\n",
       " 'type': 'object'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# View the generated model schema\n",
    "AirportsModel.model_json_schema()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (pycharter-dev)",
   "language": "python",
   "name": "pycharter-dev"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
