# Code generated - EDITING IS FUTILE. DO NOT EDIT.

import typing
import enum
from ..cog import variants as cogvariants
from ..cog import runtime as cogruntime


class Dashboard:
    # Unique numeric identifier for the dashboard.
    # `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances.
    id_val: typing.Optional[int]
    # Unique dashboard identifier that can be generated by anyone. string (8-40)
    uid: typing.Optional[str]
    # Title of dashboard.
    title: typing.Optional[str]
    # Description of dashboard.
    description: typing.Optional[str]
    # This property should only be used in dashboards defined by plugins.  It is a quick check
    # to see if the version has changed since the last time.
    revision: typing.Optional[int]
    # ID of a dashboard imported from the https://grafana.com/grafana/dashboards/ portal
    gnet_id: typing.Optional[str]
    # Tags associated with dashboard.
    tags: typing.Optional[list[str]]
    # Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc".
    timezone: typing.Optional[str]
    # Whether a dashboard is editable or not.
    editable: typing.Optional[bool]
    # Configuration of dashboard cursor sync behavior.
    # Accepted values are 0 (sync turned off), 1 (shared crosshair), 2 (shared crosshair and tooltip).
    graph_tooltip: typing.Optional['DashboardCursorSync']
    # Time range for dashboard.
    # Accepted values are relative time strings like {from: 'now-6h', to: 'now'} or absolute time strings like {from: '2020-07-10T08:00:00.000Z', to: '2020-07-10T14:00:00.000Z'}.
    time: typing.Optional['DashboardDashboardTime']
    # Configuration of the time picker shown at the top of a dashboard.
    timepicker: typing.Optional['TimePickerConfig']
    # The month that the fiscal year starts on.  0 = January, 11 = December
    fiscal_year_start_month: typing.Optional[int]
    # When set to true, the dashboard will redraw panels at an interval matching the pixel width.
    # This will keep data "moving left" regardless of the query refresh rate. This setting helps
    # avoid dashboards presenting stale live data
    live_now: typing.Optional[bool]
    # Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday".
    week_start: typing.Optional[str]
    # Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d".
    refresh: typing.Optional[typing.Union[str, typing.Literal[False]]]
    # Version of the JSON schema, incremented each time a Grafana update brings
    # changes to said schema.
    schema_version: int
    # Version of the dashboard, incremented each time the dashboard is updated.
    version: typing.Optional[int]
    # List of dashboard panels
    panels: typing.Optional[list[typing.Union['Panel', 'RowPanel']]]
    # Configured template variables
    templating: 'DashboardDashboardTemplating'
    # Contains the list of annotations that are associated with the dashboard.
    # Annotations are used to overlay event markers and overlay event tags on graphs.
    # Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API.
    # See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/
    annotations: 'AnnotationContainer'
    # Links with references to other dashboards or external websites.
    links: typing.Optional[list['DashboardLink']]
    # Snapshot options. They are present only if the dashboard is a snapshot.
    snapshot: typing.Optional['Snapshot']

    def __init__(self, id_val: typing.Optional[int] = None, uid: typing.Optional[str] = None, title: typing.Optional[str] = None, description: typing.Optional[str] = None, revision: typing.Optional[int] = None, gnet_id: typing.Optional[str] = None, tags: typing.Optional[list[str]] = None, timezone: typing.Optional[str] = "browser", editable: typing.Optional[bool] = True, graph_tooltip: typing.Optional['DashboardCursorSync'] = None, time: typing.Optional['DashboardDashboardTime'] = None, timepicker: typing.Optional['TimePickerConfig'] = None, fiscal_year_start_month: typing.Optional[int] = 0, live_now: typing.Optional[bool] = None, week_start: typing.Optional[str] = None, refresh: typing.Optional[typing.Union[str, typing.Literal[False]]] = None, schema_version: int = 36, version: typing.Optional[int] = None, panels: typing.Optional[list[typing.Union['Panel', 'RowPanel']]] = None, templating: typing.Optional['DashboardDashboardTemplating'] = None, annotations: typing.Optional['AnnotationContainer'] = None, links: typing.Optional[list['DashboardLink']] = None, snapshot: typing.Optional['Snapshot'] = None):
        self.id_val = id_val
        self.uid = uid
        self.title = title
        self.description = description
        self.revision = revision
        self.gnet_id = gnet_id
        self.tags = tags
        self.timezone = timezone
        self.editable = editable
        self.graph_tooltip = graph_tooltip if graph_tooltip is not None else DashboardCursorSync.OFF
        self.time = time
        self.timepicker = timepicker
        self.fiscal_year_start_month = fiscal_year_start_month
        self.live_now = live_now
        self.week_start = week_start
        self.refresh = refresh
        self.schema_version = schema_version
        self.version = version
        self.panels = panels
        self.templating = templating if templating is not None else DashboardDashboardTemplating()
        self.annotations = annotations if annotations is not None else AnnotationContainer()
        self.links = links
        self.snapshot = snapshot

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "schemaVersion": self.schema_version,
            "templating": self.templating,
            "annotations": self.annotations,
        }
        if self.id_val is not None:
            payload["id"] = self.id_val
        if self.uid is not None:
            payload["uid"] = self.uid
        if self.title is not None:
            payload["title"] = self.title
        if self.description is not None:
            payload["description"] = self.description
        if self.revision is not None:
            payload["revision"] = self.revision
        if self.gnet_id is not None:
            payload["gnetId"] = self.gnet_id
        if self.tags is not None:
            payload["tags"] = self.tags
        if self.timezone is not None:
            payload["timezone"] = self.timezone
        if self.editable is not None:
            payload["editable"] = self.editable
        if self.graph_tooltip is not None:
            payload["graphTooltip"] = self.graph_tooltip
        if self.time is not None:
            payload["time"] = self.time
        if self.timepicker is not None:
            payload["timepicker"] = self.timepicker
        if self.fiscal_year_start_month is not None:
            payload["fiscalYearStartMonth"] = self.fiscal_year_start_month
        if self.live_now is not None:
            payload["liveNow"] = self.live_now
        if self.week_start is not None:
            payload["weekStart"] = self.week_start
        if self.refresh is not None:
            payload["refresh"] = self.refresh
        if self.version is not None:
            payload["version"] = self.version
        if self.panels is not None:
            payload["panels"] = self.panels
        if self.links is not None:
            payload["links"] = self.links
        if self.snapshot is not None:
            payload["snapshot"] = self.snapshot
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "id" in data:
            args["id_val"] = data["id"]
        if "uid" in data:
            args["uid"] = data["uid"]
        if "title" in data:
            args["title"] = data["title"]
        if "description" in data:
            args["description"] = data["description"]
        if "revision" in data:
            args["revision"] = data["revision"]
        if "gnetId" in data:
            args["gnet_id"] = data["gnetId"]
        if "tags" in data:
            args["tags"] = data["tags"]
        if "timezone" in data:
            args["timezone"] = data["timezone"]
        if "editable" in data:
            args["editable"] = data["editable"]
        if "graphTooltip" in data:
            args["graph_tooltip"] = data["graphTooltip"]
        if "time" in data:
            args["time"] = DashboardDashboardTime.from_json(data["time"])
        if "timepicker" in data:
            args["timepicker"] = TimePickerConfig.from_json(data["timepicker"])
        if "fiscalYearStartMonth" in data:
            args["fiscal_year_start_month"] = data["fiscalYearStartMonth"]
        if "liveNow" in data:
            args["live_now"] = data["liveNow"]
        if "weekStart" in data:
            args["week_start"] = data["weekStart"]
        if "refresh" in data:
            args["refresh"] = data["refresh"]
        if "schemaVersion" in data:
            args["schema_version"] = data["schemaVersion"]
        if "version" in data:
            args["version"] = data["version"]
        if "panels" in data:
            decoding_map_panels_array_union: dict[str, typing.Union[typing.Type[RowPanel]]] = {"row": RowPanel}
            args["panels"] = [decoding_map_panels_array_union.get(item["type"], Panel).from_json(item) for item in data["panels"]]
        if "templating" in data:
            args["templating"] = DashboardDashboardTemplating.from_json(data["templating"])
        if "annotations" in data:
            args["annotations"] = AnnotationContainer.from_json(data["annotations"])
        if "links" in data:
            args["links"] = [DashboardLink.from_json(item) for item in data["links"]]
        if "snapshot" in data:
            args["snapshot"] = Snapshot.from_json(data["snapshot"])        

        return cls(**args)


class DashboardCursorSync(enum.IntEnum):
    """
    0 for no shared crosshair or tooltip (default).
    1 for shared crosshair.
    2 for shared crosshair AND shared tooltip.
    """

    OFF = 0
    CROSSHAIR = 1
    TOOLTIP = 2


class TimePickerConfig:
    """
    Time picker configuration
    It defines the default config for the time picker and the refresh picker for the specific dashboard.
    """

    # Whether timepicker is visible or not.
    hidden: bool
    # Interval options available in the refresh picker dropdown.
    refresh_intervals: list[str]
    # Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.
    time_options: list[str]

    def __init__(self, hidden: bool = False, refresh_intervals: typing.Optional[list[str]] = None, time_options: typing.Optional[list[str]] = None):
        self.hidden = hidden
        self.refresh_intervals = refresh_intervals if refresh_intervals is not None else ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
        self.time_options = time_options if time_options is not None else ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "hidden": self.hidden,
            "refresh_intervals": self.refresh_intervals,
            "time_options": self.time_options,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "hidden" in data:
            args["hidden"] = data["hidden"]
        if "refresh_intervals" in data:
            args["refresh_intervals"] = data["refresh_intervals"]
        if "time_options" in data:
            args["time_options"] = data["time_options"]        

        return cls(**args)


