Exemple #1
0
 def get_task_html(self, req: CamcopsRequest) -> str:
     h = f"""
         <div class="{CssClass.SUMMARY}">
             <table class="{CssClass.SUMMARY}">
                 {self.get_is_complete_tr(req)}
                 {tr_qa("Utility",
                        ws.number_to_dp(self.utility, DP, default=None))}
             </table>
         </div>
         <div class="{CssClass.EXPLANATION}">
             Quality of life (QoL) has anchor values of 0 (none) and 1
             (perfect health). The Standard Gamble offers a trade-off to
             determine utility (QoL).
             Values &lt;0 and &gt;1 are possible with some gambles.
         </div>
         <table class="{CssClass.TASKDETAIL}">
             <tr><th width="50%">Measure</th><th width="50%">Value</th></tr>
     """
     h += tr_qa("Category choice: start time", self.category_start_time)
     h += tr_qa(
         "Category choice: responded?",
         get_yes_no_none(req, self.category_responded),
     )
     h += tr_qa(
         "Category choice: response time", self.category_response_time
     )
     h += tr_qa("Category choice: category chosen", self.category_chosen)
     h += tr_qa("Gamble: fixed option", self.gamble_fixed_option)
     h += tr_qa(
         "Gamble: lottery option for <i>p</i>", self.gamble_lottery_option_p
     )
     h += tr_qa(
         "Gamble: lottery option for <i>q</i> = 1 – <i>p</i>",
         self.gamble_lottery_option_q,
     )
     h += tr_qa(
         "Gamble: lottery on left?",
         get_yes_no_none(req, self.gamble_lottery_on_left),
     )
     h += tr_qa("Gamble: starting <i>p</i>", self.gamble_starting_p)
     h += tr_qa("Gamble: start time", self.gamble_start_time)
     h += tr_qa(
         "Gamble: responded?", get_yes_no_none(req, self.gamble_responded)
     )
     h += tr_qa("Gamble: response time", self.gamble_response_time)
     h += tr_qa(
         "Gamble: <i>p</i>",
         ws.number_to_dp(self.gamble_p, DP, default=None),
     )
     h += tr_qa(
         "Calculated utility",
         ws.number_to_dp(self.utility, DP, default=None),
     )
     h += """
         </table>
     """
     return h
Exemple #2
0
 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     return [
         CtvInfo(content=(
             "BMI: {} kg⋅m<sup>–2</sup> [{}]. Mass: {} kg. "
             "Height: {} m.".format(ws.number_to_dp(self.bmi(), BMI_DP),
                                    self.category(req),
                                    ws.number_to_dp(self.mass_kg, KG_DP),
                                    ws.number_to_dp(self.height_m, M_DP))))
     ]
Exemple #3
0
 def get_task_html(self, req: CamcopsRequest) -> str:
     tto_qol = self.get_tto_qol()
     rs_qol = self.get_rs_qol()
     mean_qol = mean([tto_qol, rs_qol])
     h = """
         <div class="{CssClass.SUMMARY}">
             <table class="{CssClass.SUMMARY}">
                 {tr_is_complete}
                 {mean_qol}
             </table>
         </div>
         <div class="{CssClass.EXPLANATION}">
             Quality of life (QoL) has anchor values of 0 (none) and 1
             (perfect health), and can be asked about in several ways.
         </div>
         <table class="{CssClass.TASKDETAIL}">
             <tr>
                 <th width="33%">Scale</th>
                 <th width="33%">Answer</th>
                 <td width="33%">Implied QoL</th>
             </tr>
             {tto}
             {rs}
         </table>
     """.format(
         CssClass=CssClass,
         tr_is_complete=self.get_is_complete_tr(req),
         mean_qol=tr(
             "Mean QoL",
             answer(
                 ws.number_to_dp(mean_qol, DP, default=None),
                 formatter_answer=identity,
             ),
         ),
         tto=tr(
             self.wxstring(req, "tto_q_s"),
             answer(ws.number_to_dp(self.tto, DP, default=None)),
             answer(
                 ws.number_to_dp(tto_qol, DP, default=None),
                 formatter_answer=identity,
             ),
         ),
         rs=tr(
             self.wxstring(req, "rs_q_s"),
             answer(ws.number_to_dp(self.rs, DP, default=None)),
             answer(
                 ws.number_to_dp(rs_qol, DP, default=None),
                 formatter_answer=identity,
             ),
         ),
     )
     return h
Exemple #4
0
 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     tto_qol = self.get_tto_qol()
     rs_qol = self.get_rs_qol()
     mean_qol = mean([tto_qol, rs_qol])
     return [
         CtvInfo(content=(
             "Quality of life: time trade-off {}, rating scale {}, "
             "mean {}.".format(ws.number_to_dp(tto_qol, DP),
                               ws.number_to_dp(rs_qol, DP),
                               ws.number_to_dp(mean_qol, DP))))
     ]
Exemple #5
0
 def get_clinical_text(self) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     return [CtvInfo(
         content=(
             "BMI: {} kg⋅m<sup>–2</sup> [{}]. Mass: {} kg. "
             "Height: {} m.".format(
                 ws.number_to_dp(self.bmi(), BMI_DP),
                 self.category(),
                 ws.number_to_dp(self.mass_kg, KG_DP),
                 ws.number_to_dp(self.height_m, M_DP)
             )
         )
     )]
Exemple #6
0
 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     scoredict = self.get_score()
     return [
         CtvInfo(
             content=("Total {total}/n, n = {n}, score = {score}, "
                      "logit score = {logit}, severity = {severity}".format(
                          total=scoredict["total"],
                          n=scoredict["n"],
                          score=ws.number_to_dp(scoredict["score"], DP),
                          logit=ws.number_to_dp(scoredict["logit"], DP),
                          severity=scoredict["severity"],
                      )))
     ]
Exemple #7
0
 def get_clinical_text(self) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     tto_qol = self.get_tto_qol()
     rs_qol = self.get_rs_qol()
     mean_qol = mean([tto_qol, rs_qol])
     return [
         CtvInfo(
             content=(
                 "Quality of life: time trade-off {}, rating scale {}, "
                 "mean {}.".format(
                     ws.number_to_dp(tto_qol, DP), ws.number_to_dp(rs_qol, DP), ws.number_to_dp(mean_qol, DP)
                 )
             )
         )
     ]
Exemple #8
0
 def get_clinical_text(self) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     scoredict = self.get_score()
     return [CtvInfo(
         content=(
             "Total {total}/n, n = {n}, score = {score}, "
             "logit score = {logit}, severity = {severity}".format(
                 total=scoredict['total'],
                 n=scoredict['n'],
                 score=ws.number_to_dp(scoredict['score'], DP),
                 logit=ws.number_to_dp(scoredict['logit'], DP),
                 severity=scoredict['severity'],
             )
         )
     )]
Exemple #9
0
 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     return [
         CtvInfo(content="Quality of life: {}".format(
             ws.number_to_dp(self.utility, DP)))
     ]
