Coverage for cc_modules/cc_summaryelement.py: 50%

48 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_summaryelement.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**Classes to represent summary information created by tasks.** 

29 

30For example, the PHQ9 task calculates a total score; that's part of its summary 

31information. 

32 

33""" 

34 

35from collections import OrderedDict 

36from typing import Any, Dict, List, Optional, Set, Type, TYPE_CHECKING, Union 

37 

38from cardinal_pythonlib.reprfunc import auto_repr 

39from sqlalchemy.sql.schema import Column 

40from sqlalchemy.sql.type_api import TypeEngine 

41 

42from camcops_server.cc_modules.cc_dataclasses import SummarySchemaInfo 

43from camcops_server.cc_modules.cc_db import TaskDescendant 

44from camcops_server.cc_modules.cc_spreadsheet import SpreadsheetPage 

45from camcops_server.cc_modules.cc_xml import XmlElement 

46 

47if TYPE_CHECKING: 

48 from camcops_server.cc_modules.cc_task import Task 

49 

50 

51# ============================================================================= 

52# SummaryElement 

53# ============================================================================= 

54 

55 

56class SummaryElement(object): 

57 """ 

58 Returned by tasks to represent extra summary information that they 

59 calculate. 

60 

61 Use this for extra information that can be added to a row represented by a 

62 task or its ancillary object. 

63 """ 

64 

65 def __init__( 

66 self, name: str, coltype: TypeEngine, value: Any, comment: str = None 

67 ) -> None: 

68 """ 

69 Args: 

70 name: column name 

71 coltype: SQLAlchemy column type; e.g. ``Integer()``, 

72 ``String(length=50)`` 

73 value: value 

74 comment: explanatory comment 

75 """ 

76 self.name = name 

77 self.coltype = coltype 

78 self.value = value 

79 self.comment = comment 

80 

81 @property 

82 def decorated_comment(self) -> Optional[str]: 

83 return "(SUMMARY) " + self.comment if self.comment else None 

84 

85 

86# ============================================================================= 

87# ExtraSummaryTable 

88# ============================================================================= 

89 

90 

91class ExtraSummaryTable(TaskDescendant): 

92 """ 

93 Additional summary information returned by a task. 

94 

95 Use this to represent an entire table that doesn't have a 1:1 relationship 

96 with rows of a task or ancillary object. 

97 """ 

98 

99 def __init__( 

100 self, 

101 tablename: str, 

102 xmlname: str, 

103 columns: List[Column], 

104 rows: List[Union[Dict[str, Any], OrderedDict]], 

105 task: "Task", 

106 ) -> None: 

107 """ 

108 Args: 

109 tablename: name of the additional summary table 

110 xmlname: name of the XML tag to encapsulate this information 

111 columns: list of SQLAlchemy columns 

112 rows: list of rows, where each row is a dictionary mapping 

113 column names to values 

114 task: parent task (for cross-referencing in some kinds of export) 

115 """ 

116 self.tablename = tablename 

117 self.xmlname = xmlname 

118 self.columns = columns 

119 self.rows = rows 

120 self.task = task 

121 

122 def get_xml_element(self) -> XmlElement: 

123 """ 

124 Returns an :class:`camcops_server.cc_modules.cc_xml.XmlElement` 

125 representing this summary table. 

126 """ 

127 itembranches = [] # type: List[XmlElement] 

128 for valuedict in self.rows: 

129 leaves = [] # type: List[XmlElement] 

130 for k, v in valuedict.items(): 

131 leaves.append(XmlElement(name=k, value=v)) 

132 branch = XmlElement(name=self.tablename, value=leaves) 

133 itembranches.append(branch) 

134 return XmlElement(name=self.xmlname, value=itembranches) 

135 

136 def get_spreadsheet_page(self) -> SpreadsheetPage: 

137 """ 

138 Returns an 

139 :class:`camcops_server.cc_modules.cc_spreadsheet.SpreadsheetPage` 

140 representing this summary table. 

141 """ 

142 return SpreadsheetPage(name=self.tablename, rows=self.rows) 

143 

144 def get_spreadsheet_schema_elements(self) -> Set[SummarySchemaInfo]: 

145 """ 

146 Schema equivalent to :func:`get_spreadsheet_page`. 

147 """ 

148 return set( 

149 SummarySchemaInfo.from_column( 

150 c, 

151 table_name=self.tablename, 

152 source=SummarySchemaInfo.SSV_SUMMARY, 

153 ) 

154 for c in self.columns 

155 ) 

156 

157 def __repr__(self) -> str: 

158 return auto_repr(self) 

159 

160 # ------------------------------------------------------------------------- 

161 # TaskDescendant overrides 

162 # ------------------------------------------------------------------------- 

163 

164 @classmethod 

165 def task_ancestor_class(cls) -> Optional[Type["Task"]]: 

166 return None 

167 

168 def task_ancestor(self) -> Optional["Task"]: 

169 return self.task