#!/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.
#- Datumsformat in Python-Datentype date parsen.

debug = False

#Testdaten, wie sie in einer Bestellbestätigung per E-Mail erscheinen.
Bestellung = u"""Menting Mikroelektrik
------------------------------------------------------
Bestellnummer: 12345
Detailierte Bestellübersicht:
------------------------------------------------------
Telefonnr.: 0123/4567890
------------------------------------------------------
E-Mail: user@domain.tld
------------------------------------------------------
Bestelldatum: Dienstag, 08. Juni 2010

Artikel
------------------------------------------------------
1 x Speicherdrossel 150µH 6A (T27/013/1) = 3,86 EUR
5 x Speicherdrossel 300µ 3A, liegend, Cool Mµ (T27/018/1) = 17,45 EUR
------------------------------------------------------
 (Versand [ DE ]): 5,95 EUR
Zwischensumme: 21,31 EUR
Summe: 27,26 EUR
enthaltene MwSt. 19%: 4,35 EUR

Lieferanschrift
------------------------------------------------------
Vorname Name 
Straße 1
12345 Stadt
Land

Rechnungsanschrift
------------------------------------------------------
Vorname Name 
Straße 1
13245 Stadt
Land

Zahlungsweise
------------------------------------------------------
PayPal (auch Kreditkarten)
"""

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

Lieferant = 'Menting Mikroelektrik'
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):
	state = 0        #Für einfache State-Machine, die genau einmal anfängt, die Zeilen mit den Teilen zu parsen, 
	                 #  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 == 'Artikel': # Erst die Ankündigung der Liste
			state = 1
		elif line == '------------------------------------------------------': # Jetzt kommt sie
			if state == 1:
				state = 2
				if debug: print 'PARSING STARTED'
			elif state == 2: # Abschluss der Liste
				state = 3
				if debug: print 'PARSING STOPPED'

		elif line.startswith('Bestelldatum: '):
			Bestelldatum = line[14:].strip(' ')
			if debug: print 'Bestelldatum:', Bestelldatum
		else:
			if state == 2: #Parsen!
				if debug: print line
				if debug: print 'Länge:', len(line)
				try:
					#Bestelldatum, Artikelnummer, Bezeichnung, Anzahl, Gesamtpreis, Waehrung
					
					#Beispielzeile:
					#1 x Speicherdrossel 150µH 6A (T27/013/1) = 3,86 EUR
					
					pos_x = line.find('x')
					if debug: print 'pos_x:', pos_x
					Anzahl = float(line[0:pos_x].strip(' '))
					if debug: print 'Anzahl:', Anzahl
					
					assert line.endswith(' EUR')
					Waehrung = 'EUR'
					if debug: print 'Währung:', Waehrung
					
					for pos_eq in range(len(line)):
						if line[-pos_eq] == '=':
							break
					pos_eq = len(line)-pos_eq #Position des =
					if debug: print 'pos_eq:', pos_eq
					
					Bezeichnung = line[pos_x+1:pos_eq].strip(' ')
					if ('(' in Bezeichnung) and (')' in Bezeichnung):
						Artikelnummer = Bezeichnung[Bezeichnung.find('(')+1:Bezeichnung.find(')')].strip(' ')
						Bezeichnung = Bezeichnung[:Bezeichnung.find('(')].strip(' ')
					else: #Da ich nicht ganz sicher bin, ob die Referenz IMMER angegeben wird, hier ein alternativer Pfad für den Fall, dass nicht.
						Artikelnummer = ''
					if debug:
						print 'Artikelnummer:', Artikelnummer
						print 'Bezeichnung:', Bezeichnung

					Gesamtpreis = float(line[pos_eq+1:-4].strip(' ').replace(',', '.'))
					if debug: print 'Gesamtpreis:', Gesamtpreis

					if debug: print Artikelnummer, '|', Bezeichnung, '|', Anzahl, '|', Gesamtpreis, '|', Waehrung
					Teile.append(mapper(Bestelldatum, Artikelnummer, Bezeichnung, Anzahl, Gesamtpreis, Waehrung))
				except:
					Fehlerliste.append('Parse error: ' + line)

	if len(Bestelldatum) == 0:
		Fehlerliste.append('Kein Bestelldatum gefunden.') # Alle Daten der Bestellung übernommen? Bestellformat geändert?
	if state < 2:
		Fehlerliste.append('Keine Teileliste gefunden.') # Alle Daten der Bestellung übernommen? Bestellformat geändert?
	if state == 2:
		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 len(Fehlerliste) == 0, 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:' # Fehler auflisten. Sollte also normalerweise leer sein.
	for f in Fehlerliste:
		print f