class Panel:
    """
    Dashboard panels are the basic visualization building blocks.
    """

    # The panel plugin type id. This is used to find the plugin to display the panel.
    type_val: str
    # Unique identifier of the panel. Generated by Grafana when creating a new panel. It must be unique within a dashboard, but not globally.
    id_val: typing.Optional[int]
    # The version of the plugin that is used for this panel. This is used to find the plugin to display the panel and to migrate old panel configs.
    plugin_version: typing.Optional[str]
    # Tags for the panel.
    tags: typing.Optional[list[str]]
    # Depends on the panel plugin. See the plugin documentation for details.
    targets: typing.Optional[list[cogvariants.Dataquery]]
    # Panel title.
    title: typing.Optional[str]
    # Panel description.
    description: typing.Optional[str]
    # Whether to display the panel without a background.
    transparent: typing.Optional[bool]
    # The datasource used in all targets.
    datasource: typing.Optional['DataSourceRef']
    # Grid position.
    grid_pos: typing.Optional['GridPos']
    # Panel links.
    links: typing.Optional[list['DashboardLink']]
    # Name of template variable to repeat for.
    repeat: typing.Optional[str]
    # Direction to repeat in if 'repeat' is set.
    # `h` for horizontal, `v` for vertical.
    repeat_direction: typing.Optional[typing.Literal["h", "v"]]
    # Option for repeated panels that controls max items per row
    # Only relevant for horizontally repeated panels
    max_per_row: typing.Optional[float]
    # The maximum number of data points that the panel queries are retrieving.
    max_data_points: typing.Optional[float]
    # List of transformations that are applied to the panel data before rendering.
    # When there are multiple transformations, Grafana applies them in the order they are listed.
    # Each transformation creates a result set that then passes on to the next transformation in the processing pipeline.
    transformations: typing.Optional[list['DataTransformerConfig']]
    # The min time interval setting defines a lower limit for the $__interval and $__interval_ms variables.
    # This value must be formatted as a number followed by a valid time
    # identifier like: "40s", "3d", etc.
    # See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options
    interval: typing.Optional[str]
    # Overrides the relative time range for individual panels,
    # which causes them to be different than what is selected in
    # the dashboard time picker in the top-right corner of the dashboard. You can use this to show metrics from different
    # time periods or days on the same dashboard.
    # The value is formatted as time operation like: `now-5m` (Last 5 minutes), `now/d` (the day so far),
    # `now-5d/d`(Last 5 days), `now/w` (This week so far), `now-2y/y` (Last 2 years).
    # Note: Panel time overrides have no effect when the dashboard’s time range is absolute.
    # See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options
    time_from: typing.Optional[str]
    # Overrides the time range for individual panels by shifting its start and end relative to the time picker.
    # For example, you can shift the time range for the panel to be two hours earlier than the dashboard time picker setting `2h`.
    # Note: Panel time overrides have no effect when the dashboard’s time range is absolute.
    # See: https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/#query-options
    time_shift: typing.Optional[str]
    # Controls if the timeFrom or timeShift overrides are shown in the panel header
    hide_time_override: typing.Optional[bool]
    # Dynamically load the panel
    library_panel: typing.Optional['LibraryPanelRef']
    # It depends on the panel plugin. They are specified by the Options field in panel plugin schemas.
    options: typing.Optional[object]
    # Field options allow you to change how the data is displayed in your visualizations.
    field_config: typing.Optional['FieldConfigSource']

    def __init__(self, type_val: str = "", id_val: typing.Optional[int] = None, plugin_version: typing.Optional[str] = None, tags: typing.Optional[list[str]] = None, targets: typing.Optional[list[cogvariants.Dataquery]] = None, title: typing.Optional[str] = None, description: typing.Optional[str] = None, transparent: typing.Optional[bool] = False, datasource: typing.Optional['DataSourceRef'] = None, grid_pos: typing.Optional['GridPos'] = None, links: typing.Optional[list['DashboardLink']] = None, repeat: typing.Optional[str] = None, repeat_direction: typing.Optional[typing.Literal["h", "v"]] = None, max_per_row: typing.Optional[float] = None, max_data_points: typing.Optional[float] = None, transformations: typing.Optional[list['DataTransformerConfig']] = None, interval: typing.Optional[str] = None, time_from: typing.Optional[str] = None, time_shift: typing.Optional[str] = None, hide_time_override: typing.Optional[bool] = None, library_panel: typing.Optional['LibraryPanelRef'] = None, options: typing.Optional[object] = None, field_config: typing.Optional['FieldConfigSource'] = None):
        self.type_val = type_val
        self.id_val = id_val
        self.plugin_version = plugin_version
        self.tags = tags
        self.targets = targets
        self.title = title
        self.description = description
        self.transparent = transparent
        self.datasource = datasource
        self.grid_pos = grid_pos
        self.links = links
        self.repeat = repeat
        self.repeat_direction = repeat_direction if repeat_direction is not None else "h"
        self.max_per_row = max_per_row
        self.max_data_points = max_data_points
        self.transformations = transformations
        self.interval = interval
        self.time_from = time_from
        self.time_shift = time_shift
        self.hide_time_override = hide_time_override
        self.library_panel = library_panel
        self.options = options
        self.field_config = field_config

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
        }
        if self.id_val is not None:
            payload["id"] = self.id_val
        if self.plugin_version is not None:
            payload["pluginVersion"] = self.plugin_version
        if self.tags is not None:
            payload["tags"] = self.tags
        if self.targets is not None:
            payload["targets"] = self.targets
        if self.title is not None:
            payload["title"] = self.title
        if self.description is not None:
            payload["description"] = self.description
        if self.transparent is not None:
            payload["transparent"] = self.transparent
        if self.datasource is not None:
            payload["datasource"] = self.datasource
        if self.grid_pos is not None:
            payload["gridPos"] = self.grid_pos
        if self.links is not None:
            payload["links"] = self.links
        if self.repeat is not None:
            payload["repeat"] = self.repeat
        if self.repeat_direction is not None:
            payload["repeatDirection"] = self.repeat_direction
        if self.max_per_row is not None:
            payload["maxPerRow"] = self.max_per_row
        if self.max_data_points is not None:
            payload["maxDataPoints"] = self.max_data_points
        if self.transformations is not None:
            payload["transformations"] = self.transformations
        if self.interval is not None:
            payload["interval"] = self.interval
        if self.time_from is not None:
            payload["timeFrom"] = self.time_from
        if self.time_shift is not None:
            payload["timeShift"] = self.time_shift
        if self.hide_time_override is not None:
            payload["hideTimeOverride"] = self.hide_time_override
        if self.library_panel is not None:
            payload["libraryPanel"] = self.library_panel
        if self.options is not None:
            payload["options"] = self.options
        if self.field_config is not None:
            payload["fieldConfig"] = self.field_config
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        if "type" in data:
            args["type_val"] = data["type"]
        if "id" in data:
            args["id_val"] = data["id"]
        if "pluginVersion" in data:
            args["plugin_version"] = data["pluginVersion"]
        if "tags" in data:
            args["tags"] = data["tags"]
        
        if "targets" in data:
            args["targets"] = [cogruntime.dataquery_from_json(dataquery_json, data["datasource"]["type"] if data.get("datasource") is not None and data["datasource"].get("type", "") != "" else "") for dataquery_json in data["targets"]]
        if "title" in data:
            args["title"] = data["title"]
        if "description" in data:
            args["description"] = data["description"]
        if "transparent" in data:
            args["transparent"] = data["transparent"]
        if "datasource" in data:
            args["datasource"] = DataSourceRef.from_json(data["datasource"])
        if "gridPos" in data:
            args["grid_pos"] = GridPos.from_json(data["gridPos"])
        if "links" in data:
            args["links"] = data["links"]
        if "repeat" in data:
            args["repeat"] = data["repeat"]
        if "repeatDirection" in data:
            args["repeat_direction"] = data["repeatDirection"]
        if "maxPerRow" in data:
            args["max_per_row"] = data["maxPerRow"]
        if "maxDataPoints" in data:
            args["max_data_points"] = data["maxDataPoints"]
        if "transformations" in data:
            args["transformations"] = data["transformations"]
        if "interval" in data:
            args["interval"] = data["interval"]
        if "timeFrom" in data:
            args["time_from"] = data["timeFrom"]
        if "timeShift" in data:
            args["time_shift"] = data["timeShift"]
        if "hideTimeOverride" in data:
            args["hide_time_override"] = data["hideTimeOverride"]
        if "libraryPanel" in data:
            args["library_panel"] = LibraryPanelRef.from_json(data["libraryPanel"])
        
        if "options" in data:
            config = cogruntime.panelcfg_config(data.get("type", ""))
            if config is not None and config.options_from_json_hook is not None:
                args["options"] = config.options_from_json_hook(data["options"])
            else:
                args["options"] = data["options"]
        
        if "fieldConfig" in data:
            config = cogruntime.panelcfg_config(data.get("type", ""))
            field_config = FieldConfigSource.from_json(data["fieldConfig"])
    
            if config is not None and config.field_config_from_json_hook is not None:
                custom_field_config = data["fieldConfig"].get("defaults", {}).get("custom", {})
                field_config.defaults.custom = config.field_config_from_json_hook(custom_field_config)
    
            args["field_config"] = field_config
    
        return cls(**args)


