P2M Konversionsskripte

Für die Import der Daten aus Pica nach Goobi bzw. aus Pica über eine lokale MODS - Konversion nach Goobi stehen 2 Skripte auf dem EROMM Server zur Verfügung:

http://www.eromm.org/cgi-bin/pica2marc.pl?ppn=88195411X für die Konversion von Pica in ein Marc-artiges Format, das über die M21 Schnittstelle von Goobi eingelesen werden kann

http://www.eromm.org/cgi-bin/pica2mods.pl?ppn=88195411X für die Konversion von Pica nach MODS

Installation

Die Skripte, die zugehörige Pica::Record Klasse und die Templates, anhand derer die eigentliche Konversion durchgeführt wird liegen unter /jahnke/p2m als git repository. Um sie zum Laufen zu bringen, muss

falls es auf einem Server laufen soll, Perl auf dem Server laufen und für CGI Skripte eingerichtet sein (falls das nicht der Fall ist, hilft bestimmt die Apache Dokumentation weiter); falls es lokal laufen soll muss Perl lokal installiert sein (bevorzugterweise Strawberry Perl, wenn unter Windows gearbeitet wird)

Template::Toolkit muss (über die CPAN Shell) als Perl Modul installiert werden

Das Skript/die Skripte werden im CGI-Verzeichnis des Servers abgelegt und mit entsprechenden Rechten versehen, wenn man auf einem Server arbeitet.

Im CGI-Verzeichnis (bzw. im selben Verzeichnis wie das Skript, wenn man lokal arbeitet) liegen außerdem das Verzeichnis PICA mit dem Perl-Modul Record.pm, das Verzeichnis TCE mit dem Unterverzeichnis Filter mit dem Perl-Modul ResolvePND.pm und das Verzeichnis mit den Templates. Letzteres kann aber auch woanders liegen, man muss den Pfad im Skript so oder so anpassen.

Im Ordner documentation gibt es HTML Versionen der POD-Dokumentationen für beide Skripte und die Klasse Pica::Record, da steht auch, wo man den Pfad zu den Templates anpassen muss.

Anpassen der Templates

Die Konversionstemplates lassen sich einfach anpassen. Eine (fast vollständige) Übersicht welches Feld in welchem Template konvertiert wird, findet man in den Dateien Mapping_TCE_vollstaendig.xlsx bzw. Mapping_TCE_vollstaendig_m21.xlsx. Das Skript ruft jeweils nur das erste Template auf mets_fistdmdsec.tmpl für MODS und m21_core.tmpl für Marc. In diesen Templates wird das Haupttitelfeld konvertiert und dann alle weiteren Templates aufgerufen.

Die Templates verwenden die Template-Toolkit Syntax, die hier: http://www.template-toolkit.org/docs/index.html vollständig dokumentiert ist.

Beim Aufruf der Templating Engine werden vom Skript eine Referenz auf ein Array, das das zu konvertierende Pica::Record Objekt enthält, übergeben und im Fall der MODS Konversion ein Flag, ob das XML für die METS dmdSec mit ausgegeben werden soll (nomets).

Die Referenz auf das Datensatzobjekt wird an die Variable r übergeben, so dass der aktuelle Datensatz immer über r ansprechbar ist.

[%- r = records.item(0) -%]

Die vollständige Dokumentation zum Pica::Record Objekt liegt im Ordner documentation. Alle Methoden können sowohl innerhalb von Perl Skripten als auch innerhalb der Templates aufgerufen werden. (Theoretisch könnte man also auch neue Felder im Template zum Objekt hinzufügen, das macht aber sicher keinen Sinn.) Die Notation für die Dereferenzierung bzw. den Methodenaufruf ist in den Templates der Punkt statt des in Perl üblichen Pfeils.

$record->subfields('021A','a')->[0]->[0]
ist also das gleiche wie
r.subfields('021A','a').item(0).item(0)