Exemple #10
0
 def get_task_html(self) -> str:
     (total, extrapolated) = self.totalscore_extrapolated()
     main_dict = {
         None: None,
         1: "1 — " + WSTRING("demqol_a1"),
         2: "2 — " + WSTRING("demqol_a2"),
         3: "3 — " + WSTRING("demqol_a3"),
         4: "4 — " + WSTRING("demqol_a4"),
         MISSING_VALUE: WSTRING("demqol_no_response")
     }
     last_q_dict = {
         None: None,
         1: "1 — " + WSTRING("demqol_q29_a1"),
         2: "2 — " + WSTRING("demqol_q29_a2"),
         3: "3 — " + WSTRING("demqol_q29_a3"),
         4: "4 — " + WSTRING("demqol_q29_a4"),
         MISSING_VALUE: WSTRING("demqol_no_response")
     }
     instruction_dict = {
         1: WSTRING("demqolproxy_instruction11"),
         12: WSTRING("demqolproxy_instruction12"),
         21: WSTRING("demqolproxy_instruction13"),
         32: WSTRING("demqolproxy_instruction14"),
     }
     h = """
         <div class="summary">
             <table class="summary">
                 {is_complete_tr}
                 <tr>
                     <td>Total score ({min}–{max}), higher better</td>
                     <td>{t}</td>
                 </tr>
                 <tr>
                     <td>Total score extrapolated using incomplete
                     responses? <sup>[1]</sup></td>
                     <td>{e}</td>
                 </tr>
             </table>
         </div>
         <table class="taskdetail">
             <tr>
                 <th width="50%">Question</th>
                 <th width="50%">Answer</th>
             </tr>
     """.format(
         is_complete_tr=self.get_is_complete_tr(),
         min=self.MIN_SCORE,
         max=self.MAX_SCORE,
         t=answer(ws.number_to_dp(total, DP)),
         e=answer(get_yes_no(extrapolated)),
     )
     for n in range(1, self.NQUESTIONS + 1):
         if n in instruction_dict:
             h += subheading_spanning_two_columns(instruction_dict.get(n))
         d = main_dict if n <= self.N_SCORED_QUESTIONS else last_q_dict
         q = self.get_q(n)
         a = get_from_dict(d, getattr(self, "q" + str(n)))
         h += tr_qa(q, a)
     h += END_DIV + COPYRIGHT_DIV
     return h
Exemple #11
0
 def get_clinical_text(self) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     return [CtvInfo(
         content="Quality of life: {}".format(
             ws.number_to_dp(self.utility, DP))
     )]
Exemple #12
0
 def get_clinical_text(self) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     return [CtvInfo(
         content="Total score {} (range {}–{}, higher better)".format(
             ws.number_to_dp(self.total_score(), DP),
             self.MIN_SCORE, self.MAX_SCORE)
     )]
Exemple #13
0
 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     t = self.total_score()
     c = ws.number_to_dp(self.composite_score(), DP, default="?")
     return [
         CtvInfo(content="PDSS total score {t}/{mt} (composite {c}/{mc})".
                 format(t=t, mt=self.MAX_TOTAL, c=c, mc=self.MAX_COMPOSITE))
     ]
Exemple #14
0
 def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     return [
         CtvInfo(
             content="Total score {} (range {}–{}, higher better)".format(
                 ws.number_to_dp(self.total_score(), DP), self.MIN_SCORE,
                 self.MAX_SCORE))
     ]
 def get_html_table_row(self) -> str:
     return ("<tr>" + "<td>{}</td>" * 13 + "</th>").format(
         self.trial, self.trial_ignoring_catch_trials,
         self.target_presented, self.target_time,
         ws.number_to_dp(self.intensity,
                         DP), self.choice_time, self.responded,
         self.response_time, self.response_latency_ms, self.yes, self.no,
         ws.webify(self.caught_out_reset),
         ws.webify(self.trial_num_in_calculation_sequence))
Exemple #16
0
 def get_clinical_text(self) -> List[CtvInfo]:
     if not self.is_complete():
         return CTV_INCOMPLETE
     t = self.total_score()
     c = ws.number_to_dp(self.composite_score(), DP, default="?")
     return [CtvInfo(
         content="PDSS total score {t}/48 (composite {c}/4)".format(
             t=t, c=c)
     )]
Exemple #17
0
 def get_task_html(self) -> str:
     h = """
         <div class="summary">
             <table class="summary">
     """ + self.get_is_complete_tr()
     h += tr_qa("Utility", ws.number_to_dp(self.utility, DP, default=None))
     h += """
             </table>
         </div>
         <div class="explanation">
             Quality of life (QoL) has anchor values of 0 (none) and 1
             (perfect health). The Standard Gamble offers a trade-off to
             determine utility (QoL).
             Values &lt;0 and &gt;1 are possible with some gambles.
         </div>
         <table class="taskdetail">
             <tr><th width="50%">Measure</th><th width="50%">Value</th></tr>
     """
     h += tr_qa("Category choice: start time", self.category_start_time)
     h += tr_qa("Category choice: responded?",
                get_yes_no_none(self.category_responded))
     h += tr_qa("Category choice: response time",
                self.category_response_time)
     h += tr_qa("Category choice: category chosen", self.category_chosen)
     h += tr_qa("Gamble: fixed option", self.gamble_fixed_option)
     h += tr_qa("Gamble: lottery option for <i>p</i>",
                self.gamble_lottery_option_p)
     h += tr_qa("Gamble: lottery option for <i>q</i> = 1 – <i>p</i>",
                self.gamble_lottery_option_q)
     h += tr_qa("Gamble: lottery on left?",
                get_yes_no_none(self.gamble_lottery_on_left))
     h += tr_qa("Gamble: starting <i>p</i>", self.gamble_starting_p)
     h += tr_qa("Gamble: start time", self.gamble_start_time)
     h += tr_qa("Gamble: responded?",
                get_yes_no_none(self.gamble_responded))
     h += tr_qa("Gamble: response time", self.gamble_response_time)
     h += tr_qa("Gamble: <i>p</i>",
                ws.number_to_dp(self.gamble_p, DP, default=None))
     h += tr_qa("Calculated utility",
                ws.number_to_dp(self.utility, DP, default=None))
     h += """
         </table>
     """
     return h
