Coverage for tasks/icd10mixed.py: 62%

1"""

2 camcops_server/tasks/icd10mixed.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 import datetime

29 from typing import List, Optional

30

31 from cardinal_pythonlib.datetimefunc import format_datetime

32 from cardinal_pythonlib.typetests import is_false

33 import cardinal_pythonlib.rnc_web as ws

34 from sqlalchemy.orm import Mapped, mapped_column

35 from sqlalchemy.sql.sqltypes import Boolean, UnicodeText

36

37 from camcops_server.cc_modules.cc_constants import (

38 CssClass,

39 DateFormat,

40 ICD10_COPYRIGHT_DIV,

41)

42 from camcops_server.cc_modules.cc_ctvinfo import CTV_INCOMPLETE, CtvInfo

43 from camcops_server.cc_modules.cc_html import get_true_false_none, tr_qa

44 from camcops_server.cc_modules.cc_request import CamcopsRequest

45 from camcops_server.cc_modules.cc_sqla_coltypes import (

46 BIT_CHECKER,

47 mapped_camcops_column,

48)

49 from camcops_server.cc_modules.cc_string import AS

50 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

51 from camcops_server.cc_modules.cc_task import (

52 Task,

53 TaskHasClinicianMixin,

54 TaskHasPatientMixin,

55)

56 from camcops_server.cc_modules.cc_text import SS

57

58

59# =============================================================================

60# Icd10Mixed

61# =============================================================================

62

63

64 class Icd10Mixed(TaskHasClinicianMixin, TaskHasPatientMixin, Task): # type: ignore[misc] # noqa: E501

65 """

66 Server implementation of the ICD10-MIXED task.

67 """

68

69 __tablename__ = "icd10mixed"

70 shortname = "ICD10-MIXED"

71 info_filename_stem = "icd"

72

73 date_pertains_to: Mapped[Optional[datetime.date]] = mapped_column(

74 comment="Date the assessment pertains to"

75 )

76 comments: Mapped[Optional[str]] = mapped_column(

77 UnicodeText, comment="Clinician's comments"

78 )

79 mixture_or_rapid_alternation: Mapped[Optional[bool]] = (

80 mapped_camcops_column(

81 permitted_value_checker=BIT_CHECKER,

82 comment="The episode is characterized by either a mixture or "

83 "a rapid alternation (i.e. within a few hours) of hypomanic, "

84 "manic and depressive symptoms.",

85 )

86 )

87 duration_at_least_2_weeks: Mapped[Optional[bool]] = mapped_camcops_column(

88 permitted_value_checker=BIT_CHECKER,

89 comment="Both manic and depressive symptoms must be prominent"

90 " most of the time during a period of at least two weeks.",

91 )

92

93 @staticmethod

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

95 _ = req.gettext

96 return _(

97 "ICD-10 symptomatic criteria for a mixed affective episode "

98 "(as in e.g. F06.3, F25, F38.00, F31.6)"

99 )

100

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

102 if not self.is_complete():

103 return CTV_INCOMPLETE

104 category = (

105 "Meets" if self.meets_criteria() else "Does not meet"

106 ) + " criteria for mixed affective episode"

107 infolist = [

108 CtvInfo(

109 content="Pertains to: {}. {}.".format(

110 format_datetime(

111 self.date_pertains_to, DateFormat.LONG_DATE

112 ),

113 category,

114 )

115 )

116 ]

117 if self.comments:

118 infolist.append(CtvInfo(content=ws.webify(self.comments)))

119 return infolist

120

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

122 return self.standard_task_summary_fields() + [

123 SummaryElement(

124 name="meets_criteria",

125 coltype=Boolean(),

126 value=self.meets_criteria(),

127 comment="Meets criteria for a mixed affective episode?",

128 )

129 ]

130

131 # Meets criteria? These also return null for unknown.

132 def meets_criteria(self) -> Optional[bool]:

133 if (

134 self.mixture_or_rapid_alternation

135 and self.duration_at_least_2_weeks

136 ):

137 return True

138 if is_false(self.mixture_or_rapid_alternation):

139 return False

140 if is_false(self.duration_at_least_2_weeks):

141 return False

142 return None

143

144 def is_complete(self) -> bool:

145 return (

146 self.meets_criteria() is not None and self.field_contents_valid()

147 )

148

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

150 return """

151 {clinician_comments}

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

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

154 {tr_is_complete}

155 {date_pertains_to}

156 {meets_criteria}

157 </table>

158 </div>

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

160 {icd10_symptomatic_disclaimer}

161 </div>

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

163 <tr>

164 <th width="80%">Question</th>

165 <th width="20%">Answer</th>

166 </tr>

167 {mixture_or_rapid_alternation}

168 {duration_at_least_2_weeks}

169 </table>

170 {ICD10_COPYRIGHT_DIV}

171 """.format(

172 clinician_comments=self.get_standard_clinician_comments_block(

173 req, self.comments

174 ),

175 CssClass=CssClass,

176 tr_is_complete=self.get_is_complete_tr(req),

177 date_pertains_to=tr_qa(

178 req.wappstring(AS.DATE_PERTAINS_TO),

179 format_datetime(

180 self.date_pertains_to, DateFormat.LONG_DATE, default=None

181 ),

182 ),

183 meets_criteria=tr_qa(

184 req.sstring(SS.MEETS_CRITERIA),

185 get_true_false_none(req, self.meets_criteria()),

186 ),

187 icd10_symptomatic_disclaimer=req.wappstring(

188 AS.ICD10_SYMPTOMATIC_DISCLAIMER

189 ),

190 mixture_or_rapid_alternation=self.get_twocol_bool_row_true_false(

191 req, "mixture_or_rapid_alternation", self.wxstring(req, "a")

192 ),

193 duration_at_least_2_weeks=self.get_twocol_bool_row_true_false(

194 req, "duration_at_least_2_weeks", self.wxstring(req, "b")

195 ),

196 ICD10_COPYRIGHT_DIV=ICD10_COPYRIGHT_DIV,

197 )