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

24 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-06-06 12:00 +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 __next_chain_link: 'PlotResponsibilityChainBase' = None 

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

25 

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

27 """ 

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

29 

30 Parameters: 

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

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

33 

34 Returns: 

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

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

37 """ 

38 

39 key = data['key'] 

40 plot_data = data['plot_data'] 

41 

42 if (self._can_plot(key)): 

43 return self._plot(plot_data) 

44 else: 

45 if self.__next_chain_link is not None: 

46 return self.__next_chain_link.plot(data) 

47 else: 

48 return None 

49 

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

51 """ 

52 Adds the next handler to the responsibility chain. 

53 

54 Parameters: 

55 next_chain_link (PlotResponsibilityChainBase): The next handler to process 

56 requests if this handler cannot. 

57 """ 

58 

59 self.__next_chain_link = next_chain_link 

60 

61 @abstractmethod 

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

63 """ 

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

65 

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

67 

68 Parameters: 

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

70 

71 Returns: 

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

73 """ 

74 pass 

75 

76 @abstractmethod 

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

78 """ 

79 Generates the actual plot from the provided data. 

80 

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

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

83 the provided data. 

84 

85 Parameters: 

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

87 

88 Returns: 

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

90 """ 

91 

92 pass