Ein Pica::Record Objekt enthält außer dem eigentlichen Datensatz noch weitere, verlinkte bibliographische Datensätze, die ihrerseits wiederum auch Pica::Record Objekte sind (außer bei r.original handelt es sich genaugenommen jeweils um Arrays von Objekten, noch genauer genaugenommen handelt es sich um Methoden die eine Liste von Objekten, bzw. bei $record→original() ein einzelnes Objekt, zurückgeben ). Das sind im Einzelnen:

r.hostitems # für alle übergeordneten Datensätze, dh. c-Stufen für f/F-Stufen, b-Stufen für s-Stufen usw r.series # Schriftenreihen (d-Stufen) r.supplements # (fotlaufende) Beilagen (b-Stufen), kann nur in b-Stufen vorkommen r.original # die Digitalisierungsvorlage (der A-Satz zum O-Satz)

Die weiteren Templates werden über die Direktive PROCESS aufgerufen:

[%- PROCESS inc_mods_recordinfo.tmpl -%]
[%- PROCESS inc_mods_location.tmpl -%]
[%- PROCESS inc_mods_title_struct.tmpl -%]
[%- PROCESS inc_mods_persons.tmpl -%]
[%- PROCESS inc_mods_corporate.tmpl -%]
[%- PROCESS inc_mods_origininfo.tmpl -%]
[%- PROCESS inc_mods_physicaldescription.tmpl -%]
[%- PROCESS inc_mods_language.tmpl -%]
[%- PROCESS inc_mods_reproductionnote.tmpl -%]
[%- PROCESS inc_mods_originalversionnote.tmpl -%]
[%- PROCESS inc_mods_othernotes.tmpl -%]
[%- PROCESS inc_mods_abstract.tmpl -%]
[%- PROCESS inc_mods_genrecodes.tmpl -%]
[%- PROCESS inc_mods_aadgenreterms.tmpl -%]
[%- PROCESS inc_mods_subjects.tmpl -%]
[%- PROCESS inc_mods_classification.tmpl -%]
[%- PROCESS inc_mods_identifier.tmpl -%]
[%- PROCESS inc_mods_uris.tmpl -%]
[%- PROCESS inc_mods_titlewords.tmpl -%]
[%- PROCESS inc_mods_normpl.tmpl -%]
[%- PROCESS inc_mods_review.tmpl -%]
[%- PROCESS inc_mods_series.tmpl -%]
[%- PROCESS inc_mods_hostitem.tmpl -%]

wenn man das Template mit einem anderen Datensatzobjekt aufrufen will, kann man die Variable r beim Aufruf von PROCESS überschreiben:

[%- INCLUDE inc_mods_location.tmpl r=h -%]

Auf einzelne Unterfelder kann man wie folgt zugreifen:

# das erste Unterfeld $g im ersten Feld 021G im Datensatz, wobei ein möglicherweise vorkommendes @ entfernt wird r.subfields('021G','g').item(0).item(0).remove('@') # was hier stattfindet ist folgendes: my $fields_021G = $record->subfields('021G','g'); # $fields_021G enthält ein Array (Referenz) mit einem Array von Unterfeldern # tatsächlich sähe das dann so aus my $fields_021G = [ ['1. Unterfeld $g des 1. Feldes 021G','2. Unterfeld $gd des 1. Feldes 021G',...], ['1. Unterfeld $g des 2. Feldes 021G',...], ... ]; # remove() ist eine Methode die von Template Toolkit bereit gestellt wird (s. VMethods)

Auf wiederholte Felder und ganze Gruppen von Feldern kann man gut so zugreifen:

[%- aad_genreterms = r.fieldgroup(['044S']) -%] # fieldgroup() gibt ein Array mit allen Feldern 044S zurück [%- FOREACH a IN aad_genreterms -%] # über die Liste wird iteriert <mods:genre authority="aad">[%- r.in_field(a,'8') || r.in_field(a,'a') | xml -%]</mods:genre> # in_field holt das erste Unterfeld $8 [%- END -%] # oder das erste Unterfeld $a aus dem # Feld über das gerade iteriert wird # Die einzelnen Felder in PICA::Record sind selbst keine Objekte und haben daher keine eigenen # Methoden, deshalb ist in_field als Methode von PICA::Record implementiert.

