Testgetriebene Umsetzung der Titeldarstellungsvorschriften in Trefferlisten und Einzeltrefferanzeige

5. VuFind-Anwendertreffen
11. - 12. Oktober 2016
https://hebis-vz.github.io/slides/vufind2016/










Sebastian Böttger
HeBIS-Verbundzentrale
Vermittlungs- und Recherchelösungen

Begriffsdefinitionen

  • Trefferliste: Ergebnisseite einer Suchanfrage in VuFind
  • Einzeltrefferanzeige: Detailseite eines einzelnen Suchergebnisses in VuFind
  • Titeldarstellungsvorschrift: Vorschrift nach der HeBIS Katalogisate (in unserem Fall gibt es welche nach RAK, nach RDA und auch Mischformen) korrekt in der Oberfläche dargestellt werden.
  • Testgetriebene Entwicklung: Programmierparadigma

  • HDS: HeBIS Discovery System

MARC


LEADER	00914cam a22002652cb4500
001 182832104
003 DE-603
005 20100528221201.0
006 a u00 u
008 061129s2005 xx u00 u eng c
020 	|a 0201896834  |9 0-201-89683-4
035 	|a (DE-599)HEB182832104
035 	|a (OCoLC)180134860
040 	|a DE-603  |b ger  |c DE-603  |d DE-603  |e rakwb
041     |a eng
084     |a ST 230  |2 rvk
084     |a ST 130  |2 rvk
090     |n b  |n z
100 1   |0 (DE-588)121578437  |0 (DE-603)152930531  |a Knuth, Donald Ervin  |d 1938-  |4 aut
245 1 0 |a Fundamental algorithms  |c Donald E. Knuth
250     |a 3. ed., 20. print., [updat. and rev.]
260 3   |a Boston, Mass. [u.a.]  |b Addison-Wesley  |c 2005
300     |a XIX, 650 S.  |b graph. Darst.
490 1   |a The art of computer programming / Donald E. Knuth  |v 1
800 1   |a Knuth, Donald Ervin  |d 1938-  |4 aut  |t The art of computer programming  |v 1  |w (DE-603)009840354  |9 11
                

PICA+

alg: 81423106
001@    $03 $aB
001A    $01999:11-12-11
001B    $01999:11-12-11 $t13:00:00.000
001U    $0utf8
001X    $00
002@    $0rax
003@    $0814231063
009R    $qimage/gif $A2 $3Katalogkarte der UB Frankfurt $uhttp://resolver.hebis.de/retro/b0590338 $mV:DE-603;B:DE-30
010@    $aund
021A    $aDarwin
025@    $aDarwin
034D    $a28 S.
047M    $cCb in BX -I-A-- e 1zum TodeßtAg Darwine.1 Kapoli 18814s Tocco . 28 S. 29 Eumschla 29 Kl. Sehr. 34.

                
Anzeige der Marc-Felder anpassen:
Auf neuer Zeile: 260 $a_:_$b,_$c
Wenn 260 Indikator 3 vorhanden, dann nur diesen anzeigen
264 $a_:_$b,_$c
Wenn Leader Pos. 7 = s, dann 264 Indikator 1 und 2 = 31 anzeigen, sonst 264 Indikator 1 und 2 = #1
wenn 264 Indikator 2 = 1, dann anzeigen wie folgt: 264 $a_:_$b,_$c
Wenn mehr als eine 264 mit Indikator 2 = 1 vorhanden ist, dann soll nur eine angezeigt werden. Selektion und Priorisierung nach folgender Regel:
264 Indikator 1 und 2 = 31
264 Indikator 1 und 2 = #1

Ausgangssituation

  • Entwicklung einer neuen HDS-Version auf Basis von VuFind 3
  • Zu den Katalogisaten nach RAK kommen jetzt auch Katalogisate nach RDA und Mischformen.
  • Umstellung auf MARC als alleiniges Datenformat (zuvor auch PICA)
  • => Code aus HDS I (basierend auf VuFind 1) nicht nachnutzbar

