Coverage for tasks/zbi.py: 62%

1"""

2 camcops_server/tasks/zbi.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"""

27

28 from typing import Any, cast, List, Optional, Type

29

30 from cardinal_pythonlib.stringfunc import strseq

31 from sqlalchemy.sql.sqltypes import Integer

32

33 from camcops_server.cc_modules.cc_constants import (

34 CssClass,

35 DATA_COLLECTION_UNLESS_UPGRADED_DIV,

36)

37 from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo

38 from camcops_server.cc_modules.cc_db import add_multiple_columns

39 from camcops_server.cc_modules.cc_html import answer, tr

40 from camcops_server.cc_modules.cc_request import CamcopsRequest

41 from camcops_server.cc_modules.cc_string import AS

42 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

43 from camcops_server.cc_modules.cc_task import (

44 get_from_dict,

45 Task,

46 TaskHasPatientMixin,

47 TaskHasRespondentMixin,

48)

49

50

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

52# ZBI

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

54

55

56 class Zbi12( # type: ignore[misc]

57 TaskHasRespondentMixin,

58 TaskHasPatientMixin,

59 Task,

60):

61 """

62 Server implementation of the ZBI-12 task.

63 """

64

65 __tablename__ = "zbi12"

66 shortname = "ZBI-12"

67 info_filename_stem = "zbi"

68

69 MIN_PER_Q = 0

70 MAX_PER_Q = 4

71 NQUESTIONS = 12

72

73 @classmethod

74 def extend_columns(cls: Type["Zbi12"], **kwargs: Any) -> None:

75 add_multiple_columns(

76 cls,

77 "q",

78 1,

79 cls.NQUESTIONS,

80 minimum=cls.MIN_PER_Q,

81 maximum=cls.MAX_PER_Q,

82 comment_fmt="Q{n}, {s} (0-4, higher worse)",

83 comment_strings=[

84 "insufficient time for self", # 1

85 "stressed with other responsibilities",

86 "angry",

87 "other relationships affected",

88 "strained", # 5

89 "health suffered",

90 "insufficient privacy",

91 "social life suffered",

92 "lost control",

93 "uncertain", # 10

94 "should do more",

95 "could care better",

96 ],

97 )

98

99 TASK_FIELDS = strseq("q", 1, NQUESTIONS)

100 MAX_TOTAL = MAX_PER_Q * NQUESTIONS

101

102 @staticmethod

103 def longname(req: "CamcopsRequest") -> str:

104 _ = req.gettext

105 return _("Zarit Burden Interview-12")

106

107 def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:

108 return self.standard_task_summary_fields() + [

109 SummaryElement(

110 name="total_score",

111 coltype=Integer(),

112 value=self.total_score(),

113 comment=f"Total score (/ {self.MAX_TOTAL})",

114 )

115 ]

116

117 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:

118 if not self.is_complete():

119 return CTV_INCOMPLETE

120 return [

121 CtvInfo(

122 content=f"ZBI-12 total score "

123 f"{self.total_score()}/{self.MAX_TOTAL}"

124 )

125 ]

126

127 def total_score(self) -> int:

128 return cast(int, self.sum_fields(self.TASK_FIELDS))

129

130 def is_complete(self) -> bool:

131 return (

132 self.field_contents_valid()

133 and self.is_respondent_complete()

134 and self.all_fields_not_none(self.TASK_FIELDS)

135 )

136

137 def get_task_html(self, req: CamcopsRequest) -> str:

138 option_dict: dict[Optional[int], Optional[str]] = {None: None}

139 for a in range(self.MIN_PER_Q, self.MAX_PER_Q + 1):

140 option_dict[a] = req.wappstring(AS.ZBI_A_PREFIX + str(a))

141 h = f"""

142 <div class="{CssClass.SUMMARY}">

143 <table class="{CssClass.SUMMARY}">

144 {self.get_is_complete_tr(req)}

145 <tr>

146 <td>Total score (/ {self.MAX_TOTAL})</td>

147 <td>{answer(self.total_score())}</td>

148 </td>

149 </table>

150 </div>

151 <table class="{CssClass.TASKDETAIL}">

152 <tr>

153 <th width="75%">Question</th>

154 <th width="25%">Answer ({self.MIN_PER_Q}–{self.MAX_PER_Q})

155 </th>

156 </tr>

157 """

158 for q in range(1, self.NQUESTIONS + 1):

159 a = getattr(self, "q" + str(q))

160 fa = (

161 f"{a}: {get_from_dict(option_dict, a)}"

162 if a is not None

163 else None

164 )

165 h += tr(self.wxstring(req, "q" + str(q)), answer(fa))

166 h += (

167 """

168 </table>

169 """

170 + DATA_COLLECTION_UNLESS_UPGRADED_DIV

171 )

172 return h