Exemple #18
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        q_a = ""
        for q in ALL:
            q_a += tr(
                f"{q}. " + self.wxstring(req, "q" + str(q)) + " (<i>" +
                self.question_category(q) + "</i>)",
                answer(self.get_frequency(q)),
                answer(
                    self.get_distress_score(q) if self.endorsed(q) else None,
                    default=str(MIN_SCORE_PER_Q),
                ),
            )

        raw_overall = tr(
            f"Overall <sup>[1]</sup> ({ALL_MIN}–{ALL_MAX})",
            self.frequency_score(ALL),
            self.distress_score(ALL),
        )
        raw_positive = tr(
            f"Positive symptoms ({POS_MIN}–{POS_MAX})",
            self.frequency_score(POSITIVE),
            self.distress_score(POSITIVE),
        )
        raw_negative = tr(
            f"Negative symptoms ({NEG_MIN}–{NEG_MAX})",
            self.frequency_score(NEGATIVE),
            self.distress_score(NEGATIVE),
        )
        raw_depressive = tr(
            f"Depressive symptoms ({DEP_MIN}–{DEP_MAX})",
            self.frequency_score(DEPRESSIVE),
            self.distress_score(DEPRESSIVE),
        )
        weighted_overall = tr(
            f"Overall ({len(ALL)} questions)",
            ws.number_to_dp(self.weighted_frequency_score(ALL), DP),
            ws.number_to_dp(self.weighted_distress_score(ALL), DP),
        )
        weighted_positive = tr(
            f"Positive symptoms ({len(POSITIVE)} questions)",
            ws.number_to_dp(self.weighted_frequency_score(POSITIVE), DP),
            ws.number_to_dp(self.weighted_distress_score(POSITIVE), DP),
        )
        weighted_negative = tr(
            f"Negative symptoms ({len(NEGATIVE)} questions)",
            ws.number_to_dp(self.weighted_frequency_score(NEGATIVE), DP),
            ws.number_to_dp(self.weighted_distress_score(NEGATIVE), DP),
        )
        weighted_depressive = tr(
            f"Depressive symptoms ({len(DEPRESSIVE)} questions)",
            ws.number_to_dp(self.weighted_frequency_score(DEPRESSIVE), DP),
            ws.number_to_dp(self.weighted_distress_score(DEPRESSIVE), DP),
        )
        return f"""
Exemple #19
0
 def get_task_html(self) -> str:
     tto_qol = self.get_tto_qol()
     rs_qol = self.get_rs_qol()
     mean_qol = mean([tto_qol, rs_qol])
     h = """
         <div class="summary">
             <table class="summary">
     """
     h += self.get_is_complete_tr()
     h += tr("Mean QoL", answer(ws.number_to_dp(mean_qol, DP, default=None), formatter_answer=identity))
     h += """
             </table>
         </div>
         <div class="explanation">
             Quality of life (QoL) has anchor values of 0 (none) and 1
             (perfect health), and can be asked about in several ways.
         </div>
         <table class="taskdetail">
             <tr>
                 <th width="33%">Scale</th>
                 <th width="33%">Answer</th>
                 <td width="33%">Implied QoL</th>
             </tr>
     """
     h += tr(
         WSTRING("qolbasic_tto_q_s"),
         answer(ws.number_to_dp(self.tto, DP, default=None)),
         answer(ws.number_to_dp(tto_qol, DP, default=None), formatter_answer=identity),
     )
     h += tr(
         WSTRING("qolbasic_rs_q_s"),
         answer(ws.number_to_dp(self.rs, DP, default=None)),
         answer(ws.number_to_dp(rs_qol, DP, default=None), formatter_answer=identity),
     )
     h += """
         </table>
     """
     return h
 def get_html_table_row(self) -> str:
     return ("<tr>" + "<td>{}</td>" * 13 + "</th>").format(
         self.trial,
         self.trial_ignoring_catch_trials,
         self.target_presented,
         self.target_time,
         ws.number_to_dp(self.intensity, DP),
         self.choice_time,
         self.responded,
         self.response_time,
         self.response_latency_ms,
         self.yes,
         self.no,
         ws.webify(self.caught_out_reset),
         ws.webify(self.trial_num_in_calculation_sequence)
     )
Exemple #21
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        rows = ""
        for q_num in range(1, self.N_QUESTIONS + 1):
            q_field = "q" + str(q_num)
            qtext = self.xstring(req, q_field)  # includes HTML
            min_text = self.wxstring(req, q_field + "_min")
            max_text = self.wxstring(req, q_field + "_max")
            qtext += f" <i>(0 = {min_text}, 10 = {max_text})</i>"
            question_cell = f"{q_num}. {qtext}"
            score = getattr(self, q_field)

            rows += tr_qa(question_cell, score)

        basdai = ws.number_to_dp(self.basdai(), 1, default="?")

        html = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                    {basdai}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                <tr>
                    <th width="60%">Question</th>
                    <th width="40%">Answer</th>
                </tr>
                {rows}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] (A) Add scores for questions 1–4.
                    (B) Calculate the mean for questions 5 and 6.
                    (C) Add A and B and divide by 5, giving a total in the
                        range 0–10.
                    &lt;4.0 suggests inactive disease,
                    &ge;4.0 suggests active disease.
            </div>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            basdai=tr(
                self.wxstring(req, "basdai") + " <sup>[1]</sup>",
                "{} ({})".format(answer(basdai), self.activity_state(req)),
            ),
            rows=rows,
        )
        return html
Exemple #22
0
 def get_task_html(self, req: CamcopsRequest) -> str:
     h = """
         <div class="{CssClass.SUMMARY}">
             <table class="{CssClass.SUMMARY}">
                 {complete_tr}
                 <tr>
                     <td>Total score</td>
                     <td>{total} / {tmax}</td>
                 </td>
                 <tr>
                     <td>Composite (mean) score</td>
                     <td>{composite} / {cmax}</td>
                 </td>
             </table>
         </div>
         <table class="{CssClass.TASKDETAIL}">
             <tr>
                 <th width="60%">Question</th>
                 <th width="40%">Answer ({qmin}–{qmax})</th>
             </tr>
     """.format(
         CssClass=CssClass,
         complete_tr=self.get_is_complete_tr(req),
         total=answer(self.total_score()),
         tmax=self.MAX_TOTAL,
         composite=answer(
             ws.number_to_dp(self.composite_score(), DP, default="?")),
         cmax=self.MAX_COMPOSITE,
         qmin=self.MIN_PER_Q,
         qmax=self.MAX_PER_Q,
     )
     for q in range(1, self.NQUESTIONS + 1):
         qtext = self.wxstring(req, "q" + str(q))
         a = getattr(self, "q" + str(q))
         atext = (self.wxstring(req, "q{}_option{}".format(q, a), str(a))
                  if a is not None else None)
         h += tr(qtext, answer(atext))
     h += """
         </table>
     """ + DATA_COLLECTION_UNLESS_UPGRADED_DIV
     return h
Exemple #23
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        rows = ""
        for q_num in range(1, self.N_QUESTIONS + 1):
            q_field = "q" + str(q_num)
            question_cell = "{}. {}".format(q_num, self.wxstring(req, q_field))

            score = getattr(self, q_field)

            rows += tr_qa(question_cell, score)

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

        html = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                    {overall_score}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                <tr>
                    <th width="60%">Question</th>
                    <th width="40%">Answer</th>
                </tr>
                {rows}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] Mean of three numerical rating scales, each rated 0-10.
            </div>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            overall_score=tr(
                self.wxstring(req, "overall_score") + " <sup>[1]</sup>",
                "{} / {}".format(answer(formatted_score), self.MAX_SCORE),
            ),
            rows=rows,
        )
        return html
Exemple #24
0
 def get_task_html(self) -> str:
     h = """
         <div class="summary">
             <table class="summary">
                 {complete_tr}
                 <tr>
                     <td>Total score</td>
                     <td>{total} / 28</td>
                 </td>
                 <tr>
                     <td>Composite (mean) score</td>
                     <td>{composite} / 4</td>
                 </td>
             </table>
         </div>
         <table class="taskdetail">
             <tr>
                 <th width="60%">Question</th>
                 <th width="40%">Answer (0–4)</th>
             </tr>
     """.format(
         complete_tr=self.get_is_complete_tr(),
         total=answer(self.total_score()),
         composite=answer(ws.number_to_dp(self.composite_score(), DP,
                                          default="?")),
     )
     for q in range(1, self.NQUESTIONS + 1):
         qtext = self.WXSTRING("q" + str(q))
         a = getattr(self, "q" + str(q))
         atext = (self.WXSTRING("q{}_option{}".format(q, a), str(a))
                  if a is not None else None)
         h += tr(qtext, answer(atext))
     h += """
         </table>
     """ + DATA_COLLECTION_UNLESS_UPGRADED_DIV
     return h
Exemple #25
0
 def percent(score: int, maximum: int) -> str:
     return ws.number_to_dp(100 * score / maximum, PERCENT_DP)
Exemple #26
0
    def get_task_html(self) -> str:
        h = """
            <div class="summary">
                <table class="summary">
        """
        h += self.get_is_complete_tr()
        h += tr_qa("BMI (kg/m<sup>2</sup>)",
                   ws.number_to_dp(self.bmi(), BMI_DP))
        h += tr_qa("Category <sup>[1]</sup>", self.category())
        h += """
                </table>
            </div>
            <table class="taskdetail">
        """
        h += tr_qa("Mass (kg)", ws.number_to_dp(self.mass_kg, KG_DP))
        h += tr_qa("Height (m)", ws.number_to_dp(self.height_m, M_DP))
        h += tr_qa("Comment", ws.webify(self.comment))
        h += """
            </table>
            <div class="footnotes">
                [1] Categorization <b>for adults</b> (square brackets
                inclusive, parentheses exclusive; AN anorexia nervosa):

                &lt;13 very severely underweight (WHO grade 3; RCPsych severe
                    AN, high risk);
                [13, 15] very severely underweight (WHO grade 3; RCPsych severe
                    AN, medium risk);
                [15, 16) severely underweight (WHO grade 3; AN);
                [16, 17) underweight (WHO grade 2; AN);
                [17, 17.5) underweight (WHO grade 1; below ICD-10/RCPsych AN
                    cutoff);
                [17.5, 18.5) underweight (WHO grade 1);
                [18.5, 25) normal (healthy weight);
                [25, 30) overweight;
                [30, 35) obese class I (moderately obese);
                [35, 40) obese class II (severely obese);
                ≥40 obese class III (very severely obese).

                Sources:
                <ul>
                    <li>WHO Expert Committee on Physical Status (1995,
                    PMID 8594834) defined ranges as:

                    &lt;16 grade 3 thinness,
                    [16, 17) grade 2 thinness,
                    [17, 18.5) grade 1 thinness,
                    [18.5, 25) normal,
                    [25, 30) grade 1 overweight,
                    [30, 40) grade 2 overweight,
                    ≥40 grade 3 overweight

                    (sections 7.2.1 and 8.7.1 and p452).</li>

                    <li>WHO (1998 “Obesity: preventing and managing the global
                    epidemic”) use the
                    categories

                    [25, 30) “pre-obese”,
                    [30, 35) obese class I,
                    [35, 40) obese class II,
                    ≥40 obese class III

                    (p9).</li>

                    <li>A large number of web sources that don’t cite a primary
                    reference use:
                    &lt;15 very severely underweight;
                    [15, 16) severely underweight;
                    [16, 18.5) underweight;
                    [18.5, 25] normal (healthy weight);
                    [25, 30) obese class I (moderately obese);
                    [35, 40) obese class II (severely obese);
                    ≥40 obese class III (very severely obese);

                    <li>The WHO (2010 “Nutrition Landscape Information System
                    (NILS) country profile indicators: interpretation guide”)
                    use
                    &lt;16 “severe thinness” (previously grade 3 thinness),
                    (16, 17] “moderate thinness” (previously grade 2 thinness),
                    [17, 18.5) “underweight” (previously grade 1 thinness).
                    (p3).</li>

                    <li>ICD-10 BMI threshold for anorexia nervosa is ≤17.5
                    (WHO, 1992). Subsequent references (e.g. RCPsych, below)
                    use &lt;17.5.</li>

                    <li>In anorexia nervosa:

                    &lt;17.5 anorexia (threshold for diagnosis),
                    &lt;15 severe anorexia;
                    13–15 medium risk,
                    &lt;13 high risk (of death)

                    (Royal College of Psychiatrists, 2010, report CR162,
                    pp. 11, 15, 20, 56).</li>
                </ul>
            </div>
        """
        return h
Exemple #27
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        h = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                    {bmi}
                    {category}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                {mass}
                {height}
                {comment}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] Categorization <b>for adults</b> (square brackets
                inclusive, parentheses exclusive; AN anorexia nervosa):

                &lt;13 very severely underweight (WHO grade 3; RCPsych severe
                    AN, high risk);
                [13, 15] very severely underweight (WHO grade 3; RCPsych severe
                    AN, medium risk);
                [15, 16) severely underweight (WHO grade 3; AN);
                [16, 17) underweight (WHO grade 2; AN);
                [17, 17.5) underweight (WHO grade 1; below ICD-10/RCPsych AN
                    cutoff);
                [17.5, 18.5) underweight (WHO grade 1);
                [18.5, 25) normal (healthy weight);
                [25, 30) overweight;
                [30, 35) obese class I (moderately obese);
                [35, 40) obese class II (severely obese);
                ≥40 obese class III (very severely obese).

                Sources:
                <ul>
                    <li>WHO Expert Committee on Physical Status (1995,
                    PMID 8594834) defined ranges as:

                    &lt;16 grade 3 thinness,
                    [16, 17) grade 2 thinness,
                    [17, 18.5) grade 1 thinness,
                    [18.5, 25) normal,
                    [25, 30) grade 1 overweight,
                    [30, 40) grade 2 overweight,
                    ≥40 grade 3 overweight

                    (sections 7.2.1 and 8.7.1 and p452).</li>

                    <li>WHO (1998 “Obesity: preventing and managing the global
                    epidemic”) use the
                    categories

                    [25, 30) “pre-obese”,
                    [30, 35) obese class I,
                    [35, 40) obese class II,
                    ≥40 obese class III

                    (p9).</li>

                    <li>A large number of web sources that don’t cite a primary
                    reference use:
                    &lt;15 very severely underweight;
                    [15, 16) severely underweight;
                    [16, 18.5) underweight;
                    [18.5, 25] normal (healthy weight);
                    [25, 30) obese class I (moderately obese);
                    [35, 40) obese class II (severely obese);
                    ≥40 obese class III (very severely obese);

                    <li>The WHO (2010 “Nutrition Landscape Information System
                    (NILS) country profile indicators: interpretation guide”)
                    use
                    &lt;16 “severe thinness” (previously grade 3 thinness),
                    (16, 17] “moderate thinness” (previously grade 2 thinness),
                    [17, 18.5) “underweight” (previously grade 1 thinness).
                    (p3).</li>

                    <li>ICD-10 BMI threshold for anorexia nervosa is ≤17.5
                    (WHO, 1992). Subsequent references (e.g. RCPsych, below)
                    use &lt;17.5.</li>

                    <li>In anorexia nervosa:

                    &lt;17.5 anorexia (threshold for diagnosis),
                    &lt;15 severe anorexia;
                    13–15 medium risk,
                    &lt;13 high risk (of death)

                    (Royal College of Psychiatrists, 2010, report CR162,
                    pp. 11, 15, 20, 56).</li>
                </ul>
            </div>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            bmi=tr_qa("BMI (kg/m<sup>2</sup>)",
                      ws.number_to_dp(self.bmi(), BMI_DP)),
            category=tr_qa("Category <sup>[1]</sup>", self.category(req)),
            mass=tr_qa("Mass (kg)", ws.number_to_dp(self.mass_kg, KG_DP)),
            height=tr_qa("Height (m)", ws.number_to_dp(self.height_m, M_DP)),
            comment=tr_qa("Comment", ws.webify(self.comment)),
        )
        return h
Exemple #28
0
 def format_average(self, value) -> str:
     return "{} / {}".format(
         answer(ws.number_to_dp(value, 3, default="?")),
         self.MAX_SCORE_PER_Q,
     )
Exemple #29
0
 def get_task_html(self, req: CamcopsRequest) -> str:
     (total, extrapolated) = self.totalscore_extrapolated()
     main_dict = {
         None: None,
         1: "1 — " + self.wxstring(req, "a1"),
         2: "2 — " + self.wxstring(req, "a2"),
         3: "3 — " + self.wxstring(req, "a3"),
         4: "4 — " + self.wxstring(req, "a4"),
         MISSING_VALUE: self.wxstring(req, "no_response")
     }
     last_q_dict = {
         None: None,
         1: "1 — " + self.wxstring(req, "q29_a1"),
         2: "2 — " + self.wxstring(req, "q29_a2"),
         3: "3 — " + self.wxstring(req, "q29_a3"),
         4: "4 — " + self.wxstring(req, "q29_a4"),
         MISSING_VALUE: self.wxstring(req, "no_response")
     }
     instruction_dict = {
         1: self.wxstring(req, "proxy_instruction11"),
         12: self.wxstring(req, "proxy_instruction12"),
         21: self.wxstring(req, "proxy_instruction13"),
         32: self.wxstring(req, "proxy_instruction14"),
     }
     h = """
         <div class="{CssClass.SUMMARY}">
             <table class="{CssClass.SUMMARY}">
                 {is_complete_tr}
                 <tr>
                     <td>Total score ({min}–{max}), higher better</td>
                     <td>{t}</td>
                 </tr>
                 <tr>
                     <td>Total score extrapolated using incomplete
                     responses? <sup>[1]</sup></td>
                     <td>{e}</td>
                 </tr>
             </table>
         </div>
         <table class="{CssClass.TASKDETAIL}">
             <tr>
                 <th width="50%">Question</th>
                 <th width="50%">Answer</th>
             </tr>
     """.format(
         CssClass=CssClass,
         is_complete_tr=self.get_is_complete_tr(req),
         min=self.MIN_SCORE,
         max=self.MAX_SCORE,
         t=answer(ws.number_to_dp(total, DP)),
         e=answer(get_yes_no(req, extrapolated)),
     )
     for n in range(1, self.NQUESTIONS + 1):
         if n in instruction_dict:
             h += subheading_spanning_two_columns(instruction_dict.get(n))
         d = main_dict if n <= self.N_SCORED_QUESTIONS else last_q_dict
         q = self.get_q(req, n)
         a = get_from_dict(d, getattr(self, "q" + str(n)))
         h += tr_qa(q, a)
     h += END_DIV + COPYRIGHT_DIV
     return h
Exemple #30
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        sides_strings = [self.wxstring(req, s) for s in self.SIDES]
        states_strings = [self.wxstring(req, s) for s in self.STATES]

        joint_rows = table_row([""] + sides_strings, colspans=[1, 2, 2])

        joint_rows += table_row([""] + states_strings * 2)

        for joint in self.JOINTS:
            cells = [th(self.wxstring(req, joint))]
            for side in self.SIDES:
                for state in self.STATES:
                    value = "?"
                    fval = getattr(self, self.field_name(side, joint, state))
                    if fval is not None:
                        value = "✓" if fval else "×"

                    cells.append(td(value))

            joint_rows += tr(*cells, literal=True)

        das28_crp = self.das28_crp()
        das28_esr = self.das28_esr()

        other_rows = "".join([
            tr_qa(self.wxstring(req, f), getattr(self, f))
            for f in self.OTHER_FIELD_NAMES
        ])

        html = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                    {das28_crp}
                    {das28_esr}
                    {swollen_joint_count}
                    {tender_joint_count}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                {joint_rows}
            </table>
            <table class="{CssClass.TASKDETAIL}">
                {other_rows}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] 0.56 × √(tender joint count) +
                    0.28 × √(swollen joint count) +
                    0.36 × ln(CRP + 1) +
                    0.014 x VAS disease activity +
                    0.96.
                    CRP 0–300 mg/L. VAS: 0–100mm.<br>
                    Cutoffs:
                    &lt;2.4 remission,
                    &lt;2.9 low disease activity,
                    2.9–4.6 moderate disease activity,
                    &gt;4.6 high disease activity.<br>
                [2] 0.56 × √(tender joint count) +
                    0.28 × √(swollen joint count) +
                    0.70 × ln(ESR) +
                    0.014 x VAS disease activity.
                    ESR 1–300 mm/h. VAS: 0–100mm.<br>
                    Cutoffs:
                    &lt;2.6 remission,
                    &lt;3.2 low disease activity,
                    3.2–5.1 moderate disease activity,
                    &gt;5.1 high disease activity.<br>
            </div>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            das28_crp=tr(
                self.wxstring(req, "das28_crp") + " <sup>[1]</sup>",
                "{} ({})".format(
                    answer(ws.number_to_dp(das28_crp, 2, default="?")),
                    self.activity_state_crp(req, das28_crp),
                ),
            ),
            das28_esr=tr(
                self.wxstring(req, "das28_esr") + " <sup>[2]</sup>",
                "{} ({})".format(
                    answer(ws.number_to_dp(das28_esr, 2, default="?")),
                    self.activity_state_esr(req, das28_esr),
                ),
            ),
            swollen_joint_count=tr(
                self.wxstring(req, "swollen_count"),
                answer(self.swollen_joint_count()),
            ),
            tender_joint_count=tr(
                self.wxstring(req, "tender_count"),
                answer(self.tender_joint_count()),
            ),
            joint_rows=joint_rows,
            other_rows=other_rows,
        )
        return html
 def get_task_html(self) -> str:
     if self.modality == MODALITY_AUDITORY:
         modality = WSTRING("auditory")
     elif self.modality == MODALITY_VISUAL:
         modality = WSTRING("visual")
     else:
         modality = None
     h = """
         <div class="summary">
             <table class="summary">
                 {}
             </table>
         </div>
         <div class="explanation">
             The ExpDet-Threshold task measures visual and auditory
             thresholds for stimuli on a noisy background, using a
             single-interval up/down method. It is intended as a prequel to
             the Expectation–Detection task.
         </div>
         <table class="taskconfig">
             <tr>
                 <th width="50%">Configuration variable</th>
                 <th width="50%">Value</th>
             </tr>
     """.format(
         self.get_is_complete_tr(),
     )
     h += tr_qa("Modality", modality)
     h += tr_qa("Target number", self.target_number)
     h += tr_qa("Background filename", ws.webify(self.background_filename))
     h += tr_qa("Background intensity", self.background_intensity)
     h += tr_qa("Target filename", ws.webify(self.target_filename))
     h += tr_qa("(For visual targets) Target duration (s)",
                self.visual_target_duration_s)
     h += tr_qa("Start intensity (minimum)", self.start_intensity_min)
     h += tr_qa("Start intensity (maximum)", self.start_intensity_max)
     h += tr_qa("Initial (large) intensity step",
                self.initial_large_intensity_step)
     h += tr_qa("Main (small) intensity step",
                self.main_small_intensity_step)
     h += tr_qa("Number of trials in main sequence",
                self.num_trials_in_main_sequence)
     h += tr_qa("Probability of a catch trial", self.p_catch_trial)
     h += tr_qa("Prompt", self.prompt)
     h += tr_qa("Intertrial interval (ITI) (s)", self.iti_s)
     h += """
         </table>
         <table class="taskdetail">
             <tr><th width="50%">Measure</th><th width="50%">Value</th></tr>
     """
     h += tr_qa("Finished?", get_yes_no_none(self.finished))
     h += tr_qa("Logistic intercept",
                ws.number_to_dp(self.intercept,
                                DP))
     h += tr_qa("Logistic slope",
                ws.number_to_dp(self.slope, DP))
     h += tr_qa("Logistic k (= slope)",
                ws.number_to_dp(self.k, DP))
     h += tr_qa("Logistic theta (= –intercept/slope)",
                ws.number_to_dp(self.theta, DP))
     h += tr_qa("Intensity for {}% detection".format(100*LOWER_MARKER),
                ws.number_to_dp(self.logistic_x_from_p(LOWER_MARKER),
                                DP))
     h += tr_qa("Intensity for 50% detection",
                ws.number_to_dp(self.theta, DP))
     h += tr_qa("Intensity for {}% detection".format(100*UPPER_MARKER),
                ws.number_to_dp(self.logistic_x_from_p(UPPER_MARKER),
                                DP))
     h += """
         </table>
     """
     h += self.get_trial_html()
     return h
Exemple #32
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        q_a = ""
        for q in ALL:
            q_a += tr(
                "{q}. ".format(q=q) + self.wxstring(req, "q" + str(q)) +
                " (<i>" + self.question_category(q) + "</i>)",
                answer(self.get_frequency(q)),
                answer(
                    self.get_distress_score(q) if self.endorsed(q) else None,
                    default=str(MIN_SCORE_PER_Q)))

        h = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {iscomplete}
                </table>
                <table class="{CssClass.SUMMARY}">
                    <tr>
                        <th>Domain (with score range)</th>
                        <th>Frequency (total score)</th>
                        <th>Distress (total score)</th>
                    </tr>
                    {raw_overall}
                    {raw_positive}
                    {raw_negative}
                    {raw_depressive}
                </table>
                <table class="{CssClass.SUMMARY}">
                    <tr>
                        <th>Domain</th>
                        <th>Weighted frequency score <sup>[3]</sup></th>
                        <th>Weighted distress score <sup>[3]</sup></th>
                    </tr>
                    {weighted_overall}
                    {weighted_positive}
                    {weighted_negative}
                    {weighted_depressive}
                </table>
            </div>
            <div class="{CssClass.EXPLANATION}">
                FREQUENCY: 1 {f1}, 2 {f2}, 3 {f3}, 4 {f4}.
                DISTRESS: 1 {d1}, 2 {d2}, 3 {d3}, 4 {d4}.
            </div>
            <table class="{CssClass.TASKDETAIL}">
                <tr>
                    <th width="70%">
                        Question (P positive, N negative, D depressive)
                    </th>
                    <th width="15%">Frequency ({low}–{high})</th>
                    <th width="15%">Distress ({low}–{high}) <sup>[2]</sup></th>
                </tr>
                {q_a}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] “Total” score is the overall frequency score (the sum of
                frequency scores for all questions).
                [2] Distress coerced to 1 if frequency is 1.
                [3] Sum score per dimension divided by number of completed
                items. Shown to {dp} decimal places. Will be in the range
                {low}–{high}, or blank if not calculable.
            </div>
        """.format(
            CssClass=CssClass,
            iscomplete=self.get_is_complete_tr(req),
            raw_overall=tr(
                "Overall <sup>[1]</sup> ({low}–{high})".format(low=ALL_MIN,
                                                               high=ALL_MAX),
                self.frequency_score(ALL), self.distress_score(ALL)),
            raw_positive=tr(
                "Positive symptoms ({low}–{high})".format(low=POS_MIN,
                                                          high=POS_MAX),
                self.frequency_score(POSITIVE), self.distress_score(POSITIVE)),
            raw_negative=tr(
                "Negative symptoms ({low}–{high})".format(low=NEG_MIN,
                                                          high=NEG_MAX),
                self.frequency_score(NEGATIVE), self.distress_score(NEGATIVE)),
            raw_depressive=tr(
                "Depressive symptoms ({low}–{high})".format(low=DEP_MIN,
                                                            high=DEP_MAX),
                self.frequency_score(DEPRESSIVE),
                self.distress_score(DEPRESSIVE)),
            weighted_overall=tr(
                "Overall ({n} questions)".format(n=len(ALL)),
                ws.number_to_dp(self.weighted_frequency_score(ALL), DP),
                ws.number_to_dp(self.weighted_distress_score(ALL), DP)),
            weighted_positive=tr(
                "Positive symptoms ({n} questions)".format(n=len(POSITIVE)),
                ws.number_to_dp(self.weighted_frequency_score(POSITIVE), DP),
                ws.number_to_dp(self.weighted_distress_score(POSITIVE), DP)),
            weighted_negative=tr(
                "Negative symptoms ({n} questions)".format(n=len(NEGATIVE)),
                ws.number_to_dp(self.weighted_frequency_score(NEGATIVE), DP),
                ws.number_to_dp(self.weighted_distress_score(NEGATIVE), DP)),
            weighted_depressive=tr(
                "Depressive symptoms ({n} questions)".format(
                    n=len(DEPRESSIVE)),  # noqa
                ws.number_to_dp(self.weighted_frequency_score(DEPRESSIVE), DP),
                ws.number_to_dp(self.weighted_distress_score(DEPRESSIVE), DP)),
            f1=self.wxstring(req, "frequency_option1"),
            f2=self.wxstring(req, "frequency_option2"),
            f3=self.wxstring(req, "frequency_option3"),
            f4=self.wxstring(req, "frequency_option4"),
            d1=self.wxstring(req, "distress_option1"),
            d2=self.wxstring(req, "distress_option2"),
            d3=self.wxstring(req, "distress_option3"),
            d4=self.wxstring(req, "distress_option4"),
            low=MIN_SCORE_PER_Q,
            high=MAX_SCORE_PER_Q,
            q_a=q_a,
            dp=DP,
        )
        return h
 def get_task_html(self, req: CamcopsRequest) -> str:
     if self.modality == MODALITY_AUDITORY:
         modality = req.wappstring("auditory")
     elif self.modality == MODALITY_VISUAL:
         modality = req.wappstring("visual")
     else:
         modality = None
     h = """
         <div class="{CssClass.SUMMARY}">
             <table class="{CssClass.SUMMARY}">
                 {tr_is_complete}
             </table>
         </div>
         <div class="{CssClass.EXPLANATION}">
             The ExpDet-Threshold task measures visual and auditory
             thresholds for stimuli on a noisy background, using a
             single-interval up/down method. It is intended as a prequel to
             the Expectation–Detection task.
         </div>
         <table class="{CssClass.TASKCONFIG}">
             <tr>
                 <th width="50%">Configuration variable</th>
                 <th width="50%">Value</th>
             </tr>
     """.format(
         CssClass=CssClass,
         tr_is_complete=self.get_is_complete_tr(req),
     )
     h += tr_qa("Modality", modality)
     h += tr_qa("Target number", self.target_number)
     h += tr_qa("Background filename", ws.webify(self.background_filename))
     h += tr_qa("Background intensity", self.background_intensity)
     h += tr_qa("Target filename", ws.webify(self.target_filename))
     h += tr_qa("(For visual targets) Target duration (s)",
                self.visual_target_duration_s)
     h += tr_qa("Start intensity (minimum)", self.start_intensity_min)
     h += tr_qa("Start intensity (maximum)", self.start_intensity_max)
     h += tr_qa("Initial (large) intensity step",
                self.initial_large_intensity_step)
     h += tr_qa("Main (small) intensity step",
                self.main_small_intensity_step)
     h += tr_qa("Number of trials in main sequence",
                self.num_trials_in_main_sequence)
     h += tr_qa("Probability of a catch trial", self.p_catch_trial)
     h += tr_qa("Prompt", self.prompt)
     h += tr_qa("Intertrial interval (ITI) (s)", self.iti_s)
     h += """
         </table>
         <table class="{CssClass.TASKDETAIL}">
             <tr><th width="50%">Measure</th><th width="50%">Value</th></tr>
     """.format(CssClass=CssClass)
     h += tr_qa("Finished?", get_yes_no_none(req, self.finished))
     h += tr_qa("Logistic intercept", ws.number_to_dp(self.intercept, DP))
     h += tr_qa("Logistic slope", ws.number_to_dp(self.slope, DP))
     h += tr_qa("Logistic k (= slope)", ws.number_to_dp(self.k, DP))
     h += tr_qa("Logistic theta (= –intercept/slope)",
                ws.number_to_dp(self.theta, DP))
     h += tr_qa("Intensity for {}% detection".format(100 * LOWER_MARKER),
                ws.number_to_dp(self.logistic_x_from_p(LOWER_MARKER), DP))
     h += tr_qa("Intensity for 50% detection",
                ws.number_to_dp(self.theta, DP))
     h += tr_qa("Intensity for {}% detection".format(100 * UPPER_MARKER),
                ws.number_to_dp(self.logistic_x_from_p(UPPER_MARKER), DP))
     h += """
         </table>
     """
     h += self.get_trial_html(req)
     return h
