Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/tasks/zbi.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

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

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

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

15 (at your option) any later version. 

16 

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

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

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

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

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

24 

25=============================================================================== 

26 

27""" 

28 

29from typing import Any, Dict, List, Tuple, Type 

30 

31from cardinal_pythonlib.stringfunc import strseq 

32from sqlalchemy.ext.declarative import DeclarativeMeta 

33from sqlalchemy.sql.sqltypes import Integer 

34 

35from camcops_server.cc_modules.cc_constants import ( 

36 CssClass, 

37 DATA_COLLECTION_UNLESS_UPGRADED_DIV, 

38) 

39from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo 

40from camcops_server.cc_modules.cc_db import add_multiple_columns 

41from camcops_server.cc_modules.cc_html import answer, tr 

42from camcops_server.cc_modules.cc_request import CamcopsRequest 

43from camcops_server.cc_modules.cc_string import AS 

44from camcops_server.cc_modules.cc_summaryelement import SummaryElement 

45from camcops_server.cc_modules.cc_task import ( 

46 get_from_dict, 

47 Task, 

48 TaskHasPatientMixin, 

49 TaskHasRespondentMixin, 

50) 

51 

52 

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

54# ZBI 

55# ============================================================================= 

56 

57class Zbi12Metaclass(DeclarativeMeta): 

58 # noinspection PyInitNewSignature 

59 def __init__(cls: Type['Zbi12'], 

60 name: str, 

61 bases: Tuple[Type, ...], 

62 classdict: Dict[str, Any]) -> None: 

63 add_multiple_columns( 

64 cls, "q", 1, cls.NQUESTIONS, 

65 minimum=cls.MIN_PER_Q, maximum=cls.MAX_PER_Q, 

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

67 comment_strings=[ 

68 "insufficient time for self", # 1 

69 "stressed with other responsibilities", 

70 "angry", 

71 "other relationships affected", 

72 "strained", # 5 

73 "health suffered", 

74 "insufficient privacy", 

75 "social life suffered", 

76 "lost control", 

77 "uncertain", # 10 

78 "should do more", 

79 "could care better" 

80 ] 

81 ) 

82 super().__init__(name, bases, classdict) 

83 

84 

85class Zbi12(TaskHasRespondentMixin, TaskHasPatientMixin, Task, 

86 metaclass=Zbi12Metaclass): 

87 """ 

88 Server implementation of the ZBI-12 task. 

89 """ 

90 __tablename__ = "zbi12" 

91 shortname = "ZBI-12" 

92 

93 MIN_PER_Q = 0 

94 MAX_PER_Q = 4 

95 NQUESTIONS = 12 

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

97 MAX_TOTAL = MAX_PER_Q * NQUESTIONS 

98 

99 @staticmethod 

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

101 _ = req.gettext 

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

103 

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

105 return self.standard_task_summary_fields() + [ 

106 SummaryElement( 

107 name="total_score", coltype=Integer(), 

108 value=self.total_score(), 

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

110 ), 

111 ] 

112 

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

114 if not self.is_complete(): 

115 return CTV_INCOMPLETE 

116 return [CtvInfo( 

117 content=f"ZBI-12 total score {self.total_score()}/{self.MAX_TOTAL}" 

118 )] 

119 

120 def total_score(self) -> int: 

121 return self.sum_fields(self.TASK_FIELDS) 

122 

123 def is_complete(self) -> bool: 

124 return ( 

125 self.field_contents_valid() and 

126 self.is_respondent_complete() and 

127 self.all_fields_not_none(self.TASK_FIELDS) 

128 ) 

129 

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

131 option_dict = {None: None} 

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

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

134 h = f""" 

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

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

137 {self.get_is_complete_tr(req)} 

138 <tr> 

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

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

141 </td> 

142 </table> 

143 </div> 

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

145 <tr> 

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

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

148 </th> 

149 </tr> 

150 """ 

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

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

153 fa = (f"{a}: {get_from_dict(option_dict, a)}" 

154 if a is not None else None) 

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

156 h += """ 

157 </table> 

158 """ + DATA_COLLECTION_UNLESS_UPGRADED_DIV 

159 return h