Indexing-Services
Einleitung
Ein Indexing-Service ist eine spezielle SmartIndexing-Regel, die die Verarbeitung des Textes an ein eigenes, in Python 3.6 programmiertes Plugin weiterleitet.
Die Python-Module müssen im Unterverzeichnis customisation\indexingservices des
Installations-Verzeichnisses abgelegt werden und die Endung .py haben. Wenn docs365 documents
z.B. in C:\Programme\PHOENIX_Documents installiert ist, befindet sich der Indexing-Service
mymodule in der Datei
C:\Programme\PHOENIX_Documents\customisation\indexingservices\mymodule.py.
Jeder Indexing-Service kann durch eine Konfigurationsdatei weiter angepasst werden. Wenn sich neben
der Datei mymodule.py noch eine INI-Datei mymodule.ini befindet, wird diese automatisch
eingelesen und im Python-Modul bereitgestellt.
Damit ein Indexing-Service in der Applikation ausgewählt und verwendet werden kann, muss er über
eine eigene Regel in der smartindexing.xml registiert werden:
<option id="mymodule" name="Mein Indexing-Service">
<rule>service: "mymodule", group=1</rule>
</option>
Das Attribut id muss in der smartindexing.xml eindeutig sein und sollte nicht mehr verändert
werden, sobald diese Regel in mindestens einem Archiv ausgewählt ist. name ist der Name, der im
SmartIndexing-Auswahldialog angezeigt wird.
Die Regel muss vom Typ service sein und benötigt als ersten Parameter den Namen des Moduls (d.h.
den Namen der Python-Datei ohne die Erweiterung .py). String-Parameter müssen in doppelten
Anführungszeichen stehen.
Optional und durch Kommas getrennt folgen Parameter, die an den Indexing-Service durchgereicht werden.
Aufbau eines Indexing-Services
Das folgende Beispiel ist ein minimaler Indexing-Service.
Das Python-Modul muss eine Klasse mit dem Namen IndexingService enthalten, die eine Methode
process enthält:
import regex
class IndexingService:
"""
IndexingService, der die erste im Text vorkommende Zahl zurückgibt.
"""
def process(self, **kwargs):
text = self.get_text()
match = regex.search(r'(\d+)', text)
if match:
return [match.group(1)]
Die Methode process muss eine Liste von Ergebnissen zurückgeben, wobei einzelne Ergebnisse als
umso wichtiger angesehen werden, je weiter vorne sie in der Liste stehen. Wenn es keine Ergebnisse
gibt, kann die Methode anstelle eine leeren Liste auch None zurückgeben.
In **kwargs sind die zusätzlichen Parameter der smartindexing.xml enthalten, in der obigen
Beispielkonfiguration also der Parameter group mit dem Wert 1.
Die Klasse stellt mehrere Methoden bereit, um an die zu verarbeitenden Daten zu gelangen:
self.get_file()Der absolute Pfad der zu verarbeitenden Datei, die sich normalerweise in einem temporären Verzeichnis befindet. Der Indexing-Service muss damit umgehen können, dass der Rückgabewert auch
Nonesein kann, da es für Clients grundsätzlich möglich (und bei älteren Clients auch üblich) ist, direkt Text und keine Datei zu übergeben.self.get_filename()Der der Name der zu verarbeitenden Datei. Normalerweise ist das der
basename()vonget_file(), d.h. der Dateiname ohne den absoluten Pfad. Jedoch kann der Dateiname auch bei einer reinen Textübergabe vom Client mit angegeben werden. Genau wie beiget_file()muss damit gerechnet werden, dass der zurückgegebene WertNoneist.self.get_text()Der in der Datei enthaltene Text, sofern der Server die Möglichkeit hat, diesen zu extrahieren (z.B. bei PDF- oder DOCX-Dateien) oder der Client den Text direkt übergeben hat. Wenn kein Text existiert, wird
Noneoder ein leerer String zurückgegeben.
Im Indexing-Service können beliebige Module aus der Python-Standardbibliothek importiert werden. Für
reguläre Ausdrücke steht nebem den Stand-Modul re auch noch das mächtigere regex-Modul
(https://pypi.org/project/regex/) zur Verfügung, das im obigen Beispiel verwendet wird.
Konfiguration
Die zu mymodule.py gehörende Konfigurationsdatei mymodule.ini kann in der Klasse
IndexingService über self.config benutzt werden. self.config ist ein
configparser.ConfigParser-Objekt (aus der
Python-Standard-Library) und ist leer, falls keine INI-Datei vorhanden ist.
Datenbankzugang
ODBC
Es ist möglich, innerhalb der process-Methode über ODBC auf externe Datenbanken zuzugreifen.
Eine ODBC-Datenquelle kann über die Sektion Database in der zum Modul gehörenden INI-Datei
einfach konfiguriert werden:
[Database]
DSN = MY_DSN
UID = user
PWD = password
Anschließend kann über self.database mithilfe des with-Statements eine Verbindung aufgebaut
werden:
with self.database() as conn:
sql = 'SELECT data FROM mydata WHERE ID = {}'.format(documentId)
cursor = conn.cursor()
cursor.execute(sql)
value = cursor.fetchone()[0]
# ...
Sollten Daten verändert werden, muss die Transaktion mit conn.commit() abgeschlossen werden.
Beim Verlassen des with-Blocks wird die Datenbankverbindung automatisch geschlossen.
Intern wird für die Datenbankverbindungen PyODBC benutzt. Sollte innerhalb eines Archivierungsvorgangs Zugriff auf mehr als eine externe Datenbank benötigt werden, muss PyODBC direkt verwendet werden.
Wichtig: Da docs365 documents eine 64-Bit-Anwendung ist, werden aktuell 64-Bit-ODBC-Treiber vorausgesetzt.
Documents-Datenbank
Unter self.engine wird ein Objekt bereitgestellt, mit dem man ohne weitere Konfiguration eine
direkte Verbindung zur PostgreSQL-Datenbank von docs365 documents aufbauen kann. Es handelt sich
dabei um ein Engine-Objekt von
SQLAlchemy.
Mit self.engine.connect() kann eine Verbindung aufgebaut werden, es wird jedoch empfohlen, ein
with-Statement mit einer impliziten Transaktion zu verwenden:
# Verbindung aufbauen
with self.engine.begin() as conn:
conn.execute(...)
Wenn innerhalb des with-Blocks eine Exception ausgelöst wird, gibt es automatisch einen
Rollback. Wenn es keine Fehler gibt, wird automatisch ein Commit durchgeführt.
Mit der execute-Methode des Connection-Objekts können Statements ausgeführt werden. Für
Prepared Statements muss die Funktion sqlalchemy.sql.text werden, in der die dynamischen
Parameter mit einem vorangestellten Doppelpunkt bezeichnet und anschließend beim Aufruf der
execute als Dictionary übergeben werden:
from sqlalchemy.sql import text
with self.engine.begin() as conn:
stmt = text('SELECT id FROM _exported_records WHERE id = :id')
result = conn.execute(stmt, {"id": record_id}).fetchone()