Exemple #34
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        rows = ""
        for q_num in range(1, self.N_QUESTIONS + 1):
            q_field = "q" + str(q_num)
            qtext = self.wxstring(req, q_field)
            if q_num <= 4:  # not for ESR, CRP
                min_text = self.wxstring(req, q_field + "_min")
                max_text = self.wxstring(req, q_field + "_max")
                qtext += f" <i>(0 = {min_text}, 10 = {max_text})</i>"
            question_cell = f"{q_num}. {qtext}"
            score = getattr(self, q_field)

            rows += tr_qa(question_cell, score)

        asdas_crp = ws.number_to_dp(self.asdas_crp(), 2, default="?")
        asdas_esr = ws.number_to_dp(self.asdas_esr(), 2, default="?")

        html = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                    {asdas_crp}
                    {asdas_esr}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                <tr>
                    <th width="60%">Question</th>
                    <th width="40%">Answer</th>
                </tr>
                {rows}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] &lt;1.3 Inactive disease,
                    &lt;2.1 Moderate disease activity,
                    2.1-3.5 High disease activity,
                    &gt;3.5 Very high disease activity.<br>
                [2] 0.12 × back pain +
                    0.06 × duration of morning stiffness +
                    0.11 × patient global +
                    0.07 × peripheral pain +
                    0.58 × ln(CRP + 1).
                    CRP units: mg/L. When CRP&lt;2mg/L, use 2mg/L to calculate
                    ASDAS-CRP.<br>
                [3] 0.08 x back pain +
                    0.07 x duration of morning stiffness +
                    0.11 x patient global +
                    0.09 x peripheral pain +
                    0.29 x √(ESR).
                    ESR units: mm/h.
            </div>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            asdas_crp=tr(
                self.wxstring(req, "asdas_crp") + " <sup>[1][2]</sup>",
                "{} ({})".format(
                    answer(asdas_crp),
                    self.activity_state(req, self.asdas_crp()),
                ),
            ),
            asdas_esr=tr(
                self.wxstring(req, "asdas_esr") + " <sup>[1][3]</sup>",
                "{} ({})".format(
                    answer(asdas_esr),
                    self.activity_state(req, self.asdas_esr()),
                ),
            ),
            rows=rows,
        )
        return html
