Parsing a shop receipt I've been working on a parser for shop receipts which extracts data about the payment. Here is the text that I'm parsing:


  * Vic107Payment 
    Text: PINNEN
    TicketData: POI: 12345678                   
KLANTTICKET
--------------------------------
Terminal:                 ABC123
Merchant:                 123456
Periode:                    1234
Transactie:             12345678


MAESTRO         
(A0000000012345)                
MAESTRO             
Kaart:       xxxxxxxxxxxxxxx1234
Kaartserienummer:              0

BETALING            
Datum:          01/01/2020 04:15
Autorisatiecode:          123ABC

Totaal:                 1,00 EUR

Leesmethode: NFC Chip           

    CardTypeId: 1234
    CardTypeText: MAESTRO
    ReceiptNumber: 
    DrawerAmount: 1,00
    Number: 1
    DrawerId: drawers/default
    DrawerNumber: 1
    Amount: 1,00
    IsCancelable: True
===

Here is the code that parses this fragment from the receipt. Now my concern is that people will find it difficult to read and/or difficult to maintain, so I was wondering whether it could be improved in any way?

def parse_card_payment(product):
    # cp stands for card payment
    cp_poi = None
    cp_terminal = None
    cp_merchant = None
    cp_period = None
    cp_transaction = None
    cp_card = None
    cp_card_serial_number = None
    cp_date = None
    cp_authorisation_code = None
    cp_total = None
    cp_card_type_id = None
    cp_card_type_text = None
    cp_drawer_id = None
    cp_drawer_amount = None
    cp_cancelable = None
    cp_card_type = None

    for line in product.strip().split('\n'):
        if 'POI' in line:
            cp_poi = line.split(':')[1].strip()
        elif 'Terminal' in line:
            cp_terminal = line.split(':')[1].strip()
        elif 'Merchant' in line:
            cp_merchant = line.split(':')[1].strip()
        elif 'Periode' in line:
            cp_period = line.split(':')[1].strip()
        elif 'Transactie' in line:
            cp_transaction = line.split(':')[1].strip()
        elif 'Kaart:' in line:
            cp_card = line.split(':')[1].strip()
        elif 'Kaartserienummer' in line:
            cp_card_serial_number = line.split(':')[1].strip()
        elif 'Datum' in line:
            cp_date = line.split(':')[1].strip()
        elif 'Autorisatiecode' in line:
            cp_authorisation_code = line.split(':')[1].strip()
        elif 'Totaal' in line:
            cp_total = line.split(':')[1].strip()
        elif 'CardTypeId' in line:
            cp_card_type_id = line.split(':')[1].strip()
        elif 'CardTypeText' in line:
            cp_card_type_text = line.split(':')[1].strip()
        elif 'DrawerAmount' in line:
            cp_drawer_amount = line.split(':')[1].strip()
        elif 'DrawerId' in line:
            cp_drawer_id = line.split(':')[1].strip()
        elif 'Cancelable' in line:
            cp_cancelable = line.split(':')[1].strip()
        elif 'Leesmethode' in line:
            cp_card_type = line.split(':')[1].strip()

    cp = {'cp_poi': cp_poi,
          'cp_terminal': cp_terminal,
          'cp_merchant': cp_merchant,
          'cp_period': cp_period,
          'cp_transaction': cp_transaction,
          'cp_card': cp_card,
          'cp_card_serial_number': cp_card_serial_number,
          'cp_date': cp_date,
          'cp_authorisation_code': cp_authorisation_code,
          'cp_total': cp_total,
          'cp_card_type_id': cp_card_type_id,
          'cp_card_type_text': cp_card_type_text,
          'cp_drawer_id': cp_drawer_id,
          'cp_drawer_amount': cp_drawer_amount,
          'cp_cancelable': cp_cancelable,
          'cp_card_type': cp_card_type}
    return cp
In programming there is the general principle Don't Repeat Yourself (DRY). Your code is a lot of repetition of exactly the same pattern, with only the string changing.

So, just put those strings into a dictionary, with the final variable name as keys:

RECEIPT_ITEMS = {"cp_poi": "POI", "cp_terminal": "Terminal",
                 "cp_merchant": "Merchant", "cp_period": "Periode",
                 "cp_transaction": "Transactie", "cp_card": "Kaart",
                 "cp_card_serial_number": "Kaartserienummer", "cp_date": "Datum",
                 "cp_authorisation_code": "Autorisatiecode",
                 "cp_total": "Totaal", "cp_card_type_id": "CardTypeId", 
                 "cp_card_type_text": "CardTypeText",
                 "cp_drawer_amount": "DrawerAmount", "cp_drawer_id": "DrawerId",
                 "cp_cancelable": "Cancelable", "cp_card_type": "Leesmethode"}

def parse_card_payment(product):
    cp = dict.fromkeys(RECEIPT_ITEMS.keys())
    for line in product.splitlines():
        for key, value in RECEIPT_ITEMS.items():
            if value in line:
                cp[key] = line.split(":")[1].strip()
                break
    return cp


This has the advantage that if you ever have receipts in another language than Dutch (but with the same structure), you only need to localize the values of this dictionary and not change your whole code.

Note that I used str.splitlines, which automatically ignores trailing newlines.

A different approach might be to use a multi-line RegEx to perform the search directly, but that will probably be more complicated.