Пример #1
0
class Bp(BaseNlpParser):
    """Blood pressure, in mmHg. (Since we produce two variables, SBP and DBP,
    and we use something a little more complex than
    NumeratorOutOfDenominatorParser, we subclass BaseNlpParser directly.)"""
    BP = r"(?: \b blood \s+ pressure \b | \b B\.?P\.? \b )"
    SYSTOLIC_BP = r"(?: \b systolic \s+ {BP} | \b S\.?B\.?P\.? \b )".format(
        BP=BP)
    DIASTOLIC_BP = r"(?: \b diastolic \s+ {BP} | \b D\.?B\.?P\.? \b )".format(
        BP=BP)

    TWO_NUMBER_BP = r"""
        ( {SIGNED_FLOAT} )
        \s* (?: \b over \b | \/ ) \s*
        ( {SIGNED_FLOAT} )
    """.format(SIGNED_FLOAT=SIGNED_FLOAT)
    ONE_NUMBER_BP = SIGNED_FLOAT

    COMPILED_BP = compile_regex(BP)
    COMPILED_SBP = compile_regex(SYSTOLIC_BP)
    COMPILED_DBP = compile_regex(DIASTOLIC_BP)
    COMPILED_ONE_NUMBER_BP = compile_regex(ONE_NUMBER_BP)
    COMPILED_TWO_NUMBER_BP = compile_regex(TWO_NUMBER_BP)
    REGEX = r"""
        (                               # group for "BP" or equivalent
            {SYSTOLIC_BP}               # ... from more to less specific
            | {DIASTOLIC_BP}
            | {BP}
        )
        {OPTIONAL_RESULTS_IGNORABLES}
        ( {TENSE_INDICATOR} )?         # optional group for tense indicator
        {OPTIONAL_RESULTS_IGNORABLES}
        ( {RELATION} )?                # optional group for relation
        {OPTIONAL_RESULTS_IGNORABLES}
        (
            {SIGNED_FLOAT}                      # systolic
            (?:
                \s* (?: \b over \b | \/ ) \s*   # /
                {SIGNED_FLOAT}                  # diastolic
            )?
        )
        {OPTIONAL_RESULTS_IGNORABLES}
        (                              # group for units
            {MM_HG}
        )?
    """.format(
        BP=BP,
        SYSTOLIC_BP=SYSTOLIC_BP,
        DIASTOLIC_BP=DIASTOLIC_BP,
        OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
        TENSE_INDICATOR=TENSE_INDICATOR,
        RELATION=RELATION,
        SIGNED_FLOAT=SIGNED_FLOAT,
        MM_HG=MM_HG,
    )
    COMPILED_REGEX = compile_regex(REGEX)

    FN_SYSTOLIC_BP_MMHG = 'systolic_bp_mmhg'
    FN_DIASTOLIC_BP_MMHG = 'diastolic_bp_mmhg'

    NAME = "BP"
    UNIT_MAPPING = {
        MM_HG: 1,  # preferred unit
    }

    def __init__(self,
                 nlpdef: Optional[NlpDefinition],
                 cfgsection: Optional[str],
                 commit: bool = False) -> None:
        super().__init__(nlpdef=nlpdef, cfgsection=cfgsection, commit=commit)
        if nlpdef is None:  # only None for debugging!
            self.tablename = ''
        else:
            self.tablename = nlpdef.opt_str(cfgsection,
                                            'desttable',
                                            required=True)

    @classmethod
    def print_info(cls, file=sys.stdout):
        print("Blood pressure finder. Regular expression: \n{}".format(
            cls.REGEX),
              file=file)

    def dest_tables_columns(self) -> Dict[str, List[Column]]:
        return {
            self.tablename: [
                Column(FN_CONTENT, Text, doc=HELP_CONTENT),
                Column(FN_START, Integer, doc=HELP_START),
                Column(FN_END, Integer, doc=HELP_END),
                Column(FN_VARIABLE_TEXT, Text, doc=HELP_VARIABLE_TEXT),
                Column(FN_RELATION_TEXT,
                       String(MAX_RELATION_TEXT_LENGTH),
                       doc=HELP_RELATION_TEXT),
                Column(FN_RELATION,
                       String(MAX_RELATION_LENGTH),
                       doc=HELP_RELATION),
                Column(FN_VALUE_TEXT,
                       String(MAX_VALUE_TEXT_LENGTH),
                       doc=HELP_VALUE_TEXT),
                Column(FN_UNITS, String(MAX_UNITS_LENGTH), doc=HELP_UNITS),
                Column(self.FN_SYSTOLIC_BP_MMHG,
                       Float,
                       doc="Systolic blood pressure in mmHg"),
                Column(self.FN_DIASTOLIC_BP_MMHG,
                       Float,
                       doc="Diastolic blood pressure in mmHg"),
                Column(FN_TENSE, String(MAX_TENSE_LENGTH), doc=HELP_TENSE),
            ]
        }

    def parse(
        self,
        text: str,
        debug: bool = False
    ) -> Generator[Tuple[str, Dict[str, Any]], None, None]:
        """Parser for BP. Specialized because we're fetching two numbers."""
        for m in self.COMPILED_REGEX.finditer(text):
            if debug:
                print("Match {} for {}".format(m, repr(text)))
            startpos = m.start()
            endpos = m.end()
            matching_text = m.group(0)  # the whole thing
            variable_text = m.group(1)
            tense_indicator = m.group(2)
            relation_text = m.group(3)
            value_text = m.group(4)
            units = m.group(5)

            sbp = None
            dbp = None
            if self.COMPILED_SBP.match(variable_text):
                if self.COMPILED_ONE_NUMBER_BP.match(value_text):
                    sbp = to_pos_float(value_text)
            elif self.COMPILED_DBP.match(variable_text):
                if self.COMPILED_ONE_NUMBER_BP.match(value_text):
                    dbp = to_pos_float(value_text)
            elif self.COMPILED_BP.match(variable_text):
                bpmatch = self.COMPILED_TWO_NUMBER_BP.match(value_text)
                if bpmatch:
                    sbp = to_pos_float(bpmatch.group(1))
                    dbp = to_pos_float(bpmatch.group(2))
            if sbp is None and dbp is None:
                # This is OK; e.g. "BP 110", which we will ignore.
                # log.warning(
                #     "Failed interpretation: matching_text={matching_text}, "
                #     "variable_text={variable_text}, "
                #     "tense_indicator={tense_indicator}, "
                #     "relation={relation}, "
                #     "value_text={value_text}, "
                #     "units={units}".format(
                #         matching_text=repr(matching_text),
                #         variable_text=repr(variable_text),
                #         tense_indicator=repr(tense_indicator),
                #         relation=repr(relation),
                #         value_text=repr(value_text),
                #         units=repr(units),
                #     )
                # )
                continue

            tense, relation = common_tense(tense_indicator, relation_text)

            yield self.tablename, {
                FN_CONTENT: matching_text,
                FN_START: startpos,
                FN_END: endpos,
                FN_VARIABLE_TEXT: variable_text,
                FN_RELATION_TEXT: relation_text,
                FN_RELATION: relation,
                FN_VALUE_TEXT: value_text,
                FN_UNITS: units,
                self.FN_SYSTOLIC_BP_MMHG: sbp,
                self.FN_DIASTOLIC_BP_MMHG: dbp,
                FN_TENSE: tense,
            }

    def test_bp_parser(self,
                       test_expected_list: List[Tuple[str,
                                                      List[Tuple[float,
                                                                 float]]]],
                       verbose: bool = False) -> None:
        print("Testing parser: {}".format(type(self).__name__))
        if verbose:
            print("... regex:\n{}".format(self.REGEX))
        for test_string, expected_values in test_expected_list:
            actual_values = list(
                (x[self.FN_SYSTOLIC_BP_MMHG], x[self.FN_DIASTOLIC_BP_MMHG])
                for t, x in self.parse(test_string))
            assert actual_values == expected_values, (
                "Parser {name}: Expected {expected}, got {actual}, when "
                "parsing {test_string}; full result={full}".format(
                    name=type(self).__name__,
                    expected=expected_values,
                    actual=actual_values,
                    test_string=repr(test_string),
                    full=repr(list(self.parse(test_string))),
                ))
        print("... OK")

    def test(self, verbose: bool = False) -> None:
        self.test_bp_parser(
            [
                ("BP", []),  # should fail; no values
                ("his blood pressure was 120/80", [(120, 80)]),
                ("BP 120/80 mmhg", [(120, 80)]),
                ("systolic BP 120", [(120, None)]),
                ("diastolic BP 80", [(None, 80)]),
                ("BP-130/70", [(130, 70)]),
                ("BP 110 /80", [(110, 80)]),
                ("BP 110 /80 -", [(110, 80)]),  # real example
                ("BP 120 / 70 -", [(120, 70)]),  # real example
                ("BP :115 / 70 -", [(115, 70)]),  # real example
                ("B.P 110", []),  # real example
            ],
            verbose=verbose)
