Skip to content

Commit

Permalink
Merge pull request #39 from jodal/gs1-element-string-lookup
Browse files Browse the repository at this point in the history
gs1: Add methods to find parsed Element Strings
  • Loading branch information
jodal committed Aug 20, 2020
2 parents 196965e + ea8baa4 commit 1515b87
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 4 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ The library can interpret the following formats:
parse amounts with currency into `Money` values
- [x] Encode as Human Readable Interpretation (HRI),
e.g. with parenthesis around the AI numbers
- [x] Easy lookup of parsed Element Strings by:
- [x] AI prefix
- [x] Part of AI's data title
- GTIN (Global Trade Item Number)
- [x] Parse GTIN-8, e.g. from EAN-8 barcodes
- [x] Parse GTIN-12, e.g. from UPC-A and UPC-E barcodes
Expand Down
6 changes: 6 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ v0.3.0 (UNRELEASED)

:mod:`biip.gs1`

- Add :meth:`~biip.gs1.GS1Message.filter` to find all parsed Element Strings
that matches the criteria.

- Add :meth:`~biip.gs1.GS1Message.get` to find first parsed Element String
that matches the criteria.

- Add :attr:`~biip.gs1.GS1ElementString.decimal` field which is set for
AIs with weight, volume, dimensions, dicount percentages, and amounts
payable.
Expand Down
4 changes: 2 additions & 2 deletions src/biip/gs1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@
format=GtinFormat.GTIN_13, prefix=GS1Prefix(value='703', usage='GS1
Norway'), payload='703206980498', check_digit=8, packaging_level=None),
date=None, decimal=None, money=None)
>>> msg.element_strings[1]
>>> msg.get(data_title='BEST BY')
GS1ElementString(ai=GS1ApplicationIdentifier(ai='15', description='Best
before date (YYMMDD)', data_title='BEST BEFORE or BEST BY',
fnc1_required=False, format='N2+N6'), value='210526',
pattern_groups=['210526'], gtin=None, date=datetime.date(2021, 5, 26),
decimal=None, money=None)
>>> msg.element_strings[2]
>>> msg.get(ai="10")
GS1ElementString(ai=GS1ApplicationIdentifier(ai='10', description='Batch
or lot number', data_title='BATCH/LOT', fnc1_required=True,
format='N2+X..20'), value='0329', pattern_groups=['0329'], gtin=None,
Expand Down
61 changes: 59 additions & 2 deletions src/biip/gs1/_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import List, Type
from typing import List, Optional, Type, Union

from biip import ParseError
from biip.gs1 import DEFAULT_SEPARATOR_CHAR, GS1ElementString
from biip.gs1 import (
DEFAULT_SEPARATOR_CHAR,
GS1ApplicationIdentifier,
GS1ElementString,
)


@dataclass
Expand Down Expand Up @@ -80,3 +84,56 @@ def as_hri(self: GS1Message) -> str:
A human-readable string where the AIs are wrapped in parenthesis.
"""
return "".join(es.as_hri() for es in self.element_strings)

def filter(
self: GS1Message,
*,
ai: Optional[Union[str, GS1ApplicationIdentifier]] = None,
data_title: Optional[str] = None,
) -> List[GS1ElementString]:
"""Filter Element Strings by AI or data title.
Args:
ai: AI instance or string to match against the start of the
Element String's AI.
data_title: String to find anywhere in the Element String's AI
data title.
Returns:
All matching Element Strings in the message.
"""
if isinstance(ai, GS1ApplicationIdentifier):
ai = ai.ai

result = []

for element_string in self.element_strings:
if ai is not None and element_string.ai.ai.startswith(ai):
result.append(element_string)
elif (
data_title is not None
and data_title in element_string.ai.data_title
):
result.append(element_string)

return result

def get(
self: GS1Message,
*,
ai: Optional[Union[str, GS1ApplicationIdentifier]] = None,
data_title: Optional[str] = None,
) -> Optional[GS1ElementString]:
"""Get Element String by AI or data title.
Args:
ai: AI instance or string to match against the start of the
Element String's AI.
data_title: String to find anywhere in the Element String's AI
data title..
Returns:
The first matching Element String in the message.
"""
matches = self.filter(ai=ai, data_title=data_title)
return matches[0] if matches else None
98 changes: 98 additions & 0 deletions tests/gs1/test_messages.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import date
from typing import List

import pytest

Expand Down Expand Up @@ -193,3 +194,100 @@ def test_parse_fails_if_fixed_length_field_ends_with_separator_char() -> None:
)
def test_as_hri(value: str, expected: str) -> None:
assert GS1Message.parse(value).as_hri() == expected


@pytest.mark.parametrize(
"value, ai, expected",
[
("010703206980498815210526100329", "01", ["07032069804988"]),
("010703206980498815210526100329", "15", ["210526"]),
("010703206980498815210526100329", "37", []),
("7230EM123\x1d7231EM456\x1d7232EM789", "7231", ["EM456"]),
(
"7230EM123\x1d7231EM456\x1d7232EM789",
"723",
["EM123", "EM456", "EM789"],
),
],
)
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


@pytest.mark.parametrize(
"value, data_title, expected",
[
("010703206980498815210526100329", "GTIN", ["07032069804988"]),
("010703206980498815210526100329", "BEST BY", ["210526"]),
("010703206980498815210526100329", "COUNT", []),
(
"7230EM123\x1d7231EM456\x1d7232EM789",
"CERT",
["EM123", "EM456", "EM789"],
),
],
)
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


@pytest.mark.parametrize(
"value, ai, expected",
[
("010703206980498815210526100329", "01", "07032069804988"),
("010703206980498815210526100329", "15", "210526"),
("010703206980498815210526100329", "10", "0329"),
("010703206980498815210526100329", "37", None),
("7230EM123\x1d7231EM456\x1d7232EM789", "7231", "EM456"),
("7230EM123\x1d7231EM456\x1d7232EM789", "723", "EM123"),
],
)
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


@pytest.mark.parametrize(
"value, data_title, expected",
[
("010703206980498815210526100329", "GTIN", "07032069804988"),
("010703206980498815210526100329", "BEST BY", "210526"),
("010703206980498815210526100329", "BATCH", "0329"),
("010703206980498815210526100329", "COUNT", None),
("7230EM123\x1d7231EM456\x1d7232EM789", "CERT #2", "EM456"),
("7230EM123\x1d7231EM456\x1d7232EM789", "CERT", "EM123"),
],
)
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


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"

0 comments on commit 1515b87

Please sign in to comment.