class DataSourceRef:
    """
    Ref to a DataSource instance
    """

    # The plugin type-id
    type_val: typing.Optional[str]
    # Specific datasource instance
    uid: typing.Optional[str]

    def __init__(self, type_val: typing.Optional[str] = None, uid: typing.Optional[str] = None):
        self.type_val = type_val
        self.uid = uid

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.type_val is not None:
            payload["type"] = self.type_val
        if self.uid is not None:
            payload["uid"] = self.uid
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "type" in data:
            args["type_val"] = data["type"]
        if "uid" in data:
            args["uid"] = data["uid"]        

        return cls(**args)


class GridPos:
    """
    Position and dimensions of a panel in the grid
    """

    # Panel height. The height is the number of rows from the top edge of the panel.
    h: int
    # Panel width. The width is the number of columns from the left edge of the panel.
    w: int
    # Panel x. The x coordinate is the number of columns from the left edge of the grid
    x: int
    # Panel y. The y coordinate is the number of rows from the top edge of the grid
    y: int
    # Whether the panel is fixed within the grid. If true, the panel will not be affected by other panels' interactions
    static: typing.Optional[bool]

    def __init__(self, h: int = 9, w: int = 12, x: int = 0, y: int = 0, static: typing.Optional[bool] = None):
        self.h = h
        self.w = w
        self.x = x
        self.y = y
        self.static = static

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "h": self.h,
            "w": self.w,
            "x": self.x,
            "y": self.y,
        }
        if self.static is not None:
            payload["static"] = self.static
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "h" in data:
            args["h"] = data["h"]
        if "w" in data:
            args["w"] = data["w"]
        if "x" in data:
            args["x"] = data["x"]
        if "y" in data:
            args["y"] = data["y"]
        if "static" in data:
            args["static"] = data["static"]        

        return cls(**args)


class DashboardLink:
    """
    Links with references to other dashboards or external resources
    """

    # Title to display with the link
    title: str
    # Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
    type_val: 'DashboardLinkType'
    # Icon name to be displayed with the link
    icon: str
    # Tooltip to display when the user hovers their mouse over it
    tooltip: str
    # Link URL. Only required/valid if the type is link
    url: typing.Optional[str]
    # List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards
    tags: list[str]
    # If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards
    as_dropdown: bool
    # If true, the link will be opened in a new tab
    target_blank: bool
    # If true, includes current template variables values in the link as query params
    include_vars: bool
    # If true, includes current time range in the link as query params
    keep_time: bool

    def __init__(self, title: str = "", type_val: typing.Optional['DashboardLinkType'] = None, icon: str = "", tooltip: str = "", url: typing.Optional[str] = None, tags: typing.Optional[list[str]] = None, as_dropdown: bool = False, target_blank: bool = False, include_vars: bool = False, keep_time: bool = False):
        self.title = title
        self.type_val = type_val if type_val is not None else DashboardLinkType.LINK
        self.icon = icon
        self.tooltip = tooltip
        self.url = url
        self.tags = tags if tags is not None else []
        self.as_dropdown = as_dropdown
        self.target_blank = target_blank
        self.include_vars = include_vars
        self.keep_time = keep_time

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "title": self.title,
            "type": self.type_val,
            "icon": self.icon,
            "tooltip": self.tooltip,
            "tags": self.tags,
            "asDropdown": self.as_dropdown,
            "targetBlank": self.target_blank,
            "includeVars": self.include_vars,
            "keepTime": self.keep_time,
        }
        if self.url is not None:
            payload["url"] = self.url
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "title" in data:
            args["title"] = data["title"]
        if "type" in data:
            args["type_val"] = data["type"]
        if "icon" in data:
            args["icon"] = data["icon"]
        if "tooltip" in data:
            args["tooltip"] = data["tooltip"]
        if "url" in data:
            args["url"] = data["url"]
        if "tags" in data:
            args["tags"] = data["tags"]
        if "asDropdown" in data:
            args["as_dropdown"] = data["asDropdown"]
        if "targetBlank" in data:
            args["target_blank"] = data["targetBlank"]
        if "includeVars" in data:
            args["include_vars"] = data["includeVars"]
        if "keepTime" in data:
            args["keep_time"] = data["keepTime"]        

        return cls(**args)


class DashboardLinkType(enum.StrEnum):
    """
    Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
    """

    LINK = "link"
    DASHBOARDS = "dashboards"


class DataTransformerConfig:
    """
    Transformations allow to manipulate data returned by a query before the system applies a visualization.
    Using transformations you can: rename fields, join time series data, perform mathematical operations across queries,
    use the output of one transformation as the input to another transformation, etc.
    """

    # Unique identifier of transformer
    id_val: str
    # Disabled transformations are skipped
    disabled: typing.Optional[bool]
    # Optional frame matcher. When missing it will be applied to all results
    filter_val: typing.Optional['MatcherConfig']
    # Options to be passed to the transformer
    # Valid options depend on the transformer id
    options: object

    def __init__(self, id_val: str = "", disabled: typing.Optional[bool] = None, filter_val: typing.Optional['MatcherConfig'] = None, options: object = None):
        self.id_val = id_val
        self.disabled = disabled
        self.filter_val = filter_val
        self.options = options

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "id": self.id_val,
            "options": self.options,
        }
        if self.disabled is not None:
            payload["disabled"] = self.disabled
        if self.filter_val is not None:
            payload["filter"] = self.filter_val
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "id" in data:
            args["id_val"] = data["id"]
        if "disabled" in data:
            args["disabled"] = data["disabled"]
        if "filter" in data:
            args["filter_val"] = MatcherConfig.from_json(data["filter"])
        if "options" in data:
            args["options"] = data["options"]        

        return cls(**args)


class MatcherConfig:
    """
    Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.
    It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.
    """

    # The matcher id. This is used to find the matcher implementation from registry.
    id_val: str
    # The matcher options. This is specific to the matcher implementation.
    options: typing.Optional[object]

    def __init__(self, id_val: str = "", options: typing.Optional[object] = None):
        self.id_val = id_val
        self.options = options

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "id": self.id_val,
        }
        if self.options is not None:
            payload["options"] = self.options
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "id" in data:
            args["id_val"] = data["id"]
        if "options" in data:
            args["options"] = data["options"]        

        return cls(**args)


class LibraryPanelRef:
    """
    A library panel is a reusable panel that you can use in any dashboard.
    When you make a change to a library panel, that change propagates to all instances of where the panel is used.
    Library panels streamline reuse of panels across multiple dashboards.
    """

    # Library panel name
    name: str
    # Library panel uid
    uid: str

    def __init__(self, name: str = "", uid: str = ""):
        self.name = name
        self.uid = uid

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "name": self.name,
            "uid": self.uid,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "name" in data:
            args["name"] = data["name"]
        if "uid" in data:
            args["uid"] = data["uid"]        

        return cls(**args)


class FieldConfigSource:
    """
    The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.
    Each column within this structure is called a field. A field can represent a single time series or table column.
    Field options allow you to change how the data is displayed in your visualizations.
    """

    # Defaults are the options applied to all fields.
    defaults: 'FieldConfig'
    # Overrides are the options applied to specific fields overriding the defaults.
    overrides: list['DashboardFieldConfigSourceOverrides']

    def __init__(self, defaults: typing.Optional['FieldConfig'] = None, overrides: typing.Optional[list['DashboardFieldConfigSourceOverrides']] = None):
        self.defaults = defaults if defaults is not None else FieldConfig()
        self.overrides = overrides if overrides is not None else []

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "defaults": self.defaults,
            "overrides": self.overrides,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "defaults" in data:
            args["defaults"] = FieldConfig.from_json(data["defaults"])
        if "overrides" in data:
            args["overrides"] = [DashboardFieldConfigSourceOverrides.from_json(item) for item in data["overrides"]]        

        return cls(**args)