Exemple #35
0
    def get_task_html(self) -> str:
        h = """
            <div class="summary">
                <table class="summary">
                    {iscomplete}
                </table>
                <table class="summary">
                    <tr>
                        <th>Domain (with score range)</th>
                        <th>Frequency (total score)</th>
                        <th>Distress (total score)</th>
                    </tr>
        """.format(iscomplete=self.get_is_complete_tr())

        h += tr(
            "Overall <sup>[1]</sup> ({low}–{high})".format(
                low=ALL_MIN,
                high=ALL_MAX),
            self.frequency_score(ALL),
            self.distress_score(ALL))
        h += tr(
            "Positive symptoms ({low}–{high})".format(
                low=POS_MIN,
                high=POS_MAX),
            self.frequency_score(POSITIVE),
            self.distress_score(POSITIVE))
        h += tr(
            "Negative symptoms ({low}–{high})".format(
                low=NEG_MIN,
                high=NEG_MAX),
            self.frequency_score(NEGATIVE),
            self.distress_score(NEGATIVE))
        h += tr(
            "Depressive symptoms ({low}–{high})".format(
                low=DEP_MIN,
                high=DEP_MAX),
            self.frequency_score(DEPRESSIVE),
            self.distress_score(DEPRESSIVE))

        h += """
                </table>
                <table class="summary">
                    <tr>
                        <th>Domain</th>
                        <th>Weighted frequency score <sup>[3]</sup></th>
                        <th>Weighted distress score <sup>[3]</sup></th>
                    </tr>
        """

        h += tr("Overall ({n} questions)".format(n=len(ALL)),
                ws.number_to_dp(self.weighted_frequency_score(ALL), DP),
                ws.number_to_dp(self.weighted_distress_score(ALL), DP))
        h += tr("Positive symptoms ({n} questions)".format(n=len(POSITIVE)),
                ws.number_to_dp(self.weighted_frequency_score(POSITIVE), DP),
                ws.number_to_dp(self.weighted_distress_score(POSITIVE), DP))
        h += tr("Negative symptoms ({n} questions)".format(n=len(NEGATIVE)),
                ws.number_to_dp(self.weighted_frequency_score(NEGATIVE), DP),
                ws.number_to_dp(self.weighted_distress_score(NEGATIVE), DP))
        h += tr(
            "Depressive symptoms ({n} questions)".format(n=len(DEPRESSIVE)),
            ws.number_to_dp(self.weighted_frequency_score(DEPRESSIVE), DP),
            ws.number_to_dp(self.weighted_distress_score(DEPRESSIVE), DP))

        h += """
                </table>
            </div>
            <div class="explanation">
                FREQUENCY: 1 {f1}, 2 {f2}, 3 {f3}, 4 {f4}.
                DISTRESS: 1 {d1}, 2 {d2}, 3 {d3}, 4 {d4}.
            </div>
            <table class="taskdetail">
                <tr>
                    <th width="70%">
                        Question (P positive, N negative, D depressive)
                    </th>
                    <th width="15%">Frequency ({low}–{high})</th>
                    <th width="15%">Distress ({low}–{high}) <sup>[2]</sup></th>
                </tr>
        """.format(
            f1=self.WXSTRING("frequency_option1"),
            f2=self.WXSTRING("frequency_option2"),
            f3=self.WXSTRING("frequency_option3"),
            f4=self.WXSTRING("frequency_option4"),
            d1=self.WXSTRING("distress_option1"),
            d2=self.WXSTRING("distress_option2"),
            d3=self.WXSTRING("distress_option3"),
            d4=self.WXSTRING("distress_option4"),
            low=MIN_SCORE_PER_Q,
            high=MAX_SCORE_PER_Q,
        )
        for q in ALL:
            h += tr(
                "{q}. ".format(q=q) +
                self.WXSTRING("q" + str(q)) +
                " (<i>" + self.question_category(q) + "</i>)",
                answer(self.get_frequency(q)),
                answer(
                    self.get_distress_score(q) if self.endorsed(q) else None,
                    default=str(MIN_SCORE_PER_Q))
            )
        h += """
            </table>
            <div class="footnotes">
                [1] “Total” score is the overall frequency score (the sum of
                frequency scores for all questions).
                [2] Distress coerced to 1 if frequency is 1.
                [3] Sum score per dimension divided by number of completed
                items. Shown to {dp} decimal places. Will be in the range
                {low}–{high}, or blank if not calculable.
            </div>
        """.format(
            low=MIN_SCORE_PER_Q,
            high=MAX_SCORE_PER_Q,
            dp=DP,
        )
        return h
