ean-tools 0.1.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- ean_tools-0.1.0/PKG-INFO +62 -0
- ean_tools-0.1.0/README.md +41 -0
- ean_tools-0.1.0/ean_tools/__init__.py +0 -0
- ean_tools-0.1.0/ean_tools/barcode_info.py +57 -0
- ean_tools-0.1.0/ean_tools/barcode_prefixes.py +61 -0
- ean_tools-0.1.0/ean_tools/check_digits.py +44 -0
- ean_tools-0.1.0/ean_tools/data/gs1-8-prefixes.yaml +28 -0
- ean_tools-0.1.0/ean_tools/data/gs1-prefixes.yaml +655 -0
- ean_tools-0.1.0/ean_tools/data/isbn-prefixes.yaml +691 -0
- ean_tools-0.1.0/ean_tools/normalization.py +72 -0
- ean_tools-0.1.0/pyproject.toml +27 -0
ean_tools-0.1.0/PKG-INFO
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: ean-tools
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Collection of tools for validating and getting information about EAN (UPC, GTIN) and ISBN barcodes.
|
5
|
+
Home-page: https://github.com/ean-db/ean-tools
|
6
|
+
License: MIT
|
7
|
+
Keywords: barcode,ean,upc,isbn,gtin
|
8
|
+
Author: EAN-DB
|
9
|
+
Author-email: support@ean-db.com
|
10
|
+
Requires-Python: >=3.12
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
16
|
+
Requires-Dist: PyYaml (>=6)
|
17
|
+
Requires-Dist: affix-tree (>=0.1.1)
|
18
|
+
Project-URL: Repository, https://github.com/ean-db/ean-tools
|
19
|
+
Description-Content-Type: text/markdown
|
20
|
+
|
21
|
+
# ean-tools
|
22
|
+
|
23
|
+
Collection of tools for validating and getting information about EAN (UPC, GTIN) and ISBN barcodes.
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
```commandline
|
28
|
+
pip install affix-tree
|
29
|
+
```
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
### Barcode normalization
|
34
|
+
|
35
|
+
```pycon
|
36
|
+
>>> from ean_tools.normalization import normalize_barcode
|
37
|
+
|
38
|
+
>>> normalize_barcode('978-84865-4608-3')
|
39
|
+
|
40
|
+
'9788486546083'
|
41
|
+
```
|
42
|
+
|
43
|
+
### Check digit validation
|
44
|
+
|
45
|
+
```pycon
|
46
|
+
>>> from ean_tools.check_digits import has_correct_check_digit
|
47
|
+
|
48
|
+
>>> has_correct_check_digit('8510000076279')
|
49
|
+
|
50
|
+
False
|
51
|
+
```
|
52
|
+
|
53
|
+
### Getting additional barcode information
|
54
|
+
|
55
|
+
```pycon
|
56
|
+
>>> from ean_tools.barcode_info import get_barcode_info, BarcodeType
|
57
|
+
|
58
|
+
>>> get_barcode_info('4000000001140')
|
59
|
+
|
60
|
+
BarcodeInfo(barcode_type=BarcodeType.REGULAR, description='GS1 Germany', country='de')
|
61
|
+
```
|
62
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# ean-tools
|
2
|
+
|
3
|
+
Collection of tools for validating and getting information about EAN (UPC, GTIN) and ISBN barcodes.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```commandline
|
8
|
+
pip install affix-tree
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### Barcode normalization
|
14
|
+
|
15
|
+
```pycon
|
16
|
+
>>> from ean_tools.normalization import normalize_barcode
|
17
|
+
|
18
|
+
>>> normalize_barcode('978-84865-4608-3')
|
19
|
+
|
20
|
+
'9788486546083'
|
21
|
+
```
|
22
|
+
|
23
|
+
### Check digit validation
|
24
|
+
|
25
|
+
```pycon
|
26
|
+
>>> from ean_tools.check_digits import has_correct_check_digit
|
27
|
+
|
28
|
+
>>> has_correct_check_digit('8510000076279')
|
29
|
+
|
30
|
+
False
|
31
|
+
```
|
32
|
+
|
33
|
+
### Getting additional barcode information
|
34
|
+
|
35
|
+
```pycon
|
36
|
+
>>> from ean_tools.barcode_info import get_barcode_info, BarcodeType
|
37
|
+
|
38
|
+
>>> get_barcode_info('4000000001140')
|
39
|
+
|
40
|
+
BarcodeInfo(barcode_type=BarcodeType.REGULAR, description='GS1 Germany', country='de')
|
41
|
+
```
|
File without changes
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from ean_tools.barcode_prefixes import get_raw_ean8_prefix_info, get_raw_ean13_prefix_info, get_raw_isbn_prefix_info
|
6
|
+
|
7
|
+
|
8
|
+
class BarcodeType(Enum):
|
9
|
+
COUPON_ID = 'COUPON_ID'
|
10
|
+
DEMO = 'DEMO'
|
11
|
+
GENERAL_MANAGER_NUMBER = 'GENERAL_MANAGER_NUMBER'
|
12
|
+
REFUND_RECEIPT = 'REFUND_RECEIPT'
|
13
|
+
REGULAR = 'REGULAR'
|
14
|
+
RESERVED_FOR_FUTURE = 'RESERVED_FOR_FUTURE'
|
15
|
+
RESTRICTED_CIRCULATION = 'RESTRICTED_CIRCULATION'
|
16
|
+
UNUSED = 'UNUSED'
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class BarcodeInfo:
|
21
|
+
barcode_type: BarcodeType
|
22
|
+
description: str
|
23
|
+
country: Optional[str]
|
24
|
+
|
25
|
+
|
26
|
+
def get_barcode_info(normalized_barcode: str) -> BarcodeInfo:
|
27
|
+
"""Retrieves information about a given barcode.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
normalized_barcode (str): The normalized barcode string.
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
BarcodeInfo: An object containing barcode information.
|
34
|
+
"""
|
35
|
+
if len(normalized_barcode) == 13 and (normalized_barcode.startswith('978') or normalized_barcode.startswith('979')):
|
36
|
+
barcode_info = get_raw_isbn_prefix_info(normalized_barcode)
|
37
|
+
if not barcode_info:
|
38
|
+
return BarcodeInfo(BarcodeType.REGULAR, 'ISBN', None)
|
39
|
+
|
40
|
+
return BarcodeInfo(BarcodeType.REGULAR, barcode_info['description'], barcode_info.get('country'))
|
41
|
+
|
42
|
+
if len(normalized_barcode) == 8:
|
43
|
+
barcode_info = get_raw_ean8_prefix_info(normalized_barcode)
|
44
|
+
elif len(normalized_barcode) == 14:
|
45
|
+
barcode_info = get_raw_ean13_prefix_info(normalized_barcode[1:])
|
46
|
+
else:
|
47
|
+
barcode_info = get_raw_ean13_prefix_info(normalized_barcode)
|
48
|
+
|
49
|
+
if not barcode_info:
|
50
|
+
return BarcodeInfo(BarcodeType.RESERVED_FOR_FUTURE, 'Reserved for future use', None)
|
51
|
+
|
52
|
+
if 'type' in barcode_info:
|
53
|
+
barcode_type = BarcodeType[barcode_info['type']]
|
54
|
+
else:
|
55
|
+
barcode_type = BarcodeType.REGULAR
|
56
|
+
|
57
|
+
return BarcodeInfo(barcode_type, barcode_info['description'], barcode_info.get('country'))
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import os
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
import yaml
|
5
|
+
|
6
|
+
from affix_tree import AffixTree
|
7
|
+
|
8
|
+
_CURRENT_DIR = os.path.dirname(__file__)
|
9
|
+
|
10
|
+
|
11
|
+
def init_gs1_prefixes() -> AffixTree[dict]:
|
12
|
+
with open(os.path.join(_CURRENT_DIR, 'data/gs1-prefixes.yaml')) as gs1_prefixes_file:
|
13
|
+
_gs1_prefixes = yaml.load(gs1_prefixes_file.read(), yaml.CBaseLoader)
|
14
|
+
|
15
|
+
gs1_prefixes = AffixTree()
|
16
|
+
for prefix_group in _gs1_prefixes:
|
17
|
+
for p in prefix_group.pop('prefixes'):
|
18
|
+
gs1_prefixes.add(p, prefix_group)
|
19
|
+
|
20
|
+
return gs1_prefixes
|
21
|
+
|
22
|
+
|
23
|
+
def init_gs1_8_prefixes() -> AffixTree[dict]:
|
24
|
+
with open(os.path.join(_CURRENT_DIR, 'data/gs1-8-prefixes.yaml')) as gs1_8_prefixes_file:
|
25
|
+
_gs1_8_prefixes = yaml.load(gs1_8_prefixes_file.read(), yaml.CBaseLoader)
|
26
|
+
|
27
|
+
gs1_8_prefixes = AffixTree()
|
28
|
+
for prefix_group in _gs1_8_prefixes:
|
29
|
+
for p in prefix_group.pop('prefixes'):
|
30
|
+
gs1_8_prefixes.add(p, prefix_group)
|
31
|
+
|
32
|
+
return gs1_8_prefixes
|
33
|
+
|
34
|
+
|
35
|
+
def init_isbn_prefixes() -> AffixTree[dict]:
|
36
|
+
with open(os.path.join(_CURRENT_DIR, 'data/isbn-prefixes.yaml')) as isbn_prefixes_file:
|
37
|
+
_isbn_prefixes = yaml.load(isbn_prefixes_file.read(), yaml.CBaseLoader)
|
38
|
+
|
39
|
+
isbn_prefixes = AffixTree()
|
40
|
+
for prefix_group in _isbn_prefixes:
|
41
|
+
for p in prefix_group.pop('prefixes'):
|
42
|
+
isbn_prefixes.add(p, prefix_group)
|
43
|
+
|
44
|
+
return isbn_prefixes
|
45
|
+
|
46
|
+
|
47
|
+
_GS1_8_PREFIXES: AffixTree[dict] = init_gs1_8_prefixes()
|
48
|
+
_GS1_PREFIXES: AffixTree[dict] = init_gs1_prefixes()
|
49
|
+
_ISBN_PREFIXES: AffixTree[dict] = init_isbn_prefixes()
|
50
|
+
|
51
|
+
|
52
|
+
def get_raw_ean8_prefix_info(barcode: str) -> Optional[dict]:
|
53
|
+
return _GS1_8_PREFIXES.find(barcode)
|
54
|
+
|
55
|
+
|
56
|
+
def get_raw_ean13_prefix_info(barcode: str) -> Optional[dict]:
|
57
|
+
return _GS1_PREFIXES.find(barcode)
|
58
|
+
|
59
|
+
|
60
|
+
def get_raw_isbn_prefix_info(barcode: str) -> Optional[dict]:
|
61
|
+
return _ISBN_PREFIXES.find(barcode)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
def has_correct_check_digit(normalized_barcode: str) -> bool:
|
2
|
+
"""Checks if the given barcode has the correct check digit.
|
3
|
+
|
4
|
+
Args:
|
5
|
+
normalized_barcode (str): Normalized barcode string.
|
6
|
+
|
7
|
+
Returns:
|
8
|
+
bool: True if the check digit is correct, False otherwise.
|
9
|
+
"""
|
10
|
+
return get_correct_check_digit(normalized_barcode) == normalized_barcode[-1]
|
11
|
+
|
12
|
+
|
13
|
+
def get_correct_check_digit(normalized_barcode: str) -> str:
|
14
|
+
"""Calculates the correct check digit for a barcode.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
normalized_barcode (str): Normalized barcode string.
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
str: The correct check digit.
|
21
|
+
"""
|
22
|
+
check_digit = 0
|
23
|
+
barcode_length = len(normalized_barcode)
|
24
|
+
|
25
|
+
i = barcode_length - 2
|
26
|
+
while i >= 0:
|
27
|
+
weight = 3 if (barcode_length - i) % 2 == 0 else 1
|
28
|
+
check_digit += int(normalized_barcode[i]) * weight
|
29
|
+
i -= 1
|
30
|
+
|
31
|
+
return str((10 - (check_digit % 10)) % 10)
|
32
|
+
|
33
|
+
|
34
|
+
def isbn10_has_correct_check_digit(isbn10: str) -> bool:
|
35
|
+
"""Checks if an ISBN-10 barcode has the correct check digit.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
isbn10 (str): The ISBN-10 barcode string.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
bool: True if the check digit is correct, False otherwise.
|
42
|
+
"""
|
43
|
+
s = sum((10 - i) * int(c) for i, c in enumerate(isbn10))
|
44
|
+
return s % 11 == 0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
- description: Used to issue Restricted Circulation Numbers within a company
|
2
|
+
type: RESTRICTED_CIRCULATION
|
3
|
+
prefixes:
|
4
|
+
- '0'
|
5
|
+
- '2'
|
6
|
+
- description: Used to issue GTIN-8s
|
7
|
+
prefixes:
|
8
|
+
- '1'
|
9
|
+
- '3'
|
10
|
+
- '4'
|
11
|
+
- '5'
|
12
|
+
- '6'
|
13
|
+
- '7'
|
14
|
+
- '8'
|
15
|
+
- '90'
|
16
|
+
- '91'
|
17
|
+
- '92'
|
18
|
+
- '93'
|
19
|
+
- '94'
|
20
|
+
- '95'
|
21
|
+
- '96'
|
22
|
+
- '970'
|
23
|
+
- '971'
|
24
|
+
- '972'
|
25
|
+
- '973'
|
26
|
+
- '974'
|
27
|
+
- '975'
|
28
|
+
- '976'
|