Coverage for tasks/chit.py: 62%

1"""

2 camcops_server/tasks/chit.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**Cambridge-Chicago Compulsivity Trait Scale task.**

27

28"""

29

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

31

32 from cardinal_pythonlib.classes import classproperty

33 from cardinal_pythonlib.stringfunc import strseq

34 from semantic_version import Version

35 from sqlalchemy import Integer

36

37 from camcops_server.cc_modules.cc_constants import CssClass

38 from camcops_server.cc_modules.cc_db import add_multiple_columns

39 from camcops_server.cc_modules.cc_html import (

40 tr_qa,

41 tr,

42 answer,

43)

44 from camcops_server.cc_modules.cc_request import CamcopsRequest

45 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

46 from camcops_server.cc_modules.cc_task import (

47 TaskHasPatientMixin,

48 Task,

49 get_from_dict,

50)

51 from camcops_server.cc_modules.cc_text import SS

52

53

54 class Chit( # type: ignore[misc]

55 TaskHasPatientMixin,

56 Task,

57):

58 __tablename__ = "chit"

59 shortname = "CHI-T"

60

61 N_SCORED_QUESTIONS = 15

62 MIN_ANSWER = 0

63 MAX_ANSWER = 4

64 MAX_SCORE_MAIN = MAX_ANSWER * N_SCORED_QUESTIONS

65

66 @classmethod

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

68 add_multiple_columns(

69 cls,

70 "q",

71 1,

72 cls.N_SCORED_QUESTIONS,

73 minimum=cls.MIN_ANSWER,

74 maximum=cls.MAX_ANSWER,

75 comment_fmt="Q{n} ({s}) (0 strongly disagree - 4 strongly agree)",

76 comment_strings=[

77 "hate unfinished task",

78 "just right",

79 "keep doing task",

80 "get stuck",

81 "habit",

82 "addictive",

83 "stubborn rigid",

84 "urges",

85 "rewarding things",

86 "hard moving",

87 "higher standards",

88 "improvement",

89 "complete",

90 "avoid situations",

91 "hobby",

92 ],

93 )

94

95 SCORED_QUESTIONS = strseq("q", 1, N_SCORED_QUESTIONS)

96

97 @staticmethod

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

99 _ = req.gettext

100 return _("Cambridge–Chicago Compulsivity Trait Scale")

101

102 # noinspection PyMethodParameters

103 @classproperty

104 def minimum_client_version(cls) -> Version:

105 return Version("2.4.15")

106

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

108 return self.standard_task_summary_fields() + [

109 SummaryElement(

110 name="total",

111 coltype=Integer(),

112 value=self.total_score(),

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

114 )

115 ]

116

117 def is_complete(self) -> bool:

118 if self.any_fields_none(self.SCORED_QUESTIONS):

119 return False

120 if not self.field_contents_valid():

121 return False

122 return True

123

124 def total_score(self) -> int:

125 return cast(int, self.sum_fields(self.SCORED_QUESTIONS))

126

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

128 score_dict: dict[Optional[int], Optional[str]] = {None: None}

129

130 for i in range(self.MIN_ANSWER, self.MAX_ANSWER + 1):

131 score_dict[i] = f"{i} — " + self.wxstring(req, f"a{i}")

132

133 rows = ""

134 for i in range(1, self.N_SCORED_QUESTIONS + 1):

135 q_field = "q" + str(i)

136 question_cell = "{}. {}".format(i, self.wxstring(req, q_field))

137 answer_cell = get_from_dict(score_dict, getattr(self, q_field))

138

139 rows += tr_qa(question_cell, answer_cell)

140

141 html = """

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

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

144 {tr_is_complete}

145 {total_score}

146 </table>

147 </div>

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

149 <tr>

150 <th width="60%">Question</th>

151 <th width="40%">Answer <sup>[2]</sup></th>

152 </tr>

153 {rows}

154 </table>

155 <div class="{CssClass.FOOTNOTES}">

156 [1] Sum for questions 1–15. Prior to CamCOPS version 2.4.15

157 each question scored 0–3 with a maximum possible score of 45.

158 [2] Prior to CamCOPS version 2.4.15 the responses were:

159 0 — Strongly disagree, 1 — Disagree, 2 — Agree, 3 — Strongly

160 agree.

161 </div>

162 """.format(

163 CssClass=CssClass,

164 tr_is_complete=self.get_is_complete_tr(req),

165 total_score=tr(

166 req.sstring(SS.TOTAL_SCORE) + " <sup>[1]</sup>",

167 answer(self.total_score()) + f" / {self.MAX_SCORE_MAIN}",

168 ),

169 rows=rows,

170 )

171 return html