Source code for varats.plot.plot

"""Base plot module."""

import abc
import logging
import typing as tp
from pathlib import Path

import matplotlib.pyplot as plt

from varats.paper.case_study import CaseStudy
from varats.plot.plots import PlotRegistry

LOG = logging.getLogger(__name__)


[docs]class PlotDataEmpty(Exception): """Throw if there was no input data for plotting."""
[docs]class Plot(metaclass=PlotRegistry): """An abstract base class for all plots generated by VaRA-TS.""" def __init__(self, name: str, **kwargs: tp.Any) -> None: self.__name = name self.__style = "classic" self.__saved_extra_args = kwargs @property def name(self) -> str: """ Name of the current plot. Test: >>> Plot('test').name 'test' """ return self.__name @property def style(self) -> str: """ Current plot style. Test: >>> Plot('test').style 'classic' """ return self.__style @style.setter def style(self, new_style: str) -> None: """Access current style of the plot.""" self.__style = new_style @property def plot_kwargs(self) -> tp.Any: """ Access the kwargs passed to the initial plot. Test: >>> p = Plot('test', foo='bar', baz='bazzer') >>> p.plot_kwargs['foo'] 'bar' >>> p.plot_kwargs['baz'] 'bazzer' """ return self.__saved_extra_args
[docs] @staticmethod def supports_stage_separation() -> bool: """True, if the plot supports stage separation, i.e., the plot can be drawn separating the different stages in a case study.""" return False
[docs] @abc.abstractmethod def plot(self, view_mode: bool) -> None: """Plot the current plot to a file."""
[docs] def show(self) -> None: """Show the current plot.""" try: self.plot(True) except PlotDataEmpty: LOG.warning(f"No data for project {self.plot_kwargs['project']}.") return plt.show() plt.close()
[docs] def plot_file_name(self, filetype: str) -> str: """ Get the file name this plot; will be stored to when calling save. Args: filetype: the file type for the plot Returns: the file name the plot will be stored to Test: >>> p = Plot('test', project='bar') >>> p.plot_file_name('svg') 'bar_test.svg' >>> p = Plot('foo', project='bar', plot_case_study=CaseStudy('baz', 42)) >>> p.plot_file_name('png') 'baz_42_foo.png' """ plot_ident = '' if self.plot_kwargs.get('plot_case_study', None): case_study: CaseStudy = self.plot_kwargs['plot_case_study'] plot_ident = f"{case_study.project_name}_{case_study.version}_" elif 'project' in self.plot_kwargs: plot_ident = f"{self.plot_kwargs['project']}_" sep_stages = '' if self.supports_stage_separation( ) and self.plot_kwargs.get('sep_stages', None): sep_stages = 'S' return f"{plot_ident}{self.name}{sep_stages}.{filetype}"
[docs] def save( self, path: tp.Optional[Path] = None, filetype: str = 'svg' ) -> None: """ Save the current plot to a file. Args: path: The path where the file is stored (excluding the file name). filetype: The file type of the plot. """ try: self.plot(False) except PlotDataEmpty: LOG.warning(f"No data for project {self.plot_kwargs['project']}.") return if path is None: plot_dir = Path(self.plot_kwargs["plot_dir"]) else: plot_dir = path # TODO (se-passau/VaRA#545): refactor dpi into plot_config. plt.savefig( plot_dir / self.plot_file_name(filetype), dpi=1200, bbox_inches="tight", format=filetype ) plt.close()
[docs] @abc.abstractmethod def calc_missing_revisions(self, boundary_gradient: float) -> tp.Set[str]: """ Calculate a list of revisions that could improve precisions of this plot. Args: boundary_gradient: The maximal expected gradient in percent between two revisions, every thing that exceeds the boundary should be further analyzed. """