class FieldConfig:
    """
    The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.
    Each column within this structure is called a field. A field can represent a single time series or table column.
    Field options allow you to change how the data is displayed in your visualizations.
    """

    # The display value for this field.  This supports template variables blank is auto
    display_name: typing.Optional[str]
    # This can be used by data sources that return and explicit naming structure for values and labels
    # When this property is configured, this value is used rather than the default naming strategy.
    display_name_from_ds: typing.Optional[str]
    # Human readable field metadata
    description: typing.Optional[str]
    # An explicit path to the field in the datasource.  When the frame meta includes a path,
    # This will default to `${frame.meta.path}/${field.name}
    # 
    # When defined, this value can be used as an identifier within the datasource scope, and
    # may be used to update the results
    path: typing.Optional[str]
    # True if data source can write a value to the path. Auth/authz are supported separately
    writeable: typing.Optional[bool]
    # True if data source field supports ad-hoc filters
    filterable: typing.Optional[bool]
    # Unit a field should use. The unit you select is applied to all fields except time.
    # You can use the units ID availables in Grafana or a custom unit.
    # Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts
    # As custom unit, you can use the following formats:
    # `suffix:<suffix>` for custom unit that should go after value.
    # `prefix:<prefix>` for custom unit that should go before value.
    # `time:<format>` For custom date time formats type for example `time:YYYY-MM-DD`.
    # `si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.
    # `count:<unit>` for a custom count unit.
    # `currency:<unit>` for custom a currency unit.
    unit: typing.Optional[str]
    # Specify the number of decimals Grafana includes in the rendered value.
    # If you leave this field blank, Grafana automatically truncates the number of decimals based on the value.
    # For example 1.1234 will display as 1.12 and 100.456 will display as 100.
    # To display all decimals, set the unit to `String`.
    decimals: typing.Optional[float]
    # The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.
    min_val: typing.Optional[float]
    # The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.
    max_val: typing.Optional[float]
    # Convert input values into a display string
    mappings: typing.Optional[list['ValueMapping']]
    # Map numeric values to states
    thresholds: typing.Optional['ThresholdsConfig']
    # Panel color configuration
    color: typing.Optional['FieldColor']
    # The behavior when clicking on a result
    links: typing.Optional[list['DashboardLink']]
    # Alternative to empty string
    no_value: typing.Optional[str]
    # custom is specified by the FieldConfig field
    # in panel plugin schemas.
    custom: typing.Optional[object]

    def __init__(self, display_name: typing.Optional[str] = None, display_name_from_ds: typing.Optional[str] = None, description: typing.Optional[str] = None, path: typing.Optional[str] = None, writeable: typing.Optional[bool] = None, filterable: typing.Optional[bool] = None, unit: typing.Optional[str] = None, decimals: typing.Optional[float] = None, min_val: typing.Optional[float] = None, max_val: typing.Optional[float] = None, mappings: typing.Optional[list['ValueMapping']] = None, thresholds: typing.Optional['ThresholdsConfig'] = None, color: typing.Optional['FieldColor'] = None, links: typing.Optional[list['DashboardLink']] = None, no_value: typing.Optional[str] = None, custom: typing.Optional[object] = None):
        self.display_name = display_name
        self.display_name_from_ds = display_name_from_ds
        self.description = description
        self.path = path
        self.writeable = writeable
        self.filterable = filterable
        self.unit = unit
        self.decimals = decimals
        self.min_val = min_val
        self.max_val = max_val
        self.mappings = mappings
        self.thresholds = thresholds
        self.color = color
        self.links = links
        self.no_value = no_value
        self.custom = custom

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.display_name is not None:
            payload["displayName"] = self.display_name
        if self.display_name_from_ds is not None:
            payload["displayNameFromDS"] = self.display_name_from_ds
        if self.description is not None:
            payload["description"] = self.description
        if self.path is not None:
            payload["path"] = self.path
        if self.writeable is not None:
            payload["writeable"] = self.writeable
        if self.filterable is not None:
            payload["filterable"] = self.filterable
        if self.unit is not None:
            payload["unit"] = self.unit
        if self.decimals is not None:
            payload["decimals"] = self.decimals
        if self.min_val is not None:
            payload["min"] = self.min_val
        if self.max_val is not None:
            payload["max"] = self.max_val
        if self.mappings is not None:
            payload["mappings"] = self.mappings
        if self.thresholds is not None:
            payload["thresholds"] = self.thresholds
        if self.color is not None:
            payload["color"] = self.color
        if self.links is not None:
            payload["links"] = self.links
        if self.no_value is not None:
            payload["noValue"] = self.no_value
        if self.custom is not None:
            payload["custom"] = self.custom
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "displayName" in data:
            args["display_name"] = data["displayName"]
        if "displayNameFromDS" in data:
            args["display_name_from_ds"] = data["displayNameFromDS"]
        if "description" in data:
            args["description"] = data["description"]
        if "path" in data:
            args["path"] = data["path"]
        if "writeable" in data:
            args["writeable"] = data["writeable"]
        if "filterable" in data:
            args["filterable"] = data["filterable"]
        if "unit" in data:
            args["unit"] = data["unit"]
        if "decimals" in data:
            args["decimals"] = data["decimals"]
        if "min" in data:
            args["min_val"] = data["min"]
        if "max" in data:
            args["max_val"] = data["max"]
        if "mappings" in data:
            decoding_map_mappings_array_ref_union: dict[str, typing.Union[typing.Type[RangeMap], typing.Type[RegexMap], typing.Type[SpecialValueMap], typing.Type[ValueMap]]] = {"range": RangeMap, "regex": RegexMap, "special": SpecialValueMap, "value": ValueMap}
            args["mappings"] = [decoding_map_mappings_array_ref_union[item["type"]].from_json(item) for item in data["mappings"]]
        if "thresholds" in data:
            args["thresholds"] = ThresholdsConfig.from_json(data["thresholds"])
        if "color" in data:
            args["color"] = FieldColor.from_json(data["color"])
        if "links" in data:
            args["links"] = [DashboardLink.from_json(item) for item in data["links"]]
        if "noValue" in data:
            args["no_value"] = data["noValue"]
        if "custom" in data:
            args["custom"] = data["custom"]        

        return cls(**args)


# Allow to transform the visual representation of specific data values in a visualization, irrespective of their original units
ValueMapping: typing.TypeAlias = typing.Union['ValueMap', 'RangeMap', 'RegexMap', 'SpecialValueMap']


class ValueMap:
    """
    Maps text values to a color or different display text and color.
    For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
    """

    type_val: str
    # Map with <value_to_match>: ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } }
    options: dict[str, 'ValueMappingResult']

    def __init__(self, options: typing.Optional[dict[str, 'ValueMappingResult']] = None):
        self.type_val = MappingType.VALUE_TO_TEXT
        self.options = options if options is not None else {}

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
            "options": self.options,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "options" in data:
            args["options"] = {key: ValueMappingResult.from_json(data["options"][key]) for key in data["options"].keys()}        

        return cls(**args)


class MappingType(enum.StrEnum):
    """
    Supported value mapping types
    `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
    `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
    `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
    `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.
    """

    VALUE_TO_TEXT = "value"
    RANGE_TO_TEXT = "range"
    REGEX_TO_TEXT = "regex"
    SPECIAL_VALUE = "special"


class ValueMappingResult:
    """
    Result used as replacement with text and color when the value matches
    """

    # Text to display when the value matches
    text: typing.Optional[str]
    # Text to use when the value matches
    color: typing.Optional[str]
    # Icon to display when the value matches. Only specific visualizations.
    icon: typing.Optional[str]
    # Position in the mapping array. Only used internally.
    index: typing.Optional[int]

    def __init__(self, text: typing.Optional[str] = None, color: typing.Optional[str] = None, icon: typing.Optional[str] = None, index: typing.Optional[int] = None):
        self.text = text
        self.color = color
        self.icon = icon
        self.index = index

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.text is not None:
            payload["text"] = self.text
        if self.color is not None:
            payload["color"] = self.color
        if self.icon is not None:
            payload["icon"] = self.icon
        if self.index is not None:
            payload["index"] = self.index
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "text" in data:
            args["text"] = data["text"]
        if "color" in data:
            args["color"] = data["color"]
        if "icon" in data:
            args["icon"] = data["icon"]
        if "index" in data:
            args["index"] = data["index"]        

        return cls(**args)


class RangeMap:
    """
    Maps numerical ranges to a display text and color.
    For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
    """

    type_val: str
    # Range to match against and the result to apply when the value is within the range
    options: 'DashboardRangeMapOptions'

    def __init__(self, options: typing.Optional['DashboardRangeMapOptions'] = None):
        self.type_val = MappingType.RANGE_TO_TEXT
        self.options = options if options is not None else DashboardRangeMapOptions()

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
            "options": self.options,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "options" in data:
            args["options"] = DashboardRangeMapOptions.from_json(data["options"])        

        return cls(**args)


class RegexMap:
    """
    Maps regular expressions to replacement text and a color.
    For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
    """

    type_val: str
    # Regular expression to match against and the result to apply when the value matches the regex
    options: 'DashboardRegexMapOptions'

    def __init__(self, options: typing.Optional['DashboardRegexMapOptions'] = None):
        self.type_val = MappingType.REGEX_TO_TEXT
        self.options = options if options is not None else DashboardRegexMapOptions()

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
            "options": self.options,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "options" in data:
            args["options"] = DashboardRegexMapOptions.from_json(data["options"])        

        return cls(**args)


class SpecialValueMap:
    """
    Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.
    See SpecialValueMatch to see the list of special values.
    For example, you can configure a special value mapping so that null values appear as N/A.
    """

    type_val: str
    options: 'DashboardSpecialValueMapOptions'

    def __init__(self, options: typing.Optional['DashboardSpecialValueMapOptions'] = None):
        self.type_val = MappingType.SPECIAL_VALUE
        self.options = options if options is not None else DashboardSpecialValueMapOptions()

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
            "options": self.options,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "options" in data:
            args["options"] = DashboardSpecialValueMapOptions.from_json(data["options"])        

        return cls(**args)


class SpecialValueMatch(enum.StrEnum):
    """
    Special value types supported by the `SpecialValueMap`
    """

    TRUE = "true"
    FALSE = "false"
    NULL = "null"
    NA_N = "nan"
    NULL_AND_NAN = "null+nan"
    EMPTY = "empty"