Umsetzung der Titeldarstellung in VuFind

  • so genannte RecordDriver enthalten diverse Methoden
    • getGeneralNotes
    • getSeries
    • getTitleStatement
    • getTitleSection
    • ...
  • Templates setzen die detailierte Darstellung um

Umsetzung der Titeldarstellung in VuFind

SolrMarc.php

class SolrMarc extends SolrDefault
{
    //...
    public function getDeduplicatedAuthors()
    {
        $authors = [
            'main' => $this->getAuthorRolesArray(
                $this->getPrimaryAuthors(),
                $this->getPrimaryAuthorsRoles()
            ),
            'corporate' => $this->getAuthorRolesArray(
                $this->getCorporateAuthors(),
                $this->getCorporateAuthorsRoles()
            ),
            'secondary' => $this->getAuthorRolesArray(
                $this->getSecondaryAuthors(),
                $this->getSecondaryAuthorsRoles()
            )
        ];
        //...
        return $authors;
    }
    //...
}           

core.phtml


<? $authors = $this->driver->getDeduplicatedAuthors(); ?>
<? if (isset($authors['corporate']) && !empty($authors['corporate'])): ?>
  <tr>
    <th>
      <?=$this->transEsc('Corporate Author')?>:
    </th>
    <td>
    <? $i = 0; ?>
    <? foreach ($authors['corporate'] as $corporate => $roles): ?>
      <?=($i++ == 0)?'':', '?>
      <span property="creator">
        <a href="<?=$this->record($this->driver)->getLink('author', $corporate)?>">
          <?=$this->escapeHtml($corporate)?>
        </a>
        <? if (count($roles) > 0): ?>
          (<? $j = 0; foreach ($roles as $role): ?><?=($j++ == 0)?'':', '?>
            <?=$this->transEsc("CreatorRoles::" . $role)?>
          <? endforeach; ?>)
        <? endif; ?>
      </span>
    <? endforeach; ?>
    </td>
  </tr>
<? endif; ?>

Umsetzung der Titeldarstellung in VuFind

Funktioniert, hat aber einige Nachteile...

 

  • ...Individuelle Anpassungen
    Anzeige der Marc-Felder anpassen:
    Auf neuer Zeile: 260 $a_:_$b,_$c
    Wenn 260 Indikator 3 vorhanden, dann nur diesen anzeigen
    264 $a_:_$b,_$c
    Wenn Leader Pos. 7 = s, dann 264 Indikator 1 und 2 = 31 anzeigen, sonst 264 Indikator 1 und 2 = #1
    wenn 264 Indikator 2 = 1, dann anzeigen wie folgt: 264 $a_:_$b,_$c
    Wenn mehr als eine 264 mit Indikator 2 = 1 vorhanden ist, soll nur eine angezeigt werden. Selektion und Priorisierung nach folgender Regel:
    264 Indikator 1 und 2 = 31
    264 Indikator 1 und 2 = #1
  • => eigener Treiber, der vom Original erbt + Anpassung der Templates
    • Überschreiben von Methoden
    • => Keine Garantie, dass nach VuFind-Update noch alles funktioniert
  • => Separation of Concerns?
  • => Aufwendige intellektuelle Tests

Eine Lösung musste gefunden werden, die...

  1. eine Erweiterung des Record-Treibers umgeht,
  2. die Präsentation von der Datenhaltung (Model) trennt,
  3. möglichst wenig Logik in den Templates beansprucht und
  4. automatisiert testbar ist.


>> View Helper <<

View Helper

Programme, die kleine (Teil-)Aufgaben im Präsentationslayer übernehmen, die sich oft wiederholen oder zu komplex für die Umsetzung im Template sind.

Klassische Beispiele: Datumsformatierungen, Escapen von Zeichen, i18n

VuFind (ZF 2)

  • ViewHelper Klasse erstellen
  • __invoke() Methode implementieren
  • ViewHelper in Module.php registrieren

Beispiel

core.phtml


