Skip to content

Commit

Permalink
Add GS1 capability (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
OtherBarry authored Apr 27, 2021
1 parent 7b2ee8b commit 7be0af0
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
90 changes: 90 additions & 0 deletions checkdigit/gs1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# /usr/bin/env python

# This file is part of checkdigit.

# checkdigit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# checkdigit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with checkdigit. If not, see <http://www.gnu.org/licenses/>.

"""GS1 Validation Functions."""

import math

from checkdigit._data import cleanse, convert


# WARNING: Data beginning with 0 must be as a string due to PEP 3127


def calculate(data: str) -> str:
"""Calculates GS1 Check Digit.
This method works for all fixed length numeric GS1 data structures
(including GDTI, GLN, GRAI, etc.) that require a check digit.
Args:
data: A string of characters
Returns:
str: The check digit that was missing
"""
data = cleanse(data)
data = data[::-1] # Reverse the barcode, as last digit is always multiplied by 3
total_sum = 0
for index, value in enumerate(data):
if index % 2 == 0:
total_sum += int(value) * 3
else:
total_sum += int(value)
next_multiple_of_ten = int(math.ceil(total_sum / 10.0)) * 10
check_digit = next_multiple_of_ten - total_sum
return convert(check_digit, "gs1")


def validate(data: str) -> bool:
"""Validates GS1.
This method works for all fixed length numeric GS1 data structures
(including GDTI, GLN, GRAI, etc.) that require a check digit.
Args:
data: A string of characters representing a full GS1 code
Returns:
bool: A boolean representing whether the
check digit validates the data or not
"""
data = cleanse(data)
return calculate(data[:-1]) == data[-1]


def missing(data: str) -> str:
"""Calculates a missing digit in a GS1 Code.
This method works for all fixed length numeric GS1 data structures
(including GDTI, GLN, GRAI, etc.) that require a check digit.
Args:
data: A string of characters representing a full ISBN code
with a question mark representing a missing character
Returns:
str: The missing value that should've been where the question mark was
"""
data = cleanse(data)
for poss_digit in range(10): # Brute Force the 10 options
option = convert(poss_digit)
# tests it with the generated number
# If this fails, the next number is tried
if validate(data.replace("?", option)):
return option
return "Invalid"
67 changes: 67 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import checkdigit.luhn as luhn
import checkdigit.parity as parity
import checkdigit.upc as upc
import checkdigit.gs1 as gs1

# Even parity
test(parity.calculate("0110"), "01100")
Expand Down Expand Up @@ -142,3 +143,69 @@
test(luhn.missing("515853022?76176"), "1")
test(luhn.missing("?72098369216316"), "3")
test(luhn.missing("78369216316"), "Invalid")

# Determine Check Digit
test(gs1.calculate("9894552"), "8")
test(gs1.calculate("7384553"), "9")
test(gs1.calculate("6095011"), "6")
test(gs1.calculate("24497088432"), "5")
test(gs1.calculate("97379430662"), "0")
test(gs1.calculate("82778161993"), "7")
test(gs1.calculate("992802203539"), "2")
test(gs1.calculate("484614659301"), "5")
test(gs1.calculate("508533792649"), "5")
test(gs1.calculate("1268148598279"), "8")
test(gs1.calculate("2943824610504"), "6")
test(gs1.calculate("2839112640382"), "7")
test(gs1.calculate("4314789452907127"), "9")
test(gs1.calculate("6427894156382299"), "3")
test(gs1.calculate("9770280892681722"), "8")
test(gs1.calculate("08408575249213173"), "1")
test(gs1.calculate("84668430275000727"), "9")
test(gs1.calculate("67368623738347505"), "1")

# Determine Check Digit
test(gs1.validate("45649554"))
test(gs1.validate("53089328"))
test(gs1.validate("026229133848"))
test(gs1.validate("791364257635"))
test(gs1.validate("1396057123946"))
test(gs1.validate("3832293285225"))
test(gs1.validate("97781402171203"))
test(gs1.validate("02146346514943"))
test(gs1.validate("25020757663329968"))
test(gs1.validate("82648071988131734"))
test(gs1.validate("321609478518371973"))
test(gs1.validate("961552634342856982"))
test(gs1.validate("97673485"), False)
test(gs1.validate("52186777"), False)
test(gs1.validate("548785535769"), False)
test(gs1.validate("753963090728"), False)
test(gs1.validate("3293497160526"), False)
test(gs1.validate("6806652313775"), False)
test(gs1.validate("02370607330031"), False)
test(gs1.validate("52262887213914"), False)
test(gs1.validate("83494889067993460"), False)
test(gs1.validate("93976703183564468"), False)
test(gs1.validate("501469249184000304"), False)
test(gs1.validate("224245438987081447"), False)

# Missing Digit
test(gs1.missing("?8945528"), "9")
test(gs1.missing("7?845539"), "3")
test(gs1.missing("60?50116"), "9")
test(gs1.missing("244?70884325"), "9")
test(gs1.missing("9737?4306620"), "9")
test(gs1.missing("82778?619937"), "1")
test(gs1.missing("992802?035392"), "2")
test(gs1.missing("4846146?93015"), "5")
test(gs1.missing("50853379?6495"), "2")
test(gs1.missing("126814859?2798"), "8")
test(gs1.missing("2943824610?046"), "5")
test(gs1.missing("28391126403?27"), "8")
test(gs1.missing("431478945290?1279"), "7")
test(gs1.missing("6427894156382?993"), "2")
test(gs1.missing("97702808926817?28"), "2")
test(gs1.missing("084085752492131?31"), "7")
test(gs1.missing("846684302750007275"), "Invalid")
test(gs1.missing("?73686237383475059"), "0")

0 comments on commit 7be0af0

Please sign in to comment.