class ThresholdsConfig:
    """
    Thresholds configuration for the panel
    """

    # Thresholds mode.
    mode: 'ThresholdsMode'
    # Must be sorted by 'value', first value is always -Infinity
    steps: list['Threshold']

    def __init__(self, mode: typing.Optional['ThresholdsMode'] = None, steps: typing.Optional[list['Threshold']] = None):
        self.mode = mode if mode is not None else ThresholdsMode.ABSOLUTE
        self.steps = steps if steps is not None else []

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "mode": self.mode,
            "steps": self.steps,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "mode" in data:
            args["mode"] = data["mode"]
        if "steps" in data:
            args["steps"] = [Threshold.from_json(item) for item in data["steps"]]        

        return cls(**args)


class ThresholdsMode(enum.StrEnum):
    """
    Thresholds can either be `absolute` (specific number) or `percentage` (relative to min or max, it will be values between 0 and 1).
    """

    ABSOLUTE = "absolute"
    PERCENTAGE = "percentage"


class Threshold:
    """
    User-defined value for a metric that triggers visual changes in a panel when this value is met or exceeded
    They are used to conditionally style and color visualizations based on query results , and can be applied to most visualizations.
    """

    # Value represents a specified metric for the threshold, which triggers a visual change in the dashboard when this value is met or exceeded.
    # Nulls currently appear here when serializing -Infinity to JSON.
    value: typing.Optional[float]
    # Color represents the color of the visual change that will occur in the dashboard when the threshold value is met or exceeded.
    color: str

    def __init__(self, value: typing.Optional[float] = None, color: str = ""):
        self.value = value
        self.color = color

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "value": self.value,
            "color": self.color,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "value" in data:
            args["value"] = data["value"]
        if "color" in data:
            args["color"] = data["color"]        

        return cls(**args)


class FieldColor:
    """
    Map a field to a color.
    """

    # The main color scheme mode.
    mode: 'FieldColorModeId'
    # The fixed color value for fixed or shades color modes.
    fixed_color: typing.Optional[str]
    # Some visualizations need to know how to assign a series color from by value color schemes.
    series_by: typing.Optional['FieldColorSeriesByMode']

    def __init__(self, mode: typing.Optional['FieldColorModeId'] = None, fixed_color: typing.Optional[str] = None, series_by: typing.Optional['FieldColorSeriesByMode'] = None):
        self.mode = mode if mode is not None else FieldColorModeId.THRESHOLDS
        self.fixed_color = fixed_color
        self.series_by = series_by

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "mode": self.mode,
        }
        if self.fixed_color is not None:
            payload["fixedColor"] = self.fixed_color
        if self.series_by is not None:
            payload["seriesBy"] = self.series_by
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "mode" in data:
            args["mode"] = data["mode"]
        if "fixedColor" in data:
            args["fixed_color"] = data["fixedColor"]
        if "seriesBy" in data:
            args["series_by"] = data["seriesBy"]        

        return cls(**args)


class FieldColorModeId(enum.StrEnum):
    """
    Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.
    Continuous color interpolates a color using the percentage of a value relative to min and max.
    Accepted values are:
    `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold
    `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations
    `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations
    `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode
    `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode
    `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode
    `continuous-YlRd`: Continuous Yellow-Red palette mode
    `continuous-BlPu`: Continuous Blue-Purple palette mode
    `continuous-YlBl`: Continuous Yellow-Blue palette mode
    `continuous-blues`: Continuous Blue palette mode
    `continuous-reds`: Continuous Red palette mode
    `continuous-greens`: Continuous Green palette mode
    `continuous-purples`: Continuous Purple palette mode
    `shades`: Shades of a single color. Specify a single color, useful in an override rule.
    `fixed`: Fixed color mode. Specify a single color, useful in an override rule.
    """

    THRESHOLDS = "thresholds"
    PALETTE_CLASSIC = "palette-classic"
    PALETTE_CLASSIC_BY_NAME = "palette-classic-by-name"
    CONTINUOUS_GR_YL_RD = "continuous-GrYlRd"
    CONTINUOUS_RD_YL_GR = "continuous-RdYlGr"
    CONTINUOUS_BL_YL_RD = "continuous-BlYlRd"
    CONTINUOUS_YL_RD = "continuous-YlRd"
    CONTINUOUS_BL_PU = "continuous-BlPu"
    CONTINUOUS_YL_BL = "continuous-YlBl"
    CONTINUOUS_BLUES = "continuous-blues"
    CONTINUOUS_REDS = "continuous-reds"
    CONTINUOUS_GREENS = "continuous-greens"
    CONTINUOUS_PURPLES = "continuous-purples"
    FIXED = "fixed"
    SHADES = "shades"


class FieldColorSeriesByMode(enum.StrEnum):
    """
    Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.
    """

    MIN = "min"
    MAX = "max"
    LAST = "last"


class DynamicConfigValue:
    id_val: str
    value: typing.Optional[object]

    def __init__(self, id_val: str = "", value: typing.Optional[object] = None):
        self.id_val = id_val
        self.value = value

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "id": self.id_val,
        }
        if self.value is not None:
            payload["value"] = self.value
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "id" in data:
            args["id_val"] = data["id"]
        if "value" in data:
            args["value"] = data["value"]        

        return cls(**args)


class RowPanel:
    """
    Row panel
    """

    # The panel type
    type_val: typing.Literal["row"]
    # Whether this row should be collapsed or not.
    collapsed: bool
    # Row title
    title: typing.Optional[str]
    # Name of default datasource for the row
    datasource: typing.Optional['DataSourceRef']
    # Row grid position
    grid_pos: typing.Optional['GridPos']
    # Unique identifier of the panel. Generated by Grafana when creating a new panel. It must be unique within a dashboard, but not globally.
    id_val: int
    # List of panels in the row
    panels: list['Panel']
    # Name of template variable to repeat for.
    repeat: typing.Optional[str]

    def __init__(self, collapsed: bool = False, title: typing.Optional[str] = None, datasource: typing.Optional['DataSourceRef'] = None, grid_pos: typing.Optional['GridPos'] = None, id_val: int = 0, panels: typing.Optional[list['Panel']] = None, repeat: typing.Optional[str] = None):
        self.type_val = "row"
        self.collapsed = collapsed
        self.title = title
        self.datasource = datasource
        self.grid_pos = grid_pos
        self.id_val = id_val
        self.panels = panels if panels is not None else []
        self.repeat = repeat

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
            "collapsed": self.collapsed,
            "id": self.id_val,
            "panels": self.panels,
        }
        if self.title is not None:
            payload["title"] = self.title
        if self.datasource is not None:
            payload["datasource"] = self.datasource
        if self.grid_pos is not None:
            payload["gridPos"] = self.grid_pos
        if self.repeat is not None:
            payload["repeat"] = self.repeat
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "collapsed" in data:
            args["collapsed"] = data["collapsed"]
        if "title" in data:
            args["title"] = data["title"]
        if "datasource" in data:
            args["datasource"] = DataSourceRef.from_json(data["datasource"])
        if "gridPos" in data:
            args["grid_pos"] = GridPos.from_json(data["gridPos"])
        if "id" in data:
            args["id_val"] = data["id"]
        if "panels" in data:
            args["panels"] = [Panel.from_json(item) for item in data["panels"]]
        if "repeat" in data:
            args["repeat"] = data["repeat"]        

        return cls(**args)