Пример #2
0
class Height(NumericalResultParser):
    """Height. Handles metric and imperial."""
    METRIC_HEIGHT = r"""
        (                           # capture group 4
            (?:
                ( {SIGNED_FLOAT} )          # capture group 5
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {M} )                     # capture group 6
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {SIGNED_FLOAT} )          # capture group 7
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {CM} )                    # capture group 8
            )
            | (?:
                ( {SIGNED_FLOAT} )          # capture group 9
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {M} )                     # capture group 10
            )
            | (?:
                ( {SIGNED_FLOAT} )          # capture group 11
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {CM} )                    # capture group 12
            )
        )
    """.format(SIGNED_FLOAT=SIGNED_FLOAT,
               OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
               M=M,
               CM=CM)
    IMPERIAL_HEIGHT = r"""
        (                           # capture group 13
            (?:
                ( {SIGNED_FLOAT} )      # capture group 14
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {FEET} )              # capture group 15
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {SIGNED_FLOAT} )      # capture group 16
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {INCHES} )            # capture group 17
            )
            | (?:
                ( {SIGNED_FLOAT} )      # capture group 18
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {FEET} )              # capture group 19
            )
            | (?:
                ( {SIGNED_FLOAT} )      # capture group 20
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {INCHES} )            # capture group 21
            )
        )
    """.format(SIGNED_FLOAT=SIGNED_FLOAT,
               OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
               FEET=FEET,
               INCHES=INCHES)
    HEIGHT = r"(?: \b height \b)"
    REGEX = r"""
        ( {HEIGHT} )                       # group 1 for "height" or equivalent
        {OPTIONAL_RESULTS_IGNORABLES}
        ( {TENSE_INDICATOR} )?             # optional group 2 for tense
        {OPTIONAL_RESULTS_IGNORABLES}
        ( {RELATION} )?                    # optional group 3 for relation
        {OPTIONAL_RESULTS_IGNORABLES}
        (?:
            {METRIC_HEIGHT}
            | {IMPERIAL_HEIGHT}
        )
    """.format(
        HEIGHT=HEIGHT,
        OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
        TENSE_INDICATOR=TENSE_INDICATOR,
        RELATION=RELATION,
        METRIC_HEIGHT=METRIC_HEIGHT,
        IMPERIAL_HEIGHT=IMPERIAL_HEIGHT,
        SIGNED_FLOAT=SIGNED_FLOAT,
        KG_PER_SQ_M=KG_PER_SQ_M,
    )

    COMPILED_REGEX = compile_regex(REGEX)
    NAME = "Height"
    PREFERRED_UNIT_COLUMN = "value_m"

    def __init__(self,
                 nlpdef: Optional[NlpDefinition],
                 cfgsection: Optional[str],
                 commit: bool = False,
                 debug: bool = False) -> None:
        super().__init__(nlpdef=nlpdef,
                         cfgsection=cfgsection,
                         variable=self.NAME,
                         target_unit=self.PREFERRED_UNIT_COLUMN,
                         regex_str_for_debugging=self.REGEX,
                         commit=commit)
        if debug:
            print("Regex for {}: {}".format(type(self).__name__, self.REGEX))

    def parse(
        self,
        text: str,
        debug: bool = False
    ) -> Generator[Tuple[str, Dict[str, Any]], None, None]:
        """Parser for Height. Specialized for complex unit conversion."""
        for m in self.COMPILED_REGEX.finditer(text):  # watch out: 'm'/metres
            if debug:
                print("Match {} for {}".format(m, repr(text)))
            startpos = m.start()
            endpos = m.end()
            matching_text = m.group(0)  # the whole thing
            variable_text = m.group(1)
            tense_text = m.group(2)
            relation_text = m.group(3)
            metric_expression = m.group(4)
            metric_m_and_cm_m = m.group(5)
            metric_m_and_cm_m_units = m.group(6)
            metric_m_and_cm_cm = m.group(7)
            metric_m_and_cm_cm_units = m.group(8)
            metric_m_only_m = m.group(9)
            metric_m_only_m_units = m.group(10)
            metric_cm_only_cm = m.group(11)
            metric_cm_only_cm_units = m.group(12)
            imperial_expression = m.group(13)
            imperial_ft_and_in_ft = m.group(14)
            imperial_ft_and_in_ft_units = m.group(15)
            imperial_ft_and_in_in = m.group(16)
            imperial_ft_and_in_in_units = m.group(17)
            imperial_ft_only_ft = m.group(18)
            imperial_ft_only_ft_units = m.group(19)
            imperial_in_only_in = m.group(20)
            imperial_in_only_in_units = m.group(21)

            expression = None
            value_m = None
            units = None
            if metric_expression:
                expression = metric_expression
                if metric_m_and_cm_m and metric_m_and_cm_cm:
                    metres = to_pos_float(metric_m_and_cm_m)
                    # ... beware: 'm' above
                    cm = to_pos_float(metric_m_and_cm_cm)
                    value_m = m_from_m_cm(metres=metres, centimetres=cm)
                    units = assemble_units(
                        [metric_m_and_cm_m_units, metric_m_and_cm_cm_units])
                elif metric_m_only_m:
                    value_m = to_pos_float(metric_m_only_m)
                    units = metric_m_only_m_units
                elif metric_cm_only_cm:
                    cm = to_pos_float(metric_cm_only_cm)
                    value_m = m_from_m_cm(centimetres=cm)
                    units = metric_cm_only_cm_units
            elif imperial_expression:
                expression = imperial_expression
                if imperial_ft_and_in_ft and imperial_ft_and_in_in:
                    ft = to_pos_float(imperial_ft_and_in_ft)
                    inches = to_pos_float(imperial_ft_and_in_in)
                    value_m = m_from_ft_in(feet=ft, inches=inches)
                    units = assemble_units([
                        imperial_ft_and_in_ft_units,
                        imperial_ft_and_in_in_units
                    ])
                elif imperial_ft_only_ft:
                    ft = to_pos_float(imperial_ft_only_ft)
                    value_m = m_from_ft_in(feet=ft)
                    units = imperial_ft_only_ft_units
                elif imperial_in_only_in:
                    inches = to_pos_float(imperial_in_only_in)
                    value_m = m_from_ft_in(inches=inches)
                    units = imperial_in_only_in_units

            tense, relation = common_tense(tense_text, relation_text)

            result = {
                FN_VARIABLE_NAME: self.variable,
                FN_CONTENT: matching_text,
                FN_START: startpos,
                FN_END: endpos,
                FN_VARIABLE_TEXT: variable_text,
                FN_RELATION_TEXT: relation_text,
                FN_RELATION: relation,
                FN_VALUE_TEXT: expression,
                FN_UNITS: units,
                self.target_unit: value_m,
                FN_TENSE_TEXT: tense_text,
                FN_TENSE: tense,
            }
            # log.critical(result)
            yield self.tablename, result

    def test(self, verbose: bool = False) -> None:
        self.test_numerical_parser(
            [
                ("Height", []),  # should fail; no values
                ("her height was 1.6m", [1.6]),
                ("Height = 1.23 m", [1.23]),
                ("her height is 1.5m", [1.5]),
                ('''Height 5'8" ''', [m_from_ft_in(feet=5, inches=8)]),
                ("Height 5 ft 8 in", [m_from_ft_in(feet=5, inches=8)]),
                ("Height 5 feet 8 inches", [m_from_ft_in(feet=5, inches=8)]),
            ],
            verbose=verbose)
        self.detailed_test(
            "Height 5 ft 11 in",
            [{
                self.target_unit: m_from_ft_in(feet=5, inches=11),
                FN_UNITS: "ft in",
            }],
            verbose=verbose)