<? $authors = $this->corporateAuthors($this->driver); ?>
<? if (!empty($authors)): ?>
  <tr>
    <th><?=$this->transEsc('Corporate Author')?>:</th>
    <td><?=$authors?></td>
  </tr>
<? endif; ?>

CorporateAuthor.php

class CorporateAuthor extends AbstractViewHelper
{
    public function __invoke(SolrMarc $driver)
    {
        $arr = [];
        $corporate = $driver->getDeduplicatedAuthors()['corporate'];
        if (empty($corporate)) return "";
        foreach ($corporate as $author => $roles) {
            $str = '<a href="'.$this->getView()->getRecord($driver)
                    ->link('author', $author).'">' .
                   $corporate . '</a>';
            $roleArr = [];
            foreach ($roles as $role) {
                $roleArr[] = $this->getView()
                    ->transEsc("CreatorRoles::" . $role);
            }
            $str .= " (" . implode(", ", $roleArr) . ")";
            $arr[] = $str;
        }
        return implode(", ", $arr);
    }
}           

Testgetriebene Entwicklung

"Testgetriebene Entwicklung (auch [...] test-driven development) ist eine Methode, die häufig bei der agilen Entwicklung von Computerprogrammen eingesetzt wird. Bei der testgetriebenen Entwicklung erstellt der Programmierer Software-Tests konsequent vor den zu testenden Komponenten."

Wikipedia

Unit Testing / Black Box Testing

  1. Bibliothekare schreiben die Tests, indem sie in einer Tabelle für jedem View Helper die Identifier der zu testenden Beispieldatensätze und die dazu zu erwartende Ausgabe notieren.
  2. Die Entwickler schreiben PHPUnit-Tests, die die Tabelle auswerten und Test Cases generieren, welche die View Helper ausführen und deren Ausgabe mit der zu erwartenden Ausgabe abgleichen.
  3. Die Entwickler schreiben View Helper und programmieren an den Methoden, bis alle Testfälle erfolgreich durchlaufen; d. h. alle Ausgaben der View Helper zu den entsprechend zu erwartenden Ausgaben aus der Testtabelle identisch sind.

Die Testtabelle

Jedes Tabellen-Sheet beschreibt einen eigenen ViewHelper. Jeder ViewHelper entspricht normalerweise einer Zeile in der Einzeltrefferanzeige.

Genutzte Felder PPN Test Daten (MarcXML) Ziel Anzeige Einzeltreffer Ziel Anzeige Trefferliste
245 $a $b $c 078893151   Politische Theorien der Gegenwart : [eine Einführung] / André Brodocz... (Hrsg.) Politische Theorien der Gegenwart : [eine Einführung]
245 $a $h $n $c 181035510 Ch. Darwin's gesammelte Werke [Elektronische Ressource] / von Ch. Darwin. Aus dem Engl. übers. von J. Victor Carus Ch. Darwin's gesammelte Werke [Elektronische Ressource]
5 : Die Abstammung des Menschen und die geschlechtliche Zuchtwahl ; 1

Unit Tests

<Foo>ViewHelperTest

  • erbt von AbstractViewHelperTest
  • In
    setUp()
    wird zugehöriger View Helper und das zugehörige Testsheet definiert
  • Ggf. werden zusätzlich benötigte View Helper definiert

AbstractViewHelperTest

  • Interpretiert die Test-Sheet der Tabelle, lädt Datensatz vom Index, lädt den View Helper und führt ihn aus.
  • Vergleicht Ausgabe des View Helpers mit der Vorgabe

Live-Vorführung

Zusammenfassung

Eine Lösung musste gefunden werden, die...

  1. eine Erweiterung des Record-Treibers umgeht,
  2. die Präsentation von der Datenhaltung (Model) trennt,
  3. möglichst wenig Logik in den Templates beansprucht und
  4. automatisiert testbar ist.

Danke für Ihre Aufmerksamkeit!

Haben Sie Fragen?










Sebastian Böttger
HeBIS-Verbundzentrale
Vermittlungs- und Recherchelösungen

www.hebis.de