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/qolsg.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 List 

30 

31import cardinal_pythonlib.rnc_web as ws 

32from sqlalchemy.sql.schema import Column 

33from sqlalchemy.sql.sqltypes import Float, Integer, String 

34 

35from camcops_server.cc_modules.cc_constants import CssClass 

36from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo 

37from camcops_server.cc_modules.cc_html import get_yes_no_none, tr_qa 

38from camcops_server.cc_modules.cc_request import CamcopsRequest 

39from camcops_server.cc_modules.cc_snomed import SnomedExpression, SnomedLookup 

40from camcops_server.cc_modules.cc_sqla_coltypes import ( 

41 BIT_CHECKER, 

42 CamcopsColumn, 

43 PendulumDateTimeAsIsoTextColType, 

44 ZERO_TO_ONE_CHECKER, 

45) 

46from camcops_server.cc_modules.cc_task import Task, TaskHasPatientMixin 

47from camcops_server.cc_modules.cc_trackerhelpers import TrackerInfo 

48 

49 

50# ============================================================================= 

51# QoL-SG 

52# ============================================================================= 

53 

54DP = 3 

55 

56 

57class QolSG(TaskHasPatientMixin, Task): 

58 """ 

59 Server implementation of the QoL-SG task. 

60 """ 

61 __tablename__ = "qolsg" 

62 shortname = "QoL-SG" 

63 provides_trackers = True 

64 

65 category_start_time = Column( 

66 "category_start_time", PendulumDateTimeAsIsoTextColType, 

67 comment="Time categories were offered (ISO-8601)" 

68 ) 

69 category_responded = CamcopsColumn( 

70 "category_responded", Integer, 

71 permitted_value_checker=BIT_CHECKER, 

72 comment="Responded to category choice? (0 no, 1 yes)" 

73 ) 

74 category_response_time = Column( 

75 "category_response_time", PendulumDateTimeAsIsoTextColType, 

76 comment="Time category was chosen (ISO-8601)" 

77 ) 

78 category_chosen = Column( 

79 "category_chosen", String(length=len("medium")), 

80 comment="Category chosen: high (QoL > 1) " 

81 "medium (0 <= QoL <= 1) low (QoL < 0)" 

82 ) 

83 gamble_fixed_option = Column( 

84 "gamble_fixed_option", String(length=len("current")), 

85 comment="Fixed option in gamble (current, healthy, dead)" 

86 ) 

87 gamble_lottery_option_p = Column( 

88 "gamble_lottery_option_p", String(length=len("current")), 

89 comment="Gamble: option corresponding to p " 

90 "(current, healthy, dead)" 

91 ) 

92 gamble_lottery_option_q = Column( 

93 "gamble_lottery_option_q", String(length=len("current")), 

94 comment="Gamble: option corresponding to q " 

95 "(current, healthy, dead) (q = 1 - p)" 

96 ) 

97 gamble_lottery_on_left = CamcopsColumn( 

98 "gamble_lottery_on_left", Integer, 

99 permitted_value_checker=BIT_CHECKER, 

100 comment="Gamble: lottery shown on the left (0 no, 1 yes)" 

101 ) 

102 gamble_starting_p = CamcopsColumn( 

103 "gamble_starting_p", Float, 

104 permitted_value_checker=ZERO_TO_ONE_CHECKER, 

105 comment="Gamble: starting value of p" 

106 ) 

107 gamble_start_time = Column( 

108 "gamble_start_time", PendulumDateTimeAsIsoTextColType, 

109 comment="Time gamble was offered (ISO-8601)" 

110 ) 

111 gamble_responded = CamcopsColumn( 

112 "gamble_responded", Integer, 

113 permitted_value_checker=BIT_CHECKER, 

114 comment="Gamble was responded to? (0 no, 1 yes)" 

115 ) 

116 gamble_response_time = Column( 

117 "gamble_response_time", PendulumDateTimeAsIsoTextColType, 

118 comment="Time subject responded to gamble (ISO-8601)" 

119 ) 

120 gamble_p = CamcopsColumn( 

121 "gamble_p", Float, 

122 permitted_value_checker=ZERO_TO_ONE_CHECKER, 

123 comment="Final value of p" 

124 ) 

125 utility = Column( 

126 "utility", Float, 

127 comment="Calculated utility, h" 

128 ) 

129 

130 @staticmethod 

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

132 _ = req.gettext 

133 return _("Quality of Life: Standard Gamble") 

134 

135 def get_trackers(self, req: CamcopsRequest) -> List[TrackerInfo]: 

136 return [TrackerInfo( 

137 value=self.utility, 

138 plot_label="Quality of life: standard gamble", 

139 axis_label="QoL (0-1)", 

140 axis_min=0, 

141 axis_max=1 

142 )] 

143 

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

145 if not self.is_complete(): 

146 return CTV_INCOMPLETE 

147 return [CtvInfo( 

148 content=f"Quality of life: {ws.number_to_dp(self.utility, DP)}" 

149 )] 

150 

151 def is_complete(self) -> bool: 

152 return ( 

153 self.utility is not None and 

154 self.field_contents_valid() 

155 ) 

156 

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

158 h = f""" 

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

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

161 {self.get_is_complete_tr(req)} 

162 {tr_qa("Utility", 

163 ws.number_to_dp(self.utility, DP, default=None))} 

164 </table> 

165 </div> 

166 <div class="{CssClass.EXPLANATION}"> 

167 Quality of life (QoL) has anchor values of 0 (none) and 1 

168 (perfect health). The Standard Gamble offers a trade-off to 

169 determine utility (QoL). 

170 Values &lt;0 and &gt;1 are possible with some gambles. 

171 </div> 

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

173 <tr><th width="50%">Measure</th><th width="50%">Value</th></tr> 

174 """ 

175 h += tr_qa("Category choice: start time", self.category_start_time) 

176 h += tr_qa("Category choice: responded?", 

177 get_yes_no_none(req, self.category_responded)) 

178 h += tr_qa("Category choice: response time", 

179 self.category_response_time) 

180 h += tr_qa("Category choice: category chosen", self.category_chosen) 

181 h += tr_qa("Gamble: fixed option", self.gamble_fixed_option) 

182 h += tr_qa("Gamble: lottery option for <i>p</i>", 

183 self.gamble_lottery_option_p) 

184 h += tr_qa("Gamble: lottery option for <i>q</i> = 1 – <i>p</i>", 

185 self.gamble_lottery_option_q) 

186 h += tr_qa("Gamble: lottery on left?", 

187 get_yes_no_none(req, self.gamble_lottery_on_left)) 

188 h += tr_qa("Gamble: starting <i>p</i>", self.gamble_starting_p) 

189 h += tr_qa("Gamble: start time", self.gamble_start_time) 

190 h += tr_qa("Gamble: responded?", 

191 get_yes_no_none(req, self.gamble_responded)) 

192 h += tr_qa("Gamble: response time", self.gamble_response_time) 

193 h += tr_qa("Gamble: <i>p</i>", 

194 ws.number_to_dp(self.gamble_p, DP, default=None)) 

195 h += tr_qa("Calculated utility", 

196 ws.number_to_dp(self.utility, DP, default=None)) 

197 h += """ 

198 </table> 

199 """ 

200 return h 

201 

202 def get_snomed_codes(self, req: CamcopsRequest) -> List[SnomedExpression]: 

203 if not self.is_complete(): 

204 return [] 

205 return [SnomedExpression(req.snomed(SnomedLookup.QOL_SCALE))]