Coder Social home page Coder Social logo

biip's Introduction

📦 Biip

Biip interprets the data in barcodes.

Tests Docs Coverage PyPI


Biip is a Python library for making sense of the data in barcodes.

The library can interpret the following formats:

  • GTIN-8, GTIN-12, GTIN-13, and GTIN-14 numbers, commonly found in EAN-8, EAN-13, and ITF-14 barcodes.

  • GS1 AI element strings, commonly found in GS1-128 barcodes.

  • UPC-A and UPC-E numbers, as found in UPC-A and UPC-E barcodes.

For a quickstart guide and a complete API reference, see the documentation.

Installation

Biip requires Python 3.8 or newer.

Biip is available from PyPI:

python3 -m pip install biip

Optionally, with the help of py-moneyed, Biip can convert amounts with currency information to moneyed.Money objects. To install Biip with py-moneyed, run:

python3 -m pip install "biip[money]"

Project resources

Development status

All planned features have been implemented. Please open an issue if you have any barcode parsing related needs that are not covered.

Features

  • GS1 (multiple Element Strings with Application Identifiers)
    • Recognize all specified Application Identifiers.
    • Recognize allocating GS1 Member Organization from the GS1 Company Prefix.
    • Recognize the GS1 Company Prefix.
    • Parse fixed-length Element Strings.
    • Parse variable-length Element Strings.
      • Support configuring the separation character.
    • Parse AI 00 as SSCC.
    • Parse AI 01 and 02 as GTIN.
    • Parse AI 410-417 as GLN.
    • Parse dates into datetime.date values.
      • Interpret the year to be within -49/+50 years from today.
      • Interpret dates with day "00" as the last day of the month.
    • Parse variable measurement fields into Decimal values.
    • Parse discount percentage into Decimal values.
    • Parse amounts into Decimal values.
      • Additionally, if py-moneyed is installed, parse amounts with currency into Money values.
    • Encode as Human Readable Interpretation (HRI), e.g. with parenthesis around the AI numbers.
    • Parse Human Readable Interpretation (HRI) strings.
    • Easy lookup of parsed Element Strings by:
      • Application Identifier (AI) prefix
      • Part of AI's data title
  • GLN (Global Location Number)
    • Parse.
    • Extract and validate check digit.
    • Extract GS1 Prefix.
    • Extract GS1 Company Prefix.
  • GTIN (Global Trade Item Number)
    • Parse GTIN-8, e.g. from EAN-8 barcodes.
    • Parse GTIN-12, e.g. from UPC-A and UPC-E barcodes.
    • Parse GTIN-13, e.g. from EAN-13 barcodes.
    • Parse GTIN-14, e.g. from ITF-14 and GS1-128 barcodes.
    • Extract and validate check digit.
    • Extract GS1 Prefix.
    • Extract GS1 Company Prefix.
    • Extract packaging level digit from GTIN-14.
    • Encode GTIN-8 as GTIN-12/13/14.
    • Encode GTIN-12 as GTIN-13/14.
    • Encode GTIN-13 as GTIN-14.
  • RCN (Restricted Circulation Numbers), a subset of GTINs
    • Classification of RCN usage to either a geographical region or a company.
    • Parsing of variable measurements (price/weight) into Decimal values.
    • Parsing of price values into Money values if py-moneyed is installed and the region's RCN parsing rules specifies a currency.
    • Denmark: Parsing of weight and price.
    • Estland: Parsing of weight.
    • Finland: Parsing of weight.
    • Germany: Parsing of weight, price, and count, including validation of measurement check digit.
    • Great Britain: Parsing of price, including validation of measurement check digit.
    • Latvia: Parsing of weight.
    • Lithuania: Parsing of weight.
    • Norway: Parsing of weight and price.
    • Sweden: Parsing of weight and price.
    • Encode RCN with the variable measure part zeroed out, to help looking up the correct trade item.
  • SSCC (Serial Shipping Container Code)
    • Extract and validate check digit.
    • Extract GS1 Prefix.
    • Extract GS1 Company Prefix.
    • Extract extension digit.
    • Encode for human consumption, with the logical groups separated by whitespace.
  • UPC (Universal Product Code)
    • Parse 12-digit UPC-A.
    • Parse 6-digit UPC-E, with implicit number system 0 and no check digit.
    • Parse 7-digit UPC-E, with explicit number system and no check digit.
    • Parse 8-digit UPC-E, with explicit number system and a check digit.
    • Expand UPC-E to UPC-A.
    • Suppress UPC-A to UPC-E, for the values where it is supported.
  • Symbology Identifiers, e.g. ]EO
    • Recognize all specified Symbology Identifier code characters.
    • Strip Symbology Identifiers before parsing the remainder.
    • Use Symbology Identifiers when automatically selecting what parser to use.

