Exemple #1
0
def test_extract_fails_if_unknown_code_character() -> None:
    with pytest.raises(ParseError) as exc_info:
        SymbologyIdentifier.extract("]J0")

    assert str(
        exc_info.value) == ("Failed to get Symbology Identifier from ']J0'. "
                            "'J' is not a recognized code character.")
Exemple #2
0
def test_extract_fails_if_no_flag_character() -> None:
    with pytest.raises(ParseError) as exc_info:
        SymbologyIdentifier.extract("E0")

    assert str(
        exc_info.value) == ("Failed to get Symbology Identifier from 'E0'. "
                            "No initial ']' flag character found.")
Exemple #3
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()
    config = ParseConfig(
        rcn_region=rcn_region,
        separator_chars=separator_chars,
    )
    result = ParseResult(value=value)

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

    # Select parsers
    queue: ParseQueue = []
    if result.symbology_identifier is not None:
        if result.symbology_identifier.gs1_symbology in GS1Symbology.with_gtin():
            queue.append((_parse_gtin, value))
        if (
            result.symbology_identifier.gs1_symbology
            in GS1Symbology.with_ai_element_strings()
        ):
            queue.append((_parse_gs1_message, value))
    if not queue:
        # If we're not able to select a subset based on Symbology Identifiers,
        # run all parsers on the full value.
        queue = [
            (_parse_gs1_message, value),
            (_parse_gtin, value),
            (_parse_sscc, value),
            (_parse_upc, value),
        ]

    # Work through queue of parsers and the values to run them on. Any parser may
    # add additional work to the queue. Only the first result for a field is kept.
    while queue:
        (parse_func, val) = queue.pop(0)
        parse_func(val, config=config, queue=queue, result=result)

    if result._has_result():
        return result
    else:
        raise ParseError(f"Failed to parse {value!r}:\n{result._get_errors_list()}")
Exemple #4
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()}")
Exemple #5
0
def test_str(value: str) -> None:
    assert str(SymbologyIdentifier.extract(value)) == value
Exemple #6
0
def test_len(value: str) -> None:
    assert len(SymbologyIdentifier.extract(value)) == len(value)
Exemple #7
0
def test_extract(value: str, expected: SymbologyIdentifier) -> None:
    assert SymbologyIdentifier.extract(value) == expected
Exemple #8
0
from biip.gs1 import GS1Symbology
from biip.symbology import Symbology, SymbologyIdentifier


def test_symbology_enum() -> None:
    assert Symbology("C") == Symbology.CODE_128


@pytest.mark.parametrize(
    "value, expected",
    [
        (
            "]E0abc",
            SymbologyIdentifier(
                value="]E0",
                symbology=Symbology.EAN_UPC,
                modifiers="0",
                gs1_symbology=GS1Symbology.EAN_13,
            ),
        ),
        (
            "]C0abc",
            SymbologyIdentifier(
                value="]C0",
                symbology=Symbology.CODE_128,
                modifiers="0",
                gs1_symbology=None,
            ),
        ),
        (
            "]C1abc",
            SymbologyIdentifier(
Exemple #9
0
         ),
         gs1_message_error=
         ("Failed to get GS1 Application Identifier from '5901234123457'."
          ),
         sscc_error=("Failed to parse '5901234123457' as SSCC: "
                     "Expected 18 digits, got 13."),
     ),
 ),
 (
     # GTIN-13 with Symbology Identifier
     "]E05901234123457",
     ParseResult(
         value="]E05901234123457",
         symbology_identifier=SymbologyIdentifier(
             value="]E0",
             symbology=Symbology.EAN_UPC,
             modifiers="0",
             gs1_symbology=GS1Symbology.EAN_13,
         ),
         gtin=Gtin(
             value="5901234123457",
             format=GtinFormat.GTIN_13,
             prefix=GS1Prefix(value="590", usage="GS1 Poland"),
             payload="590123412345",
             check_digit=7,
         ),
     ),
 ),
 (
     # GTIN-14
     "05901234123457",
     ParseResult(