class VariableModel:
    """
    A variable is a placeholder for a value. You can use variables in metric queries and in panel titles.
    """

    # Type of variable
    type_val: 'VariableType'
    # Name of variable
    name: str
    # Optional display name
    label: typing.Optional[str]
    # Visibility configuration for the variable
    hide: typing.Optional['VariableHide']
    # Whether the variable value should be managed by URL query params or not
    skip_url_sync: typing.Optional[bool]
    # Description of variable. It can be defined but `null`.
    description: typing.Optional[str]
    # Query used to fetch values for a variable
    query: typing.Optional[typing.Union[str, dict[str, object]]]
    # Data source used to fetch values for a variable. It can be defined but `null`.
    datasource: typing.Optional['DataSourceRef']
    # Shows current selected variable text/value on the dashboard
    current: typing.Optional['VariableOption']
    # Whether multiple values can be selected or not from variable value list
    multi: typing.Optional[bool]
    # Options that can be selected for a variable.
    options: typing.Optional[list['VariableOption']]
    # Options to config when to refresh a variable
    refresh: typing.Optional['VariableRefresh']
    # Options sort order
    sort: typing.Optional['VariableSort']
    # Whether all value option is available or not
    include_all: typing.Optional[bool]
    # Custom all value
    all_value: typing.Optional[str]
    # Optional field, if you want to extract part of a series name or metric node segment.
    # Named capture groups can be used to separate the display text and value.
    regex: typing.Optional[str]
    # Dynamically calculates interval by dividing time range by the count specified.
    auto: typing.Optional[bool]
    # The minimum threshold below which the step count intervals will not divide the time.
    auto_min: typing.Optional[str]
    # How many times the current time range should be divided to calculate the value, similar to the Max data points query option.
    # For example, if the current visible time range is 30 minutes, then the auto interval groups the data into 30 one-minute increments.
    auto_count: typing.Optional[int]

    def __init__(self, type_val: typing.Optional['VariableType'] = None, name: str = "", label: typing.Optional[str] = None, hide: typing.Optional['VariableHide'] = None, skip_url_sync: typing.Optional[bool] = False, description: typing.Optional[str] = None, query: typing.Optional[typing.Union[str, dict[str, object]]] = None, datasource: typing.Optional['DataSourceRef'] = None, current: typing.Optional['VariableOption'] = None, multi: typing.Optional[bool] = False, options: typing.Optional[list['VariableOption']] = None, refresh: typing.Optional['VariableRefresh'] = None, sort: typing.Optional['VariableSort'] = None, include_all: typing.Optional[bool] = False, all_value: typing.Optional[str] = None, regex: typing.Optional[str] = None, auto: typing.Optional[bool] = False, auto_min: typing.Optional[str] = "10s", auto_count: typing.Optional[int] = 30):
        self.type_val = type_val if type_val is not None else VariableType.QUERY
        self.name = name
        self.label = label
        self.hide = hide
        self.skip_url_sync = skip_url_sync
        self.description = description
        self.query = query
        self.datasource = datasource
        self.current = current
        self.multi = multi
        self.options = options
        self.refresh = refresh
        self.sort = sort
        self.include_all = include_all
        self.all_value = all_value
        self.regex = regex
        self.auto = auto
        self.auto_min = auto_min
        self.auto_count = auto_count

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "type": self.type_val,
            "name": self.name,
        }
        if self.label is not None:
            payload["label"] = self.label
        if self.hide is not None:
            payload["hide"] = self.hide
        if self.skip_url_sync is not None:
            payload["skipUrlSync"] = self.skip_url_sync
        if self.description is not None:
            payload["description"] = self.description
        if self.query is not None:
            payload["query"] = self.query
        if self.datasource is not None:
            payload["datasource"] = self.datasource
        if self.current is not None:
            payload["current"] = self.current
        if self.multi is not None:
            payload["multi"] = self.multi
        if self.options is not None:
            payload["options"] = self.options
        if self.refresh is not None:
            payload["refresh"] = self.refresh
        if self.sort is not None:
            payload["sort"] = self.sort
        if self.include_all is not None:
            payload["includeAll"] = self.include_all
        if self.all_value is not None:
            payload["allValue"] = self.all_value
        if self.regex is not None:
            payload["regex"] = self.regex
        if self.auto is not None:
            payload["auto"] = self.auto
        if self.auto_min is not None:
            payload["auto_min"] = self.auto_min
        if self.auto_count is not None:
            payload["auto_count"] = self.auto_count
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "type" in data:
            args["type_val"] = data["type"]
        if "name" in data:
            args["name"] = data["name"]
        if "label" in data:
            args["label"] = data["label"]
        if "hide" in data:
            args["hide"] = data["hide"]
        if "skipUrlSync" in data:
            args["skip_url_sync"] = data["skipUrlSync"]
        if "description" in data:
            args["description"] = data["description"]
        if "query" in data:
            args["query"] = data["query"]
        if "datasource" in data:
            args["datasource"] = DataSourceRef.from_json(data["datasource"])
        if "current" in data:
            args["current"] = VariableOption.from_json(data["current"])
        if "multi" in data:
            args["multi"] = data["multi"]
        if "options" in data:
            args["options"] = [VariableOption.from_json(item) for item in data["options"]]
        if "refresh" in data:
            args["refresh"] = data["refresh"]
        if "sort" in data:
            args["sort"] = data["sort"]
        if "includeAll" in data:
            args["include_all"] = data["includeAll"]
        if "allValue" in data:
            args["all_value"] = data["allValue"]
        if "regex" in data:
            args["regex"] = data["regex"]
        if "auto" in data:
            args["auto"] = data["auto"]
        if "auto_min" in data:
            args["auto_min"] = data["auto_min"]
        if "auto_count" in data:
            args["auto_count"] = data["auto_count"]        

        return cls(**args)


class VariableType(enum.StrEnum):
    """
    Dashboard variable type
    `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on.
    `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only).
    `constant`: 	Define a hidden constant.
    `datasource`: Quickly change the data source for an entire dashboard.
    `interval`: Interval variables represent time spans.
    `textbox`: Display a free text input field with an optional default value.
    `custom`: Define the variable options manually using a comma-separated list.
    `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables
    """

    QUERY = "query"
    ADHOC = "adhoc"
    CONSTANT = "constant"
    DATASOURCE = "datasource"
    INTERVAL = "interval"
    TEXTBOX = "textbox"
    CUSTOM = "custom"
    SYSTEM = "system"


class VariableHide(enum.IntEnum):
    """
    Determine if the variable shows on dashboard
    Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing).
    """

    DONT_HIDE = 0
    HIDE_LABEL = 1
    HIDE_VARIABLE = 2


class VariableOption:
    """
    Option to be selected in a variable.
    """

    # Whether the option is selected or not
    selected: typing.Optional[bool]
    # Text to be displayed for the option
    text: typing.Union[str, list[str]]
    # Value of the option
    value: typing.Union[str, list[str]]

    def __init__(self, selected: typing.Optional[bool] = None, text: typing.Optional[typing.Union[str, list[str]]] = None, value: typing.Optional[typing.Union[str, list[str]]] = None):
        self.selected = selected
        self.text = text if text is not None else ""
        self.value = value if value is not None else ""

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "text": self.text,
            "value": self.value,
        }
        if self.selected is not None:
            payload["selected"] = self.selected
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "selected" in data:
            args["selected"] = data["selected"]
        if "text" in data:
            args["text"] = data["text"]
        if "value" in data:
            args["value"] = data["value"]        

        return cls(**args)


class VariableRefresh(enum.IntEnum):
    """
    Options to config when to refresh a variable
    `0`: Never refresh the variable
    `1`: Queries the data source every time the dashboard loads.
    `2`: Queries the data source when the dashboard time range changes.
    """

    NEVER = 0
    ON_DASHBOARD_LOAD = 1
    ON_TIME_RANGE_CHANGED = 2


class VariableSort(enum.IntEnum):
    """
    Sort variable options
    Accepted values are:
    `0`: No sorting
    `1`: Alphabetical ASC
    `2`: Alphabetical DESC
    `3`: Numerical ASC
    `4`: Numerical DESC
    `5`: Alphabetical Case Insensitive ASC
    `6`: Alphabetical Case Insensitive DESC
    `7`: Natural ASC
    `8`: Natural DESC
    """

    DISABLED = 0
    ALPHABETICAL_ASC = 1
    ALPHABETICAL_DESC = 2
    NUMERICAL_ASC = 3
    NUMERICAL_DESC = 4
    ALPHABETICAL_CASE_INSENSITIVE_ASC = 5
    ALPHABETICAL_CASE_INSENSITIVE_DESC = 6
    NATURAL_ASC = 7
    NATURAL_DESC = 8


class AnnotationContainer:
    """
    Contains the list of annotations that are associated with the dashboard.
    Annotations are used to overlay event markers and overlay event tags on graphs.
    Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API.
    See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/
    """

    # List of annotations
    list_val: typing.Optional[list['AnnotationQuery']]

    def __init__(self, list_val: typing.Optional[list['AnnotationQuery']] = None):
        self.list_val = list_val

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.list_val is not None:
            payload["list"] = self.list_val
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "list" in data:
            args["list_val"] = [AnnotationQuery.from_json(item) for item in data["list"]]        

        return cls(**args)


class AnnotationQuery:
    """
    TODO docs
    FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
    """

    # Name of annotation.
    name: str
    # Datasource where the annotations data is
    datasource: 'DataSourceRef'
    # When enabled the annotation query is issued with every dashboard refresh
    enable: bool
    # Annotation queries can be toggled on or off at the top of the dashboard.
    # When hide is true, the toggle is not shown in the dashboard.
    hide: typing.Optional[bool]
    # Color to use for the annotation event markers
    icon_color: str
    # Filters to apply when fetching annotations
    filter_val: typing.Optional['AnnotationPanelFilter']
    # TODO.. this should just be a normal query target
    target: typing.Optional['AnnotationTarget']
    # TODO -- this should not exist here, it is based on the --grafana-- datasource
    type_val: typing.Optional[str]
    # Set to 1 for the standard annotation query all dashboards have by default.
    built_in: typing.Optional[float]
    expr: typing.Optional[str]

    def __init__(self, name: str = "", datasource: typing.Optional['DataSourceRef'] = None, enable: bool = True, hide: typing.Optional[bool] = False, icon_color: str = "", filter_val: typing.Optional['AnnotationPanelFilter'] = None, target: typing.Optional['AnnotationTarget'] = None, type_val: typing.Optional[str] = None, built_in: typing.Optional[float] = 0, expr: typing.Optional[str] = None):
        self.name = name
        self.datasource = datasource if datasource is not None else DataSourceRef()
        self.enable = enable
        self.hide = hide
        self.icon_color = icon_color
        self.filter_val = filter_val
        self.target = target
        self.type_val = type_val
        self.built_in = built_in
        self.expr = expr

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "name": self.name,
            "datasource": self.datasource,
            "enable": self.enable,
            "iconColor": self.icon_color,
        }
        if self.hide is not None:
            payload["hide"] = self.hide
        if self.filter_val is not None:
            payload["filter"] = self.filter_val
        if self.target is not None:
            payload["target"] = self.target
        if self.type_val is not None:
            payload["type"] = self.type_val
        if self.built_in is not None:
            payload["builtIn"] = self.built_in
        if self.expr is not None:
            payload["expr"] = self.expr
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "name" in data:
            args["name"] = data["name"]
        if "datasource" in data:
            args["datasource"] = DataSourceRef.from_json(data["datasource"])
        if "enable" in data:
            args["enable"] = data["enable"]
        if "hide" in data:
            args["hide"] = data["hide"]
        if "iconColor" in data:
            args["icon_color"] = data["iconColor"]
        if "filter" in data:
            args["filter_val"] = AnnotationPanelFilter.from_json(data["filter"])
        if "target" in data:
            args["target"] = AnnotationTarget.from_json(data["target"])
        if "type" in data:
            args["type_val"] = data["type"]
        if "builtIn" in data:
            args["built_in"] = data["builtIn"]
        if "expr" in data:
            args["expr"] = data["expr"]        

        return cls(**args)


