Esempio n. 1
0
def test_parse_with_too_long_separator_char_fails() -> None:
    with pytest.raises(ValueError) as exc_info:
        GS1Message.parse("10222--15210526", separator_chars=["--"])

    assert (
        str(exc_info.value) ==
        "All separator characters must be exactly 1 character long, got ['--']."
    )
Esempio n. 2
0
def test_parse_fails_if_unparsed_data_left() -> None:
    # 10 = AI for BATCH/LOT
    # 222... = Max length BATCH/LOT
    # aaa = Superflous data
    value = "1022222222222222222222aaa"

    with pytest.raises(ParseError) as exc_info:
        GS1Message.parse(value)

    assert str(exc_info.value
               ) == "Failed to get GS1 Application Identifier from 'aaa'."
Esempio n. 3
0
def test_parse_fails_if_fixed_length_field_ends_with_separator_char() -> None:
    # 15... is a fixed length date.
    # \1xd is the default separator character in an illegal position.
    # 10... is any other field.
    value = "15210526\x1d100329"

    with pytest.raises(ParseError) as exc_info:
        GS1Message.parse(value)

    assert str(exc_info.value) == (
        r"Element String '(15)210526' has fixed length and "
        r"should not end with a separator character. "
        r"Separator character '\x1d' found in '15210526\x1d100329'.")
Esempio n. 4
0
def _parse_gs1_message(
    value: str,
    *,
    config: ParseConfig,
    queue: ParseQueue,
    result: ParseResult,
) -> None:
    if result.gs1_message is not None:
        return  # pragma: no cover

    try:
        result.gs1_message = GS1Message.parse(
            value,
            rcn_region=config.rcn_region,
            separator_chars=config.separator_chars,
        )
        result.gs1_message_error = None
    except ParseError as exc:
        result.gs1_message = None
        result.gs1_message_error = str(exc)
    else:
        # If the GS1 Message contains an SSCC, set SSCC on the top-level result.
        ai_00 = result.gs1_message.get(ai="00")
        if ai_00 is not None and ai_00.sscc is not None:
            queue.append((_parse_sscc, ai_00.sscc.value))

        # If the GS1 Message contains an GTIN, set GTIN on the top-level result.
        ai_01 = result.gs1_message.get(ai="01")
        if ai_01 is not None and ai_01.gtin is not None:
            queue.append((_parse_gtin, ai_01.gtin.value))
Esempio n. 5
0
def test_parse_with_separator_char(
    value: str, separator_chars: Iterable[str], expected_hri: str
) -> None:
    assert (
        GS1Message.parse(value, separator_chars=separator_chars).as_hri()
        == expected_hri
    )
Esempio n. 6
0
def test_filter_element_strings_by_ai_instance() -> None:
    ai = GS1ApplicationIdentifier.extract("01")
    msg = GS1Message.parse("010703206980498815210526100329")

    element_string = msg.get(ai=ai)

    assert element_string is not None
    assert element_string.value == "07032069804988"
Esempio n. 7
0
def test_get_element_string_by_ai(value: str, ai: str, expected: str) -> None:
    element_string = GS1Message.parse(value).get(ai=ai)

    if expected is None:
        assert element_string is None
    else:
        assert element_string is not None
        assert element_string.value == expected
Esempio n. 8
0
def test_get_element_string_by_data_title(value: str, data_title: str,
                                          expected: str) -> None:
    element_string = GS1Message.parse(value).get(data_title=data_title)

    if expected is None:
        assert element_string is None
    else:
        assert element_string is not None
        assert element_string.value == expected
Esempio n. 9
0
def test_parse(value: str, expected: GS1Message) -> None:
    assert GS1Message.parse(value) == expected
Esempio n. 10
0
def test_filter_element_strings_by_data_title(value: str, data_title: str,
                                              expected: List[str]) -> None:
    matches = GS1Message.parse(value).filter(data_title=data_title)

    assert [element_string.value for element_string in matches] == expected
Esempio n. 11
0
def test_filter_element_strings_by_ai(value: str, ai: str,
                                      expected: List[str]) -> None:
    matches = GS1Message.parse(value).filter(ai=ai)

    assert [element_string.value for element_string in matches] == expected
Esempio n. 12
0
def test_as_hri(value: str, expected: str) -> None:
    assert GS1Message.parse(value).as_hri() == expected
Esempio n. 13
0
def test_parse_strips_surrounding_whitespace() -> None:
    message = GS1Message.parse("  \t 800370713240010220085952 \n  ")

    assert message.value == "800370713240010220085952"
