Belegdatum in verschiedenen Formaten ermitteln (Teil II)

Guten Morgen zusammen.

Vorab vielen Dank an ‘cgrunenberg’ für den entscheidenden Hinweis im Post ‘[url]Belegdatum aus PDF+Text ermitteln und anzeigen]’, dass es sich bei dem Search Pattern nicht um RegEx sondern um Grep handeln muss.

Nun denn - es hat zwar etwas gedauert, aber ich bin jetzt in der Lage einen Beleg nach dem Belegdatum zu durchsuchen und formatiert nach JJJJ-MM-TT anzeigen zu lassen. In meinem eigenen Workflow wird das gefundene Belegdatum dann vor den Belegnamen gesetzt und in das Erstellungsdatum des Beleges geschrieben. Der neue Belegname sieht in DEVONthink dann etwa wie folgt aus.

[2012-04-01] Speditionsrechnung #123987

Das folgende Skript funktioniert in über 99 % der Fälle. Das Belegdatum darf dabei in folgenden Formaten vorliegen.

1.4.12
01.04.2012

  1. April 12
  2. April 2012
  3. Apr 12
  4. Apr 2012

Ich bin zwar ein Applescript Newbie, aber die Geschwindigkeit mit der das Skript aktuell arbeitet liegt bei max. 1 Sekunde pro Beleg und ist zumindest für meine Belange mehr als ausreichend. Sollte es von euch noch jemand weiter optimieren können, so ist er herzlich eingeladen - ich würde mich freuen und bin für jeden Trick dankbar.

Allerdings haben sich im Laufe dieses Projektes bei mir zwei Fragen ergeben, zu denen ich bitte eure Hilfe brauche.

Frage 1:
Ich vermute mal die Frage kann, wenn überhaupt nur ABBYY oder DEVON Technologies beantworten.
Nehmen wir an, ich habe eine normale Standardrechnung, mit Kopf- und Fußtext, bei der das Belegdatum, als erstes Datum im oberen Drittel rechts am Rand steht. So wie man das allgemein kennt. Mein Script sucht jetzt nach dem ersten Wert auf dem das Search Pattern zutrifft und liefert mir eine Kontonummer (12 1223 1222) aus dem Fußtext. Im Applescript Event sehe ich, das direkt nach der Kontonummer das Datum gefunden wurde. Bei genauerer Analyse des Events zeigt sich, das der PDF Text den Originalbeleg nicht 1:1 wiedergibt sondern der erkannte Text durcheinander steht. Kurzgefasst - die Kontonummer steht vor dem Datum und wird deshalb richtigerweise als erstes gefunden. Warum geht die OCR nicht zeilenweise vor?

Frage 2:
Die ergibt sich zwangsweise aus Frage 1, da man vermutlich nicht in der Lage sein wird die Reihenfolge mit der die OCR vorgeht zu beinflussen und somit Frage 1 zu lösen.

Das Search Pattern findet aktuell auch Leerzeichen, da es die unterschiedliche Schreibweise eines Datums im Deutschen erfordert. Was aber alle obigen Datumsbeispiele gemeinsam haben, ist der Punkt nach dem Tageswert. Dieser ist in jeder Schreibweise immer vorhanden. Mal ist dieser an der zweiten Stelle zu finden, mal an der dritten. Je nach Schreibweise folgt ggf. noch ein Leerzeichen. Kann mir bitte jemand sagen wie ich mein Pattern


set mypattern to "[0-9]{1,2}([.]|. )([0-9]{1,2}|Januar|Jan|Februar|Feb|März|Mrz|April|Apr|Mai|Juni|Jun|Juli|Jul|August|Aug|September|Sep|Oktober|Okt|November|Nov|Dezember|Dez)([.]| )[0-9]{2,4}"

dahingehend abändere, das entweder an der zweiten oder an der dritten Stelle des Ergebnisses immer ein Punkt erwartet wird. Es würden dann nur noch vermeintliche Datumswerte in der Form

‘1.’ oder ‘10.’ jeweils ohne Leerzeichen
‘1.’ oder '10. ’ jeweils mit Leerzeichen

gefunden und keine durch ein Leerzeichen getrennte Zahlenfolgen mehr. Das würde dann automatisch das Problem der falschen Ergebnisse wie der Kontonummer oder ausländischen Telefonnummern erledigen.

