def get_html_correct_by_group_and_block( self, trialarray: List[ExpDetTrial]) -> str: if not trialarray: return div(italic("No trials")) html = """ <table class="{CssClass.EXTRADETAIL}"> <tr> <th>Block</th> """.format(CssClass=CssClass) for g in range(N_CUES): # Have spaces around | to allow web browsers to word-wrap html += """ <th>Group {0} P(detected | present)</th> <th>Group {0} P(detected | absent)</th> <th>Group {0} c</th> <th>Group {0} d'</th> """.format(g) html += """ </th> </tr> """ for b in range(self.num_blocks): html += "<tr>" + td(str(b)) for g in range(N_CUES): (p_detected_given_present, p_detected_given_absent, c, dprime, n_trials) = self.get_p_detected(trialarray, [b], [g]) html += td(a(p_detected_given_present)) html += td(a(p_detected_given_absent)) html += td(a(c)) html += td(a(dprime)) html += "</tr>\n" html += """ </table> """ return html
def get_task_html(self, req: CamcopsRequest) -> str: scoredict = self.get_score() # Q1 q_a = self.get_simple_tr_qa(req, "q1") # Q2 q_a += self.get_simple_tr_qa(req, "q2") # Q3 q_a += self.get_simple_tr_qa(req, "q3") # Q4 q_a += tr(td(self.wxstring(req, "q4_title")), td("", td_class=CssClass.SUBHEADING), literal=True) required = True for n in self.Q4_DIGIT_LENGTHS: val1 = getattr(self, "q4_len{}_1".format(n)) val2 = getattr(self, "q4_len{}_2".format(n)) q = ( "… " + self.wxstring(req, "q4_seq_len{}_1".format(n)) + " / " + self.wxstring(req, "q4_seq_len{}_2".format(n)) ) if required: score = 1 if val1 or val2 else 0 a = ( answer(get_correct_incorrect_none(val1)) + " / " + answer(get_correct_incorrect_none(val2)) + " (scores {})".format(score) ) else: a = "" q_a += tr(q, a) if not val1 and not val2: required = False # Q5 q_a += self.get_simple_tr_qa(req, "q5") # Q6 q_a += tr(td(self.wxstring(req, "q6_title")), td("", td_class=CssClass.SUBHEADING), literal=True) for n in self.Q6_SEQUENCE_NUMS: nstr = str(n) val = getattr(self, "q6_seq" + nstr) q_a += tr_qa("… " + self.wxstring(req, "q6_seq" + nstr), val) # Q7 q7map = { None: None, 1: self.wxstring(req, "q7_a_1"), 0.5: self.wxstring(req, "q7_a_half"), 0: self.wxstring(req, "q7_a_0"), } q_a += tr(td(self.wxstring(req, "q7_title")), td("", td_class=CssClass.SUBHEADING), literal=True) for n in self.Q7_PROVERB_NUMS: nstr = str(n) val = getattr(self, "q7_proverb" + nstr) a = q7map.get(val, INVALID_VALUE) q_a += tr_qa("… " + self.wxstring(req, "q7_proverb" + nstr), a) # Q8 q8map = { None: None, 2: self.wxstring(req, "q8_a2"), 1: self.wxstring(req, "q8_a1"), 0: self.wxstring(req, "q8_a0"), } q_a += tr(td(self.wxstring(req, "q8_title")), td("", td_class=CssClass.SUBHEADING), literal=True) for n in self.Q8_SENTENCE_NUMS: nstr = str(n) val = getattr(self, "q8_sentence" + nstr) a = q8map.get(val, INVALID_VALUE) q_a += tr_qa("… " + self.wxstring(req, "q8_sentence_" + nstr), a) h = """ <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {complete_tr} <tr> <td>Total (higher better)</td> <td>{total} / {tmax}</td> </td> <tr> <td>Working memory index <sup>1</sup></td> <td>{wm} / {wmax}</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] Sum of scores for Q4 + Q6. </div> {DATA_COLLECTION_UNLESS_UPGRADED_DIV} """.format( CssClass=CssClass, complete_tr=self.get_is_complete_tr(req), total=answer(scoredict['total']), tmax=self.MAX_TOTAL, wm=answer(scoredict['wm']), wmax=self.MAX_WM, q_a=q_a, DATA_COLLECTION_UNLESS_UPGRADED_DIV=DATA_COLLECTION_UNLESS_UPGRADED_DIV, # noqa ) return h
def get_task_html(self, req: CamcopsRequest) -> str: score = self.total_score() # Suicidal thoughts: suicidality_score = getattr(self, SUICIDALITY_FN) if suicidality_score is None: suicidality_text = bold("? (not completed)") suicidality_css_class = CssClass.INCOMPLETE elif suicidality_score == 0: suicidality_text = str(suicidality_score) suicidality_css_class = "" else: suicidality_text = bold(str(suicidality_score)) suicidality_css_class = CssClass.WARNING # Custom somatic score for Khandaker Insight study: somatic_css_class = "" if self.is_bdi_ii(): somatic_values = self.get_values( CUSTOM_SOMATIC_KHANDAKER_BDI_II_FIELDS) somatic_missing = False somatic_score = 0 for v in somatic_values: if v is None: somatic_missing = True somatic_css_class = CssClass.INCOMPLETE break else: somatic_score += int(v) somatic_text = ("incomplete" if somatic_missing else str(somatic_score)) else: somatic_text = "N/A" # not the BDI-II # Question rows: q_a = "" qdict = TOPICS_BY_SCALE.get(self.bdi_scale) topic = "?" for q in range(1, NQUESTIONS + 1): if qdict: topic = qdict.get(q, "??") q_a += tr_qa( f"{req.sstring(SS.QUESTION)} {q} ({topic})", getattr(self, "q" + str(q)), ) # HTML: tr_somatic_score = tr( td("Custom somatic score for Insight study <sup>[2]</sup> " "(sum of scores for questions {}, for BDI-II only)".format( ", ".join( "Q" + str(qnum) for qnum in CUSTOM_SOMATIC_KHANDAKER_BDI_II_QNUMS))), td(somatic_text, td_class=somatic_css_class), literal=True, ) tr_which_scale = tr_qa( req.wappstring(AS.BDI_WHICH_SCALE) + " <sup>[3]</sup>", ws.webify(self.bdi_scale), ) return f"""
def get_task_html(self, req: CamcopsRequest) -> str: score = self.total_score() category = self.category(req) h = """ {clinician_comments} <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {tr_is_complete} {total_score} {category} </table> </div> <table class="{CssClass.TASKDETAIL}"> <tr> <th width="80%">Question</th> <th width="20%">Score</th> </tr> """.format( clinician_comments=self.get_standard_clinician_comments_block( req, self.comments), CssClass=CssClass, tr_is_complete=self.get_is_complete_tr(req), total_score=tr( req.sstring(SS.TOTAL_SCORE), answer(score) + f" / {self.MAX_SCORE}", ), category=tr_qa( req.sstring(SS.CATEGORY) + " <sup>[1]</sup>", category), ) h += tr_qa(self.wxstring(req, "alert_s"), get_yes_no_none(req, self.alert)) h += tr_qa( self.wxstring(req, "highschool_s"), get_yes_no_none(req, self.highschooleducation), ) h += tr_qa(self.wxstring(req, "q1_s"), self.q1) h += tr_qa(self.wxstring(req, "q2_s"), self.q2) h += tr_qa(self.wxstring(req, "q3_s"), self.q3) h += tr( "Q5 <sup>[2]</sup> (money spent, money left " "[<i>scores 2</i>]", ", ".join(answer(x) for x in (self.q5a, self.q5b)), ) h += tr_qa( "Q6 (animal fluency) [<i>≥15 scores 3, 10–14 scores 2, " "5–9 scores 1, 0–4 scores 0</i>]", self.q6, ) h += tr( "Q7 (recall: apple, pen, tie, house, car)", ", ".join( answer(x) for x in (self.q7a, self.q7b, self.q7c, self.q7d, self.q7e)), ) h += tr( "Q8 (backwards: 648, 8537)", ", ".join(answer(x) for x in (self.q8b, self.q8c)), ) h += tr( "Q9 (clock: hour markers, time [<i>score 2 each</i>]", ", ".join(answer(x) for x in (self.q9a, self.q9b)), ) h += tr( "Q10 (X in triangle; which is biggest?)", ", ".join(answer(x) for x in (self.q10a, self.q10b)), ) h += tr( "Q11 (story: Female’s name? Job? When back to work? " "State she lived in? [<i>score 2 each</i>])", ", ".join( answer(x) for x in (self.q11a, self.q11b, self.q11c, self.q11d)), ) h += f""" </table> <table class="{CssClass.TASKDETAIL}"> """ h += subheading_spanning_two_columns("Images of tests: clock, shapes") # noinspection PyTypeChecker h += tr( td( get_blob_img_html(self.clockpicture), td_width="50%", td_class=CssClass.PHOTO, ), td( get_blob_img_html(self.shapespicture), td_width="50%", td_class=CssClass.PHOTO, ), literal=True, ) h += f""" </table> <div class="{CssClass.FOOTNOTES}"> [1] With high school education: ≥27 normal, ≥21 MCI, ≤20 dementia. Without high school education: ≥25 normal, ≥20 MCI, ≤19 dementia. (Tariq et al. 2006, PubMed ID 17068312.) [2] Q4 (learning the five words) isn’t scored. </div> """ return h
def get_task_html(self, req: CamcopsRequest) -> str: dict_q1 = {None: None} dict_q3 = {None: None} dict_q4 = {None: None} dict_q5 = {None: None} dict_q11 = {None: None} dict_q12 = {None: None} for option in range(1, 5): dict_q1[option] = self.wxstring(req, "q1_option" + str(option)) for option in range(1, 6): dict_q3[option] = self.wxstring(req, "q3_option" + str(option)) dict_q11[option] = self.wxstring(req, "q11_option" + str(option)) for option in range(0, 6): prefix = str(option) + " – " if option > 0 else "" dict_q4[option] = prefix + self.wxstring(req, "q4_option" + str(option)) dict_q5[option] = prefix + self.wxstring(req, "q5_option" + str(option)) for option in range(1, 17): dict_q12[option] = self.wxstring(req, "ethnicity_option" + str(option)) h = f""" <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {self.get_is_complete_tr(req)} </table> </div> <table class="{CssClass.TASKDETAIL}"> <tr> <th width="60%">Question</th> <th width="40%">Answer</th> </tr> """ ell = "… " # horizontal ellipsis sep_row = subheading_spanning_two_columns("<br>") blank_cell = td("", td_class=CssClass.SUBHEADING) h += tr_qa(self.wxstring(req, "q_doctor"), ws.webify(self.doctor)) h += sep_row h += tr_qa(self.wxstring(req, "q1"), get_from_dict(dict_q1, self.q1)) h += tr(td(self.wxstring(req, "q2")), blank_cell, literal=True) h += tr_qa( ell + self.wxstring(req, "q2_a"), get_yes_no_none(req, self.q2a), default="", ) h += tr_qa( ell + self.wxstring(req, "q2_b"), get_yes_no_none(req, self.q2b), default="", ) h += tr_qa( ell + self.wxstring(req, "q2_c"), get_yes_no_none(req, self.q2c), default="", ) h += tr_qa( ell + self.wxstring(req, "q2_d"), get_yes_no_none(req, self.q2d), default="", ) h += tr_qa( ell + self.wxstring(req, "q2_e"), get_yes_no_none(req, self.q2e), default="", ) h += tr_qa( ell + self.wxstring(req, "q2_f"), get_yes_no_none(req, self.q2f), default="", ) h += tr_qa( ell + ell + self.wxstring(req, "q2f_s"), ws.webify(self.q2f_details), ) h += tr_qa(self.wxstring(req, "q3"), get_from_dict(dict_q3, self.q3)) h += tr(td(self.wxstring(req, "q4")), blank_cell, literal=True) h += tr_qa(ell + self.wxstring(req, "q4_a"), get_from_dict(dict_q4, self.q4a)) h += tr_qa(ell + self.wxstring(req, "q4_b"), get_from_dict(dict_q4, self.q4b)) h += tr_qa(ell + self.wxstring(req, "q4_c"), get_from_dict(dict_q4, self.q4c)) h += tr_qa(ell + self.wxstring(req, "q4_d"), get_from_dict(dict_q4, self.q4d)) h += tr_qa(ell + self.wxstring(req, "q4_e"), get_from_dict(dict_q4, self.q4e)) h += tr_qa(ell + self.wxstring(req, "q4_f"), get_from_dict(dict_q4, self.q4f)) h += tr_qa(ell + self.wxstring(req, "q4_g"), get_from_dict(dict_q4, self.q4g)) h += tr(td(self.wxstring(req, "q5")), blank_cell, literal=True) h += tr_qa(ell + self.wxstring(req, "q5_a"), get_from_dict(dict_q5, self.q5a)) h += tr_qa(ell + self.wxstring(req, "q5_b"), get_from_dict(dict_q5, self.q5b)) h += tr_qa(self.wxstring(req, "q6"), get_yes_no_none(req, self.q6)) h += tr_qa(self.wxstring(req, "q7"), get_yes_no_none(req, self.q7)) h += tr_qa(self.wxstring(req, "q8"), get_yes_no_none(req, self.q8)) h += tr_qa(self.wxstring(req, "q9_s"), ws.webify(self.q9)) h += sep_row h += tr_qa(req.sstring(SS.SEX), ws.webify(self.q10)) h += tr_qa(self.wxstring(req, "q11"), get_from_dict(dict_q11, self.q11)) h += tr_qa(self.wxstring(req, "q12"), get_from_dict(dict_q12, self.q12)) h += tr_qa( ell + self.wxstring(req, "ethnicity_other_s"), ws.webify(self.q12_details), ) h += """ </table> """ return h
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: <2.4 remission, <2.9 low disease activity, 2.9–4.6 moderate disease activity, >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: <2.6 remission, <3.2 low disease activity, 3.2–5.1 moderate disease activity, >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, req: CamcopsRequest) -> str: # Table rows for individual words q_a = "" nwords = len(WORDLIST) ncolumns = 3 nrows = int(math.ceil(float(nwords) / float(ncolumns))) column = 0 row = 0 # x: word index (shown in top-to-bottom then left-to-right sequence) for unused_loopvar in range(nwords): x = (column * nrows) + row if column == 0: # first column q_a += "<tr>" q_a += td(ACCENTED_WORDLIST[x]) q_a += td(answer(getattr(self, WORDLIST[x]))) if column == (ncolumns - 1): # last column q_a += "</tr>" row += 1 column = (column + 1) % ncolumns # Annotations nelson = "; Nelson 1982 <sup>[1]</sup>" nelson_willison = "; Nelson & Willison 1991 <sup>[2]</sup>" bright = "; Bright 2016 <sup>[3]</sup>" # HTML h = """ <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {tr_is_complete} {tr_total_errors} {nelson_full_scale_iq} {nelson_verbal_iq} {nelson_performance_iq} {nelson_willison_full_scale_iq} {bright_full_scale_iq} {bright_general_ability} {bright_verbal_comprehension} {bright_perceptual_reasoning} {bright_working_memory} {bright_perceptual_speed} </table> </div> <div class="{CssClass.EXPLANATION}"> Estimates premorbid IQ by pronunciation of irregular words. </div> <table class="{CssClass.TASKDETAIL}"> <tr> <th width="16%">Word</th><th width="16%">Correct?</th> <th width="16%">Word</th><th width="16%">Correct?</th> <th width="16%">Word</th><th width="16%">Correct?</th> </tr> {q_a} </table> <div class="{CssClass.FOOTNOTES}"> [1] Nelson HE (1982), <i>National Adult Reading Test (NART): For the Assessment of Premorbid Intelligence in Patients with Dementia: Test Manual</i>, NFER-Nelson, Windsor, UK. [2] Nelson HE, Wilson J (1991) <i>National Adult Reading Test (NART)</i>, NFER-Nelson, Windsor, UK; see [3]. [3] Bright P et al (2016). The National Adult Reading Test: restandardisation against the Wechsler Adult Intelligence Scale—Fourth edition. <a href="https://www.ncbi.nlm.nih.gov/pubmed/27624393">PMID 27624393</a>. </div> <div class="{CssClass.COPYRIGHT}"> NART: Copyright © Hazel E. Nelson. Used with permission. </div> """.format( CssClass=CssClass, tr_is_complete=self.get_is_complete_tr(req), tr_total_errors=tr_qa("Total errors", self.n_errors()), nelson_full_scale_iq=tr_qa( "Predicted WAIS full-scale IQ = 127.7 – 0.826 × errors" + nelson, # noqa self.nelson_full_scale_iq()), nelson_verbal_iq=tr_qa( "Predicted WAIS verbal IQ = 129.0 – 0.919 × errors" + nelson, self.nelson_verbal_iq()), nelson_performance_iq=tr_qa( "Predicted WAIS performance IQ = 123.5 – 0.645 × errors" + nelson, self.nelson_performance_iq()), nelson_willison_full_scale_iq=tr_qa( "Predicted WAIS-R full-scale IQ " "= 130.6 – 1.24 × errors" + nelson_willison, self.nelson_willison_full_scale_iq()), bright_full_scale_iq=tr_qa( "Predicted WAIS-IV full-scale IQ " "= 126.41 – 0.9775 × errors" + bright, self.bright_full_scale_iq()), bright_general_ability=tr_qa( "Predicted WAIS-IV General Ability Index " "= 126.5 – 0.9656 × errors" + bright, self.bright_general_ability()), bright_verbal_comprehension=tr_qa( "Predicted WAIS-IV Verbal Comprehension Index " "= 126.81 – 1.0745 × errors" + bright, self.bright_verbal_comprehension()), bright_perceptual_reasoning=tr_qa( "Predicted WAIS-IV Perceptual Reasoning Index " "= 120.18 – 0.6242 × errors" + bright, self.bright_perceptual_reasoning()), bright_working_memory=tr_qa( "Predicted WAIS-IV Working Memory Index " "= 120.53 – 0.7901 × errors" + bright, self.bright_working_memory()), bright_perceptual_speed=tr_qa( "Predicted WAIS-IV Perceptual Speed Index " "= 114.53 – 0.5285 × errors" + bright, self.bright_perceptual_speed()), q_a=q_a, ) return h
def get_task_html(self, req: CamcopsRequest) -> str: vsp = self.score_vsp() naming = self.score_naming() attention = self.score_attention() language = self.score_language() abstraction = self.score_abstraction() memory = self.score_memory() orientation = self.score_orientation() totalscore = self.total_score() category = self.category(req) h = """ {clinician_comments} <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {tr_is_complete} {total_score} {category} </table> </div> <table class="{CssClass.TASKDETAIL}"> <tr> <th width="69%">Question</th> <th width="31%">Score</th> </tr> """.format( clinician_comments=self.get_standard_clinician_comments_block( req, self.comments), CssClass=CssClass, tr_is_complete=self.get_is_complete_tr(req), total_score=tr( req.sstring(SS.TOTAL_SCORE), answer(totalscore) + f" / {self.MAX_SCORE}", ), category=tr_qa( self.wxstring(req, "category") + " <sup>[1]</sup>", category), ) h += tr( self.wxstring(req, "subscore_visuospatial"), answer(vsp) + " / 5", tr_class=CssClass.SUBHEADING, ) h += tr( "Path, cube, clock/contour, clock/numbers, clock/hands", ", ".join( answer(x) for x in (self.q1, self.q2, self.q3, self.q4, self.q5)), ) h += tr( self.wxstring(req, "subscore_naming"), answer(naming) + " / 3", tr_class=CssClass.SUBHEADING, ) h += tr( "Lion, rhino, camel", ", ".join(answer(x) for x in (self.q6, self.q7, self.q8)), ) h += tr( self.wxstring(req, "subscore_attention"), answer(attention) + " / 6", tr_class=CssClass.SUBHEADING, ) h += tr( "5 digits forwards, 3 digits backwards, tapping, serial 7s " "[<i>scores 3</i>]", ", ".join( answer(x) for x in (self.q9, self.q10, self.q11, self.q12)), ) h += tr( self.wxstring(req, "subscore_language"), answer(language) + " / 3", tr_class=CssClass.SUBHEADING, ) h += tr( "Repeat sentence 1, repeat sentence 2, fluency to letter ‘F’", ", ".join(answer(x) for x in (self.q13, self.q14, self.q15)), ) h += tr( self.wxstring(req, "subscore_abstraction"), answer(abstraction) + " / 2", tr_class=CssClass.SUBHEADING, ) h += tr( "Means of transportation, measuring instruments", ", ".join(answer(x) for x in (self.q16, self.q17)), ) h += tr( self.wxstring(req, "subscore_memory"), answer(memory) + " / 5", tr_class=CssClass.SUBHEADING, ) h += tr( "Registered on first trial [<i>not scored</i>]", ", ".join( answer(x, formatter_answer=italic) for x in ( self.register_trial1_1, self.register_trial1_2, self.register_trial1_3, self.register_trial1_4, self.register_trial1_5, )), ) h += tr( "Registered on second trial [<i>not scored</i>]", ", ".join( answer(x, formatter_answer=italic) for x in ( self.register_trial2_1, self.register_trial2_2, self.register_trial2_3, self.register_trial2_4, self.register_trial2_5, )), ) h += tr( "Recall FACE, VELVET, CHURCH, DAISY, RED with no cue", ", ".join( answer(x) for x in (self.q18, self.q19, self.q20, self.q21, self.q22)), ) h += tr( "Recall with category cue [<i>not scored</i>]", ", ".join( answer(x, formatter_answer=italic) for x in ( self.recall_category_cue_1, self.recall_category_cue_2, self.recall_category_cue_3, self.recall_category_cue_4, self.recall_category_cue_5, )), ) h += tr( "Recall with multiple-choice cue [<i>not scored</i>]", ", ".join( answer(x, formatter_answer=italic) for x in ( self.recall_mc_cue_1, self.recall_mc_cue_2, self.recall_mc_cue_3, self.recall_mc_cue_4, self.recall_mc_cue_5, )), ) h += tr( self.wxstring(req, "subscore_orientation"), answer(orientation) + " / 6", tr_class=CssClass.SUBHEADING, ) h += tr( "Date, month, year, day, place, city", ", ".join( answer(x) for x in ( self.q23, self.q24, self.q25, self.q26, self.q27, self.q28, )), ) h += subheading_spanning_two_columns(self.wxstring(req, "education_s")) h += tr_qa("≤12 years’ education?", self.education12y_or_less) # noinspection PyTypeChecker h += """ </table> <table class="{CssClass.TASKDETAIL}"> {tr_subhead_images} {tr_images_1} {tr_images_2} </table> <div class="{CssClass.FOOTNOTES}"> [1] Normal is ≥26 (Nasreddine et al. 2005, PubMed ID 15817019). </div> <div class="{CssClass.COPYRIGHT}"> MoCA: Copyright © Ziad Nasreddine. In 2012, could be reproduced without permission for CLINICAL and EDUCATIONAL use (with permission from the copyright holder required for any other use), with no special restrictions on electronic versions. However, as of 2021, electronic versions are prohibited without specific authorization from the copyright holder; see <a href="https://camcops.readthedocs.io/en/latest/tasks/moca.html"> https://camcops.readthedocs.io/en/latest/tasks/moca.html</a>. </div> """.format( CssClass=CssClass, tr_subhead_images=subheading_spanning_two_columns( "Images of tests: trail, cube, clock", th_not_td=True), tr_images_1=tr( td( get_blob_img_html(self.trailpicture), td_class=CssClass.PHOTO, td_width="50%", ), td( get_blob_img_html(self.cubepicture), td_class=CssClass.PHOTO, td_width="50%", ), literal=True, ), tr_images_2=tr( td( get_blob_img_html(self.clockpicture), td_class=CssClass.PHOTO, td_width="50%", ), td("", td_class=CssClass.SUBHEADING), literal=True, ), ) return h
def get_task_html(self, req: CamcopsRequest) -> str: scoredict = self.get_score() # Q1 q_a = self.get_simple_tr_qa(req, "q1") # Q2 q_a += self.get_simple_tr_qa(req, "q2") # Q3 q_a += self.get_simple_tr_qa(req, "q3") # Q4 q_a += tr( td(self.wxstring(req, "q4_title")), td("", td_class=CssClass.SUBHEADING), literal=True, ) required = True for n in self.Q4_DIGIT_LENGTHS: val1 = getattr(self, f"q4_len{n}_1") val2 = getattr(self, f"q4_len{n}_2") q = ("… " + self.wxstring(req, f"q4_seq_len{n}_1") + " / " + self.wxstring(req, f"q4_seq_len{n}_2")) if required: score = 1 if val1 or val2 else 0 a = (answer(get_correct_incorrect_none(val1)) + " / " + answer(get_correct_incorrect_none(val2)) + f" (scores {score})") else: a = "" q_a += tr(q, a) if not val1 and not val2: required = False # Q5 q_a += self.get_simple_tr_qa(req, "q5") # Q6 q_a += tr( td(self.wxstring(req, "q6_title")), td("", td_class=CssClass.SUBHEADING), literal=True, ) for n in self.Q6_SEQUENCE_NUMS: nstr = str(n) val = getattr(self, "q6_seq" + nstr) q_a += tr_qa("… " + self.wxstring(req, "q6_seq" + nstr), val) # Q7 q7map = { None: None, 1: self.wxstring(req, "q7_a_1"), 0.5: self.wxstring(req, "q7_a_half"), 0: self.wxstring(req, "q7_a_0"), } q_a += tr( td(self.wxstring(req, "q7_title")), td("", td_class=CssClass.SUBHEADING), literal=True, ) for n in self.Q7_PROVERB_NUMS: nstr = str(n) val = getattr(self, "q7_proverb" + nstr) a = q7map.get(val, INVALID_VALUE) q_a += tr_qa("… " + self.wxstring(req, "q7_proverb" + nstr), a) # Q8 q8map = { None: None, 2: self.wxstring(req, "q8_a2"), 1: self.wxstring(req, "q8_a1"), 0: self.wxstring(req, "q8_a0"), } q_a += tr( td(self.wxstring(req, "q8_title")), td("", td_class=CssClass.SUBHEADING), literal=True, ) for n in self.Q8_SENTENCE_NUMS: nstr = str(n) val = getattr(self, "q8_sentence" + nstr) a = q8map.get(val, INVALID_VALUE) q_a += tr_qa("… " + self.wxstring(req, "q8_sentence_" + nstr), a) return f"""
def text_row(self, req: CamcopsRequest, wstringname: str) -> str: return tr( td(self.wxstring(req, wstringname)), td("", td_class=CssClass.SUBHEADING), literal=True, )
def get_task_html(self, req: CamcopsRequest) -> str: score = self.total_score() # Suicidal thoughts: suicidality_score = getattr(self, SUICIDALITY_FN) if suicidality_score is None: suicidality_text = bold("? (not completed)") suicidality_css_class = CssClass.INCOMPLETE elif suicidality_score == 0: suicidality_text = str(suicidality_score) suicidality_css_class = "" else: suicidality_text = bold(str(suicidality_score)) suicidality_css_class = CssClass.WARNING # Custom somatic score for Khandaker Insight study: somatic_css_class = "" if self.is_bdi_ii(): somatic_values = self.get_values( CUSTOM_SOMATIC_KHANDAKER_BDI_II_FIELDS) somatic_missing = False somatic_score = 0 for v in somatic_values: if v is None: somatic_missing = True somatic_css_class = CssClass.INCOMPLETE break else: somatic_score += int(v) somatic_text = ("incomplete" if somatic_missing else str(somatic_score)) else: somatic_text = "N/A" # not the BDI-II # Question rows: q_a = "" qdict = TOPICS_BY_SCALE.get(self.bdi_scale) topic = "?" for q in range(1, NQUESTIONS + 1): if qdict: topic = qdict.get(q, "??") q_a += tr_qa( "{question} {qnum} ({topic})".format( question=req.wappstring("question"), qnum=q, topic=topic, ), getattr(self, "q" + str(q)) ) # HTML: h = """ <div class="{CssClass.SUMMARY}"> <table class="{CssClass.SUMMARY}"> {tr_is_complete} {tr_total_score} <tr> <td> Suicidal thoughts/wishes score (Q{suicidality_qnum}) <sup>[1]</sup> </td> {td_suicidality} </tr> {tr_somatic_score} </table> </div> <div class="{CssClass.EXPLANATION}"> All questions are scored from 0–3 (0 free of symptoms, 3 most symptomatic). </div> <table class="{CssClass.TASKDETAIL}"> <tr> <th width="70%">Question</th> <th width="30%">Answer</th> </tr> {tr_which_scale} {q_a} </table> <div class="{CssClass.FOOTNOTES}"> [1] Suicidal thoughts are asked about in Q{suicidality_qnum} for all of: BDI-I (1961), BDI-IA (1978), and BDI-II (1996). [2] Insight study: <a href="https://doi.org/10.1186/ISRCTN16942542">doi:10.1186/ISRCTN16942542</a> [3] See the <a href="https://camcops.readthedocs.io/en/latest/tasks/bdi.html">CamCOPS BDI help</a> for full references and bibliography for the citations that follow. <b>The BDI rates “right now” [Beck1988]. The BDI-IA rates the past week [Beck1988]. The BDI-II rates the past two weeks [Beck1996b].</b> 1961 BDI(-I) question topics from [Beck1988]. 1978 BDI-IA question topics from [Beck1996b]. 1996 BDI-II question topics from [Steer1999], [Gary2018]. </ul> </div> {data_collection_only_div} """.format( # noqa CssClass=CssClass, tr_is_complete=self.get_is_complete_tr(req), tr_total_score=tr( req.wappstring("total_score"), answer(score) + " / {}".format(MAX_SCORE) ), suicidality_qnum=SUICIDALITY_QNUM, td_suicidality=td(suicidality_text, td_class=suicidality_css_class), # noqa tr_somatic_score=tr( td( "Custom somatic score for Insight study <sup>[2]</sup> " "(sum of scores for questions {}, for BDI-II only)".format( ", ".join("Q" + str(qnum) for qnum in CUSTOM_SOMATIC_KHANDAKER_BDI_II_QNUMS)) ), td(somatic_text, td_class=somatic_css_class), literal=True ), tr_which_scale=tr_qa( req.wappstring("bdi_which_scale") + " <sup>[3]</sup>", ws.webify(self.bdi_scale) ), q_a=q_a, data_collection_only_div=DATA_COLLECTION_ONLY_DIV ) return h