Coverage for tasks/esspri.py: 62%

1"""

2 camcops_server/tasks/esspri.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**EULAR Sjögren’s Syndrome Patient Reported Index (ESSPRI) task.**

27

28"""

29

30 from camcops_server.cc_modules.cc_constants import CssClass

31 from camcops_server.cc_modules.cc_html import tr_qa, tr, answer

32 from camcops_server.cc_modules.cc_request import CamcopsRequest

33 from camcops_server.cc_modules.cc_sqla_coltypes import (

34 camcops_column,

35 ZERO_TO_10_CHECKER,

36)

37

38 from camcops_server.cc_modules.cc_summaryelement import SummaryElement

39 from camcops_server.cc_modules.cc_task import TaskHasPatientMixin, Task

40 import cardinal_pythonlib.rnc_web as ws

41 from cardinal_pythonlib.stringfunc import strseq

42 from sqlalchemy import Float, Integer

43 from typing import Any, List, Type

44

45

46 class Esspri( # type: ignore[misc]

47 TaskHasPatientMixin,

48 Task,

49):

50 __tablename__ = "esspri"

51 shortname = "ESSPRI"

52

53 N_QUESTIONS = 3

54 MAX_SCORE = 10 # Mean of 3 scores of 10

55

56 @classmethod

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

58

59 comment_strings = ["dryness", "fatigue", "pain"]

60

61 for q_index in range(0, cls.N_QUESTIONS):

62 q_num = q_index + 1

63 q_field = "q{}".format(q_num)

64

65 score_comment = "(0 none - 10 maximum imaginable)"

66

67 setattr(

68 cls,

69 q_field,

70 camcops_column(

71 q_field,

72 Integer,

73 permitted_value_checker=ZERO_TO_10_CHECKER,

74 comment="Q{} ({}) {}".format(

75 q_num, comment_strings[q_index], score_comment

76 ),

77 ),

78 )

79

80 ALL_QUESTIONS = strseq("q", 1, N_QUESTIONS)

81

82 @staticmethod

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

84 _ = req.gettext

85 return _("EULAR Sjögren’s Syndrome Patient Reported Index")

86

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

88 return self.standard_task_summary_fields() + [

89 SummaryElement(

90 name="overall_score",

91 coltype=Float(),

92 value=self.overall_score(),

93 comment=f"Overall score (/{self.MAX_SCORE})",

94 )

95 ]

96

97 def is_complete(self) -> bool:

98 if self.any_fields_none(self.ALL_QUESTIONS):

99 return False

100 if not self.field_contents_valid():

101 return False

102 return True

103

104 def overall_score(self) -> float:

105 return self.mean_fields(self.ALL_QUESTIONS)

106

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

108 rows = ""

109 for q_num in range(1, self.N_QUESTIONS + 1):

110 q_field = "q" + str(q_num)

111 question_cell = "{}. {}".format(q_num, self.wxstring(req, q_field))

112

113 score = getattr(self, q_field)

114

115 rows += tr_qa(question_cell, score)

116

117 formatted_score = ws.number_to_dp(self.overall_score(), 3, default="?")

118

119 html = """

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

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

122 {tr_is_complete}

123 {overall_score}

124 </table>

125 </div>

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

127 <tr>

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

129 <th width="40%">Answer</th>

130 </tr>

131 {rows}

132 </table>

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

134 [1] Mean of three numerical rating scales, each rated 0-10.

135 </div>

136 """.format(

137 CssClass=CssClass,

138 tr_is_complete=self.get_is_complete_tr(req),

139 overall_score=tr(

140 self.wxstring(req, "overall_score") + " <sup>[1]</sup>",

141 "{} / {}".format(answer(formatted_score), self.MAX_SCORE),

142 ),

143 rows=rows,

144 )

145 return html