#!/usr/bin/python
#-*- coding:utf8 -*-

#Parser für Bestellbestätigungen

#TODO:
#- Umbauen in eine class "Bestellung", deren Instanzen dann die Daten enthalten.
#- Evtl. die Gesamtdaten der Bestellung mit einlesen.
#- Striktere Tests, mit denen Formatänderungen festgestellt werden können, und Rückgabe des entsprechenden Status.

debug = False

#Testdaten, wie sie in einer Bestellbestätigung per E-Mail erscheinen.
Bestellung = u"""Vielen Dank für Ihre Bestellung, die wir schnellstmöglich prüfen und bearbeiten.
Folgende Daten haben Sie an uns übermittelt:

Ihre I-Net Nummer (bei Rückfragen bitte angeben): 123456

Bestelldatum                  : 10.06.2010                                      
Kundennummer                  : 123456789                                       
Name / Firma                  : Vorname Nachname                                
Postleitzahl                  : 12345                                           
Lieferland                    : Deutschland                                     
E-Mail-Adresse                : user@domain.tld                                 
Zahlart                       : Bankeinzug                                      
Ihre Bemerkung                :                                                 

Ich akzeptiere die AGB der Firma reichelt elektronik GmbH & Co. KG              

Die Lieferung der Ware erfolgt per DHL/Post.                                    


                                                                     Gesamtpreis
Artikelnummer    Bezeichnung                          Anzahl                    
________________________________________________________________________________
TMC 428-PI24     Schrittmotor-Controller SO-24             1          12,95 Euro
SA 56-11 RT      7-Segment-Anzeige, rot, 14,2mm, gem.     10           4,50 Euro
TORX 173         Toshiba LWL-Empfänger, Simplex            2           6,60 Euro
TOTX 173         Toshiba LWL-Sender, Simplex               2           7,30 Euro
LWL TOS 8        2X Toslink-Stecker, Ø= 5,0mm, 5,0 Met     2           6,60 Euro
1N 5822          Schottky Diode, DO201AD, 40V, 3A         10           2,10 Euro
1N 5819          Schottky Diode, DO41, 40V, 1A            10           0,60 Euro
1N 5818          Schottky Diode, DO41, 30V, 1A            10           0,60 Euro
FIN 40.52.6 12V  Bistabiles Steckrelais, 12V, 2 Wechsl     1           5,60 Euro
LM 393 DIP       Comparator,  DIP-8                        5           0,75 Euro
LM 393 D SMD     Comparator,  SO-8                         5           0,95 Euro
BF 245A          Transistor HF N-FET TO-92 30V 0,025A     10           1,50 Euro
2W DRAHT 0,33    Draht-Widerstand 2W, 10% 0,33 Ohm         5           1,15 Euro
BUZ 171          Transistor P-FET TO-220AB 50V 8A 40W      3           8,25 Euro
SB 540           Schottky Diode, DO201, 40V, 5A            5           1,50 Euro
FT 114-61        Ferritring 5961001001                     1           2,50 Euro
FT 82-61         Ferritring 5961000601                     1           1,50 Euro
RIK 10           Ringkern aus Ferroxube, N30               1           0,71 Euro
RIK 12           Ringkern aus Ferroxube, N27               1           0,77 Euro
RIK 20           Ringkern aus Ferroxube, N30               1           1,40 Euro
T 106-26         Eisenpulver-Ringkern                      5           5,75 Euro
BC 337-40        Transistor NPN TO-92 45V 0,5A 0,625W     10           0,40 Euro
KUPFER 0,5MM     Kupferlackdraht, Ø 0,5mm, Länge: 23M      1           1,65 Euro
ENC 28J60-I/SO   Ethernet Controller, Stand-Alone, SOI     1           2,55 Euro
ENC 28J60-I/SP   Ethernet Controller, Stand-Alone, S-D     1           2,60 Euro
SMD-0805 27,0    SMD-Chip-Widerstand, Bauform 0805, 27    10           0,82 Euro
SMD-0805 270     SMD-Chip-Widerstand, Bauform 0805, 27    10           0,82 Euro
L-0805F 10µ      SMD-Induktivität, 0805, Ferrit, 10µ       5           1,10 Euro
BAT 43 SMD       Schottky Diode SMD, Mini-Melf, 30V, 0    10           1,00 Euro
BAT 43WS         SMD-Schottky-Dioden, SOD323F, 30V, 0,    10           0,50 Euro


________________________________________________________________________________
                                                           =          85,02 Euro
Versandkosten      : 0.66 Kg                               =           5,60 Euro
________________________________________________________________________________
Gesamtkosten                                               =          90,62 Euro
________________________________________________________________________________


Alle Preise verstehen sich inklusive gesetzlicher Mehrwertsteuer.               
Sollte einer der Artikel ausnahmsweise nicht lieferbar sein, oder wir uns       
aus anderen Gründen nicht in der Lage sehen die Bestellung zu akzeptieren,      
werden wir Sie benachrichtigen.                                                 

Newsletter bestellen


Mit freundlichen Grüßen                                                         

Ihr reichelt elektronik Team                                                    


reichelt elektronik GmbH & Co. KG
Elektronikring 1
26452 Sande

Telefon (0 44 22) 9 55 - 333
Telefax (0 44 22) 9 55 - 111
web: www.reichelt.de


Handelsregister: HRA 200654 Oldenburg
USt-IdNr.: DE 259817039
WEEE-Nr.: DE 58715331

Persönlich haftende Gesellschafterin:
reichelt elektronik Verwaltungs GmbH
Sitz Sande Amtsgericht Oldenburg HRB 130281
Geschäftsführer: Reto Welte, Dr. René Trauffer 
"""