Ich hoffe das Skript ist für einige von euch so hilfreich wie für mich.

Efty


-- Script:  Belegdatum ermitteln
-- Version: 1.01
-- Code:    Efty Edge
-- Datum:   4.4.2012

global myDate

-- zu suchende Datumsformate definieren
set mypattern to "[0-9]{1,2}([.]|. )([0-9]{1,2}|Januar|Jan|Februar|Feb|März|Mrz|April|Apr|Mai|Juni|Jun|Juli|Jul|August|Aug|September|Sep|Oktober|Okt|November|Nov|Dezember|Dez)([.]| )[0-9]{2,4}"

tell application id "com.devon-technologies.thinkpro2"
	
	try
		set theselection to the selection
		if theselection is {} then error "Bitte mindestens einen Beleg auswählen."
		repeat with theRecord in theselection
			set rcdtext to the plain text of theRecord
			set thisRecord to the name of theRecord
			set mycat to rcdtext as text
			set myDate to "XXXX-XX-XX"
			
			try
				-- PDF Text nach der Logik des 'mypattern durchsuchen
				do shell script "echo " & quoted form of mycat & " | grep -E -o -e " & quoted form of mypattern
				set myDate to first paragraph of result as text
				
			end try
			
			-- Gefundenen Datums-Strings zerlegen
			if (count of myDate) = 6 and (count of myDate) = 11 then
				
				-- Ergebnis an 'text'-String übergeben
				set od to text item delimiters of AppleScript
				set text item delimiters of AppleScript to "."
				
				-- String zerlegen und an Variablen übergeben
				set myDay to text item 1 of myDate
				set myMonth to text item 2 of myDate
				set myYear to text item 3 of myDate
				
				-- Einstellige Tage in Tage mit führender Null umwandeln
				if myDay is in {"1", "2", "3", "4", "5", "6", "7", "8", "9"} then
					set myDay to "0" & myDay
				end if
				
				-- Einstellige Monate in Monate mit führender Null umwandeln
				if myMonth is in {"1", "2", "3", "4", "5", "6", "7", "8", "9"} then
					set myMonth to "0" & myMonth
				end if
				
				--Zweistellige Jahreszahl in vierstellige Jahreszahl umwandeln
				if (count of myYear) is 2 then
					set myYear to "20" & myYear
				end if
				
			else
				
				set AppleScript's text item delimiters to space
				set myDate to the text items of myDate
				set AppleScript's text item delimiters to {""}
				-- return these_items
				
				set myDay to text item 1 of myDate
				set myMonth to text item 2 of myDate
				set myYear to text item 3 of myDate
				
				-- Einstellige Tage in Tage mit führender Null umwandeln
				set myDayLength to count of myDay
				set myDay to texts 1 thru (myDayLength - 1) of myDay
				if myDay is in {"1", "2", "3", "4", "5", "6", "7", "8", "9"} then
					set myDay to "0" & myDay
				end if
				
				--Monatsnamen in Monatszahl umwandeln
				if myMonth = "Januar" or myMonth = "Jan" then set myMonth to "01"
				if myMonth = "Februar" or myMonth = "Feb" then set myMonth to "02"
				if myMonth = "März" or myMonth = "Mrz" then set myMonth to "03"
				if myMonth = "April" or myMonth = "Apr" then set myMonth to "04"
				if myMonth = "Mai" then set myMonth to "05"
				if myMonth = "Juni" or myMonth = "Jun" then set myMonth to "06"
				if myMonth = "Juli" or myMonth = "Jul" then set myMonth to "07"
				if myMonth = "August" or myMonth = "Aug" then set myMonth to "08"
				if myMonth = "September" or myMonth = "Sep" then set myMonth to "09"
				if myMonth = "Oktober" or myMonth = "Okt" then set myMonth to "10"
				if myMonth = "November" or myMonth = "Nov" then set myMonth to "11"
				if myMonth = "Dezember" or myMonth = "Dez" then set myMonth to "12"
				
				--Zweistellige Jahreszahl in vierstellige Jahreszahl umwandeln
				if (count of myYear) is 2 then
					set myYear to "20" & myYear
				end if
				
			end if
			
			--Gefundenes Datum formatiert ausgeben
			display dialog myYear & "-" & myMonth & "-" & myDay
			
		end repeat
		
	end try
