Coverage for cc_modules/cc_summaryelement.py: 49%

1"""

2 camcops_server/cc_modules/cc_summaryelement.py

3

4===============================================================================

5

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

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

8

9 This file is part of CamCOPS.

10

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

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

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

14 (at your option) any later version.

15

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

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

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

19 GNU General Public License for more details.

20

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

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

23

24===============================================================================

25

26**Classes to represent summary information created by tasks.**

27

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

29 information.

30

31"""

32

33 from collections import OrderedDict

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

35

36 from cardinal_pythonlib.reprfunc import auto_repr

37 from sqlalchemy.sql.schema import Column

38 from sqlalchemy.sql.type_api import TypeEngine

39

40 from camcops_server.cc_modules.cc_dataclasses import SummarySchemaInfo

41 from camcops_server.cc_modules.cc_db import TaskDescendant

42 from camcops_server.cc_modules.cc_spreadsheet import SpreadsheetPage

43 from camcops_server.cc_modules.cc_xml import XmlElement

44

45 if TYPE_CHECKING:

46 from camcops_server.cc_modules.cc_task import Task

47

48

49# =============================================================================

50# SummaryElement

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

52

53

54 class SummaryElement(object):

55 """

56 Returned by tasks to represent extra summary information that they

57 calculate.

58

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

60 task or its ancillary object.

61 """

62

63 def __init__(

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

65 ) -> None:

66 """

67 Args:

68 name: column name

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

70 ``String(length=50)``

71 value: value

72 comment: explanatory comment

73 """

74 self.name = name

75 self.coltype = coltype

76 self.value = value

77 self.comment = comment

78

79 @property

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

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

82

83

84# =============================================================================

85# ExtraSummaryTable

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

87

88

89 class ExtraSummaryTable(TaskDescendant):

90 """

91 Additional summary information returned by a task.

92

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

94 with rows of a task or ancillary object.

95 """

96

97 def __init__(

98 self,

99 tablename: str,

100 xmlname: str,

101 columns: List[Column],

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

103 task: "Task",

104 ) -> None:

105 """

106 Args:

107 tablename: name of the additional summary table

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

109 columns: list of SQLAlchemy columns

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

111 column names to values

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

113 """

114 self.tablename = tablename

115 self.xmlname = xmlname

116 self.columns = columns

117 self.rows = rows

118 self.task = task

119

120 def get_xml_element(self) -> XmlElement:

121 """

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

123 representing this summary table.

124 """

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

126 for valuedict in self.rows:

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

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

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

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

131 itembranches.append(branch)

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

133

134 def get_spreadsheet_page(self) -> SpreadsheetPage:

135 """

136 Returns an

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

138 representing this summary table.

139 """

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

141

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

143 """

144 Schema equivalent to :func:`get_spreadsheet_page`.

145 """

146 return set(

147 SummarySchemaInfo.from_column(

148 c,

149 table_name=self.tablename,

150 source=SummarySchemaInfo.SSV_SUMMARY,

151 )

152 for c in self.columns

153 )

154

155 def __repr__(self) -> str:

156 return auto_repr(self)

157

158 # -------------------------------------------------------------------------

159 # TaskDescendant overrides

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

161

162 @classmethod

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

164 return None

165

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

167 return self.task