class AnnotationPanelFilter:
    # Should the specified panels be included or excluded
    exclude: typing.Optional[bool]
    # Panel IDs that should be included or excluded
    ids: list[int]

    def __init__(self, exclude: typing.Optional[bool] = False, ids: typing.Optional[list[int]] = None):
        self.exclude = exclude
        self.ids = ids if ids is not None else []

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "ids": self.ids,
        }
        if self.exclude is not None:
            payload["exclude"] = self.exclude
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "exclude" in data:
            args["exclude"] = data["exclude"]
        if "ids" in data:
            args["ids"] = data["ids"]        

        return cls(**args)


class AnnotationTarget:
    """
    TODO: this should be a regular DataQuery that depends on the selected dashboard
    these match the properties of the "grafana" datasouce that is default in most dashboards
    """

    # Only required/valid for the grafana datasource...
    # but code+tests is already depending on it so hard to change
    limit: int
    # Only required/valid for the grafana datasource...
    # but code+tests is already depending on it so hard to change
    match_any: bool
    # Only required/valid for the grafana datasource...
    # but code+tests is already depending on it so hard to change
    tags: list[str]
    # Only required/valid for the grafana datasource...
    # but code+tests is already depending on it so hard to change
    type_val: str

    def __init__(self, limit: int = 0, match_any: bool = False, tags: typing.Optional[list[str]] = None, type_val: str = ""):
        self.limit = limit
        self.match_any = match_any
        self.tags = tags if tags is not None else []
        self.type_val = type_val

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "limit": self.limit,
            "matchAny": self.match_any,
            "tags": self.tags,
            "type": self.type_val,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "limit" in data:
            args["limit"] = data["limit"]
        if "matchAny" in data:
            args["match_any"] = data["matchAny"]
        if "tags" in data:
            args["tags"] = data["tags"]
        if "type" in data:
            args["type_val"] = data["type"]        

        return cls(**args)


class Snapshot:
    """
    A dashboard snapshot shares an interactive dashboard publicly.
    It is a read-only version of a dashboard, and is not editable.
    It is possible to create a snapshot of a snapshot.
    Grafana strips away all sensitive information from the dashboard.
    Sensitive information stripped: queries (metric, template,annotation) and panel links.
    """

    # Time when the snapshot was created
    created: str
    # Time when the snapshot expires, default is never to expire
    expires: str
    # Is the snapshot saved in an external grafana instance
    external: bool
    # external url, if snapshot was shared in external grafana instance
    external_url: str
    # Unique identifier of the snapshot
    id_val: int
    # Optional, defined the unique key of the snapshot, required if external is true
    key: str
    # Optional, name of the snapshot
    name: str
    # org id of the snapshot
    org_id: int
    # last time when the snapshot was updated
    updated: str
    # url of the snapshot, if snapshot was shared internally
    url: typing.Optional[str]
    # user id of the snapshot creator
    user_id: int
    dashboard: typing.Optional['Dashboard']

    def __init__(self, created: str = "", expires: str = "", external: bool = False, external_url: str = "", id_val: int = 0, key: str = "", name: str = "", org_id: int = 0, updated: str = "", url: typing.Optional[str] = None, user_id: int = 0, dashboard: typing.Optional['Dashboard'] = None):
        self.created = created
        self.expires = expires
        self.external = external
        self.external_url = external_url
        self.id_val = id_val
        self.key = key
        self.name = name
        self.org_id = org_id
        self.updated = updated
        self.url = url
        self.user_id = user_id
        self.dashboard = dashboard

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "created": self.created,
            "expires": self.expires,
            "external": self.external,
            "externalUrl": self.external_url,
            "id": self.id_val,
            "key": self.key,
            "name": self.name,
            "orgId": self.org_id,
            "updated": self.updated,
            "userId": self.user_id,
        }
        if self.url is not None:
            payload["url"] = self.url
        if self.dashboard is not None:
            payload["dashboard"] = self.dashboard
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "created" in data:
            args["created"] = data["created"]
        if "expires" in data:
            args["expires"] = data["expires"]
        if "external" in data:
            args["external"] = data["external"]
        if "externalUrl" in data:
            args["external_url"] = data["externalUrl"]
        if "id" in data:
            args["id_val"] = data["id"]
        if "key" in data:
            args["key"] = data["key"]
        if "name" in data:
            args["name"] = data["name"]
        if "orgId" in data:
            args["org_id"] = data["orgId"]
        if "updated" in data:
            args["updated"] = data["updated"]
        if "url" in data:
            args["url"] = data["url"]
        if "userId" in data:
            args["user_id"] = data["userId"]
        if "dashboard" in data:
            args["dashboard"] = Dashboard.from_json(data["dashboard"])        

        return cls(**args)


class AnnotationActions:
    can_add: typing.Optional[bool]
    can_delete: typing.Optional[bool]
    can_edit: typing.Optional[bool]

    def __init__(self, can_add: typing.Optional[bool] = None, can_delete: typing.Optional[bool] = None, can_edit: typing.Optional[bool] = None):
        self.can_add = can_add
        self.can_delete = can_delete
        self.can_edit = can_edit

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.can_add is not None:
            payload["canAdd"] = self.can_add
        if self.can_delete is not None:
            payload["canDelete"] = self.can_delete
        if self.can_edit is not None:
            payload["canEdit"] = self.can_edit
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "canAdd" in data:
            args["can_add"] = data["canAdd"]
        if "canDelete" in data:
            args["can_delete"] = data["canDelete"]
        if "canEdit" in data:
            args["can_edit"] = data["canEdit"]        

        return cls(**args)


class AnnotationPermission:
    dashboard: typing.Optional['AnnotationActions']
    organization: typing.Optional['AnnotationActions']

    def __init__(self, dashboard: typing.Optional['AnnotationActions'] = None, organization: typing.Optional['AnnotationActions'] = None):
        self.dashboard = dashboard
        self.organization = organization

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.dashboard is not None:
            payload["dashboard"] = self.dashboard
        if self.organization is not None:
            payload["organization"] = self.organization
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "dashboard" in data:
            args["dashboard"] = AnnotationActions.from_json(data["dashboard"])
        if "organization" in data:
            args["organization"] = AnnotationActions.from_json(data["organization"])        

        return cls(**args)