Пример #3
0
class Weight(NumericalResultParser):
    """Weight. Handles metric and imperial."""
    METRIC_WEIGHT = r"""
        (                           # capture group 4
            ( {SIGNED_FLOAT} )          # capture group 5
            {OPTIONAL_RESULTS_IGNORABLES}
            ( {KG} )                    # capture group 6
        )
    """.format(SIGNED_FLOAT=SIGNED_FLOAT,
               OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
               KG=KG)
    IMPERIAL_WEIGHT = r"""
        (                           # capture group 7
            (?:
                ( {SIGNED_FLOAT} )      # capture group 8
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {STONES} )            # capture group 9
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {SIGNED_FLOAT} )      # capture group 10
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {LB} )                # capture group 11
            )
            | (?:
                ( {SIGNED_FLOAT} )      # capture group 12
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {STONES} )            # capture group 13
            )
            | (?:
                ( {SIGNED_FLOAT} )      # capture group 14
                {OPTIONAL_RESULTS_IGNORABLES}
                ( {LB} )                # capture group 15
            )
        )
    """.format(SIGNED_FLOAT=SIGNED_FLOAT,
               OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
               STONES=STONES,
               LB=LB)
    WEIGHT = r"(?: \b weigh[ts] \b )"  # weight, weighs
    REGEX = r"""
        ( {WEIGHT} )                       # group 1 for "weight" or equivalent
        {OPTIONAL_RESULTS_IGNORABLES}
        ( {TENSE_INDICATOR} )?             # optional group 2 for tense
        {OPTIONAL_RESULTS_IGNORABLES}
        ( {RELATION} )?                    # optional group 3 for relation
        {OPTIONAL_RESULTS_IGNORABLES}
        (?:
            {METRIC_WEIGHT}
            | {IMPERIAL_WEIGHT}
        )
    """.format(
        WEIGHT=WEIGHT,
        OPTIONAL_RESULTS_IGNORABLES=OPTIONAL_RESULTS_IGNORABLES,
        TENSE_INDICATOR=TENSE_INDICATOR,
        RELATION=RELATION,
        METRIC_WEIGHT=METRIC_WEIGHT,
        IMPERIAL_WEIGHT=IMPERIAL_WEIGHT,
        SIGNED_FLOAT=SIGNED_FLOAT,
        KG_PER_SQ_M=KG_PER_SQ_M,
    )

    COMPILED_REGEX = compile_regex(REGEX)
    NAME = "Weight"
    PREFERRED_UNIT_COLUMN = "value_kg"

    def __init__(self,
                 nlpdef: Optional[NlpDefinition],
                 cfgsection: Optional[str],
                 commit: bool = False,
                 debug: bool = False) -> None:
        super().__init__(nlpdef=nlpdef,
                         cfgsection=cfgsection,
                         variable=self.NAME,
                         target_unit=self.PREFERRED_UNIT_COLUMN,
                         regex_str_for_debugging=self.REGEX,
                         commit=commit)
        if debug:
            print("Regex for {}: {}".format(type(self).__name__, self.REGEX))

    def parse(
        self,
        text: str,
        debug: bool = False
    ) -> Generator[Tuple[str, Dict[str, Any]], None, None]:
        """Parser for Weight. Specialized for complex unit conversion."""
        for m in self.COMPILED_REGEX.finditer(text):
            if debug:
                print("Match {} for {}".format(m, repr(text)))
            startpos = m.start()
            endpos = m.end()
            matching_text = m.group(0)  # the whole thing
            variable_text = m.group(1)
            tense_text = m.group(2)
            relation_text = m.group(3)
            metric_expression = m.group(4)
            metric_value = m.group(5)
            metric_units = m.group(6)
            imperial_expression = m.group(7)
            imperial_st_and_lb_st = m.group(8)
            imperial_st_and_lb_st_units = m.group(9)
            imperial_st_and_lb_lb = m.group(10)
            imperial_st_and_lb_lb_units = m.group(11)
            imperial_st_only_st = m.group(12)
            imperial_st_only_st_units = m.group(13)
            imperial_lb_only_lb = m.group(14)
            imperial_lb_only_lb_units = m.group(15)

            expression = None
            value_kg = None
            units = None
            if metric_expression:
                expression = metric_expression
                value_kg = to_float(metric_value)
                units = metric_units
            elif imperial_expression:
                expression = imperial_expression
                if imperial_st_and_lb_st and imperial_st_and_lb_lb:
                    st = to_float(imperial_st_and_lb_st)
                    lb = to_float(imperial_st_and_lb_lb)
                    value_kg = kg_from_st_lb_oz(stones=st, pounds=lb)
                    units = assemble_units([
                        imperial_st_and_lb_st_units,
                        imperial_st_and_lb_lb_units
                    ])
                elif imperial_st_only_st:
                    st = to_float(imperial_st_only_st)
                    value_kg = kg_from_st_lb_oz(stones=st)
                    units = imperial_st_only_st_units
                elif imperial_lb_only_lb:
                    lb = to_float(imperial_lb_only_lb)
                    value_kg = kg_from_st_lb_oz(pounds=lb)
                    units = imperial_lb_only_lb_units

            # All left as signed float, as you definitely see things like
            # "weight -0.3 kg" for weight changes.

            tense, relation = common_tense(tense_text, relation_text)

            result = {
                FN_VARIABLE_NAME: self.variable,
                FN_CONTENT: matching_text,
                FN_START: startpos,
                FN_END: endpos,
                FN_VARIABLE_TEXT: variable_text,
                FN_RELATION_TEXT: relation_text,
                FN_RELATION: relation,
                FN_VALUE_TEXT: expression,
                FN_UNITS: units,
                self.target_unit: value_kg,
                FN_TENSE_TEXT: tense_text,
                FN_TENSE: tense,
            }
            # log.critical(result)
            yield self.tablename, result

    def test(self, verbose: bool = False) -> None:
        self.test_numerical_parser(
            [
                ("Weight", []),  # should fail; no values
                ("her weight was 60.2kg", [60.2]),
                ("Weight = 52.3kg", [52.3]),
                ("Weight: 80.8kgs", [80.8]),
                ("she weighs 61kg", [61]),
                ("she weighs 61 kg", [61]),
                ("she weighs 61 kgs", [61]),
                ("she weighs 61 kilo", [61]),
                ("she weighs 61 kilos", [61]),
                ("she weighs 8 stones ", [kg_from_st_lb_oz(stones=8)]),
                ("she weighs 200 lb", [kg_from_st_lb_oz(pounds=200)]),
                ("she weighs 200 pounds", [kg_from_st_lb_oz(pounds=200)]),
                ("she weighs 6 st 12 lb",
                 [kg_from_st_lb_oz(stones=6, pounds=12)]),
                ("change in weight -0.4kg", [-0.4]),
                ("change in weight - 0.4kg",
                 [0.4]),  # ASCII hyphen (hyphen-minus)
                ("change in weight ‐ 0.4kg", [0.4]),  # Unicode hyphen
                # ("failme", [999]),
                ("change in weight −0.4kg", [-0.4]),  # Unicode minus
                ("change in weight –0.4kg", [-0.4]),  # en dash
                ("change in weight —0.4kg", [0.4]),  # em dash
            ],
            verbose=verbose)
        self.detailed_test("Weight: 80.8kgs", [{
            self.target_unit: 80.8,
            FN_UNITS: "kgs",
        }],
                           verbose=verbose)