Coverage for cc_modules/cc_trackerhelpers.py: 42%

53 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-08 23:14 +0000

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/cc_trackerhelpers.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry. 

9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk). 

10 

11 This file is part of CamCOPS. 

12 

13 CamCOPS is free software: you can redistribute it and/or modify 

14 it under the terms of the GNU General Public License as published by 

15 the Free Software Foundation, either version 3 of the License, or 

16 (at your option) any later version. 

17 

18 CamCOPS is distributed in the hope that it will be useful, 

19 but WITHOUT ANY WARRANTY; without even the implied warranty of 

20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

21 GNU General Public License for more details. 

22 

23 You should have received a copy of the GNU General Public License 

24 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

25 

26=============================================================================== 

27 

28**Helper representations for trackers.** 

29 

30""" 

31 

32from enum import Enum 

33from typing import List, Optional 

34 

35import numpy as np 

36 

37 

38DEFAULT_TRACKER_ASPECT_RATIO = 2.0 # width / height 

39 

40 

41class LabelAlignment(Enum): 

42 """ 

43 Enum representing figure label alignment. 

44 """ 

45 

46 center = "center" 

47 top = "top" 

48 bottom = "bottom" 

49 baseline = "baseline" 

50 

51 

52class TrackerLabel(object): 

53 """ 

54 Representation of a label on a 

55 :class:`camcops_server.cc_modules.cc_tracker.Tracker` figure. 

56 """ 

57 

58 def __init__( 

59 self, 

60 y: float, 

61 label: str, 

62 vertical_alignment: LabelAlignment = LabelAlignment.center, 

63 ): 

64 """ 

65 Args: 

66 y: Y axis (vertical) position 

67 label: text for label 

68 vertical_alignment: :class:`LabelAlignment` enum 

69 """ 

70 self.y = y 

71 self.label = label 

72 self.vertical_alignment = vertical_alignment 

73 

74 def __str__(self) -> str: 

75 return f"{self.y}: {self.label}" 

76 

77 

78class TrackerAxisTick(object): 

79 """ 

80 Representation of a Y-axis tick mark and associated label on a 

81 :class:`camcops_server.cc_modules.cc_tracker.Tracker` figure. 

82 """ 

83 

84 def __init__(self, y: float, label: str): 

85 self.y = y 

86 self.label = label 

87 

88 

89class TrackerInfo(object): 

90 """ 

91 Tasks return one or more of these (one for each tracker to be shown), from 

92 which :class:`camcops_server.cc_modules.cc_tracker.Tracker` displays are 

93 created. 

94 """ 

95 

96 def __init__( 

97 self, 

98 value: float, 

99 plot_label: str = None, 

100 axis_label: str = None, 

101 axis_min: float = None, 

102 axis_max: float = None, 

103 axis_ticks: Optional[List[TrackerAxisTick]] = None, 

104 horizontal_lines: Optional[List[float]] = None, 

105 horizontal_labels: Optional[List[TrackerLabel]] = None, 

106 aspect_ratio: Optional[float] = DEFAULT_TRACKER_ASPECT_RATIO, 

107 ): 

108 """ 

109 Args: 

110 value: numerical value 

111 plot_label: label for the whole plot 

112 axis_label: label for the Y axis 

113 axis_min: minimum value for the Y axis 

114 axis_max: maximum value for the Y axis 

115 axis_ticks: optional list of :class:`TrackerAxisTick` objects 

116 describing where to put tick marks/labels on the Y axis 

117 horizontal_lines: optional list of y values at which to draw 

118 horizontal (dotted) lines 

119 horizontal_labels: optional list of :class:`TrackerLabel` objects 

120 indicating which additional labels to place on the main plot 

121 (such as: to describe the meaning of the horizontal lines) 

122 aspect_ratio: optional aspect ratio (width / height) 

123 """ 

124 self.value = value 

125 self.plot_label = plot_label 

126 self.axis_label = axis_label 

127 self.axis_min = axis_min 

128 self.axis_max = axis_max 

129 self.axis_ticks = axis_ticks or [] 

130 self.horizontal_lines = horizontal_lines or [] 

131 self.horizontal_labels = horizontal_labels or [] 

132 self.aspect_ratio = aspect_ratio 

133 

134 

135def equally_spaced_ndarray( 

136 start: float, stop: float, num: int, endpoint: bool = True 

137) -> np.ndarray: 

138 """ 

139 Produces equally spaced numbers. See 

140 https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value. 

141 

142 Args: 

143 start: starting value 

144 stop: stopping value 

145 num: number of values to return 

146 endpoint: include the endpoint? 

147 

148 Returns: 

149 list of floats 

150 

151 """ # noqa 

152 return np.linspace(start, stop, num, endpoint=endpoint, dtype=float) 

153 

154 

155def equally_spaced_float( 

156 start: float, stop: float, num: int, endpoint: bool = True 

157) -> List[float]: 

158 """ 

159 Returns a float equivalent of :func:`equally_spaced_float` (q.v.). 

160 """ 

161 return list(equally_spaced_ndarray(start, stop, num, endpoint=endpoint)) 

162 

163 

164def equally_spaced_int( 

165 start: int, stop: int, step: int, endpoint: bool = True 

166) -> List[int]: 

167 """ 

168 Almost a synonym for :func:`range`! 

169 

170 Args: 

171 start: starting value 

172 stop: stopping value (INCLUSIVE if endpoint is True) 

173 step: step size 

174 endpoint: bool 

175 

176 Returns: 

177 list of integers 

178 """ 

179 if endpoint: 

180 if start <= stop: # normal 

181 range_stop = stop + 1 

182 else: # counting backwards: start > stop 

183 range_stop = stop - 1 

184 else: 

185 range_stop = stop 

186 return list(range(start, range_stop, step)) 

187 

188 

189def regular_tracker_axis_ticks_float( 

190 start: float, stop: float, num: int, endpoint: bool = True 

191) -> List[TrackerAxisTick]: 

192 """ 

193 Args: 

194 start: starting value 

195 stop: stopping value 

196 num: number of values to return 

197 endpoint: include the endpoint? 

198 

199 Returns: 

200 a list of simple numerical TrackerAxisTick objects 

201 

202 """ 

203 ticks = [] # type: List[TrackerAxisTick] 

204 for val in equally_spaced_ndarray(start, stop, num, endpoint=endpoint): 

205 ticks.append(TrackerAxisTick(val, str(val))) 

206 return ticks 

207 

208 

209def regular_tracker_axis_ticks_int( 

210 start: int, stop: int, step: int, endpoint: bool = True 

211) -> List[TrackerAxisTick]: 

212 """ 

213 Args: 

214 start: starting value 

215 stop: stopping value 

216 step: step size 

217 endpoint: include the endpoint? 

218 

219 Returns: 

220 a list of simple numerical TrackerAxisTick objects 

221 

222 """ 

223 ticks = [] # type: List[TrackerAxisTick] 

224 for val in equally_spaced_int(start, stop, step, endpoint=endpoint): 

225 ticks.append(TrackerAxisTick(val, str(val))) 

226 return ticks