class DashboardMeta:
    annotations_permissions: typing.Optional['AnnotationPermission']
    can_admin: typing.Optional[bool]
    can_delete: typing.Optional[bool]
    can_edit: typing.Optional[bool]
    can_save: typing.Optional[bool]
    can_star: typing.Optional[bool]
    created: typing.Optional[str]
    created_by: typing.Optional[str]
    expires: typing.Optional[str]
    # Deprecated: use FolderUID instead
    folder_id: typing.Optional[int]
    folder_title: typing.Optional[str]
    folder_uid: typing.Optional[str]
    folder_url: typing.Optional[str]
    has_acl: typing.Optional[bool]
    is_folder: typing.Optional[bool]
    is_snapshot: typing.Optional[bool]
    is_starred: typing.Optional[bool]
    provisioned: typing.Optional[bool]
    provisioned_external_id: typing.Optional[str]
    public_dashboard_enabled: typing.Optional[bool]
    public_dashboard_uid: typing.Optional[str]
    slug: typing.Optional[str]
    type_val: typing.Optional[str]
    updated: typing.Optional[str]
    updated_by: typing.Optional[str]
    url: typing.Optional[str]
    version: typing.Optional[int]

    def __init__(self, annotations_permissions: typing.Optional['AnnotationPermission'] = None, can_admin: typing.Optional[bool] = None, can_delete: typing.Optional[bool] = None, can_edit: typing.Optional[bool] = None, can_save: typing.Optional[bool] = None, can_star: typing.Optional[bool] = None, created: typing.Optional[str] = None, created_by: typing.Optional[str] = None, expires: typing.Optional[str] = None, folder_id: typing.Optional[int] = None, folder_title: typing.Optional[str] = None, folder_uid: typing.Optional[str] = None, folder_url: typing.Optional[str] = None, has_acl: typing.Optional[bool] = None, is_folder: typing.Optional[bool] = None, is_snapshot: typing.Optional[bool] = None, is_starred: typing.Optional[bool] = None, provisioned: typing.Optional[bool] = None, provisioned_external_id: typing.Optional[str] = None, public_dashboard_enabled: typing.Optional[bool] = None, public_dashboard_uid: typing.Optional[str] = None, slug: typing.Optional[str] = None, type_val: typing.Optional[str] = None, updated: typing.Optional[str] = None, updated_by: typing.Optional[str] = None, url: typing.Optional[str] = None, version: typing.Optional[int] = None):
        self.annotations_permissions = annotations_permissions
        self.can_admin = can_admin
        self.can_delete = can_delete
        self.can_edit = can_edit
        self.can_save = can_save
        self.can_star = can_star
        self.created = created
        self.created_by = created_by
        self.expires = expires
        self.folder_id = folder_id
        self.folder_title = folder_title
        self.folder_uid = folder_uid
        self.folder_url = folder_url
        self.has_acl = has_acl
        self.is_folder = is_folder
        self.is_snapshot = is_snapshot
        self.is_starred = is_starred
        self.provisioned = provisioned
        self.provisioned_external_id = provisioned_external_id
        self.public_dashboard_enabled = public_dashboard_enabled
        self.public_dashboard_uid = public_dashboard_uid
        self.slug = slug
        self.type_val = type_val
        self.updated = updated
        self.updated_by = updated_by
        self.url = url
        self.version = version

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.annotations_permissions is not None:
            payload["annotationsPermissions"] = self.annotations_permissions
        if self.can_admin is not None:
            payload["canAdmin"] = self.can_admin
        if self.can_delete is not None:
            payload["canDelete"] = self.can_delete
        if self.can_edit is not None:
            payload["canEdit"] = self.can_edit
        if self.can_save is not None:
            payload["canSave"] = self.can_save
        if self.can_star is not None:
            payload["canStar"] = self.can_star
        if self.created is not None:
            payload["created"] = self.created
        if self.created_by is not None:
            payload["createdBy"] = self.created_by
        if self.expires is not None:
            payload["expires"] = self.expires
        if self.folder_id is not None:
            payload["folderId"] = self.folder_id
        if self.folder_title is not None:
            payload["folderTitle"] = self.folder_title
        if self.folder_uid is not None:
            payload["folderUid"] = self.folder_uid
        if self.folder_url is not None:
            payload["folderUrl"] = self.folder_url
        if self.has_acl is not None:
            payload["hasAcl"] = self.has_acl
        if self.is_folder is not None:
            payload["isFolder"] = self.is_folder
        if self.is_snapshot is not None:
            payload["isSnapshot"] = self.is_snapshot
        if self.is_starred is not None:
            payload["isStarred"] = self.is_starred
        if self.provisioned is not None:
            payload["provisioned"] = self.provisioned
        if self.provisioned_external_id is not None:
            payload["provisionedExternalId"] = self.provisioned_external_id
        if self.public_dashboard_enabled is not None:
            payload["publicDashboardEnabled"] = self.public_dashboard_enabled
        if self.public_dashboard_uid is not None:
            payload["publicDashboardUid"] = self.public_dashboard_uid
        if self.slug is not None:
            payload["slug"] = self.slug
        if self.type_val is not None:
            payload["type"] = self.type_val
        if self.updated is not None:
            payload["updated"] = self.updated
        if self.updated_by is not None:
            payload["updatedBy"] = self.updated_by
        if self.url is not None:
            payload["url"] = self.url
        if self.version is not None:
            payload["version"] = self.version
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "annotationsPermissions" in data:
            args["annotations_permissions"] = AnnotationPermission.from_json(data["annotationsPermissions"])
        if "canAdmin" in data:
            args["can_admin"] = data["canAdmin"]
        if "canDelete" in data:
            args["can_delete"] = data["canDelete"]
        if "canEdit" in data:
            args["can_edit"] = data["canEdit"]
        if "canSave" in data:
            args["can_save"] = data["canSave"]
        if "canStar" in data:
            args["can_star"] = data["canStar"]
        if "created" in data:
            args["created"] = data["created"]
        if "createdBy" in data:
            args["created_by"] = data["createdBy"]
        if "expires" in data:
            args["expires"] = data["expires"]
        if "folderId" in data:
            args["folder_id"] = data["folderId"]
        if "folderTitle" in data:
            args["folder_title"] = data["folderTitle"]
        if "folderUid" in data:
            args["folder_uid"] = data["folderUid"]
        if "folderUrl" in data:
            args["folder_url"] = data["folderUrl"]
        if "hasAcl" in data:
            args["has_acl"] = data["hasAcl"]
        if "isFolder" in data:
            args["is_folder"] = data["isFolder"]
        if "isSnapshot" in data:
            args["is_snapshot"] = data["isSnapshot"]
        if "isStarred" in data:
            args["is_starred"] = data["isStarred"]
        if "provisioned" in data:
            args["provisioned"] = data["provisioned"]
        if "provisionedExternalId" in data:
            args["provisioned_external_id"] = data["provisionedExternalId"]
        if "publicDashboardEnabled" in data:
            args["public_dashboard_enabled"] = data["publicDashboardEnabled"]
        if "publicDashboardUid" in data:
            args["public_dashboard_uid"] = data["publicDashboardUid"]
        if "slug" in data:
            args["slug"] = data["slug"]
        if "type" in data:
            args["type_val"] = data["type"]
        if "updated" in data:
            args["updated"] = data["updated"]
        if "updatedBy" in data:
            args["updated_by"] = data["updatedBy"]
        if "url" in data:
            args["url"] = data["url"]
        if "version" in data:
            args["version"] = data["version"]        

        return cls(**args)


class DashboardDashboardTime:
    from_val: str
    to: str

    def __init__(self, from_val: str = "now-6h", to: str = "now"):
        self.from_val = from_val
        self.to = to

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "from": self.from_val,
            "to": self.to,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "from" in data:
            args["from_val"] = data["from"]
        if "to" in data:
            args["to"] = data["to"]        

        return cls(**args)


class DashboardDashboardTemplating:
    # List of configured template variables with their saved values along with some other metadata
    list_val: typing.Optional[list['VariableModel']]

    def __init__(self, list_val: typing.Optional[list['VariableModel']] = None):
        self.list_val = list_val

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
        }
        if self.list_val is not None:
            payload["list"] = self.list_val
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "list" in data:
            args["list_val"] = [VariableModel.from_json(item) for item in data["list"]]        

        return cls(**args)


class DashboardFieldConfigSourceOverrides:
    matcher: 'MatcherConfig'
    properties: list['DynamicConfigValue']

    def __init__(self, matcher: typing.Optional['MatcherConfig'] = None, properties: typing.Optional[list['DynamicConfigValue']] = None):
        self.matcher = matcher if matcher is not None else MatcherConfig()
        self.properties = properties if properties is not None else []

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "matcher": self.matcher,
            "properties": self.properties,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "matcher" in data:
            args["matcher"] = MatcherConfig.from_json(data["matcher"])
        if "properties" in data:
            args["properties"] = [DynamicConfigValue.from_json(item) for item in data["properties"]]        

        return cls(**args)


class DashboardRangeMapOptions:
    # Min value of the range. It can be null which means -Infinity
    from_val: typing.Optional[float]
    # Max value of the range. It can be null which means +Infinity
    to: typing.Optional[float]
    # Config to apply when the value is within the range
    result: 'ValueMappingResult'

    def __init__(self, from_val: typing.Optional[float] = None, to: typing.Optional[float] = None, result: typing.Optional['ValueMappingResult'] = None):
        self.from_val = from_val
        self.to = to
        self.result = result if result is not None else ValueMappingResult()

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "from": self.from_val,
            "to": self.to,
            "result": self.result,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "from" in data:
            args["from_val"] = data["from"]
        if "to" in data:
            args["to"] = data["to"]
        if "result" in data:
            args["result"] = ValueMappingResult.from_json(data["result"])        

        return cls(**args)


class DashboardRegexMapOptions:
    # Regular expression to match against
    pattern: str
    # Config to apply when the value matches the regex
    result: 'ValueMappingResult'

    def __init__(self, pattern: str = "", result: typing.Optional['ValueMappingResult'] = None):
        self.pattern = pattern
        self.result = result if result is not None else ValueMappingResult()

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "pattern": self.pattern,
            "result": self.result,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "pattern" in data:
            args["pattern"] = data["pattern"]
        if "result" in data:
            args["result"] = ValueMappingResult.from_json(data["result"])        

        return cls(**args)


class DashboardSpecialValueMapOptions:
    # Special value to match against
    match: 'SpecialValueMatch'
    # Config to apply when the value matches the special value
    result: 'ValueMappingResult'

    def __init__(self, match: typing.Optional['SpecialValueMatch'] = None, result: typing.Optional['ValueMappingResult'] = None):
        self.match = match if match is not None else SpecialValueMatch.TRUE
        self.result = result if result is not None else ValueMappingResult()

    def to_json(self) -> dict[str, object]:
        payload: dict[str, object] = {
            "match": self.match,
            "result": self.result,
        }
        return payload

    @classmethod
    def from_json(cls, data: dict[str, typing.Any]) -> typing.Self:
        args: dict[str, typing.Any] = {}
        
        if "match" in data:
            args["match"] = data["match"]
        if "result" in data:
            args["result"] = ValueMappingResult.from_json(data["result"])        

        return cls(**args)