Exemple #36
0
    def get_task_html(self, req: CamcopsRequest) -> str:
        rows = tr_span_col(
            f'{self.wxstring(req, "q1")}<br>'
            f'{self.wxstring(req, "q1sub")}',
            cols=2,
        )
        for letter in self.q1_all_letters():
            q_fieldname = f"q1{letter}"

            qtext = self.wxstring(req, q_fieldname)
            score = getattr(self, q_fieldname)

            description = "?"
            if score is not None:
                description = self.wxstring(req, f"q1_option{score}")

            rows += tr_qa(qtext, f"{score} — {description}")

        for q_num in (2, 3):
            q_fieldname = f"q{q_num}"
            qtext = self.wxstring(req, q_fieldname)
            min_text = self.wxstring(req, f"{q_fieldname}_min")
            max_text = self.wxstring(req, f"{q_fieldname}_max")
            qtext += f" <i>(0.0 = {min_text}, 10.0 = {max_text})</i>"
            score = getattr(self, q_fieldname)

            rows += tr_qa(qtext, score)

        rapid3 = ws.number_to_dp(self.rapid3(), 1, default="?")

        html = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                    {rapid3}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                <tr>
                    <th width="60%">Question</th>
                    <th width="40%">Answer</th>
                </tr>
                {rows}
            </table>
            <div class="{CssClass.FOOTNOTES}">
                [1] Add scores for questions 1a–1j (ten questions each scored
                    0–3), divide by 3, and round to 1 decimal place (giving a
                    score for Q1 in the range 0–10). Then add this to scores
                    for Q2 and Q3 (each scored 0–10) to get the RAPID3
                    cumulative score (0–30), as shown here.
                    Interpretation of the cumulative score:
                    ≤3: Near remission (NR).
                    3.1–6: Low severity (LS).
                    6.1–12: Moderate severity (MS).
                    >12: High severity (HS).

                    Note also: questions 1k–1m are each scored 0, 1.1, 2.2, or
                    3.3 in the PDF/paper version of the RAPID3, but do not
                    contribute to the formal score. They are shown here with
                    values 0, 1, 2, 3 (and, similarly, do not contribute to
                    the overall score).

            </div>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            rapid3=tr(
                self.wxstring(req, "rapid3") + " (0–30) <sup>[1]</sup>",
                "{} ({})".format(answer(rapid3), self.disease_severity(req)),
            ),
            rows=rows,
        )
        return html