#Hiermit werden die Feldnamen des Lieferanten auf einheitliche Feldnamen der Software gemappt.
#Gibt ein Wörterbuch mit den vereinheitlichten Feldern zurück.

Lieferant = 'Reichelt'
parser_version = '1'
valid_from = None #Hier kann eingetragen werden, ab wann die Daten in diesem Format versandt werden
valid_until = None #und bis wann dieses Format verwendet wurde.

def mapper(Bestelldatum, Artikelnummer, Bezeichnung, Anzahl, Gesamtpreis, Waehrung):
	return {'B_Lieferant': Lieferant, \
		'B_Bestelldatum': Bestelldatum, \
		'L_Bestellnummer': Artikelnummer, \
		'L_Bezeichnung': Bezeichnung, \
		'B_Anzahl': Anzahl, \
		'B_Einzelpreis': Gesamtpreis / Anzahl}

#Bestellung Zeile für Zeile durchgehen und Daten extrahieren.
#Gibt eine Liste der in der Bestellung aufgeführten Teile mit den in der Funktion mapper definierten Daten zurück.
def parse(bestellung):
	getlines = False #Für einfache State-Machine, die genau einmal anfängt, die Zeilen mit den Teilen zu parsen, 
	getmore = True   #  und am Ende der Teileliste aufhört und auch nicht wieder anfängt.
	Fehlerliste = [] #Zur Ausgabe eventueller Probleme beim Parsen
	OK = True        #Wenn alles ok war, wird True zurückgeben, bei Fehlern False
	Teile = []       #Teileliste der Bestellung, die am Ende zurückgegeben wird
	Bestelldatum = ''#Leer vorbelegen, falls kein Bestelldatum in den Daten gefunden wird.
	for line in bestellung.split('\n'):
		line = line.strip(' \t\r\n') #Überflüssigen Whitespace weglassen
		#Start of parts listing
		if line == '________________________________________________________________________________':
			if getmore == True:
				getlines = True #Start parsing lines
				if debug: print 'PARSING STARTED'
		#End of parts listing
		elif line == u'':
			if getlines == True:
				getlines = False #Stop parsing lines after empty line at the end of the list
				getmore = False
				if debug: print 'PARSING STOPPED'
		elif line.startswith('Bestelldatum                  : '):
			Bestelldatum = line[32:].strip(' ')
		else:
			if getlines == True:
				if debug: print line
				try:
					Artikelnummer = line[0:16].strip(' ')
					Bezeichnung = line[16:54].strip(' ')
					Anzahl = float(line[54:60].strip(' '))
					Gesamtpreis = float(line[60:76].strip(' ').replace(',', '.'))
					Waehrung = line[76:].strip(' ')
					if debug: print Artikelnummer, '|', Bezeichnung, '|', Anzahl, '|', Gesamtpreis, '|', Waehrung
					assert Waehrung == 'Euro'
					Teile.append(mapper(Bestelldatum, Artikelnummer, Bezeichnung, Anzahl, Gesamtpreis, Waehrung))
				except:
					Fehlerliste.append('Parse error: ' + line)
					OK = False
	if len(Bestelldatum) == 0:
		Fehlerliste.append('Kein Bestelldatum gefunden.') # Alle Daten der Bestellung übernommen? Bestellformat geändert?
	if getmore == True:
		Fehlerliste.append('Keine Teileliste gefunden.') # Alle Daten der Bestellung übernommen? Bestellformat geändert?
	if getlines == True:
		Fehlerliste.append('Teileliste nicht ordnungsgemäß abgeschlossen.') # Alle Daten der Bestellung übernommen?
	if len(Teile) == 0:
		Fehlerliste.append('Keine bestellten Teile gefunden.') # Alle Daten der Bestellung übernommen? Bestellformat geändert?
	return OK, Teile, Fehlerliste

if __name__ == '__main__':
	print 'Parsen von Testdaten...'
	OK, Teile, Fehlerliste = parse(Bestellung)
	print 'Status:', 'OK' if OK == True else 'Fehler aufgetreten, siehe Fehlerliste!' # Status signalisieren
	print 'Bestellte Teile:'
	for teil in Teile:
		print teil
	print 'Fehlerliste:', Fehlerliste # Fehler auflisten. Sollte also normalerweise leer sein.
