Exemplo n.º 1
0
 def __init__(
     cls: Type["YbocsSc"],
     name: str,
     bases: Tuple[Type, ...],
     classdict: Dict[str, Any],
 ) -> None:
     for item in cls.ITEMS:
         setattr(
             cls,
             item + cls.SUFFIX_CURRENT,
             CamcopsColumn(
                 item + cls.SUFFIX_CURRENT,
                 Boolean,
                 permitted_value_checker=BIT_CHECKER,
                 comment=item + " (current symptom)",
             ),
         )
         setattr(
             cls,
             item + cls.SUFFIX_PAST,
             CamcopsColumn(
                 item + cls.SUFFIX_PAST,
                 Boolean,
                 permitted_value_checker=BIT_CHECKER,
                 comment=item + " (past symptom)",
             ),
         )
         setattr(
             cls,
             item + cls.SUFFIX_PRINCIPAL,
             CamcopsColumn(
                 item + cls.SUFFIX_PRINCIPAL,
                 Boolean,
                 permitted_value_checker=BIT_CHECKER,
                 comment=item + " (principal symptom)",
             ),
         )
         if item.endswith(cls.SUFFIX_OTHER):
             setattr(
                 cls,
                 item + cls.SUFFIX_DETAIL,
                 Column(
                     item + cls.SUFFIX_DETAIL,
                     UnicodeText,
                     comment=item + " (details)",
                 ),
             )
     super().__init__(name, bases, classdict)
