from ._private._field_api import FieldAPI
from ._private._well_dto import PlotInstanceDto, PlotInstancePaneDto
from ._private.dto_converters._plot_dto_converter import PlotDtoConverter
from .data import Data
from .vector import Vector
from typing import Union, List, Optional, Tuple
from .plot_channel import PlotChannel
from .plots_enum import PlotDataTypesEnum, PlotProperties
from datetime import datetime
from .measure_enum import MeasureEnum
from .datetime_utils import datetime_to_str
from .field_data_types_catalog import FieldDataTypesCatalog
[docs]
class Plot:
""" Plot object.
Presents a KAPPA Automate Plot object.
"""
def __init__(self, field_id: str, well_id: str, plot_id: str, plot_name: str, field_api: FieldAPI, data_types_catalog: FieldDataTypesCatalog, dto_converter: PlotDtoConverter) -> None:
self.__field_api: FieldAPI = field_api
self.__field_id: str = field_id
self.__well_id: str = well_id
self.__id: str = plot_id
self.__name: str = plot_name
self.__data_types_catalog: FieldDataTypesCatalog = data_types_catalog
self.__channels: List[PlotChannel] = list()
self.__pane: List[PlotInstancePaneDto] = list()
self.__dto_converter: PlotDtoConverter = dto_converter
def rename(self, name: str) -> None:
"""
Rename the :class:`Plot` object.
Parameters
----------
name:
Name of the plot.
"""
dto = {'name': name}
self.__name = self.__field_api.rename_plot(self.__field_id, self.__well_id, self.__id, dto)
def add_existing_data(self, data: Data, channel_name: Optional[str] = None, pane_name: Optional[str] = None,
show_symbols: Optional[bool] = None, show_lines: bool = True, use_elapsed: bool = False, is_y_log: bool = False,
hide_in_legend: bool = False, is_raw: bool = False) -> PlotChannel:
"""
Add data to the :class:`Plot` object.
Parameters
----------
data:
Data to plot.
channel_name:
Name of the data.
pane_name:
Name of the pane.
show_symbols:
Show the point on the plot.
show_lines:
Show the lines on the plot.
is_y_log:
Show Y axis as log
use_elapsed:
use elapsed times instead of datetime values
hide_in_legend:
Whether this channel should be hidden or not on the legend
is_raw:
Whether to display raw data, as opposed to preview (default: false)
Returns
-------
:class:`PlotChannel`:
A new Channel added to the :class:`Plot` object
"""
properties = PlotProperties(show_symbols=show_symbols, show_lines=show_lines, channel_name=channel_name, is_y_log=is_y_log, use_elapsed=use_elapsed,
hide_in_legend=hide_in_legend, is_raw=is_raw)
channel = PlotChannel(self.__field_id, self.__data_types_catalog, pane_name)
channel.build_time_series(self.__well_id, data, properties)
self.__channels.append(channel)
return channel
def add_embedded_data(self, data: Union[Vector, Tuple[List[float], List[float]]], channel_name: Optional[str] = None, pane_name: Optional[str] = None,
show_symbols: Optional[bool] = None, show_lines: bool = True, data_type: Optional[Union[PlotDataTypesEnum, str]] = None,
is_by_step: bool = False, x_measure: Optional[MeasureEnum] = None, y_measure: Optional[MeasureEnum] = None, is_y_log: bool = False,
first_x: Optional[datetime] = None, use_elapsed: bool = False, hide_in_legend: bool = False) -> PlotChannel:
"""
Add data to the :class:`Plot` object.
Parameters
----------
data:
Data to plot.
channel_name:
Name of the data.
pane_name:
Name of the pane.
show_symbols:
Show the point on the plot.
show_lines:
Show the lines on the plot.
data_type:
Type of the data.
is_by_step:
True if it is step data.
x_measure:
Unit of the x_axis of the Channel (only for scatter plot).
y_measure:
Unit of the y_axis of the Channel.
is_y_log:
Show Y axis as log
first_x:
specify the first x of step data
use_elapsed:
use elapsed times instead of datetime values
hide_in_legend:
Whether this channel should be hidden or not on the legend
Returns
-------
:class:`PlotChannel`:
A new Channel added to the :class:`Plot` object
"""
properties = PlotProperties(show_symbols=show_symbols, show_lines=show_lines, channel_name=channel_name, is_by_step=is_by_step, x_measure=x_measure,
y_measure=y_measure, first_x=datetime_to_str(first_x), use_elapsed=use_elapsed, hide_in_legend=hide_in_legend,
is_y_log=is_y_log)
channel = PlotChannel(self.__field_id, self.__data_types_catalog, pane_name)
channel.build_embedded(self.__well_id, data, data_type, properties)
self.__channels.append(channel)
return channel
def add_cross_plot_data(self, x_data: Data, y_data: Data, channel_name: Optional[str] = None, pane_name: Optional[str] = None,
show_symbols: Optional[bool] = None, show_lines: bool = True, data_type: Optional[Union[PlotDataTypesEnum, str]] = None,
is_y_log: bool = False, hide_in_legend: bool = False) -> PlotChannel:
"""
Adds cross plot data to the visualization, enabling plotting of X versus Y data points
with customizable display options and metadata. This function is used to define how
data is represented in a cross plot channel and associated visualization components.
Parameters
----------
x_data : Data
The dataset representing the X-axis values for the cross plot.
y_data : Data
The dataset representing the Y-axis values for the cross plot.
channel_name : Optional[str], optional
An optional name associated with the created plot channel. Defaults to None.
pane_name : Optional[str], optional
An optional pane name where the cross plot is defined. Defaults to None.
show_symbols : Optional[bool], optional
Indicates if symbols should be displayed on the plot. Defaults to None.
show_lines : bool, optional
Determines whether lines should be displayed connecting the data points.
Defaults to True.
data_type : Optional[Union[PlotDataTypesEnum, str]], optional
Indicates the data type of the plot, allowing for specific type categorization.
Defaults to None.
is_y_log : bool, optional
Specifies whether the Y-axis of the plot should use a logarithmic scale.
Defaults to False.
hide_in_legend : bool, optional
Determines if the plot should be hidden from the legend in the visualization.
Defaults to False.
Returns
-------
PlotChannel
The constructed and configured plot channel that represents the cross plot
data and associated properties.
"""
properties = PlotProperties(show_symbols=show_symbols, show_lines=show_lines, channel_name=channel_name, hide_in_legend=hide_in_legend,
is_y_log=is_y_log)
channel = PlotChannel(self.__field_id, self.__data_types_catalog, pane_name)
channel.build_cross_plot_channel(self.__well_id, x_data.vector_id, y_data.vector_id, data_type, properties)
self.__channels.append(channel)
return channel
def update_plot(self) -> None:
""" Update the plot in Kappa Automate"""
plot_dto = self.__field_api.get_plot_dto(self.__field_id, self.__well_id, self.__id)
updated_plot_dto = self.__dto_converter.get_plot_update_dto(plot_dto, self.__channels, self.__pane)
try:
self.__field_api.update_plot(self.__field_id, self.__well_id, self.__id, updated_plot_dto)
self.__pane = list()
self.__channels = list()
except ConnectionError as error:
if error.args[0].split(" ")[1] == str(400):
raise ValueError("You probably have nan or None values in your data, you need to remove them")
self.delete()
raise error
def get_plot_dto(self) -> PlotInstanceDto:
"""
Get the plot dto from Kappa Automate
Returns
-------
:class:`PlotInstanceDto`:
The Plot instance dto from Kappa Automate
"""
return self.__field_api.get_plot_dto(self.__field_id, self.__well_id, self.__id)
def delete(self) -> None:
"""
Delete the current plot
"""
self.__field_api.delete_plot(self.__field_id, self.__well_id, self.__id)
@property
def id(self) -> str:
"""
Gets the id of the :class:`Plot` object.
"""
return self.__id
@property
def name(self) -> str:
"""
Gets the name of the :class:`Plot` object.
"""
return self.__name
def add_sub_plot(self, name: str, square_log_cycles: bool = False, stacked_bars: bool = False) -> None:
"""
Add a new subplot or pane to the :class:`Plot` object
Parameters
----------
name:
The name of the subplot
square_log_cycles:
Whether or not to use squared logarithmic cycles
stacked_bars:
Whether or not to use stacked bars
"""
pane = PlotInstancePaneDto(name=name,
squareLogCycles=square_log_cycles,
stackedBars=stacked_bars,
channels=[])
self.__pane.append(pane)