Coverage for source/plotting/plot_responsibility_chain_base.py: 93%

29 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-07-30 20:59 +0000

1# plotting/plot_responsibility_chain_base.py 

2 

3# global imports 

4import matplotlib.pyplot as plt 

5from abc import ABC, abstractmethod 

6from reportlab.lib.pagesizes import letter 

7from reportlab.lib.units import inch 

8from typing import Optional 

9 

10# local imports 

11 

12class PlotResponsibilityChainBase(ABC): 

13 """ 

14 Base class for implementing the responsibility chain pattern for plotting. 

15 

16 This class provides a framework for creating a chain of plotting handlers where 

17 each handler can decide whether it can handle a specific plot request. If a handler 

18 cannot process the request, it passes the request to the next handler in the chain. 

19 All plotting chain implementations should inherit from this class and implement 

20 the _can_plot and _plot methods. 

21 """ 

22 

23 # local constants 

24 __KEY_STR: str = 'key' 

25 __PLOT_DATA_STR: str = 'plot_data' 

26 __next_chain_link: 'PlotResponsibilityChainBase' = None 

27 

28 # derived constants 

29 _EXPECTED_FIGURE_SIZE = (int(letter[0] // inch - 1), int(letter[1] // inch - 2)) 

30 

31 def plot(self, data) -> Optional[plt.Axes]: 

32 """ 

33 Attempts to plot data by finding an appropriate handler in the chain. 

34 

35 Parameters: 

36 data (dict): Dictionary containing 'key' identifying the plot type 

37 and 'plot_data' containing the actual data to be plotted. 

38 

39 Returns: 

40 Optional[plt.Axes]: The matplotlib Axes object if plotting was successful, 

41 None if no handler in the chain could process the request. 

42 """ 

43 

44 key = data[self.__KEY_STR] 

45 plot_data = data[self.__PLOT_DATA_STR] 

46 

47 if (self._can_plot(key)): 

48 return self._plot(plot_data) 

49 else: 

50 if self.__next_chain_link is not None: 

51 return self.__next_chain_link.plot(data) 

52 else: 

53 return None 

54 

55 def add_next_chain_link(self, next_chain_link: 'PlotResponsibilityChainBase'): 

56 """ 

57 Adds the next handler to the responsibility chain. 

58 

59 Parameters: 

60 next_chain_link (PlotResponsibilityChainBase): The next handler to process 

61 requests if this handler cannot. 

62 """ 

63 

64 current_last_chain_link = self 

65 while current_last_chain_link.__next_chain_link is not None: 

66 current_last_chain_link = current_last_chain_link.__next_chain_link 

67 

68 current_last_chain_link.__next_chain_link = next_chain_link 

69 

70 @abstractmethod 

71 def _can_plot(self, key: str) -> bool: 

72 """ 

73 Determines if this handler can plot the given plot type. 

74 

75 This is an abstract method that should be implemented by all subclasses. 

76 

77 Parameters: 

78 key (str): String identifier of the plot type. 

79 

80 Returns: 

81 bool: True if this handler can process the plot request, False otherwise. 

82 """ 

83 pass 

84 

85 @abstractmethod 

86 def _plot(self, plot_data: dict) -> plt.Axes: 

87 """ 

88 Generates the actual plot from the provided data. 

89 

90 This is an abstract method that should be implemented by all subclasses. 

91 The implementation should create and return a matplotlib plot based on 

92 the provided data. 

93 

94 Parameters: 

95 plot_data (dict): Dictionary containing the data needed for plotting. 

96 

97 Returns: 

98 plt.Axes: Matplotlib Axes object containing the generated plot. 

99 """ 

100 

101 pass