License

Copyright 2020-2024 Stein Magnus Jodal and contributors. Licensed under the Apache License, Version 2.0.

biip's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar jodal avatar klausmcm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

biip's Issues

Unable to parse '000000' date

Hello!

Some of the GS1 barcodes I am scanning contain some silly dates, such as '000000'. Currently parsing fails completely, as month 0 and day 0 are not a valid month/day respectively.

Is it possible to decide how certain errors are handled, and if they should fallback to defaults or fail completely?

Example of a GS1 barcode that fails: 019906600092731431030078881500000010215359

Invalid GTIN prevent getting other useful information

It is actually a feature request.
Sometimes in internal usage, we don't care for a valid GTIN. In parsers, there should be an option to skip check for invalid GTIN.
For example. Something like:
biip.gs1.GS1Message.parser(code, fnc1=some_fnc1, check_gtin=False)

If AI 01 or 02 is 14 digit long, it is okay. No need to check for check digit. It will save a little time and system resources if an organization has all valid GTIN, and they don't want to check its validity, and it will also enable an organization to parse any 14-digit code (valid or invalid) for internal use.
Furthermore, currently, if someone wants to get only Lot number or serial number or any other information other than GTIN, if the code has invalid GTIN, he is unable to get any other information because GTIN validation is returning the function with exception.
So, if someone wants to skip check for Valid GTIN, he should have a bool argument to send to the parser function.

Additional_ID parsing (code 240)

Hi,

I am parsing UDIs and can parse product_number (01), serial_number (21), lot_number (10), expiration and manufacture date (17, 11), but I cannot parse additional_id (240) from the UDI.
I managed to fix that by adding the separator '\x1d' before the '240' in UDI, but sometimes the string '240' appears more than once or not in the spot for the additional_id.
Examples:

UDI = '0104062102661608112405271727112710S2400424282400522010301NNN003'

If I don't add a separator, the response is just GS1Message class, and I can only get product_number:

<class 'biip.gs1._messages.GS1Message'>

{'product_number': '04062102661608', 'serial_number': None, 'lot_number': None, 'expiration_date': None, 'manufacture_date': None, 'additional_id': None}

If I add a separator, then I get everything correct:

GS1Message(value='0104062102661738112401241727072410S230091624\x1d2400523000302NNN003', element_strings=[GS1ElementString(ai=GS1ApplicationIdentifier(ai='01', description='Global Trade Item Number (GTIN)', data_title='GTIN', fnc1_required=False, format='N2+N14'), value='04062102661738', pattern_groups=['04062102661738'], gln=None, gln_error=None, gtin=Gtin(value='04062102661738', format=GtinFormat.GTIN_13, prefix=GS1Prefix(value='406', usage='GS1 Germany'), company_prefix=GS1CompanyPrefix(value='4062102'), payload='406210266173', check_digit=8, packaging_level=None), gtin_error=None, sscc=None, sscc_error=None, date=None, decimal=None, money=None), GS1ElementString(ai=GS1ApplicationIdentifier(ai='11', description='Production date (YYMMDD)', data_title='PROD DATE', fnc1_required=False, format='N2+N6'), value='240124', pattern_groups=['240124'], gln=None, gln_error=None, gtin=None, gtin_error=None, sscc=None, sscc_error=None, date=datetime.date(2024, 1, 24), decimal=None, money=None), GS1ElementString(ai=GS1ApplicationIdentifier(ai='17', description='Expiration date (YYMMDD)', data_title='USE BY OR EXPIRY', fnc1_required=False, format='N2+N6'), value='270724', pattern_groups=['270724'], gln=None, gln_error=None, gtin=None, gtin_error=None, sscc=None, sscc_error=None, date=datetime.date(2027, 7, 24), decimal=None, money=None), GS1ElementString(ai=GS1ApplicationIdentifier(ai='10', description='Batch or lot number', data_title='BATCH/LOT', fnc1_required=True, format='N2+X..20'), value='S230091624', pattern_groups=['S230091624'], gln=None, gln_error=None, gtin=None, gtin_error=None, sscc=None, sscc_error=None, date=None, decimal=None, money=None), GS1ElementString(ai=GS1ApplicationIdentifier(ai='240', description='Additional product identification assigned by the manufacturer', data_title='ADDITIONAL ID', fnc1_required=True, format='N3+X..30'), value='0523000302NNN003', pattern_groups=['0523000302NNN003'], gln=None, gln_error=None, gtin=None, gtin_error=None, sscc=None, sscc_error=None, date=None, decimal=None, money=None)])