Esempio n. 14
0
 GS1Message(
     value="010703206980498815210526100329",
     element_strings=[
         GS1ElementString(
             ai=GS1ApplicationIdentifier(
                 ai="01",
                 description="Global Trade Item Number (GTIN)",
                 data_title="GTIN",
                 fnc1_required=False,
                 format="N2+N14",
                 pattern="^01(\\d{14})$",
             ),
             value="07032069804988",
             pattern_groups=["07032069804988"],
             gtin=Gtin(
                 value="07032069804988",
                 format=GtinFormat.GTIN_13,
                 prefix=GS1Prefix(value="703", usage="GS1 Norway"),
                 payload="703206980498",
                 check_digit=8,
             ),
         ),
         GS1ElementString(
             ai=GS1ApplicationIdentifier(
                 ai="15",
                 description="Best before date (YYMMDD)",
                 data_title="BEST BEFORE or BEST BY",
                 fnc1_required=False,
                 format="N2+N6",
                 pattern="^15(\\d{6})$",
             ),
             value="210526",
             pattern_groups=["210526"],
             date=date(2021, 5, 26),
         ),
         GS1ElementString(
             ai=GS1ApplicationIdentifier(
                 ai="10",
                 description="Batch or lot number",
                 data_title="BATCH/LOT",
                 fnc1_required=True,
                 format="N2+X..20",
                 pattern=(
                     r"^10([\x21-\x22\x25-\x2F\x30-\x39\x3A-\x3F"
                     r"\x41-\x5A\x5F\x61-\x7A]{0,20})$"),
             ),
             value="0329",
             pattern_groups=["0329"],
         ),
     ],
 ),
Esempio n. 15
0
def parse(
    value: str,
    *,
    rcn_region: Optional[RcnRegion] = None,
    separator_chars: Iterable[str] = DEFAULT_SEPARATOR_CHARS,
) -> ParseResult:
    """Identify data format and parse data.

    The current strategy is:

    1. If Symbology Identifier prefix indicates a GTIN or GS1 Message,
       attempt to parse and validate as that.
    2. Else, if not Symbology Identifier, attempt to parse with all parsers.

    Args:
        value: The data to classify and parse.
        rcn_region: The geographical region whose rules should be used to
            interpret Restricted Circulation Numbers (RCN).
            Needed to extract e.g. variable weight/price from GTIN.
        separator_chars: Characters used in place of the FNC1 symbol.
            Defaults to `<GS>` (ASCII value 29).
            If variable-length fields in the middle of the message are
            not terminated with a separator character, the parser might
            greedily consume the rest of the message.

    Returns:
        A data class depending upon what type of data is parsed.

    Raises:
        ParseError: If parsing of the data fails.
    """
    value = value.strip()
    result = ParseResult(value=value)

    # Extract Symbology Identifier
    if value.startswith("]"):
        result.symbology_identifier = SymbologyIdentifier.extract(value)
        value = value[len(result.symbology_identifier):]

    # Select parsers
    parsers: List[ParserType] = []
    if result.symbology_identifier is not None:
        if (result.symbology_identifier.gs1_symbology
                in GS1Symbology.with_gtin()):
            parsers.append(Gtin)
        if (result.symbology_identifier.gs1_symbology
                in GS1Symbology.with_ai_element_strings()):
            parsers.append(GS1Message)
    if not parsers:
        # If we're not able to select a subset based on Symbology Identifiers,
        # run all parsers in the default order.
        parsers = [Gtin, Sscc, GS1Message]

    # Run all parsers in order
    for parser in parsers:
        if parser == Gtin:
            try:
                result.gtin = Gtin.parse(value, rcn_region=rcn_region)
            except ParseError as exc:
                result.gtin_error = str(exc)

        if parser == Sscc:
            try:
                result.sscc = Sscc.parse(value)
            except ParseError as exc:
                result.sscc_error = str(exc)

        if parser == GS1Message:
            try:
                result.gs1_message = GS1Message.parse(
                    value,
                    rcn_region=rcn_region,
                    separator_chars=separator_chars,
                )
            except ParseError as exc:
                result.gs1_message_error = str(exc)
            else:
                ai_00 = result.gs1_message.get(ai="00")
                if ai_00 is not None:
                    # GS1 Message contains an SSCC
                    result.sscc = ai_00.sscc
                    # Clear error from parsing full value a SSCC.
                    result.sscc_error = None

                ai_01 = result.gs1_message.get(ai="01")
                if ai_01 is not None:
                    # GS1 Message contains a GTIN.
                    result.gtin = ai_01.gtin
                    # Clear error from parsing full value as GTIN.
                    result.gtin_error = None

    if result._has_result():
        return result
    else:
        raise ParseError(
            f"Failed to parse {value!r}:\n{result._get_errors_list()}")
Esempio n. 16
0
     # GTIN-8
     "96385074",
     ParseResult(
         value="96385074",
         gtin=Gtin(
             value="96385074",
             format=GtinFormat.GTIN_8,
             prefix=GS1Prefix(value="00009", usage="GS1 US"),
             payload="9638507",
             check_digit=4,
         ),
         gs1_message=GS1Message(
             value="96385074",
             element_strings=[
                 GS1ElementString(
                     ai=GS1ApplicationIdentifier.extract("96"),
                     value="385074",
                     pattern_groups=["385074"],
                 )
             ],
         ),
         sscc_error=
         ("Failed to parse '96385074' as SSCC: Expected 18 digits, got 8."
          ),
     ),
 ),
 (
     # GTIN-12
     "123601057072",
     ParseResult(
         value="123601057072",
         gtin=Gtin(