end tell


Bei dieser Frage bin ich leider überfragt, die OCR-Engine ist auch für uns mehr oder weniger nur eine Blackbox.

@cgrunenberg

Danke für die Info. Das habe ich mir schon gedacht. Allerdings ist es doch merkwürdig, das nicht jeder PDF Text eines Beleges durcheinander ist sondern die Masse eigentlich chronologisch zum Beleg abgebildet wird.


Ich habe das gestrige Skript dahingehend verfeinert, dass beim GREP immer darauf geachtet wird, dass an der 2ten oder 3ten Stelle des Suchergebnisses immer ein Punkt steht. Das erreicht man einfach in dem man im Pattern ([.]|. ) in [.] ? ändert.

Ferner prüfe ich zur Unterscheidung ob ein Monatsname im Datum verwendet wurde nicht mehr auf die Stringlänge sonder darauf, ob an der 3ten oder 4ten Stelle des Strings ein Leerzeichen sitzt.

Das von GREP gefundene Datum wird unverändert in einer InputBox angezeigt und kann bei Bedarf vor der Verarbeitung geändert werden.

Die ersten beiden Änderungen zusammen sorgen jetzt auch bei meinen Problembelegen für ein 100 %iges Ergebnis. Die Möglichkeit des manuellen Eingriffs tut sein übriges.

Hier also das neue Skript: Das Ergebnis wird hier auch nur angezeigt die Änderung des Namens und des Erstellt- sowie Geändert-Datums habe ich auskommentiert. Wobei letztere beiden aus unerfindlichen Gründen nicht mehr funktionieren.

-- Script: Belegdatum in einem Buchungssbeleg ermitteln
-- Version: 1.02
-- Code: Efty Edge
-- Datum: 5.4.2012


-- Zu suchendes Datumsformat definieren (aktuell: d.m.yyyy)
set mypattern to "[0-9]{1,2}[.] ?([0-9]{1,2}|Januar|Jan|Februar|Feb|März|Mrz|April|Apr|Mai|Juni|Jun|Juli|Jul|August|Aug|September|Sep|Oktober|Okt|November|Nov|Dezember|Dez)([.]| )[0-9]{2,4}"