Exemple #37
0
 def get_task_html(self, req: CamcopsRequest) -> str:
     scoredict = self.get_score()
     q_a = ""
     for q in range(1, NQUESTIONS + 1):
         qtext = self.wxstring(req, "q" + str(q) + "_q")
         atext = self.get_answer(req, q)
         q_a += tr_qa(qtext, atext)
     h = """
         <div class="{CssClass.SUMMARY}">
             <table class="{CssClass.SUMMARY}">
                 {complete_tr}
                 <tr>
                     <td>Total (0–n, higher better) <sup>1</sup></td>
                     <td>{total}</td>
                 </td>
                 <tr>
                     <td>n (applicable questions)</td>
                     <td>{n}</td>
                 </td>
                 <tr>
                     <td>Score (total / n; 0–1)</td>
                     <td>{score}</td>
                 </td>
                 <tr>
                     <td>logit score <sup>2</sup></td>
                     <td>{logit}</td>
                 </td>
                 <tr>
                     <td>Severity <sup>3</sup></td>
                     <td>{severity}</td>
                 </td>
             </table>
         </div>
         <table class="{CssClass.TASKDETAIL}">
             <tr>
                 <th width="50%">Question</th>
                 <th width="50%">Answer</th>
             </tr>
             {q_a}
         </table>
         <div class="{CssClass.FOOTNOTES}">
             [1] ‘Never’ scores 1 and ‘sometimes’/‘always’ both score 0,
             i.e. there is no scoring difference between ‘sometimes’ and
             ‘always’.
             [2] This is not the simple logit, log(score/[1 – score]).
             Instead, it is determined by a lookup table, as per
             <a href="http://www.ftdrg.org/wp-content/uploads/FRS-Score-conversion.pdf">http://www.ftdrg.org/wp-content/uploads/FRS-Score-conversion.pdf</a>.
             The logit score that is looked up is very close to the logit
             of the raw score (on a 0–1 scale); however, it differs in that
             firstly it is banded rather than continuous, and secondly it
             is subtly different near the lower scores and at the extremes.
             The original is based on a Rasch analysis but the raw method of
             converting the score to the tabulated logit is not given.
             [3] Where <i>x</i> is the logit score, severity is determined
             as follows (after Mioshi et al. 2010, Neurology 74: 1591, PMID
             20479357, with sharp cutoffs).
             <i>Very mild:</i> <i>x</i> ≥ 4.12.
             <i>Mild:</i> 1.92 ≤ <i>x</i> &lt; 4.12.
             <i>Moderate:</i> –0.40 ≤ <i>x</i> &lt; 1.92.
             <i>Severe:</i> –2.58 ≤ <i>x</i> &lt; –0.40.
             <i>Very severe:</i> –4.99 ≤ <i>x</i> &lt; –2.58.
             <i>Profound:</i> <i>x</i> &lt; –4.99.
         </div>
     """.format(  # noqa
         CssClass=CssClass,
         complete_tr=self.get_is_complete_tr(req),
         total=scoredict['total'],
         n=scoredict['n'],
         score=ws.number_to_dp(scoredict['score'], DP),
         logit=ws.number_to_dp(scoredict['logit'], DP),
         severity=scoredict['severity'],
         q_a=q_a,
     )
     return h
