{ "cells": [ { "cell_type": "markdown", "id": "06e9f7fd-69d8-44d2-b3f3-11b5aaef804e", "metadata": {}, "source": [ "# Development\n", "\n", "```{eval-rst}\n", ".. currentmodule:: ctao_datamodel\n", "```\n", "\n", "To create a model, inherit from {py:class}`ModelBase` and use\n", "{py:func}`AstroField` to add field metadata, which can include things\n", "like UCDs, units, and fits and IVOA keyword mappings. You can also use\n", "some helper types like ``astropydantic.AstroPydanticTime``. For _PlantUML_ diagrams, you\n", "can optionally add namespaces to your classes by adding a ``_namespace``\n", "attribute." ] }, { "cell_type": "code", "execution_count": null, "id": "93156f24-d09b-4e97-9dc7-eb081d213344", "metadata": {}, "outputs": [], "source": [ "from enum import StrEnum, auto, nonmember\n", "from typing import ClassVar\n", "\n", "import ctao_datamodel as dm\n", "from astropydantic import AstroPydanticTime\n", "\n", "\n", "class AnEnumOption(StrEnum):\n", " \"\"\"An option.\"\"\"\n", "\n", " _namespace = nonmember(\"Example\") # PlantUML namespace\n", "\n", " ONE = auto()\n", " TWO = auto()\n", " THREE = auto()\n", "\n", "\n", "class SubModel(dm.ModelBase):\n", " \"An example sub-model.\"\n", "\n", " _namespace: ClassVar = \"Example.Models\"\n", "\n", " option: AnEnumOption | None = dm.AstroField(description=dm.enum_doc(AnEnumOption))\n", " energy_min: float = dm.AstroField(\n", " description=\"A value with a unit and FITS keyword mapping\",\n", " unit=\"TeV\",\n", " fits_keyword=\"E_MIN\",\n", " )\n", "\n", "\n", "class MyModel(dm.ModelBase):\n", " \"\"\"An example model.\"\"\"\n", "\n", " start_time: AstroPydanticTime = dm.AstroField(\n", " description=\"The time\",\n", " fits_keyword=\"TSTART\",\n", " examples=[\"2025-10-02 15:23:32.12\", \"2025-10-02T15:23:32.12\"],\n", " )\n", " value: float\n", " sub: SubModel" ] }, { "cell_type": "markdown", "id": "9e100852-2382-4faa-a45e-f355e3fbbeb5", "metadata": {}, "source": [ "You can inspect the model:" ] }, { "cell_type": "code", "execution_count": null, "id": "5faff842-9601-4bba-baaa-6a3362007ecb", "metadata": {}, "outputs": [], "source": [ "dm.print_model(MyModel)" ] }, { "cell_type": "markdown", "id": "ea2d7e76-01fb-4d9b-8cff-2cbae15f479e", "metadata": {}, "source": [ "## JSONSchema output\n", "\n", "You can just use the built-in Pydantic functionality to generate JSONSchema, and note that the special field metadata like ``unit`` and ``fits_keyword`` are included automatically" ] }, { "cell_type": "code", "execution_count": null, "id": "be563c6e-ecbb-44b8-98ab-d880b04f4a9a", "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "print(json.dumps(MyModel.model_json_schema(), indent=2))" ] }, { "cell_type": "markdown", "id": "5203f333-ed03-4cdb-b84a-b67714ac3b39", "metadata": {}, "source": [ "## Model Instances\n", "\n", "When you construct an instance of the model, you also get some nice\n", "functions to turn it into flat metadata, for example:" ] }, { "cell_type": "code", "execution_count": null, "id": "9ecda2db-c1b9-469a-b55d-2694ee0fb6df", "metadata": {}, "outputs": [], "source": [ "m = MyModel(\n", " start_time=\"2025-10-10 15:23:23.2\",\n", " value=6.2,\n", " sub=SubModel(option=\"two\", energy_min=10.0),\n", ")\n", "flat = dm.flatten_model_instance(m)\n", "flat" ] }, { "cell_type": "code", "execution_count": null, "id": "db3ab403-113f-4adb-b782-a55a4bae97df", "metadata": {}, "outputs": [], "source": [ "dm.unflatten_model_instance(flat, MyModel)" ] }, { "cell_type": "markdown", "id": "29c74d47-b868-46d2-9076-712716c91c69", "metadata": {}, "source": [ "## PlantUML and LaTeX output\n", "\n", "You can turn this model into PlantUML diagrams and LaTeX tables:" ] }, { "cell_type": "code", "execution_count": null, "id": "783b900e-376c-4879-b383-6e491e615fe9", "metadata": {}, "outputs": [], "source": [ "dm.generate_latex_table_includes([MyModel, SubModel], \"./generated/includes\")\n", "dm.generate_plantuml_diagrams([MyModel, SubModel], \"./generated/plantuml\")" ] }, { "cell_type": "markdown", "id": "a1a0a380-c914-461e-8eca-9aafb4ab393e", "metadata": {}, "source": [ "That will generate:" ] }, { "cell_type": "code", "execution_count": null, "id": "538c1299-90e0-49f3-b63b-4f7608a581b6", "metadata": {}, "outputs": [], "source": [ "! tree ./generated" ] }, { "cell_type": "markdown", "id": "917cf0dd-a1f4-4f24-83ed-c5ca897f7899", "metadata": {}, "source": [ "The latex files can be included in any latex document:" ] }, { "cell_type": "markdown", "id": "7554a7de-75e2-4a18-864e-f30652a9c2a0", "metadata": {}, "source": [ "``./generated/includes/table_MyModel.inc.tex:``\n", "```latex\n", "\\begin{classdef}\n", "\\caption{\\texttt{MyModel}: An example model. Fields marked with ${^\\oslash}$ are optional.}\n", "\\label{tab:MyModel}\n", "\n", "\\begin{tblr}{\n", " width=\\linewidth,\n", " colspec={Q[wd=3.2cm] Q[wd=3cm] X[l]},\n", " row{odd}={bg=moongray},\n", " column{1-2} = {font=\\scriptsize},\n", " column{1} = {font=\\ttfamily\\bfseries\\scriptsize},\n", " column{3} = {font=\\scriptsize},\n", " row{1} = {bg=galaxyblue, font=\\normalfont\\bfseries, fg=white}\n", "}\n", "\n", "Name & Description & Type (Unit) \\\\\n", "start\\_time & The time & Time ($\\mathrm{}$) \\\\\n", "value & & float ($\\mathrm{}$) \\\\\n", "sub & & SubModel ($\\mathrm{}$) \\\\\n", "\n", "\\end{tblr}\n", "\\end{classdef}\n", "```" ] }, { "cell_type": "markdown", "id": "f6392d56-0627-4188-b894-24cb3d227e01", "metadata": {}, "source": [ "The plantuml outputs can be turned into diagrams using e.g." ] }, { "cell_type": "code", "execution_count": null, "id": "4ad6c7c1-943c-4842-9615-22be27174a27", "metadata": {}, "outputs": [], "source": [ "! plantuml generated/plantuml/full_MyModel.plantuml" ] }, { "cell_type": "markdown", "id": "18a0dfc9-e91a-46fd-8bb6-ee7ddf3a8bf7", "metadata": {}, "source": [ "![image](generated/plantuml/full_MyModel.png)" ] }, { "cell_type": "markdown", "id": "787a582b-0fe2-45a1-bc31-f7543a123550", "metadata": {}, "source": [ "In a notebook, you can get a quick preview of PlantUML files using the following. There, the diagram images are not written to a file." ] }, { "cell_type": "code", "execution_count": null, "id": "43f9e706-0ed7-4fd8-b5a0-77d948c157d1", "metadata": {}, "outputs": [], "source": [ "dm.PlantUMLDiagram(MyModel)" ] }, { "cell_type": "code", "execution_count": null, "id": "ff70edd1-f7f2-48e3-bcfb-32eeb3e3c746", "metadata": {}, "outputs": [], "source": [ "dm.PlantUMLDiagram.from_path(\"generated/plantuml/full_MyModel.plantuml\")" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 5 }