Coverage for cc_modules/cc_summaryelement.py: 49%
47 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
1"""
2camcops_server/cc_modules/cc_summaryelement.py
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
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.
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.
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/>.
24===============================================================================
26**Classes to represent summary information created by tasks.**
28For example, the PHQ9 task calculates a total score; that's part of its summary
29information.
31"""
33from collections import OrderedDict
34from typing import Any, Dict, List, Optional, Set, Type, TYPE_CHECKING, Union
36from cardinal_pythonlib.reprfunc import auto_repr
37from sqlalchemy.sql.schema import Column
38from sqlalchemy.sql.type_api import TypeEngine
40from camcops_server.cc_modules.cc_dataclasses import SummarySchemaInfo
41from camcops_server.cc_modules.cc_db import TaskDescendant
42from camcops_server.cc_modules.cc_spreadsheet import SpreadsheetPage
43from camcops_server.cc_modules.cc_xml import XmlElement
45if TYPE_CHECKING:
46 from camcops_server.cc_modules.cc_task import Task
49# =============================================================================
50# SummaryElement
51# =============================================================================
54class SummaryElement(object):
55 """
56 Returned by tasks to represent extra summary information that they
57 calculate.
59 Use this for extra information that can be added to a row represented by a
60 task or its ancillary object.
61 """
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
79 @property
80 def decorated_comment(self) -> Optional[str]:
81 return "(SUMMARY) " + self.comment if self.comment else None
84# =============================================================================
85# ExtraSummaryTable
86# =============================================================================
89class ExtraSummaryTable(TaskDescendant):
90 """
91 Additional summary information returned by a task.
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 """
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
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)
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)
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 )
155 def __repr__(self) -> str:
156 return auto_repr(self)
158 # -------------------------------------------------------------------------
159 # TaskDescendant overrides
160 # -------------------------------------------------------------------------
162 @classmethod
163 def task_ancestor_class(cls) -> Optional[Type["Task"]]:
164 return None
166 def task_ancestor(self) -> Optional["Task"]:
167 return self.task