Exemple #38
0
 def get_task_html(self) -> str:
     scoredict = self.get_score()
     h = """
         <div class="summary">
             <table class="summary">
                 {complete_tr}
                 <tr>
                     <td>Total (0–n, higher better) <sup>1</sup></td>
                     <td>{total}</td>
                 </td>
                 <tr>
                     <td>n (applicable questions)</td>
                     <td>{n}</td>
                 </td>
                 <tr>
                     <td>Score (total / n; 0–1)</td>
                     <td>{score}</td>
                 </td>
                 <tr>
                     <td>logit score <sup>2</sup></td>
                     <td>{logit}</td>
                 </td>
                 <tr>
                     <td>Severity <sup>3</sup></td>
                     <td>{severity}</td>
                 </td>
             </table>
         </div>
         <table class="taskdetail">
             <tr>
                 <th width="50%">Question</th>
                 <th width="50%">Answer</th>
             </tr>
     """.format(
         complete_tr=self.get_is_complete_tr(),
         total=scoredict['total'],
         n=scoredict['n'],
         score=ws.number_to_dp(scoredict['score'], DP),
         logit=ws.number_to_dp(scoredict['logit'], DP),
         severity=scoredict['severity'],
     )
     for q in range(1, NQUESTIONS + 1):
         qtext = self.WXSTRING("q" + str(q) + "_q")
         atext = self.get_answer(q)
         h += tr_qa(qtext, atext)
     h += """
         </table>
         <div class="footnotes">
             [1] ‘Never’ scores 1 and ‘sometimes’/‘always’ both score 0,
             i.e. there is no scoring difference between ‘sometimes’ and
             ‘always’.
             [2] This is not the simple logit, log(score/[1 – score]).
             Instead, it is determined by a lookup table, as per
             <a href="http://www.ftdrg.org/wp-content/uploads/FRS-Score-conversion.pdf">http://www.ftdrg.org/wp-content/uploads/FRS-Score-conversion.pdf</a>.
             The logit score that is looked up is very close to the logit
             of the raw score (on a 0–1 scale); however, it differs in that
             firstly it is banded rather than continuous, and secondly it
             is subtly different near the lower scores and at the extremes.
             The original is based on a Rasch analysis but the raw method of
             converting the score to the tabulated logit is not given.
             [3] Where <i>x</i> is the logit score, severity is determined
             as follows (after Mioshi et al. 2010, Neurology 74: 1591, PMID
             20479357, with sharp cutoffs).
             <i>Very mild:</i> <i>x</i> ≥ 4.12.
             <i>Mild:</i> 1.92 ≤ <i>x</i> &lt; 4.12.
             <i>Moderate:</i> –0.40 ≤ <i>x</i> &lt; 1.92.
             <i>Severe:</i> –2.58 ≤ <i>x</i> &lt; –0.40.
             <i>Very severe:</i> –4.99 ≤ <i>x</i> &lt; –2.58.
             <i>Profound:</i> <i>x</i> &lt; –4.99.
         </div>
     """  # noqa
     return h