Auf den Feldbezeichner, die Occurrence und den Datensatztyp lässt sich wie folgt zugreifen:

[%- FOREACH t IN titles -%] [% UNLESS (t.tag == '022A' && t.occurrence && r.type == 'c') %] ... [% END %] [%- END -%]

Als Beispiel ein Auszug aus dem Template, das die Identifier bzw. Standardnummern im Datensatz konvertiert mit Kommentaren:

[%- id_type_by_field = { # definiere ein assoziatives Array, dass die P+ Feldbezeichner in Wert für das '003@' => 'GBVKat', # type-Attribut von mods:identifier übersetzt '003D' => 'GBVKat', '003O' => 'oclc', '004L' => 'ean', '004R' => 'hdl', '004U' => 'urn', '004V' => 'doi', '006A' => 'lccn', '006G' => 'dnb', '006L' => 'other', '006M' => 'vd18', '006N' => 'swets', '006W' => 'vd17', '006X' => 'other', '006Y' => 'other', '006Z' => 'zdb', '007B' => 'government publication', '007C' => 'coden', '007D' => 'order', '007E' => 'thesis', '007F' => 'report', '007G' => 'other', '007H' => 'standard', '007N' => 'order', '007P' => 'fei', '007T' => 'fingerprint', '007Y' => 'other', '007Z' => 'contract', '004A' => 'isbn', '004F' => 'ismn', '005A' => 'issn' } -%] [%- invalid_id_type_by_field = { # definiere ein assoziatives Array, dass einen wahren Wert für alle Feldbezeichner enthält. '003D' => 1, # die einen ungültigen Identifier enthalten } -%] [%- IF r.rda -%] # wenn es sich um einen RDA-Satz handelt werden die folgenden Felder zu einer Gruppe zusammen- # gefasst und in identifiers gespeichert [%- identifiers = r.original.fieldgroup(['003@','006A','004U','004V','004R','006G','003O','006L','006X','003D','006N','006W','006M','007Y','006Y','007C','004L','007F','007H','007E','007B','007D','007N','007Z', '007G']) -%] [%- ELSE -%] # falls es sich um einen RAK-Satz handelt, die folgenden: [%- identifiers = r.fieldgroup(['003@','006A','004U','004V','004R','006G','003O','006Z','006L','006X','003D','006N','006W','006M','007Y','006Y','007C','004L','007F','007H','007E','007B','007D','007N','007P','007T','007Z', '007G']) -%] [%- END -%] [%- FOREACH p IN identifiers -%] # Iteriere über die Felder # Wenn wir es mit Feld 007P (Fingerprint) zu tun haben, setze den Wert für das type-Attribut auf den Inhalt von Unterfeld $S # oder, falls es kein Unterfeld $S gibt, auf 'fei' [%- IF (p.tag == '007P'); id_type_by_field.item(p.tag) = r.in_field(p,'S') || 'fei'; END -%] # Verwende den Inhalt von $c als Wert für den Type oder nimm ihn aus dem assoziativen Array id_type_by_field # wenn das Tag in der List mit den ungültigen Identifiern ist, für das Attribut invalid="yes" ein # gib den Inhalt von $0 als Textknoten von mods:identifier aus <mods:identifier type="[%- r.in_field(p,'c') || id_type_by_field.item(p.tag) -%]"[%- IF invalid_id_type_by_field.item(p.tag) -%] invalid="yes"[%- END -%]>[%- r.in_field(p,'0') | xml -%]</mods:identifier> [%- END -%] # hole die ISSN und die ZDB-Id der Digitalisierungvorlage aus dem A-Satz und gib sie als mods:identifer[@type="other"] aus [%- isszdborig = r.original.fieldgroup(['005A', '006Z']) -%] [%- FOREACH is IN isszdborig -%] <mods:identifier type="other">[%- r.original.in_field(is,'0') | xml -%]</mods:identifier> [%- END -%] # gibt die PPN der Digitalsierungsvorlage als mods:identifier[@type"PPNanalog"] aus <mods:identifier type="PPNanalog">[% r.original.id %]</mods:identifier>