{'product_number': '04062102661738', 'serial_number': None, 'lot_number': 'S230091624', 'expiration_date': datetime.date(2027, 7, 24), 'manufacture_date': datetime.date(2024, 1, 24), 'additional_id': '0523000302NNN003'}

So basically, is there something wrong with my UDI, is there something missing?

Thanks!

Failed to match pattern on GSI UDI Datamatrix

Hello!

I tried parsing data from the following barcode and revieved the following error:

Failed to match '158993a' with GS1 AI (15) pattern '^15(\d{6})$'.

Barcode:

  • Data: 0104150011164140211GTFG32DLF1723033110158993A

grafik

I tried parsing with the following code:

def parse_udi(udi):
        print("parse udi")
        print(udi)

        result = GS1Message.parse(udi)
        
        print(result)

        print(result.get(ai="17"))
        print(result.get(ai="10"))

Other Barcodes like 0104046963351946172401011019A21G8345 are working fine!
grafik

It seems like the parser runs into an error with the (21) AI. Using the same faulty barcode string without the (21) AI works fine
01041500111641401723033110158993A

I tried looking into #113 and #137 but didnt get any clues that helped me.

Edit

Another barcode with the (21) AI seems to work partialy.

grafik

01041500111641401726013110V1002521JGFMG4P38R gets parsed successfully but returns

GS1ElementString(ai=GS1ApplicationIdentifier(ai='10', description='Batch or lot number', data_title='BATCH/LOT', fnc1_required=True, format='N2+X..20'), value='V1002521JGFMG4P38R', pattern_groups=['V1002521JGFMG4P38R'], gln=None, gln_error=None, gtin=None, gtin_error=None, sscc=None, sscc_error=None, date=None, decimal=None, money=None) where the (21) AI is located in the (10) AI:

  • V1002521JGFMG4P38R

ISBT Support

Is there a plan to add ISBT barcodes to the list of supported standards? If you don't plan on adding this do you have any pointers on implementation?

Is there a problem with this AI ?

I try to decode the following GS1 string from a datamatrix:

01103663023000171021004371716020021925768618163

... which should result in: (01)10366302300017(10)2100437(17)160200(21)925768618163. But biip raise an exception:

biip._exceptions.ParseError: Failed to parse '01103663023000171021004371716020021925768618163':
- GTIN: Failed to parse '01103663023000171021004371716020021925768618163' as GTIN: Expected 8, 12, 13, or 14 digits, got 47.
- UPC: Failed to parse '01103663023000171021004371716020021925768618163' as UPC: Expected 6, 7, 8, or 12 digits, got 47.
- SSCC: Failed to parse '01103663023000171021004371716020021925768618163' as SSCC: Expected 18 digits, got 47.
- GS1: Failed to get GS1 Application Identifier from '768618163'.

Parsing is too strict

Thank you for the project, @jodal

I have encountered some issues with barcodes not always being encoded correctly. Specifically, when an element string with a fixed length ends with a separator character, and when the barcode is encoded as a human-readable interpretation. Perhaps, making the parsing less strict with an optional argument like "strict=False" would be helpful.

TypeError raised when parsing gtin in 3.2.0

Hello,

I tried parsing the same gtin value in 3.1.0 and 3.2.0 and it seems that 3.2.0 is raising an unhandled exception. Maybe this is related to #292?

  • With 3.1.0, it's working fine:
python
Python 3.12.0 (main, Oct 30 2023, 16:10:32) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import biip
>>> biip.parse("701197206489")
ParseResult(value='701197206489', symbology_identifier=None, gtin=Gtin(value='701197206489', format=GtinFormat.GTIN_12, prefix=GS1Prefix(value='070', usage='GS1 US'), company_prefix=GS1CompanyPrefix(value='0701197'), payload='70119720648', check_digit=9, packaging_level=None), gtin_error=None, upc=Upc(value='701197206489', format=UpcFormat.UPC_A, number_system_digit=7, payload='70119720648', check_digit=9), upc_error=None, sscc=None, sscc_error="Failed to parse '701197206489' as SSCC: Expected 18 digits, got 12.", gs1_message=None, gs1_message_error="Failed to get GS1 Application Identifier from '701197206489'.")
  • With 3.2.0, it's raising TypeError
$ python
Python 3.12.0 (main, Oct 30 2023, 16:10:32) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import biip
>>> biip.parse("701197206489")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "~/test/.venv/lib/python3.12/site-packages/biip/_parser.py", line 89, in parse
    parse_func(val, config, queue, result)
  File "~/test/.venv/lib/python3.12/site-packages/biip/_parser.py", line 236, in _parse_gs1_message
    result.gs1_message = GS1Message.parse(
                         ^^^^^^^^^^^^^^^^^
  File "~/test/.venv/lib/python3.12/site-packages/biip/gs1/_messages.py", line 76, in parse
    element_string = GS1ElementString.extract(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/test/.venv/lib/python3.12/site-packages/biip/gs1/_element_strings.py", line 151, in extract
    value = "".join(pattern_groups)
            ^^^^^^^^^^^^^^^^^^^^^^^
TypeError: sequence item 1: expected str instance, NoneType found

EAN-13 edge cases

I noticed that some EAN-13 barcodes cannot be parsed, even though they seem to be in use.
For example, biip cannot find a valid GS1 prefix for 6712670000276 (according to the list here, 671 indeed doesn't seem to be used yet), but I can find a product associated with this barcode.
Is this barcode an edge case that is still unhandled by biip? Any ideas on how this could be fixed?
cc @c-w

Compressed UPC-E are not supported

Hi @jodal,

First of all thanks for your awesome library.
I would like to know if you have plans to support compressed UPC-E barcodes. As far as I know compressed version is not valid GTIN.

I might be wrong but it seems like the algorithm to calculate the check digit is either different than the one in GTIN or there's no such algorithm at all. When uncompressed UPC-E becomes valid GTIN-12 (it becomes a UPC-A barcode). But when compressed it is not valid GTIN-8, it must be expanded to become GTIN-12 (though it might look like it is valid, as with this example 02345673 which can be treated as GTIN-8).

You can check here for GTIN compatibility https://www.barcodefaq.com/1d/upc-ean/#GTIN_Compliance. Note that in the given example they expanded 02349036 to 00023400000900 which i believe is incorrect because the check digit must be 6.

Here's a sample code to expand UPC-E to UPC-A - https://code.activestate.com/recipes/528911-barcodes-convert-upc-e-to-upc-a. I tested i on 02349036 and it gives 023400000906 which is a valid UPC-A/GTIN-12.
And here's how to compress UPC-A back to UPC-E - https://gist.github.com/corpit/8204456. Also works as expected.

I think the biggest problem is to distinguish EAN-8 from UPC-E to know when to make the decompression.

GS1-128 Internal product variant not detected

For some reason AI20 is not begin detected

Here's an example, what is begin returned
(01)10884521761599(11)210701(10)21G0073JZ2023

vs what I expect
(01)10884521761599(11)210701(10)21G0073JZ(20)23

Any advice would be helpful.

Error while parsing GS1 barcode

Hello, I tried parsing the data from the following barcode (data is 1021072911172405310100693570007182 and does not include any separator/FNC1 characters) and received a ParseError

IMG_20220331_173635

The data should be the following: (10)21072911(17)240531(01)00693570007182 (lot, expiry, GTIN)

I'm aware that ai 10 (lot) can vary in length so I wanted to check my understanding and confirm that this is not an issue with biip. Has this barcode not been constructed properly?

Thank you,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.