{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5ca00701",
   "metadata": {
    "papermill": {
     "duration": 0.002743,
     "end_time": "2025-09-28T13:17:11.108357",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.105614",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# GO-CAM AI Demo\n",
    "\n",
    "This notebook demonstrates the key functionality of the noctua-py library for programmatically manipulating GO-CAM models via the Noctua/Minerva/Barista API stack.\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, ensure you have noctua-py installed:\n",
    "\n",
    "```bash\n",
    "pip install noctua-py\n",
    "# or with uv:\n",
    "uv add noctua-py\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7254fbb",
   "metadata": {
    "papermill": {
     "duration": 0.001593,
     "end_time": "2025-09-28T13:17:11.111851",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.110258",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Environment Setup\n",
    "\n",
    "You'll need a Barista token to interact with the API. For development/testing, you can use the dev server token:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c35b4b9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:11.115868Z",
     "iopub.status.busy": "2025-09-28T13:17:11.115662Z",
     "iopub.status.idle": "2025-09-28T13:17:11.172235Z",
     "shell.execute_reply": "2025-09-28T13:17:11.171962Z"
    },
    "papermill": {
     "duration": 0.059493,
     "end_time": "2025-09-28T13:17:11.172975",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.113482",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": "import os\nimport json\nfrom noctua.barista import BaristaClient, BaristaError\n\n# Ensure BARISTA_TOKEN is set in your environment\n# For development: Contact the GO team for a dev token\n# For production: Get token from Noctua login\nif not os.environ.get(\"BARISTA_TOKEN\"):\n    print(\"ERROR: BARISTA_TOKEN environment variable not set\")\n    print(\"Please set: export BARISTA_TOKEN=your-token-here\")\n    raise SystemExit(1)\n\n# Test model ID - change this to an existing model on your server\nTEST_MODEL_ID = \"gomodel:6796b94c00003233\"\n\nprint(\"Environment configured for Barista API\")"
  },
  {
   "cell_type": "markdown",
   "id": "0f3d1429",
   "metadata": {
    "papermill": {
     "duration": 0.001596,
     "end_time": "2025-09-28T13:17:11.176426",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.174830",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 1. Creating a Barista Client\n",
    "\n",
    "The BaristaClient is your main interface to the GO-CAM API:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "030a001e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:11.180530Z",
     "iopub.status.busy": "2025-09-28T13:17:11.180383Z",
     "iopub.status.idle": "2025-09-28T13:17:11.232999Z",
     "shell.execute_reply": "2025-09-28T13:17:11.232752Z"
    },
    "papermill": {
     "duration": 0.055606,
     "end_time": "2025-09-28T13:17:11.233736",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.178130",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Client configured for: http://barista-dev.berkeleybop.org\n",
      "Namespace: minerva_public_dev\n"
     ]
    }
   ],
   "source": [
    "# Create a client for the dev server (default)\n",
    "client = BaristaClient()\n",
    "\n",
    "print(f\"Client configured for: {client.base_url}\")\n",
    "print(f\"Namespace: {client.namespace}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de3592ec",
   "metadata": {
    "papermill": {
     "duration": 0.001443,
     "end_time": "2025-09-28T13:17:11.236954",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.235511",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 2. Listing Models\n",
    "\n",
    "List all available GO-CAM models with filtering options:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "07b1aa05",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:11.240635Z",
     "iopub.status.busy": "2025-09-28T13:17:11.240505Z",
     "iopub.status.idle": "2025-09-28T13:17:12.069944Z",
     "shell.execute_reply": "2025-09-28T13:17:12.069548Z"
    },
    "papermill": {
     "duration": 0.832462,
     "end_time": "2025-09-28T13:17:12.070849",
     "exception": false,
     "start_time": "2025-09-28T13:17:11.238387",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 5 total models, showing 5:\n",
      "\n",
      "ID: gomodel:R-HSA-1660516\n",
      "   Title: Synthesis of PIPs at the early endosome membrane - imported from: Reactome\n",
      "   State: development\n",
      "\n",
      "ID: gomodel:R-HSA-1660514\n",
      "   Title: Synthesis of PIPs at the Golgi membrane - imported from: Reactome\n",
      "   State: development\n",
      "\n",
      "ID: gomodel:68cc36ee00000000\n",
      "   Title: enabled by undefined\n",
      "   State: development\n",
      "\n",
      "ID: gomodel:68af725b00000031\n",
      "   Title: enabled by Abca12 Rnor\n",
      "   State: development\n",
      "\n",
      "ID: gomodel:68af725b00000008\n",
      "   Title: enabled by Brpf1 Mmus\n",
      "   State: development\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# List models with various filters\n",
    "result = client.list_models(\n",
    "    limit=5,  # Limit to 5 models\n",
    "    state=\"development\"  # Only development models\n",
    ")\n",
    "\n",
    "models = result.get(\"models\", [])\n",
    "total = result.get(\"n\", 0)\n",
    "\n",
    "print(f\"Found {total} total models, showing {len(models)}:\")\n",
    "print()\n",
    "\n",
    "for model in models:\n",
    "    model_id = model.get(\"id\", \"\")\n",
    "    title = model.get(\"title\", \"(no title)\")\n",
    "    state = model.get(\"state\", \"\")\n",
    "    print(f\"ID: {model_id}\")\n",
    "    print(f\"   Title: {title[:80]}...\" if len(title) > 80 else f\"   Title: {title}\")\n",
    "    print(f\"   State: {state}\")\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fbc5faf4",
   "metadata": {
    "papermill": {
     "duration": 0.002471,
     "end_time": "2025-09-28T13:17:12.075604",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.073133",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 3. Creating a New Model\n",
    "\n",
    "Create a new empty GO-CAM model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "aff5bfb1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:12.079739Z",
     "iopub.status.busy": "2025-09-28T13:17:12.079606Z",
     "iopub.status.idle": "2025-09-28T13:17:12.131864Z",
     "shell.execute_reply": "2025-09-28T13:17:12.131565Z"
    },
    "papermill": {
     "duration": 0.05544,
     "end_time": "2025-09-28T13:17:12.132786",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.077346",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully created model: gomodel:68d6f96e00000222\n"
     ]
    }
   ],
   "source": [
    "# Create a new model with a title\n",
    "response = client.create_model(title=\"Demo GO-CAM Model from Jupyter\")\n",
    "\n",
    "if response.ok:\n",
    "    new_model_id = response.model_id\n",
    "    print(f\"Successfully created model: {new_model_id}\")\n",
    "else:\n",
    "    print(f\"Failed to create model: {response.raw}\")\n",
    "    new_model_id = TEST_MODEL_ID  # Fall back to test model\n",
    "    print(f\"Using existing test model: {new_model_id}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4f13b47",
   "metadata": {
    "papermill": {
     "duration": 0.002652,
     "end_time": "2025-09-28T13:17:12.137756",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.135104",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 4. Getting Model Details\n",
    "\n",
    "Retrieve the full details of a GO-CAM model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3b147815",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:12.142712Z",
     "iopub.status.busy": "2025-09-28T13:17:12.142564Z",
     "iopub.status.idle": "2025-09-28T13:17:12.192140Z",
     "shell.execute_reply": "2025-09-28T13:17:12.191830Z"
    },
    "papermill": {
     "duration": 0.053837,
     "end_time": "2025-09-28T13:17:12.193765",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.139928",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model ID: gomodel:68d6f96e00000222\n",
      "State: development\n",
      "Number of individuals: 0\n",
      "Number of facts/edges: 0\n"
     ]
    }
   ],
   "source": [
    "# Get model details\n",
    "response = client.get_model(new_model_id)\n",
    "\n",
    "if response.ok:\n",
    "    print(f\"Model ID: {response.model_id}\")\n",
    "    print(f\"State: {response.model_state}\")\n",
    "    print(f\"Number of individuals: {len(response.individuals)}\")\n",
    "    print(f\"Number of facts/edges: {len(response.facts)}\")\n",
    "    \n",
    "    # Show first few individuals if any\n",
    "    if response.individuals:\n",
    "        print(\"\\nFirst 3 individuals:\")\n",
    "        for ind in response.individuals[:3]:\n",
    "            ind_id = ind.get(\"id\", \"\")\n",
    "            types = ind.get(\"type\", [])\n",
    "            if types:\n",
    "                type_str = \", \".join([t.get(\"label\", t.get(\"id\", \"\")) for t in types if t])\n",
    "                print(f\"  - {ind_id}: {type_str}\")\n",
    "else:\n",
    "    print(f\"Failed to get model: {response.raw}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3437136",
   "metadata": {
    "papermill": {
     "duration": 0.002873,
     "end_time": "2025-09-28T13:17:12.199023",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.196150",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 5. Adding Individuals (Nodes)\n",
    "\n",
    "Add molecular function individuals to the model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "fb26efbe",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:12.203551Z",
     "iopub.status.busy": "2025-09-28T13:17:12.203409Z",
     "iopub.status.idle": "2025-09-28T13:17:12.679836Z",
     "shell.execute_reply": "2025-09-28T13:17:12.679564Z"
    },
    "papermill": {
     "duration": 0.479648,
     "end_time": "2025-09-28T13:17:12.680540",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.200892",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Added GTPase activity: gomodel:68d6f96e00000222/68d6f96e00000223\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Added protein serine/threonine kinase activity: gomodel:68d6f96e00000222/68d6f96e00000224\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Added MAP kinase activity: gomodel:68d6f96e00000222/68d6f96e00000225\n"
     ]
    }
   ],
   "source": [
    "# Add some molecular function individuals\n",
    "activities = [\n",
    "    (\"GO:0003924\", \"GTPase activity\", \"ras\"),\n",
    "    (\"GO:0004674\", \"protein serine/threonine kinase activity\", \"raf\"),\n",
    "    (\"GO:0004707\", \"MAP kinase activity\", \"erk\")\n",
    "]\n",
    "\n",
    "added_individuals = {}\n",
    "\n",
    "for go_id, label, var_name in activities:\n",
    "    response = client.add_individual(new_model_id, go_id, assign_var=var_name)\n",
    "    if response.ok:\n",
    "        # Find the newly added individual\n",
    "        for ind in response.individuals:\n",
    "            types = ind.get(\"type\", [])\n",
    "            for t in types:\n",
    "                if t.get(\"id\") == go_id:\n",
    "                    added_individuals[var_name] = ind[\"id\"]\n",
    "                    print(f\"Added {label}: {ind['id']}\")\n",
    "                    break\n",
    "    else:\n",
    "        print(f\"Failed to add {label}: {response.raw.get('message', 'Unknown error')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5353ece",
   "metadata": {
    "papermill": {
     "duration": 0.002464,
     "end_time": "2025-09-28T13:17:12.684976",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.682512",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 6. Adding Facts (Edges)\n",
    "\n",
    "Connect individuals with causal relationships:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "34e95022",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:12.689257Z",
     "iopub.status.busy": "2025-09-28T13:17:12.689143Z",
     "iopub.status.idle": "2025-09-28T13:17:13.059237Z",
     "shell.execute_reply": "2025-09-28T13:17:13.058648Z"
    },
    "papermill": {
     "duration": 0.373228,
     "end_time": "2025-09-28T13:17:13.060122",
     "exception": false,
     "start_time": "2025-09-28T13:17:12.686894",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Added edge: RAS -> RAF\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Added edge: RAF -> ERK\n"
     ]
    }
   ],
   "source": [
    "# Add causal relationships if we have individuals\n",
    "if len(added_individuals) >= 2:\n",
    "    # Get the actual IDs\n",
    "    ras_id = added_individuals.get(\"ras\")\n",
    "    raf_id = added_individuals.get(\"raf\")\n",
    "    erk_id = added_individuals.get(\"erk\")\n",
    "    \n",
    "    # Add edges if we have the nodes\n",
    "    if ras_id and raf_id:\n",
    "        response = client.add_fact(\n",
    "            new_model_id,\n",
    "            subject_id=ras_id,\n",
    "            object_id=raf_id,\n",
    "            predicate_id=\"RO:0002413\"  # directly positively regulates\n",
    "        )\n",
    "        if response.ok:\n",
    "            print(\"Added edge: RAS -> RAF\")\n",
    "        else:\n",
    "            print(f\"Failed to add edge: {response.raw.get('message', '')}\")\n",
    "    \n",
    "    if raf_id and erk_id:\n",
    "        response = client.add_fact(\n",
    "            new_model_id,\n",
    "            subject_id=raf_id,\n",
    "            object_id=erk_id,\n",
    "            predicate_id=\"RO:0002413\"\n",
    "        )\n",
    "        if response.ok:\n",
    "            print(\"Added edge: RAF -> ERK\")\n",
    "        else:\n",
    "            print(f\"Failed to add edge: {response.raw.get('message', '')}\")\n",
    "else:\n",
    "    print(\"Not enough individuals to create edges\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f47f8b48",
   "metadata": {
    "papermill": {
     "duration": 0.003199,
     "end_time": "2025-09-28T13:17:13.076492",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.073293",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 7. Adding Evidence\n",
    "\n",
    "Add evidence annotations to support facts:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ff3c0df8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:13.101388Z",
     "iopub.status.busy": "2025-09-28T13:17:13.100984Z",
     "iopub.status.idle": "2025-09-28T13:17:13.329317Z",
     "shell.execute_reply": "2025-09-28T13:17:13.329020Z"
    },
    "papermill": {
     "duration": 0.233286,
     "end_time": "2025-09-28T13:17:13.330065",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.096779",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully added evidence to RAS->RAF edge\n"
     ]
    }
   ],
   "source": [
    "# Add evidence to an edge if we have one\n",
    "if len(added_individuals) >= 2 and \"ras\" in added_individuals and \"raf\" in added_individuals:\n",
    "    ras_id = added_individuals[\"ras\"]\n",
    "    raf_id = added_individuals[\"raf\"]\n",
    "    \n",
    "    # Build the evidence requests\n",
    "    evidence_requests = client.req_add_evidence_to_fact(\n",
    "        new_model_id,\n",
    "        subject_id=ras_id,\n",
    "        object_id=raf_id,\n",
    "        predicate_id=\"RO:0002413\",\n",
    "        eco_id=\"ECO:0000314\",  # direct assay evidence\n",
    "        sources=[\"PMID:12345678\"],\n",
    "        with_from=[\"UniProtKB:P01112\"]  # HRAS\n",
    "    )\n",
    "    \n",
    "    # Execute the batch request\n",
    "    response = client.m3_batch(evidence_requests)\n",
    "    \n",
    "    if response.ok:\n",
    "        print(\"Successfully added evidence to RAS->RAF edge\")\n",
    "    else:\n",
    "        print(f\"Failed to add evidence: {response.raw.get('message', '')}\")\n",
    "else:\n",
    "    print(\"No edges available to add evidence\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f0d03d8",
   "metadata": {
    "papermill": {
     "duration": 0.002216,
     "end_time": "2025-09-28T13:17:13.335095",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.332879",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 8. Exporting Models\n",
    "\n",
    "Export models in different formats:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b9369e9d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:13.340731Z",
     "iopub.status.busy": "2025-09-28T13:17:13.340562Z",
     "iopub.status.idle": "2025-09-28T13:17:13.441097Z",
     "shell.execute_reply": "2025-09-28T13:17:13.440807Z"
    },
    "papermill": {
     "duration": 0.104713,
     "end_time": "2025-09-28T13:17:13.441998",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.337285",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model structure (native Minerva JSON):\n",
      "- Model ID: gomodel:68d6f96e00000222\n",
      "- Individuals: 4\n",
      "- Facts: 2\n",
      "\n",
      "Compact view:\n",
      "{\n",
      "  \"id\": \"gomodel:68d6f96e00000222\",\n",
      "  \"individuals_count\": 4,\n",
      "  \"facts_count\": 2,\n",
      "  \"annotations\": [\n",
      "    {\n",
      "      \"key\": \"state\",\n",
      "      \"value\": \"development\"\n",
      "    },\n",
      "    {\n",
      "      \"key\": \"contributor\",\n",
      "      \"value\": \"https://orcid.org/0000-0002-6601-2165\"\n",
      "    },\n",
      "    {\n",
      "      \"key\": \"date\",\n",
      "      \"value\": \"2025-09-28\"\n",
      "    },\n",
      "    {\n",
      "      \"key\": \"providedBy\",\n",
      "      \"value\": \"http://geneontology.org\"\n",
      "    }\n",
      "  ]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "# Get the model in native Minerva JSON format\n",
    "response = client.get_model(new_model_id)\n",
    "\n",
    "if response.ok:\n",
    "    print(\"Model structure (native Minerva JSON):\")\n",
    "    print(f\"- Model ID: {response.model_id}\")\n",
    "    print(f\"- Individuals: {len(response.individuals)}\")\n",
    "    print(f\"- Facts: {len(response.facts)}\")\n",
    "    \n",
    "    # Show compact version\n",
    "    data = response.raw.get(\"data\", {})\n",
    "    compact = {\n",
    "        \"id\": data.get(\"id\"),\n",
    "        \"individuals_count\": len(data.get(\"individuals\", [])),\n",
    "        \"facts_count\": len(data.get(\"facts\", [])),\n",
    "        \"annotations\": data.get(\"annotations\", [])\n",
    "    }\n",
    "    print(\"\\nCompact view:\")\n",
    "    print(json.dumps(compact, indent=2))\n",
    "else:\n",
    "    print(f\"Failed to export: {response.raw}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "269dbe63",
   "metadata": {
    "papermill": {
     "duration": 0.00199,
     "end_time": "2025-09-28T13:17:13.446968",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.444978",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 9. Converting to GO-CAM Format\n",
    "\n",
    "Convert the Minerva JSON to standard GO-CAM format (if the model has proper structure):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d69cf1eb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:13.452373Z",
     "iopub.status.busy": "2025-09-28T13:17:13.452234Z",
     "iopub.status.idle": "2025-09-28T13:17:13.702541Z",
     "shell.execute_reply": "2025-09-28T13:17:13.701841Z"
    },
    "papermill": {
     "duration": 0.254133,
     "end_time": "2025-09-28T13:17:13.703582",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.449449",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Missing RO:0002333 facts in defaultdict(<class 'list'>, {'RO:0002413': [{'subject': 'gomodel:68d6f96e00000222/68d6f96e00000223', 'property': 'RO:0002413', 'property-label': 'provides input for', 'object': 'gomodel:68d6f96e00000222/68d6f96e00000224', 'annotations': [{'key': 'evidence', 'value': 'gomodel:68d6f96e00000222/68d6f96e00000226', 'value-type': 'IRI'}, {'key': 'contributor', 'value': 'https://orcid.org/0000-0002-6601-2165'}, {'key': 'date', 'value': '2025-09-28'}, {'key': 'providedBy', 'value': 'http://geneontology.org'}]}, {'subject': 'gomodel:68d6f96e00000222/68d6f96e00000224', 'property': 'RO:0002413', 'property-label': 'provides input for', 'object': 'gomodel:68d6f96e00000222/68d6f96e00000225', 'annotations': [{'key': 'contributor', 'value': 'https://orcid.org/0000-0002-6601-2165'}, {'key': 'date', 'value': '2025-09-28'}, {'key': 'providedBy', 'value': 'http://geneontology.org'}]}]})\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: Model doesn't follow standard GO-CAM structure: 'title'\n",
      "This is normal for models without enabled_by facts linking to gene products\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    from gocam.translation.minerva_wrapper import MinervaWrapper\n",
    "    \n",
    "    # Get the model data\n",
    "    response = client.get_model(new_model_id)\n",
    "    if response.ok:\n",
    "        minerva_data = response.raw.get(\"data\", {})\n",
    "        \n",
    "        # Ensure required fields\n",
    "        if \"id\" not in minerva_data:\n",
    "            minerva_data[\"id\"] = new_model_id\n",
    "        if \"title\" not in minerva_data:\n",
    "            minerva_data[\"title\"] = \"Demo Model\"\n",
    "        \n",
    "        # Try to convert (may fail if model doesn't have enabled_by facts)\n",
    "        try:\n",
    "            gocam_model = MinervaWrapper.minerva_object_to_model(minerva_data)\n",
    "            print(\"Successfully converted to GO-CAM format!\")\n",
    "            print(f\"Model ID: {gocam_model.id}\")\n",
    "            print(f\"Title: {gocam_model.title}\")\n",
    "            if gocam_model.activities:\n",
    "                print(f\"Activities: {len(gocam_model.activities)}\")\n",
    "        except Exception as e:\n",
    "            print(f\"Note: Model doesn't follow standard GO-CAM structure: {e}\")\n",
    "            print(\"This is normal for models without enabled_by facts linking to gene products\")\n",
    "    else:\n",
    "        print(\"Failed to get model\")\n",
    "        \n",
    "except ImportError:\n",
    "    print(\"gocam package not installed. Install with: pip install gocam\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30f0a55a",
   "metadata": {
    "papermill": {
     "duration": 0.001988,
     "end_time": "2025-09-28T13:17:13.707961",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.705973",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 10. Deleting Model Elements\n",
    "\n",
    "Remove edges and individuals from the model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d091d3ef",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:13.712663Z",
     "iopub.status.busy": "2025-09-28T13:17:13.712523Z",
     "iopub.status.idle": "2025-09-28T13:17:14.024372Z",
     "shell.execute_reply": "2025-09-28T13:17:14.024086Z"
    },
    "papermill": {
     "duration": 0.315246,
     "end_time": "2025-09-28T13:17:14.025174",
     "exception": false,
     "start_time": "2025-09-28T13:17:13.709928",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully deleted RAF->ERK edge\n",
      "Successfully deleted ERK individual: gomodel:68d6f96e00000222/68d6f96e00000225\n"
     ]
    }
   ],
   "source": [
    "# Delete an edge if we have one\n",
    "if len(added_individuals) >= 2 and \"raf\" in added_individuals and \"erk\" in added_individuals:\n",
    "    raf_id = added_individuals[\"raf\"]\n",
    "    erk_id = added_individuals[\"erk\"]\n",
    "    \n",
    "    response = client.delete_edge(\n",
    "        new_model_id,\n",
    "        subject_id=raf_id,\n",
    "        object_id=erk_id,\n",
    "        predicate_id=\"RO:0002413\"\n",
    "    )\n",
    "    \n",
    "    if response.ok:\n",
    "        print(\"Successfully deleted RAF->ERK edge\")\n",
    "    else:\n",
    "        print(f\"Failed to delete edge: {response.raw.get('message', '')}\")\n",
    "\n",
    "# Delete an individual\n",
    "if \"erk\" in added_individuals:\n",
    "    erk_id = added_individuals[\"erk\"]\n",
    "    \n",
    "    response = client.delete_individual(new_model_id, erk_id)\n",
    "    \n",
    "    if response.ok:\n",
    "        print(f\"Successfully deleted ERK individual: {erk_id}\")\n",
    "    else:\n",
    "        print(f\"Failed to delete individual: {response.raw.get('message', '')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "200fedae",
   "metadata": {
    "papermill": {
     "duration": 0.002366,
     "end_time": "2025-09-28T13:17:14.030150",
     "exception": false,
     "start_time": "2025-09-28T13:17:14.027784",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 11. Clearing a Model\n",
    "\n",
    "Remove all content from a model (use with caution!):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "9fc0b0ed",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-09-28T13:17:14.035045Z",
     "iopub.status.busy": "2025-09-28T13:17:14.034874Z",
     "iopub.status.idle": "2025-09-28T13:17:14.206869Z",
     "shell.execute_reply": "2025-09-28T13:17:14.206615Z"
    },
    "papermill": {
     "duration": 0.175335,
     "end_time": "2025-09-28T13:17:14.207622",
     "exception": false,
     "start_time": "2025-09-28T13:17:14.032287",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Failed to clear: {'packet-id': '44e1426a6e8b7117f', 'uid': 'https://orcid.org/0000-0002-6601-2165', 'provided-by': ['http://geneontology.org'], 'is-reasoned': False, 'intention': 'action', 'message-type': 'error', 'message': \"Could not successfully handle batch request. Exception: org.geneontology.minerva.MolecularModelManager$UnknownIdentifierException. No individual found for id: 'gomodel:68d6f96e00000222/68d6f96e00000226' and IRI: http://model.geneontology.org/68d6f96e00000222/68d6f96e00000226 in model: http://model.geneontology.org/68d6f96e00000222\", 'commentary': \"org.geneontology.minerva.MolecularModelManager$UnknownIdentifierException: No individual found for id: 'gomodel:68d6f96e00000222/68d6f96e00000226' and IRI: http://model.geneontology.org/68d6f96e00000222/68d6f96e00000226 in model: http://model.geneontology.org/68d6f96e00000222\\n\\tat org.geneontology.minerva.server.handler.OperationsImpl.getIndividual(OperationsImpl.java:112)\\n\\tat org.geneontology.minerva.server.handler.OperationsImpl.handleRequestForIndividual(OperationsImpl.java:186)\\n\\tat org.geneontology.minerva.server.handler.JsonOrJsonpBatchHandler.m3Batch(JsonOrJsonpBatchHandler.java:149)\\n\\tat org.geneontology.minerva.server.handler.JsonOrJsonpBatchHandler.m3Batch(JsonOrJsonpBatchHandler.java:125)\\n\\tat org.geneontology.minerva.server.handler.JsonOrJsonpBatchHandler.m3BatchPostPrivileged(JsonOrJsonpBatchHandler.java:84)\\n\\tat jdk.internal.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)\\n\\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.base/java.lang.reflect.Method.invoke(Method.java:566)\\n\\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:124)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:167)\\n\\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:469)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:391)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:80)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:253)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:292)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:274)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:244)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:232)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:392)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:365)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:318)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:769)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1125)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1059)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:497)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:248)\\n\\tat org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:610)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:539)\\n\\tat java.base/java.lang.Thread.run(Thread.java:829)\\n\"}\n"
     ]
    }
   ],
   "source": [
    "# Only clear if this is a test model we created\n",
    "if new_model_id != TEST_MODEL_ID:\n",
    "    try:\n",
    "        response = client.clear_model(new_model_id)\n",
    "        if response.ok:\n",
    "            print(f\"Successfully cleared model {new_model_id}\")\n",
    "        else:\n",
    "            print(f\"Failed to clear: {response.raw}\")\n",
    "    except BaristaError as e:\n",
    "        print(f\"Cannot clear model: {e}\")\n",
    "else:\n",
    "    print(\"Not clearing the shared test model\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0393f601",
   "metadata": {
    "papermill": {
     "duration": 0.00261,
     "end_time": "2025-09-28T13:17:14.212752",
     "exception": false,
     "start_time": "2025-09-28T13:17:14.210142",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 12. Using the CLI\n",
    "\n",
    "You can also use the command-line interface for all these operations:\n",
    "\n",
    "```bash\n",
    "# List models\n",
    "noctua-py barista list-models --limit 10 --state production\n",
    "\n",
    "# Create a model\n",
    "noctua-py barista create-model --title \"My Model\"\n",
    "\n",
    "# Add an individual\n",
    "noctua-py barista add-individual --model gomodel:XXX --class GO:0003924\n",
    "\n",
    "# Add a fact/edge\n",
    "noctua-py barista add-fact --model gomodel:XXX --subject id1 --object id2 --predicate RO:0002413\n",
    "\n",
    "# Export model in different formats\n",
    "noctua-py barista export-model --model gomodel:XXX --format minerva-json\n",
    "noctua-py barista export-model --model gomodel:XXX --format gocam-json\n",
    "noctua-py barista export-model --model gomodel:XXX --format gocam-yaml\n",
    "\n",
    "# Delete elements\n",
    "noctua-py barista delete-edge --model gomodel:XXX --subject id1 --object id2 --predicate RO:0002413\n",
    "noctua-py barista delete-individual --model gomodel:XXX --individual id1\n",
    "\n",
    "# Clear a model\n",
    "noctua-py barista clear-model --model gomodel:XXX\n",
    "```\n",
    "\n",
    "Add `--live` flag to use production server instead of dev server.\n",
    "Add `--dry-run` to see the request without executing it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79d83659",
   "metadata": {
    "papermill": {
     "duration": 0.002078,
     "end_time": "2025-09-28T13:17:14.217184",
     "exception": false,
     "start_time": "2025-09-28T13:17:14.215106",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Summary\n",
    "\n",
    "This notebook demonstrated:\n",
    "\n",
    "1. **Client Setup** - Configuring BaristaClient for dev/production servers\n",
    "2. **Model Management** - Creating, listing, and retrieving models\n",
    "3. **Building Models** - Adding individuals (nodes) and facts (edges)\n",
    "4. **Evidence** - Annotating facts with evidence and references\n",
    "5. **Export Formats** - Native Minerva JSON, GO-CAM JSON, and YAML\n",
    "6. **Model Editing** - Deleting elements and clearing models\n",
    "7. **CLI Usage** - Command-line interface for all operations\n",
    "\n",
    "For more information:\n",
    "- [GO-CAM Documentation](http://geneontology.org/docs/gocam-overview/)\n",
    "- [noctua-py GitHub Repository](https://github.com/geneontology/noctua-py)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "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.9"
  },
  "papermill": {
   "default_parameters": {},
   "duration": 5.204722,
   "end_time": "2025-09-28T13:17:14.435407",
   "environment_variables": {},
   "exception": null,
   "input_path": "docs/examples/noctua_demo.ipynb",
   "output_path": "docs/examples/noctua_demo_executed.ipynb",
   "parameters": {},
   "start_time": "2025-09-28T13:17:09.230685",
   "version": "2.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}