tell application id "com.devon-technologies.thinkpro2"
	
	try
		set theselection to the selection
		if theselection is {} then error "Bitte mindestens einen Beleg auswählen."
		repeat with theRecord in theselection
			set rcdtext to the plain text of theRecord
			set thisRecord to the name of theRecord
			set mycat to rcdtext as text
			
			try
				-- PDF Text nach der Logik des 'mypattern durchsuchen
				do shell script "echo " & quoted form of mycat & " | grep -E -o -e " & quoted form of mypattern
				set myDate to first paragraph of result as text
				
			end try
			
			--Gefundenen Datums-String anzeigen und ggf ändern
			set myDate to text returned of (display dialog "Bitte Belegdatum überprüfen:" default answer myDate)
			
			--Prüfung auf ausgeschriebenen Monatsnamen anhand der Länge des Monatsstrings
			set od to text item delimiters of AppleScript
			set text item delimiters of AppleScript to "."
			set myCheck to text item 2 of myDate
			set myCheck to count of myCheck
			
			
			-- Datums-Strings in Tag, Monat und Jahr zerlegen
			if myCheck = 2 then
				
				-- Ergebnis an 'text'-String übergeben
				set od to text item delimiters of AppleScript
				set text item delimiters of AppleScript to "."
				
				-- String zerlegen und an Variablen übergeben
				set myDay to text item 1 of myDate
				set myMonth to text item 2 of myDate
				set myYear to text item 3 of myDate
				
				-- Einstellige Tage in Tage mit führender Null umwandeln
				if myDay is in {"1", "2", "3", "4", "5", "6", "7", "8", "9"} then
					set myDay to "0" & myDay
				end if
				
				-- Einstellige Monate in Monate mit führender Null umwandeln
				if myMonth is in {"1", "2", "3", "4", "5", "6", "7", "8", "9"} then
					set myMonth to "0" & myMonth
				end if
				
				--Zweistellige Jahre in vierstellige Jahre umwandeln
				if (count of myYear) is 2 then
					set myYear to "20" & myYear
				end if
				
			else
				
				set AppleScript's text item delimiters to space
				set myDateLong to the text items of myDate
				set AppleScript's text item delimiters to {""}
				-- return these_items
				
				set myDay to text item 1 of myDateLong
				set myMonth to text item 2 of myDateLong
				set myYear to text item 3 of myDateLong
				
				-- Einstellige Tage in Tage mit führender Null umwandeln
				set myDayLength to count of myDay
				set myDay to texts 1 thru (myDayLength - 1) of myDay
				if myDay is in {"1", "2", "3", "4", "5", "6", "7", "8", "9"} then
					set myDay to "0" & myDay
				end if
				
				--Monatsnamen in Monate umwandeln
				if myMonth = "Januar" or myMonth = "Jan" then set myMonth to "01"
				if myMonth = "Februar" or myMonth = "Feb" then set myMonth to "02"
				if myMonth = "März" or myMonth = "Mrz" then set myMonth to "03"
				if myMonth = "April" or myMonth = "Apr" then set myMonth to "04"
				if myMonth = "Mai" then set myMonth to "05"
				if myMonth = "Juni" or myMonth = "Jun" then set myMonth to "06"
				if myMonth = "Juli" or myMonth = "Jul" then set myMonth to "07"
				if myMonth = "August" or myMonth = "Aug" then set myMonth to "08"
				if myMonth = "September" or myMonth = "Sep" then set myMonth to "09"
				if myMonth = "Oktober" or myMonth = "Okt" then set myMonth to "10"
				if myMonth = "November" or myMonth = "Nov" then set myMonth to "11"
				if myMonth = "Dezember" or myMonth = "Dez" then set myMonth to "12"
				
				--Zweistellige Jahre in vierstellige Jahre umwandeln
				if (count of myYear) is 2 then
					set myYear to "20" & myYear
				end if
				
			end if

			--Datum anzeigen
			display dialog "[" & myYear & "-" & myMonth & "-" & myDay & "]"
			
			--Name des Datensatzes ändern
			--set the name of theRecord to "[" & myYear & "-" & myMonth & "-" & myDay & "] " & myName
			
			--Erstellt-Datum und Geändert-Datum ändern
			--set the creation date of theRecord to myDay & "." & myMonth & "." & myYear
			--set the modification date of theRecord to myDay & "." & myMonth & "." & myYear
			
		end repeat
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink Pro" message error_message as warning
		
	end try
end tell

@cgrunenberg
Haben Sie vielleicht einen Tipp für mich warum ich z. B. das Erstelltdatum nicht mehr ändern kann!?

Ich vermute es liegt an der falschen Deklaration der verwendeten Variablen, aber ich bin an der Stelle wo die Änderung durchgeführt wird

--Erstellt-Datum und Geändert-Datum ändern
			set the creation date of theRecord to myDay & "." & myMonth & "." & myYear

nicht in der Lage eine ‘text’ Variable als ‘date’ zu deklarieren. Der Appleskript Editor meldet mir dabei an dieser Stelle immer einen Fehler. Was mache ich bitte falsch, bzw. wie muss das Skript geändert werden damit ich das Datum ändern kann?

Danke mal wieder.
Efty

Als Eingabe wird nur ein Datum, aber kein Text akzeptiert. Folgendes sollte aber klappen:


set theDate to date (myDay & "." & myMonth & "." & myYear)
set the creation date of theRecord to theDate

Hy,

ich bekomme leider jetzt die Fehlermeldung “Variable myDate ist nicht definiert”.

Folgendes habe ich in der Applescript-Konsole gefunden:

| grep -E -o -e ‘[0-9]{1,2}[.] ?([0-9]{1,2}|Januar|Jan|Februar|Feb|März|Mrz|April|Apr|Mai|Juni|Jun|Juli|Jul|August|Aug|September|Sep|Oktober|Okt|November|Nov|Dezember|Dez)([.]| )[0-9]{2,4}’"
–> error “Der Befehl wurde mit einem Ergebnis ungleich Null beendet.” number 1

.
Hat vorher die Analyse eines Belegs funktioniert, dann wird auch der oben genannte “akzeptiert” Fehler tritt in der Konsole immer noch auf aber der Name des Beleges wird geändert. Ich habe es etwas entschärft in dem ich die Variable myDate auf 01.01.2012 gesetzt habe.

nE2n