Beispiel Entity-Tagger

Wie im Vorfeld bereits beschrieben, können Inhalte von Dokumenten für die automatische Zuordnung von „Title“ (Dokument-Name), „KeyWords“ und „DocumentDate“ (Dokumenten-Datum) verwendet werden.

Das Modul soll in einem Dokument nach dem Text „Vodafone“ suchen, in die Keywords dann „Vodafone“ eintragen und dann abschließend den Tag „Vodafone“ zuweisen.

Das Beispiel beruht auf Eclipse (der Autor nutzt seit Jahren Eclipse und ist damit zufrieden).

Für die Umsetzung werden die Jar-File „dmsapi.jar“ und „dms.jar“ benötigt.

Um das Modul dem System bekannt zu machen, müssen einige Annotation’s bei der Deklaration gesetzten werden:

AnnotationVerwendung (Target)Beschreibung
@ProcessNameTYPEName des Moduls z.B.
@ProcessName(value = „Vodafone Tagger“)
@ProcessAuthorTYPEName des Autors, die Webseite, die Major und Minor-Version z.B. @ProcessAuthor(author = „mlange“, url = „www.biffo.de“, major = 0, minor = 1 )
@DescriptionTYPEEine Beschreibung des Moduls z.B.
@Description(„Sucht nach dem Text Vodafone“)
@ConfiguredTagFIELDWie soll der zugeordnete Tag aussehen z.B.
@ConfiguredTag(tagvalue=“Vodafone“, bgcolor=“#ff2020″, fgcolor=“#ffffff“)

Der Definitionsblock würde dann wie folgt umgesetzt:

Die einzig wirklich relevanten Funktionen in dem Beispiel sind:

FunktionBeschreibung
addTagTags werden vor Ausführung mit den bereits existierenden Tags („Menü->Tags“) abgeglichen und an die Funktion zur Verwendung zugewiesen. Bei einem erfolgreichen Erkennen der Funktion doText erfolgt dann die Zuordnung des definierten Tags über getTags.
doTextHauptroutine zur Bestimmung der Inhalte. Bei Ausführung werden vom System nur die erste Seite oder alle Dokument-Seiten übergeben. In diesem Beispiel wird gezielt nach dem Fragment „Vodafone“ gesucht. Bei einem Treffer erfolgt die Rückgabe als „True“ ansonsten „False“.
getKeyWordsBeinhaltet die KeyWords, welche der Entität Dokument zugeordnet werden sollen
getTagssiehe addTag

Das beschriebene Modul könnte daher wie folgt implementiert werden:

package de.biffo.tagger;

import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.biffo.dms.api.ConfiguredTag;
import org.biffo.dms.api.Description;
import org.biffo.dms.api.IDocumentEntity;
import org.biffo.dms.api.ProcessAuthor;
import org.biffo.dms.api.ProcessName;
import org.biffo.dms.entity.Fragments.Fragment;
import org.biffo.dms.entity.Tags.Tag;

@ProcessName(value = "Vodafone Tagger")
@ProcessAuthor(author = "mlange", url = "www.biffo.de", major = 0, minor = 1)
@Description("Sucht nach dem Text Vodafone")
public class vodafonetagger implements IDocumentEntity, Serializable {

	private static final long serialVersionUID = 1L;

	@ConfiguredTag(tagvalue="Vodafone", bgcolor="#ff2020", fgcolor="#ffffff")
	public Tag tVodafone;
	
	private String keyWords;
	private List<Tag> lstTags = new LinkedList<Tag>();
	
	@Override
	public void addTag(Tag tagToAdd) {
		lstTags.add(tagToAdd);
	}

	@Override
	public boolean doText(String inputText) {
		if(inputText==null) return false;
		if(inputText.length()==0) return false;
		
		if(inputText.toLowerCase().contains("vodafone")) {
			keyWords ="Vodafone";
			return true;
		}
		
		return false;
	}

	@Override
	public Date getDocumentDatum() {
		return null;
	}

	@Override
	public List<Fragment> getFragments() {
		return null;
	}

	@Override
	public String getKeyWords() {
		return keyWords;
	}

	@Override
	public double getScore() {
		return -1;
	}

	@Override
	public List<Tag> getTags() {
		return lstTags;
	}

	@Override
	public String getTitle() {
		return null;
	}

	@Override
	public boolean hasFragments() {
		return false;
	}

	@Override
	public void init() throws Exception {
	}

}

Nachdem das Modul als JAR exportiert wurde, kann es über den Modul-Upload dem System bekannt gemacht und genutzt werden.

Das Modul lässt sich nach einem Test (siehe Moduls / Test Modul) auf ausgewählte Dokumente, innerhalb der Dokumentansicht, oder in einer Import-Routine nutzen.

  1. Drag&Drop „de.biffo.tagger.vodafonetagger“ auf ein Dokument.

2. Modul startet auf dem jeweiligen Dokument.

3. Nach erfolgreicher Erkennung des Texts „Vodafone“

Bereitgestellte Funktionen von „IDocumentEntity“ und deren Nutzung:

addTag

Beim Starten des Moduls z.B. mittels Import-Prozess, werden Tags die mittels @ConfigureTag dem Modul zugeordnet wurden, innerhalb der Tabelle dms_tags ermittelt und bei erfolgreichem Abschluss der „doText“ Routine dem Dokument zugeordnet. Sollte die Farbgebung innerhalb der Applikation geändert worden sein, so werden FG- und BG-Farben aus dem System herangezogen. Die Farbgebung innerhalb @ConfigureTag hat keinen Einfluss mehr.

EingabeRückgabe
Tagvoid

doText

Routine zur Überprüfung der einzelnen Seitentexte des jeweiligen Dokumentes, auf den das Modul angewendet wurde. Das Modul kann für die Verwendung Aller oder nur der ersten Seite in den Moduleinstellungen konfiguriert werden.

Bei Rückgabe von „true“ werden die Funktionen „getDocumentDatum“, „getKeyWords“ und „getTitle“ auf das gerade im Zugriff befindliche Dokument angewendet und die Entität entsp. abgeändert. Ferner wird die Routine „getTags“ und „getFragments“ ausgeführt und im Bedarfsfalls die Werte übermittelt.

EingabeRückgabe
Stringboolean

getDocumentDatum

Liefert ein dem Dokument zuzuordnendes Datum (Dokumenten Datum). Falls Rückgabe „null“ wird der Datumswert nicht geändert.

EingabeRückgabe
NoneDate

getFragments

Liefert eine Liste mit generierten Fragmenten, die während des Aufrufs der „doText“ Funktion erstellt wurden. Die generierten Fragmente werden vor Speicherung auf ein mögliches Vorhandensein überprüft und ggf. mit bestehende Fragmente ersetzt und mit dem Dokument verknüpft.

EingabeRückgabe
NoneList<Fragment>

getKeyWords

Liefert dem Dokument zuzuordnende Keywords. Bei der Rückgabe von „null“ werden die KeyWords nicht verändert.

EingabeRückgabe
NoneString

getScore

Liefert einen %-Wert des gefundenen Ergebnisses.

Derzeit noch keine Verwendung.

EingabeRückgabe
Nonedouble

getTags

Liste mit Tags, welche dem Dokumente im Falle eines positiven „doText“ zugeordnet werden solle. Die Liste wird automatisch vor Ausführung.

EingabeRückgabe
NoneList<Tag>

getTitle

Liefert ein Textwert, der dem Dokument als Dokument-Name zugeordnet werden soll. Falls Rückgabe „null“ wird der Name nicht geändert.

EingabeRückgabe
NoneString

hasFragments

Positive Rückgabe, falls Fragmente dem Ergebnis der „doText“ Funktion zugeordnet werden können. Falls Fragment aufgebaut wurden und die Rückgabe „false“ liefert, erfolgt keine Verarbeitung der Fragmente.

EingabeRückgabe
Noneboolean

init

Wird beim Laden des Moduls in den Hauptspeicher ausgeführt. Hierüber können z.B. dynamisch Open-NLP Modelle geladen werden.

EingabeRückgabe
NoneNone

Beispiel Metro-Tagger

Der Metro-Tagger sucht nach dem vorher bestimmten Muster „25.04.2020/048/0/0/0004/021615“ innerhalb der übergebenen Texte. Das verwendete NLP-Modell wurde anhand von 6 vorhandenen und eingescannten Metro-Rechnungen trainiert und dem Tagger innerhalb des JAR-Files mitgegeben (siehe „init()“). Der Code ist im Vergleich zu dem Vodafone-Tagger etwas umfangreicher, dennoch entfällt die aufwendige Logik mittels RegEx und anschließenden Parsing.

@ProcessName(value = "Metro Tagger")
@ProcessAuthor(author = "mlange", url = "www.biffo.de", major = 0, minor = 5)
@Description("Sucht nach einem bestimmten String innerhalb einer Metro Rechnung "
		+ "und liefert entsp. die gewünschten Informationen zurück")
public class MetroTagger implements IDocumentEntity, Serializable {
 
	private static final long serialVersionUID = 1L;

	//  25.04.2020/048/0/0/0004/021615
	
	@ConfiguredTag(tagvalue = "METRO", bgcolor = "#394d99", fgcolor = "#f8ed07")
	public Tag tag1; 

	// Datumsformat für den Dokument-Name
	private final static SimpleDateFormat _sdf = new SimpleDateFormat("dd.MM.yyyy");
	
	// Aufbau des Dokumenten-Namens
	private String docTitlePattern="METRO_RECHNUNG_<DocumentDate>";
	private Date documentDatum;
	private String keyWords;
	private List<Tag> lstTags = new LinkedList<Tag>();
	private List<Fragment> lstFragments = new LinkedList<Fragment>();
	
	// Open-NLP Routinen
	private transient TokenNameFinderModel _model;
	private transient NameFinderME _nameDetector;
	
	private double score;
	
	public MetroTagger() {
	}
	
	@Override
	public void init() throws Exception {
		// Laden des NLP-Models direkt aus dem JAR-File
		String packageName = this.getClass().getPackageName();
		packageName = packageName.replaceAll("\\.", "/");
		InputStream input = this.getClass().getClassLoader().getResourceAsStream(packageName+"/metro_NAIVEBAYES.bin");

		// Initialisierung der Open-NLP Funktionen
		_model = new TokenNameFinderModel(input);
		_nameDetector = new NameFinderME(_model);
	}
	
	
	@Override
	public String getTitle() {
		String temp = docTitlePattern;
		if(documentDatum!=null) {
			temp = temp.replaceAll("<DocumentDate>", _sdf.format(getDocumentDatum()));
		}
		return temp;
	}

	@Override
	public Date getDocumentDatum() {
		return documentDatum;
	}

	@Override
	public String getKeyWords() {
		return keyWords;
	}
	
	@Override
	public double getScore() {
		return score;
	}

	@Override
	public boolean doText(String input) {
		boolean found=false;
		try {
			clearAll();
			String contentIn = input;
			
			// Übergabe des Seitentextes an die Open-NLP Funktion
			String [] sentence = WhitespaceTokenizer.INSTANCE.tokenize(contentIn);
			Span nameSpans[] = _nameDetector.find(sentence);
	
			// Falls ein Fragment erkannt wurde, wird dieses weiterverabeitet
			if(null!=nameSpans && nameSpans.length>0) {
				Span nameSpan = nameSpans[0];
				score = nameSpan.getProb();
				int fromWord =nameSpan.getStart();
				int toWord=nameSpan.getEnd();
		
				StringBuilder sb = new StringBuilder();
				for(int i=fromWord;i<toWord;i++) {
					sb.append(sentence[i]);
					sb.append(" ");
				}
				
				// Überprüfung ob das gefundene Fragment auch den Aufbau einer Metro-Rechnungsnummer hat
				found= parseTag(sb.toString(), nameSpan);
			}
			_nameDetector.clearAdaptiveData();
		}catch(Exception e) {
		}
		return found;
	}
	
	

	
	
	private boolean parseTag(String strIn, Span nameSpan) {
		if(null==strIn || strIn.isEmpty()) return false;
		
		
		String temp = strIn.trim();
		temp = temp.replaceAll(" ", "");
		
		Fragment fragment = new Fragment();
		fragment.setErstelltVon(this.getClass().getSimpleName());
		fragment.setGeaendertVon(fragment.getErstelltVon());
		
		fragment.setFragmentname(nameSpan.getType());
		fragment.setFragmentvalue(strIn);
		fragment.setValid(true);
		fragment.setFragmenttype("IDocumentFragments");
		fragment.setProbability(nameSpan.getProb());
		fragment.setPage(-1);
		lstFragments.add(fragment);

		// Pattern dd.MM.yyyy/000/0/0/0000/000000
		String strArr[] = temp.split("\\/");
		if(strArr.length!=6) return false;

		try {
			documentDatum = _sdf.parse(strArr[0]);
			return true;
		}catch(Exception e) {
		}
		return false;
	}
	
	private void clearAll() {
		keyWords = null;
		documentDatum = null;
		score = 0;
		lstFragments.clear();
	}

	@Override
	public List<Tag> getTags() {
		return lstTags;
	}

	@Override
	public void addTag(Tag tagToAdd) {
		lstTags.add(tagToAdd);
	}

	@Override
	public boolean hasFragments() {
		if(lstFragments.size()>0) return true;
		return false;
	}

	@Override
	public List<Fragment> getFragments() {
		return lstFragments;
	}
}