Exemplo n.º 2
0
 def __init__(
     cls: Type["Frs"],
     name: str,
     bases: Tuple[Type, ...],
     classdict: Dict[str, Any],
 ) -> None:
     for n in range(1, NQUESTIONS + 1):
         pv = [NEVER, ALWAYS]
         pc = [f"{NEVER} = never", f"{ALWAYS} = always"]
         if n not in NO_SOMETIMES_QUESTIONS:
             pv.append(SOMETIMES)
             pc.append(f"{SOMETIMES} = sometimes")
         if n in NA_QUESTIONS:
             pv.append(NA)
             pc.append(f"{NA} = N/A")
         comment = f"Q{n}, {QUESTION_SNIPPETS[n - 1]} ({', '.join(pc)})"
         colname = f"q{n}"
         setattr(
             cls,
             colname,
             CamcopsColumn(
                 colname,
                 Integer,
                 permitted_value_checker=PermittedValueChecker(
                     permitted_values=pv),
                 comment=comment,
             ),
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 3
0
    def __init__(
        cls: Type["Esspri"],
        name: str,
        bases: Tuple[Type, ...],
        classdict: Dict[str, Any],
    ) -> None:

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

        for q_index in range(0, cls.N_QUESTIONS):
            q_num = q_index + 1
            q_field = "q{}".format(q_num)

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

            setattr(
                cls,
                q_field,
                CamcopsColumn(
                    q_field,
                    Integer,
                    permitted_value_checker=ZERO_TO_10_CHECKER,
                    comment="Q{} ({}) {}".format(
                        q_num, comment_strings[q_index], score_comment
                    ),
                ),
            )

        super().__init__(name, bases, classdict)
Exemplo n.º 4
0
    def __init__(
        cls: Type["Das28"],
        name: str,
        bases: Tuple[Type, ...],
        classdict: Dict[str, Any],
    ) -> None:
        for field_name in cls.get_joint_field_names():
            setattr(cls, field_name,
                    BoolColumn(field_name, comment="0 no, 1 yes"))

        setattr(
            cls,
            "vas",
            CamcopsColumn(
                "vas",
                Integer,
                comment="Patient assessment of health (0-100mm)",
                permitted_value_checker=PermittedValueChecker(minimum=0,
                                                              maximum=100),
            ),
        )

        setattr(cls, "crp", Column("crp", Float, comment="CRP (0-300 mg/L)"))

        setattr(cls, "esr", Column("esr", Float, comment="ESR (1-300 mm/h)"))

        super().__init__(name, bases, classdict)
 def rating(cls) -> Column:
     return CamcopsColumn(
         "rating",
         Integer,
         permitted_value_checker=ZERO_TO_FOUR_CHECKER,
         comment="Rating (0 very poor - 4 excellent)",
     )
Exemplo n.º 6
0
 def __init__(cls: Type['Ybocs'],
              name: str,
              bases: Tuple[Type, ...],
              classdict: Dict[str, Any]) -> None:
     cls.TARGET_COLUMNS = []  # type: List[Column]
     for target in ["obsession", "compulsion", "avoidance"]:
         for n in range(1, cls.NTARGETS + 1):
             fname = "target_{}_{}".format(target, n)
             col = Column(
                 fname, UnicodeText,
                 comment="Target symptoms: {} {}".format(target, n)
             )
             setattr(cls, fname, col)
             cls.TARGET_COLUMNS.append(col)
     for qnumstr, maxscore, comment in cls.QINFO:
         fname = "q" + qnumstr
         setattr(
             cls,
             fname,
             CamcopsColumn(
                 fname, Integer,
                 permitted_value_checker=PermittedValueChecker(
                     minimum=0, maximum=maxscore),
                 comment="Q{n}, {s} (0-{m}, higher worse)".format(
                     n=qnumstr, s=comment, m=maxscore)
             )
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 7
0
 def __init__(
     cls: Type["Ybocs"],
     name: str,
     bases: Tuple[Type, ...],
     classdict: Dict[str, Any],
 ) -> None:
     cls.TARGET_COLUMNS = []  # type: List[Column]
     for target in ("obsession", "compulsion", "avoidance"):
         for n in range(1, cls.NTARGETS + 1):
             fname = f"target_{target}_{n}"
             col = Column(
                 fname,
                 UnicodeText,
                 comment=f"Target symptoms: {target} {n}",
             )
             setattr(cls, fname, col)
             cls.TARGET_COLUMNS.append(col)
     for qnumstr, maxscore, comment in cls.QINFO:
         fname = "q" + qnumstr
         setattr(
             cls,
             fname,
             CamcopsColumn(
                 fname,
                 Integer,
                 permitted_value_checker=PermittedValueChecker(
                     minimum=0, maximum=maxscore),
                 comment=f"Q{qnumstr}, {comment} "
                 f"(0-{maxscore}, higher worse)",
             ),
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 8
0
 def __init__(cls: Type['Frs'],
              name: str,
              bases: Tuple[Type, ...],
              classdict: Dict[str, Any]) -> None:
     for n in range(1, NQUESTIONS + 1):
         pv = [NEVER, ALWAYS]
         pc = ["{} = never".format(NEVER), "{} = always".format(ALWAYS)]
         if n not in NO_SOMETIMES_QUESTIONS:
             pv.append(SOMETIMES)
             pc.append("{} = sometimes".format(SOMETIMES))
         if n in NA_QUESTIONS:
             pv.append(NA)
             pc.append("{} = N/A".format(NA))
         comment = "Q{}, {} ({})".format(n, QUESTION_SNIPPETS[n - 1],
                                         ", ".join(pc))
         colname = "q" + str(n)
         setattr(
             cls,
             colname,
             CamcopsColumn(
                 colname, Integer,
                 permitted_value_checker=PermittedValueChecker(
                     permitted_values=pv),
                 comment=comment
             )
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 9
0
 def description(cls) -> Column:
     return CamcopsColumn(
         "description",
         UnicodeText,
         exempt_from_anonymisation=True,
         comment="Description of the diagnostic code",
     )
Exemplo n.º 10
0
 def __init__(
     cls: Type["Pbq"],
     name: str,
     bases: Tuple[Type, ...],
     classdict: Dict[str, Any],
 ) -> None:
     comment_strings = [
         # This is the Brockington 2006 order; see XML for notes.
         # 1-5
         "I feel close to my baby",
         "I wish the old days when I had no baby would come back",
         "I feel distant from my baby",
         "I love to cuddle my baby",
         "I regret having this baby",
         # 6-10
         "The baby does not seem to be mine",
         "My baby winds me up",
         "I love my baby to bits",
         "I feel happy when my baby smiles or laughs",
         "My baby irritates me",
         # 11-15
         "I enjoy playing with my baby",
         "My baby cries too much",
         "I feel trapped as a mother",
         "I feel angry with my baby",
         "I resent my baby",
         # 16-20
         "My baby is the most beautiful baby in the world",
         "I wish my baby would somehow go away",
         "I have done harmful things to my baby",
         "My baby makes me anxious",
         "I am afraid of my baby",
         # 21-25
         "My baby annoys me",
         "I feel confident when changing my baby",
         "I feel the only solution is for someone else to look after my baby",  # noqa
         "I feel like hurting my baby",
         "My baby is easily comforted",
     ]
     pvc = PermittedValueChecker(minimum=cls.MIN_PER_Q,
                                 maximum=cls.MAX_PER_Q)
     for n in range(1, cls.NQUESTIONS + 1):
         i = n - 1
         colname = f"q{n}"
         if n in cls.SCORED_A0N5_Q:
             explan = "always 0 - never 5"
         else:
             explan = "always 5 - never 0"
         comment = f"Q{n}, {comment_strings[i]} ({explan}, higher worse)"
         setattr(
             cls,
             colname,
             CamcopsColumn(
                 colname,
                 Integer,
                 comment=comment,
                 permitted_value_checker=pvc,
             ),
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 11
0
class KhandakerMojoMedicationItem(KhandakerMojoTableItem):
    __tablename__ = "khandaker_mojo_medication_item"

    medicationtable_id = CamcopsColumn(
        "medicationtable_id",
        Integer,
        nullable=False,
        comment="FK to medicationtable",
    )
    seqnum = CamcopsColumn(
        "seqnum",
        Integer,
        nullable=False,
        comment="Sequence number of this medication",
    )
    brand_name = CamcopsColumn("brand_name", UnicodeText, comment="Brand name")
    chemical_name = CamcopsColumn("chemical_name",
                                  UnicodeText,
                                  comment="Chemical name for study team")
    dose = CamcopsColumn("dose", UnicodeText, comment="Dose")
    frequency = CamcopsColumn("frequency", UnicodeText, comment="Frequency")
    duration_months = CamcopsColumn("duration_months",
                                    Float,
                                    comment="Duration (months)")
    indication = CamcopsColumn(
        "indication",
        UnicodeText,
        comment="Indication (what is the medication used for?)",
    )
    response = CamcopsColumn(
        "response",
        Integer,
        comment=("1 = treats all symptoms, "
                 "2 = most symptoms, "
                 "3 = some symptoms, "
                 "4 = no symptoms)"),
    )

    @classmethod
    def mandatory_fields(cls) -> List[str]:
        return [
            "brand_name",
            "chemical_name",
            "dose",
            "frequency",
            "duration_months",
            "indication",
            "response",
        ]

    def get_html_table_row(self, req: "CamcopsRequest") -> str:
        return f"""
Exemplo n.º 12
0
class Irac(TaskHasPatientMixin, Task):
    """
    Server implementation of the IRAC task.
    """
    __tablename__ = "irac"
    shortname = "IRAC"
    longname = "Identify and Rate the Aim of the Contact"

    aim = Column("aim", UnicodeText, comment="Main aim of the contact")
    achieved = CamcopsColumn(
        "achieved",
        Integer,
        permitted_value_checker=ZERO_TO_TWO_CHECKER,
        comment="Was the aim achieved? (0 not, 1 partially, 2 fully)")

    TASK_FIELDS = ["aim", "achieved"]

    def is_complete(self) -> bool:
        return (self.are_all_fields_complete(self.TASK_FIELDS)
                and self.field_contents_valid())

    def get_achieved_text(self, req: CamcopsRequest) -> str:
        achieveddict = {
            None: None,
            0: self.wxstring(req, "achieved_0"),
            1: self.wxstring(req, "achieved_1"),
            2: self.wxstring(req, "achieved_2"),
        }
        return get_from_dict(achieveddict, self.achieved)

    def get_task_html(self, req: CamcopsRequest) -> str:
        if self.achieved is not None:
            achieved = "{}. {}".format(self.achieved,
                                       self.get_achieved_text(req))
        else:
            achieved = None
        h = """
            <div class="{CssClass.SUMMARY}">
                <table class="{CssClass.SUMMARY}">
                    {tr_is_complete}
                </table>
            </div>
            <table class="{CssClass.TASKDETAIL}">
                <tr>
                    <th width="50%">Question</th>
                    <th width="50%">Answer</th>
                </tr>
                {aim}
                {achieved}
            </table>
        """.format(
            CssClass=CssClass,
            tr_is_complete=self.get_is_complete_tr(req),
            aim=tr_qa(self.wxstring(req, "q_aim"), ws.webify(self.aim)),
            achieved=tr_qa(self.wxstring(req, "q_achieved"), achieved),
        )
        return h
Exemplo n.º 13
0
class TreeNode(AbstractConcreteBase, Base):
    id = Column(Integer, primary_key=True)
    name = Column(String)
    if WITH_CAMCOPS_COLUMNS:
        counter = CamcopsColumn("counter", Integer)
        flipswitch = BoolColumn("flipswitch")

    @declared_attr
    def __mapper_args__(cls):
        return {'polymorphic_identity': cls.__name__, 'concrete': True}
Exemplo n.º 14
0
    def __init__(
        cls: Type["Suppsp"],
        name: str,
        bases: Tuple[Type, ...],
        classdict: Dict[str, Any],
    ) -> None:

        comment_strings = [
            "see to end",
            "careful and purposeful",
            "problem situations",
            "unfinished bother",
            "stop and think",
            "do things regret",
            "hate to stop",
            "can't stop what I'm doing",
            "enjoy risks",
            "lose control",
            "finish",
            "rational sensible",
            "act without thinking upset",
            "new and exciting",
            "say things regret",
            "airplane",
            "others shocked",
            "skiing",
            "think carefully",
            "act without thinking excited",
        ]

        reverse_questions = {3, 6, 8, 9, 10, 13, 14, 15, 16, 17, 18, 20}

        for q_index in range(0, cls.N_QUESTIONS):
            q_num = q_index + 1
            q_field = "q{}".format(q_num)

            score_comment = "(1 strongly agree - 4 strongly disagree)"

            if q_num in reverse_questions:
                score_comment = "(1 strongly disagree - 4 strongly agree)"

            setattr(
                cls,
                q_field,
                CamcopsColumn(
                    q_field,
                    Integer,
                    permitted_value_checker=ONE_TO_FOUR_CHECKER,
                    comment="Q{} ({}) {}".format(q_num,
                                                 comment_strings[q_index],
                                                 score_comment),
                ),
            )

        super().__init__(name, bases, classdict)
Exemplo n.º 15
0
    def __init__(
        cls: Type["Sfmpq2"],
        name: str,
        bases: Tuple[Type, ...],
        classdict: Dict[str, Any],
    ) -> None:

        # Field descriptions are open access, as per:
        # https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5221718/
        # https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3225325/
        comment_strings = [
            "throbbing",
            "shooting",
            "stabbing",
            "sharp",
            "cramping",
            "gnawing",
            "hot-burning",
            "aching",
            "heavy",
            "tender",
            "splitting",
            "tiring–exhausting",
            "sickening",
            "fearful",
            "punishing–cruel",
            "electric-shock",
            "cold-freezing",
            "piercing",
            "light touch",
            "itching",
            "tingling",
            "numbness",
        ]
        score_comment = "(0 none - 10 worst)"

        for q_index in range(0, cls.N_QUESTIONS):
            q_num = q_index + 1
            q_field = "q{}".format(q_num)

            setattr(
                cls,
                q_field,
                CamcopsColumn(
                    q_field,
                    Integer,
                    permitted_value_checker=ZERO_TO_10_CHECKER,
                    comment="Q{} ({}) {}".format(q_num,
                                                 comment_strings[q_index],
                                                 score_comment),
                ),
            )

        super().__init__(name, bases, classdict)
Exemplo n.º 16
0
 def __init__(cls: Type['Nart'], name: str, bases: Tuple[Type, ...],
              classdict: Dict[str, Any]) -> None:
     for w in WORDLIST:
         setattr(
             cls, w,
             CamcopsColumn(
                 w,
                 Boolean,
                 permitted_value_checker=BIT_CHECKER,
                 comment="Pronounced {} correctly (0 no, 1 yes)".format(w)))
     super().__init__(name, bases, classdict)
Exemplo n.º 17
0
 def __init__(cls: Type['Dad'], name: str, bases: Tuple[Type, ...],
              classdict: Dict[str, Any]) -> None:
     explan = " ({} yes, {} no, {} not applicable)".format(YES, NO, NA)
     for colname in cls.ITEMS:
         setattr(
             cls, colname,
             CamcopsColumn(colname,
                           Integer,
                           permitted_value_checker=YN_NA_CHECKER,
                           comment=colname + explan))
     super().__init__(name, bases, classdict)
Exemplo n.º 18
0
class KhandakerMojoTherapyItem(KhandakerMojoTableItem):
    __tablename__ = "khandaker_mojo_therapy_item"

    medicationtable_id = CamcopsColumn(
        "medicationtable_id",
        Integer,
        nullable=False,
        comment="FK to medicationtable",
    )
    seqnum = CamcopsColumn(
        "seqnum",
        Integer,
        nullable=False,
        comment="Sequence number of this therapy",
    )
    therapy = CamcopsColumn("therapy", UnicodeText, comment="Therapy")
    frequency = CamcopsColumn("frequency", UnicodeText, comment="Frequency")
    sessions_completed = CamcopsColumn("sessions_completed",
                                       Integer,
                                       comment="Sessions completed")
    sessions_planned = CamcopsColumn("sessions_planned",
                                     Integer,
                                     comment="Sessions planned")
    indication = CamcopsColumn(
        "indication",
        UnicodeText,
        comment="Indication (what is the medication used for?)",
    )
    response = CamcopsColumn(
        "response",
        Integer,
        comment=("1 = treats all symptoms, "
                 "2 = most symptoms, "
                 "3 = some symptoms, "
                 "4 = no symptoms)"),
    )

    @classmethod
    def mandatory_fields(cls) -> List[str]:
        return [
            "therapy",
            "frequency",
            "sessions_completed",
            "sessions_planned",
            "indication",
            "response",
        ]

    def get_html_table_row(self, req: "CamcopsRequest") -> str:
        return f"""
Exemplo n.º 19
0
class CgiI(TaskHasPatientMixin, TaskHasClinicianMixin, Task):
    __tablename__ = "cgi_i"
    shortname = "CGI-I"
    extrastring_taskname = "cgi"  # shares with CGI
    info_filename_stem = "cgi"

    q = CamcopsColumn(
        "q",
        Integer,
        permitted_value_checker=PermittedValueChecker(minimum=0, maximum=7),
        comment="Global improvement (1-7, higher worse)",
    )

    TASK_FIELDS = ["q"]

    @staticmethod
    def longname(req: "CamcopsRequest") -> str:
        _ = req.gettext
        return _("Clinical Global Impressions – Improvement")

    def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
        if not self.is_complete():
            return CTV_INCOMPLETE
        return [
            CtvInfo(
                content="CGI-I rating: {}".format(self.get_rating_text(req)))
        ]

    def is_complete(self) -> bool:
        return (self.all_fields_not_none(self.TASK_FIELDS)
                and self.field_contents_valid())

    def get_rating_text(self, req: CamcopsRequest) -> str:
        qdict = self.get_q_dict(req)
        return get_from_dict(qdict, self.q)

    def get_q_dict(self, req: CamcopsRequest) -> Dict:
        return {
            None: None,
            0: self.wxstring(req, "q2_option0"),
            1: self.wxstring(req, "q2_option1"),
            2: self.wxstring(req, "q2_option2"),
            3: self.wxstring(req, "q2_option3"),
            4: self.wxstring(req, "q2_option4"),
            5: self.wxstring(req, "q2_option5"),
            6: self.wxstring(req, "q2_option6"),
            7: self.wxstring(req, "q2_option7"),
        }

    def get_task_html(self, req: CamcopsRequest) -> str:
        return f"""
Exemplo n.º 20
0
    def __init__(
        cls: Type["Mfi20"],
        name: str,
        bases: Tuple[Type, ...],
        classdict: Dict[str, Any],
    ) -> None:

        comment_strings = [
            "feel fit",
            "physically little",
            "feel active",
            "nice things",
            "tired",
            "do a lot",
            "keep thought on",
            "take on a lot",
            "dread",
            "think little",
            "concentrate",
            "rested",
            "effort concentrate",
            "bad condition",
            "plans",
            "tire",
            "get little done",
            "don't feel like",
            "thoughts wander",
            "excellent condition",
        ]
        score_comment = "(1 yes - 5 no)"

        for q_index in range(0, cls.N_QUESTIONS):
            q_num = q_index + 1
            q_field = "q{}".format(q_num)

            setattr(
                cls,
                q_field,
                CamcopsColumn(
                    q_field,
                    Integer,
                    permitted_value_checker=ONE_TO_FIVE_CHECKER,
                    comment="Q{} ({}) {}".format(
                        q_num, comment_strings[q_index], score_comment
                    ),
                ),
            )

        super().__init__(name, bases, classdict)
Exemplo n.º 21
0
class Fft(TaskHasPatientMixin, Task):
    """
    Server implementation of the FFT task.
    """

    __tablename__ = "fft"
    shortname = "FFT"

    service = Column("service",
                     UnicodeText,
                     comment="Clinical service being rated")
    rating = CamcopsColumn(
        "rating",
        Integer,
        permitted_value_checker=PermittedValueChecker(minimum=1, maximum=6),
        comment="Likelihood of recommendation to friends/family (1 "
        "extremely likely - 5 extremely unlikely, 6 don't know)",
    )

    @staticmethod
    def longname(req: "CamcopsRequest") -> str:
        _ = req.gettext
        return _("Friends and Family Test")

    def is_complete(self) -> bool:
        return self.rating is not None and self.field_contents_valid()

    def get_rating_text(self, req: CamcopsRequest) -> str:
        ratingdict = {
            None: None,
            1: self.wxstring(req, "a1"),
            2: self.wxstring(req, "a2"),
            3: self.wxstring(req, "a3"),
            4: self.wxstring(req, "a4"),
            5: self.wxstring(req, "a5"),
            6: self.wxstring(req, "a6"),
        }
        return get_from_dict(ratingdict, self.rating)

    def get_task_html(self, req: CamcopsRequest) -> str:
        if self.rating is not None:
            r = f"{self.rating}. {self.get_rating_text(req)}"
        else:
            r = None
        return f"""
Exemplo n.º 22
0
 def __init__(
     cls: Type["Dad"],
     name: str,
     bases: Tuple[Type, ...],
     classdict: Dict[str, Any],
 ) -> None:
     explan = f" ({YES} yes, {NO} no, {NA} not applicable)"
     for colname in cls.ITEMS:
         setattr(
             cls,
             colname,
             CamcopsColumn(
                 colname,
                 Integer,
                 permitted_value_checker=YN_NA_CHECKER,
                 comment=colname + explan,
             ),
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 23
0
class Irac(TaskHasPatientMixin, Task):
    """
    Server implementation of the IRAC task.
    """

    __tablename__ = "irac"
    shortname = "IRAC"

    aim = Column("aim", UnicodeText, comment="Main aim of the contact")
    achieved = CamcopsColumn(
        "achieved",
        Integer,
        permitted_value_checker=ZERO_TO_TWO_CHECKER,
        comment="Was the aim achieved? (0 not, 1 partially, 2 fully)",
    )

    TASK_FIELDS = ["aim", "achieved"]

    @staticmethod
    def longname(req: "CamcopsRequest") -> str:
        _ = req.gettext
        return _("Identify and Rate the Aim of the Contact")

    def is_complete(self) -> bool:
        return (self.all_fields_not_none(self.TASK_FIELDS)
                and self.field_contents_valid())

    def get_achieved_text(self, req: CamcopsRequest) -> str:
        achieveddict = {
            None: None,
            0: self.wxstring(req, "achieved_0"),
            1: self.wxstring(req, "achieved_1"),
            2: self.wxstring(req, "achieved_2"),
        }
        return get_from_dict(achieveddict, self.achieved)

    def get_task_html(self, req: CamcopsRequest) -> str:
        if self.achieved is not None:
            achieved = f"{self.achieved}. {self.get_achieved_text(req)}"
        else:
            achieved = None
        return f"""
Exemplo n.º 24
0
class PhotoSequenceSinglePhoto(GenericTabletRecordMixin, Base):
    __tablename__ = "photosequence_photos"

    photosequence_id = Column("photosequence_id",
                              Integer,
                              nullable=False,
                              comment="Tablet FK to photosequence")
    seqnum = Column("seqnum",
                    Integer,
                    nullable=False,
                    comment="Sequence number of this photo "
                    "(consistently 1-based as of 2018-12-01)")
    description = Column("description",
                         UnicodeText,
                         comment="Description of the photograph")
    photo_blobid = CamcopsColumn(
        "photo_blobid",
        Integer,
        is_blob_id_field=True,
        blob_relationship_attr_name="photo",
        comment="ID of the BLOB (foreign key to blobs.id, given "
        "matching device and current/frozen record status)")
    # IGNORED. REMOVE WHEN ALL PRE-2.0.0 TABLETS GONE:
    rotation = Column(  # DEFUNCT as of v2.0.0
        "rotation",
        Integer,
        comment="(DEFUNCT COLUMN) "
        "Rotation (clockwise, in degrees) to be applied for viewing")

    photo = blob_relationship("PhotoSequenceSinglePhoto", "photo_blobid")

    def get_html_table_rows(self) -> str:
        # noinspection PyTypeChecker
        return """
            <tr class="{CssClass.SUBHEADING}">
                <td>Photo {num}: <b>{description}</b></td>
            </tr>
            <tr><td>{photo}</td></tr>
        """.format(CssClass=CssClass,
                   num=self.seqnum,
                   description=ws.webify(self.description),
                   photo=get_blob_img_html(self.photo))
Exemplo n.º 25
0
def extra_id_column(req: "CamcopsRequest", which_idnum: int) -> CamcopsColumn:
    """
    The column definition used for the extra ID number columns provided by the
    ``DB_PATIENT_ID_PER_ROW`` export option.

    Args:
        req: a :class:`camcops_server.cc_modules.cc_request.CamcopsRequest`
        which_idnum: ID number type

    Returns:
        the column definition

    """
    desc = req.get_id_desc(which_idnum)
    return CamcopsColumn(
        extra_id_colname(which_idnum),
        BigInteger,
        identifies_patient=True,
        comment=EXTRA_COMMENT_PREFIX + f"ID number {which_idnum}: {desc}",
    )
Exemplo n.º 26
0
class ComplexTask(ExtraMixin,
                  PretendTaskBase,
                  Base,
                  metaclass=SimpleTaskMetaclass):
    # The LEFT-MOST HAS PRIORITY WHEN ATTRIBUTES CLASH.
    # Similarly, the left-most comes first in the Method Resolution Order.
    __tablename__ = "table_for_complex_task"
    some_pk = Column("some_pk", Integer, primary_key=True)

    checkme = CamcopsColumn(
        "checkme_internal",
        Integer,
        # NB: different SQL name from attr name (this tests whether we have
        # the naming system right!).
        permitted_value_checker=PermittedValueChecker(
            not_null=True,
            # permitted_values=[3, 4],
            maximum=3))

    def __repr__(self) -> str:
        return auto_repr(self)
Exemplo n.º 27
0
    def __init__(
        cls: Type["CpftResearchPreferences"],
        name: str,
        bases: Tuple[Type, ...],
        classdict: Dict[str, Any],
    ) -> None:
        setattr(
            cls,
            cls.FN_CONTACT_PREFERENCE,
            CamcopsColumn(
                cls.FN_CONTACT_PREFERENCE,
                CharColType,
                permitted_value_checker=PermittedValueChecker(
                    permitted_values=PV.RYG),
            ),
        )
        setattr(cls, cls.FN_CONTACT_BY_EMAIL,
                BoolColumn(cls.FN_CONTACT_BY_EMAIL))
        setattr(cls, cls.FN_RESEARCH_OPT_OUT,
                BoolColumn(cls.FN_RESEARCH_OPT_OUT))

        super().__init__(name, bases, classdict)
Exemplo n.º 28
0
class CopeBrief(TaskHasPatientMixin, Task, metaclass=CopeBriefMetaclass):
    """
    Server implementation of the COPE-Brief task.
    """

    __tablename__ = "cope_brief"
    shortname = "COPE-Brief"
    extrastring_taskname = "cope"
    info_filename_stem = "cope"

    NQUESTIONS = 28
    RELATIONSHIP_OTHER_CODE = 0
    RELATIONSHIPS_FIRST = 0
    RELATIONSHIPS_FIRST_NON_OTHER = 1
    RELATIONSHIPS_LAST = 9

    completed_by_patient = CamcopsColumn(
        "completed_by_patient",
        Integer,
        permitted_value_checker=BIT_CHECKER,
        comment="Task completed by patient? (0 no, 1 yes)",
    )
    completed_by = Column(
        "completed_by",
        UnicodeText,
        comment="Name of person task completed by (if not by patient)",
    )
    relationship_to_patient = CamcopsColumn(
        "relationship_to_patient",
        Integer,
        permitted_value_checker=PermittedValueChecker(minimum=0, maximum=9),
        comment="Relationship of responder to patient (0 other, 1 wife, "
        "2 husband, 3 daughter, 4 son, 5 sister, 6 brother, "
        "7 mother, 8 father, 9 friend)",
    )
    relationship_to_patient_other = Column(
        "relationship_to_patient_other",
        UnicodeText,
        comment="Relationship of responder to patient (if OTHER chosen)",
    )

    @staticmethod
    def longname(req: "CamcopsRequest") -> str:
        _ = req.gettext
        return _("Brief COPE Inventory")

    def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
        return self.standard_task_summary_fields() + [
            SummaryElement(
                name="self_distraction",
                coltype=Integer(),
                value=self.self_distraction(),
                comment="Self-distraction (2-8)",
            ),
            SummaryElement(
                name="active_coping",
                coltype=Integer(),
                value=self.active_coping(),
                comment="Active coping (2-8)",
            ),
            SummaryElement(
                name="denial",
                coltype=Integer(),
                value=self.denial(),
                comment="Denial (2-8)",
            ),
            SummaryElement(
                name="substance_use",
                coltype=Integer(),
                value=self.substance_use(),
                comment="Substance use (2-8)",
            ),
            SummaryElement(
                name="emotional_support",
                coltype=Integer(),
                value=self.emotional_support(),
                comment="Use of emotional support (2-8)",
            ),
            SummaryElement(
                name="instrumental_support",
                coltype=Integer(),
                value=self.instrumental_support(),
                comment="Use of instrumental support (2-8)",
            ),
            SummaryElement(
                name="behavioural_disengagement",
                coltype=Integer(),
                value=self.behavioural_disengagement(),
                comment="Behavioural disengagement (2-8)",
            ),
            SummaryElement(
                name="venting",
                coltype=Integer(),
                value=self.venting(),
                comment="Venting (2-8)",
            ),
            SummaryElement(
                name="positive_reframing",
                coltype=Integer(),
                value=self.positive_reframing(),
                comment="Positive reframing (2-8)",
            ),
            SummaryElement(
                name="planning",
                coltype=Integer(),
                value=self.planning(),
                comment="Planning (2-8)",
            ),
            SummaryElement(
                name="humour",
                coltype=Integer(),
                value=self.humour(),
                comment="Humour (2-8)",
            ),
            SummaryElement(
                name="acceptance",
                coltype=Integer(),
                value=self.acceptance(),
                comment="Acceptance (2-8)",
            ),
            SummaryElement(
                name="religion",
                coltype=Integer(),
                value=self.religion(),
                comment="Religion (2-8)",
            ),
            SummaryElement(
                name="self_blame",
                coltype=Integer(),
                value=self.self_blame(),
                comment="Self-blame (2-8)",
            ),
        ]

    def is_complete_responder(self) -> bool:
        if self.completed_by_patient is None:
            return False
        if self.completed_by_patient:
            return True
        if not self.completed_by or self.relationship_to_patient is None:
            return False
        if (self.relationship_to_patient == self.RELATIONSHIP_OTHER_CODE
                and not self.relationship_to_patient_other):
            return False
        return True

    def is_complete(self) -> bool:
        return (self.is_complete_responder() and self.all_fields_not_none(
            [f"q{n}" for n in range(1, self.NQUESTIONS + 1)])
                and self.field_contents_valid())

    def self_distraction(self) -> int:
        return self.sum_fields(["q1", "q19"])

    def active_coping(self) -> int:
        return self.sum_fields(["q2", "q7"])

    def denial(self) -> int:
        return self.sum_fields(["q3", "q8"])

    def substance_use(self) -> int:
        return self.sum_fields(["q4", "q11"])

    def emotional_support(self) -> int:
        return self.sum_fields(["q5", "q15"])

    def instrumental_support(self) -> int:
        return self.sum_fields(["q10", "q23"])

    def behavioural_disengagement(self) -> int:
        return self.sum_fields(["q6", "q16"])

    def venting(self) -> int:
        return self.sum_fields(["q9", "q21"])

    def positive_reframing(self) -> int:
        return self.sum_fields(["q12", "q17"])

    def planning(self) -> int:
        return self.sum_fields(["q14", "q25"])

    def humour(self) -> int:
        return self.sum_fields(["q18", "q28"])

    def acceptance(self) -> int:
        return self.sum_fields(["q20", "q24"])

    def religion(self) -> int:
        return self.sum_fields(["q22", "q27"])

    def self_blame(self) -> int:
        return self.sum_fields(["q13", "q26"])

    def get_task_html(self, req: CamcopsRequest) -> str:
        answer_dict = {None: None}
        for option in range(0, 3 + 1):
            answer_dict[option] = (str(option) + " — " +
                                   self.wxstring(req, "a" + str(option)))
        q_a = ""
        for q in range(1, self.NQUESTIONS + 1):
            q_a += tr_qa(
                f"Q{q}. {self.wxstring(req, 'q' + str(q))}",
                get_from_dict(answer_dict, getattr(self, "q" + str(q))),
            )
        return f"""
Exemplo n.º 29
0
 def __init__(cls: Type['Ifs'],
              name: str,
              bases: Tuple[Type, ...],
              classdict: Dict[str, Any]) -> None:
     for seqlen in cls.Q4_DIGIT_LENGTHS:
         fname1 = "q4_len{}_1".format(seqlen)
         fname2 = "q4_len{}_2".format(seqlen)
         setattr(
             cls,
             fname1,
             CamcopsColumn(
                 fname1, Boolean,
                 permitted_value_checker=BIT_CHECKER,
                 comment="Q4. Digits backward, length {}, trial 1".format(
                     seqlen)
             )
         )
         setattr(
             cls,
             fname2,
             CamcopsColumn(
                 fname2, Boolean,
                 permitted_value_checker=BIT_CHECKER,
                 comment="Q4. Digits backward, length {}, trial 2".format(
                     seqlen)
             )
         )
     for n in cls.Q6_SEQUENCE_NUMS:
         fname = "q6_seq{}".format(n)
         setattr(
             cls,
             fname,
             CamcopsColumn(
                 fname, Integer,
                 permitted_value_checker=BIT_CHECKER,
                 comment="Q6. Spatial working memory, sequence {}".format(n)
             )
         )
     for n in cls.Q7_PROVERB_NUMS:
         fname = "q7_proverb{}".format(n)
         setattr(
             cls,
             fname,
             CamcopsColumn(
                 fname, Float,
                 permitted_value_checker=ZERO_TO_ONE_CHECKER,
                 comment="Q7. Proverb {} (1 = correct explanation, "
                         "0.5 = example, 0 = neither)".format(n)
             )
         )
     for n in cls.Q8_SENTENCE_NUMS:
         fname = "q8_sentence{}".format(n)
         setattr(
             cls,
             fname,
             CamcopsColumn(
                 fname, Integer,
                 permitted_value_checker=ZERO_TO_TWO_CHECKER,
                 comment="Q8. Hayling, sentence {}".format(n)
             )
         )
     super().__init__(name, bases, classdict)
Exemplo n.º 30
0
class Ifs(TaskHasPatientMixin, TaskHasClinicianMixin, Task,
          metaclass=IfsMetaclass):
    """
    Server implementation of the IFS task.
    """
    __tablename__ = "ifs"
    shortname = "IFS"
    longname = "INECO Frontal Screening"
    provides_trackers = True

    q1 = CamcopsColumn(
        "q1", Integer,
        permitted_value_checker=ZERO_TO_THREE_CHECKER,
        comment="Q1. Motor series (motor programming)"
    )
    q2 = CamcopsColumn(
        "q2", Integer,
        permitted_value_checker=ZERO_TO_THREE_CHECKER,
        comment="Q2. Conflicting instructions (interference sensitivity)"
    )
    q3 = CamcopsColumn(
        "q3", Integer,
        permitted_value_checker=ZERO_TO_THREE_CHECKER,
        comment="Q3. Go/no-go (inhibitory control)"
    )
    q5 = CamcopsColumn(
        "q5", Integer,
        permitted_value_checker=ZERO_TO_TWO_CHECKER,
        comment="Q5. Verbal working memory"
    )

    Q4_DIGIT_LENGTHS = list(range(2, 7 + 1))
    Q6_SEQUENCE_NUMS = list(range(1, 4 + 1))
    Q7_PROVERB_NUMS = list(range(1, 3 + 1))
    Q8_SENTENCE_NUMS = list(range(1, 3 + 1))
    SIMPLE_Q = (
        ["q1", "q2", "q3", "q5"] +
        ["q6_seq{}".format(n) for n in Q6_SEQUENCE_NUMS] +
        ["q7_proverb{}".format(n) for n in Q7_PROVERB_NUMS] +
        ["q8_sentence{}".format(n) for n in Q8_SENTENCE_NUMS]
    )
    MAX_TOTAL = 30
    MAX_WM = 10

    def get_trackers(self, req: CamcopsRequest) -> List[TrackerInfo]:
        scoredict = self.get_score()
        return [
            TrackerInfo(
                value=scoredict['total'],
                plot_label="IFS total score (higher is better)",
                axis_label="Total score (out of {})".format(self.MAX_TOTAL),
                axis_min=-0.5,
                axis_max=self.MAX_TOTAL + 0.5
            ),
            TrackerInfo(
                value=scoredict['wm'],
                plot_label="IFS working memory index (higher is better)",
                axis_label="Total score (out of {})".format(self.MAX_WM),
                axis_min=-0.5,
                axis_max=self.MAX_WM + 0.5
            ),
        ]

    def get_summaries(self, req: CamcopsRequest) -> List[SummaryElement]:
        scoredict = self.get_score()
        return self.standard_task_summary_fields() + [
            SummaryElement(
                name="total",
                coltype=Float(),
                value=scoredict['total'],
                comment="Total (out of {}, higher better)".format(
                    self.MAX_TOTAL)),
            SummaryElement(
                name="wm",
                coltype=Integer(),
                value=scoredict['wm'],
                comment="Working memory index (out of {}; "
                        "sum of Q4 + Q6".format(self.MAX_WM)),
        ]

    def get_clinical_text(self, req: CamcopsRequest) -> List[CtvInfo]:
        scoredict = self.get_score()
        if not self.is_complete():
            return CTV_INCOMPLETE
        return [CtvInfo(
            content=(
                "Total: {t}/{tmax}; working memory index {w}/{wmax}".format(
                    t=scoredict['total'],
                    tmax=self.MAX_TOTAL,
                    w=scoredict['wm'],
                    wmax=self.MAX_WM,
                )
            )
        )]

    def get_score(self) -> Dict:
        q1 = getattr(self, "q1", 0) or 0
        q2 = getattr(self, "q2", 0) or 0
        q3 = getattr(self, "q3", 0) or 0
        q4 = 0
        for seqlen in self.Q4_DIGIT_LENGTHS:
            val1 = getattr(self, "q4_len{}_1".format(seqlen))
            val2 = getattr(self, "q4_len{}_2".format(seqlen))
            if val1 or val2:
                q4 += 1
            if not val1 and not val2:
                break
        q5 = getattr(self, "q5", 0) or 0
        q6 = self.sum_fields(["q6_seq" + str(s) for s in range(1, 4 + 1)])
        q7 = self.sum_fields(["q7_proverb" + str(s) for s in range(1, 3 + 1)])
        q8 = self.sum_fields(["q8_sentence" + str(s) for s in range(1, 3 + 1)])
        total = q1 + q2 + q3 + q4 + q5 + q6 + q7 + q8
        wm = q4 + q6  # working memory index (though not verbal)
        return dict(
            total=total,
            wm=wm
        )

    def is_complete(self) -> bool:
        if not self.field_contents_valid():
            return False
        if not self.are_all_fields_complete(self.SIMPLE_Q):
            return False
        for seqlen in self.Q4_DIGIT_LENGTHS:
            val1 = getattr(self, "q4_len{}_1".format(seqlen))
            val2 = getattr(self, "q4_len{}_2".format(seqlen))
            if val1 is None or val2 is None:
                return False
            if not val1 and not val2:
                return True  # all done
        return True

    def get_simple_tr_qa(self, req: CamcopsRequest, qprefix: str) -> str:
        q = self.wxstring(req, qprefix + "_title")
        val = getattr(self, qprefix)
        if val is not None:
            a = self.wxstring(req, qprefix + "_a" + str(val))
        else:
            a = None
        return tr_qa(q, a)

    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