TPAC: Fine-grained credits in record details
authorDan Scott <dscott@laurentian.ca>
Wed, 5 Sep 2012 04:27:03 +0000 (00:27 -0400)
committerDan Scott <dscott@laurentian.ca>
Fri, 28 Sep 2012 04:51:43 +0000 (00:51 -0400)
For 7xx fields with $4 subfields containing relator codes, generate a
relator code map from http://www.loc.gov/marc/relators/relacode.html
that we can use in the record details to provide specific credits,
rather than the generic (and often inappropriate) "Added author" label.

For example:

700 1. ‡aMaley, Desmond, ‡d1954- ‡4prf.

This now generates "Maley, Desmond, 1954- (Performer)" in the record
details credit section.

Signed-off-by: Dan Scott <dscott@laurentian.ca>
Signed-off-by: Ben Shum <bshum@biblio.org>
Open-ILS/src/templates/opac/parts/record/authors.tt2
Open-ILS/src/templates/opac/parts/relators.tt2 [new file with mode: 0644]
build/tools/relator_map [new file with mode: 0644]

index 4a0419c..bc61e48 100644 (file)
@@ -1,5 +1,7 @@
 [%-  
 
+PROCESS "opac/parts/relators.tt2";
+
 authors = [
     {
         type => 'author', 
@@ -10,10 +12,6 @@ authors = [
         label => l('Added Author'),
         xpath => '//*[@tag="700"]|//*[@tag="710"]|//*[@tag="711"]'
     }, {
-        type => 'credits', 
-        label => l('Credited'),
-        xpath => '//*[@tag="100"]|//*[@tag="110"]|//*[@tag="111"]'
-    }, {
         type => 'cast', 
         label => l('Cast'),
         xpath => '//*[@tag="508"]'
@@ -29,9 +27,14 @@ BLOCK build_author_links;
         term = '';
         qterm = '';
         iprop = '';
+        tlabel = '';
         FOR subfield IN node.childNodes;
             NEXT UNLESS subfield.nodeName == "subfield";
             code = subfield.getAttribute('code');
+            IF code == '4';
+                relcode = subfield.textContent.substr(0,3);
+                tlabel = relators.$relcode || label;
+            END;
             NEXT UNLESS code.match('[a-z]');
             sf_raw = subfield.textContent;
             sf = subfield.textContent | html;
@@ -39,7 +42,7 @@ BLOCK build_author_links;
             qterm = qterm _ ' ' _ sf_raw;
         END;
         url = mkurl(ctx.opac_root _ '/results', {query => qterm, qtype => 'author'}, ['page', 'expand']);
-        author_type = label | html;
+        author_type = (tlabel || label) | html;
         
         # schema.org changes
         IF type == 'author';
diff --git a/Open-ILS/src/templates/opac/parts/relators.tt2 b/Open-ILS/src/templates/opac/parts/relators.tt2
new file mode 100644 (file)
index 0000000..3fd4a93
--- /dev/null
@@ -0,0 +1,228 @@
+[%-  
+# Generated from http://www.loc.gov/marc/relators/relacode.html
+# using the build/tools/relator_map script
+relators = {
+    'acp' => l('Art copyist'),
+    'act' => l('Actor'),
+    'adp' => l('Adapter'),
+    'aft' => l('Author of afterword, colophon, etc.'),
+    'anl' => l('Analyst'),
+    'anm' => l('Animator'),
+    'ann' => l('Annotator'),
+    'ant' => l('Bibliographic antecedent'),
+    'app' => l('Applicant'),
+    'aqt' => l('Author in quotations or text abstracts'),
+    'arc' => l('Architect'),
+    'ard' => l('Artistic director'),
+    'arr' => l('Arranger'),
+    'art' => l('Artist'),
+    'asg' => l('Assignee'),
+    'asn' => l('Associated name'),
+    'att' => l('Attributed name'),
+    'auc' => l('Auctioneer'),
+    'aud' => l('Author of dialog'),
+    'aui' => l('Author of introduction'),
+    'aus' => l('Author of screenplay'),
+    'aut' => l('Author'),
+    'bdd' => l('Binding designer'),
+    'bjd' => l('Bookjacket designer'),
+    'bkd' => l('Book designer'),
+    'bkp' => l('Book producer'),
+    'blw' => l('Blurb writer'),
+    'bnd' => l('Binder'),
+    'bpd' => l('Bookplate designer'),
+    'bsl' => l('Bookseller'),
+    'ccp' => l('Conceptor'),
+    'chr' => l('Choreographer'),
+    'clb' => l('Collaborator'),
+    'cli' => l('Client'),
+    'cll' => l('Calligrapher'),
+    'clr' => l('Colorist'),
+    'clt' => l('Collotyper'),
+    'cmm' => l('Commentator'),
+    'cmp' => l('Composer'),
+    'cmt' => l('Compositor'),
+    'cnd' => l('Conductor'),
+    'cng' => l('Cinematographer'),
+    'cns' => l('Censor'),
+    'coe' => l('Contestant -appellee'),
+    'col' => l('Collector'),
+    'com' => l('Compiler'),
+    'con' => l('Conservator'),
+    'cos' => l('Contestant'),
+    'cot' => l('Contestant -appellant'),
+    'cov' => l('Cover designer'),
+    'cpc' => l('Copyright claimant'),
+    'cpe' => l('Complainant-appellee'),
+    'cph' => l('Copyright holder'),
+    'cpl' => l('Complainant'),
+    'cpt' => l('Complainant-appellant'),
+    'cre' => l('Creator'),
+    'crp' => l('Correspondent'),
+    'crr' => l('Corrector'),
+    'csl' => l('Consultant'),
+    'csp' => l('Consultant to a project'),
+    'cst' => l('Costume designer'),
+    'ctb' => l('Contributor'),
+    'cte' => l('Contestee-appellee'),
+    'ctg' => l('Cartographer'),
+    'ctr' => l('Contractor'),
+    'cts' => l('Contestee'),
+    'ctt' => l('Contestee-appellant'),
+    'cur' => l('Curator'),
+    'cwt' => l('Commentator for written text'),
+    'dfd' => l('Defendant'),
+    'dfe' => l('Defendant-appellee'),
+    'dft' => l('Defendant-appellant'),
+    'dgg' => l('Degree grantor'),
+    'dis' => l('Dissertant'),
+    'dln' => l('Delineator'),
+    'dnc' => l('Dancer'),
+    'dnr' => l('Donor'),
+    'dpb' => l('Distribution place'),
+    'dpc' => l('Depicted'),
+    'dpt' => l('Depositor'),
+    'drm' => l('Draftsman'),
+    'drt' => l('Director'),
+    'dsr' => l('Designer'),
+    'dst' => l('Distributor'),
+    'dtc' => l('Data contributor'),
+    'dte' => l('Dedicatee'),
+    'dtm' => l('Data manager'),
+    'dto' => l('Dedicator'),
+    'dub' => l('Dubious author'),
+    'edt' => l('Editor'),
+    'egr' => l('Engraver'),
+    'elg' => l('Electrician'),
+    'elt' => l('Electrotyper'),
+    'eng' => l('Engineer'),
+    'etr' => l('Etcher'),
+    'evp' => l('Event place'),
+    'exp' => l('Expert'),
+    'fac' => l('Facsimilist'),
+    'fld' => l('Field director'),
+    'flm' => l('Film editor'),
+    'fmo' => l('Former owner'),
+    'fnd' => l('Funder'),
+    'fpy' => l('First party'),
+    'frg' => l('Forger'),
+    'gis' => l('Geographic information specialist'),
+    'grt' => l('Graphic technician'),
+    'hnr' => l('Honoree'),
+    'hst' => l('Host'),
+    'ill' => l('Illustrator'),
+    'ilu' => l('Illuminator'),
+    'ins' => l('Inscriber'),
+    'inv' => l('Inventor'),
+    'itr' => l('Instrumentalist'),
+    'ive' => l('Interviewee'),
+    'ivr' => l('Interviewer'),
+    'lbr' => l('Laboratory'),
+    'lbt' => l('Librettist'),
+    'ldr' => l('Laboratory director'),
+    'led' => l('Lead'),
+    'lee' => l('Libelee-appellee'),
+    'lel' => l('Libelee'),
+    'len' => l('Lender'),
+    'let' => l('Libelee-appellant'),
+    'lgd' => l('Lighting designer'),
+    'lie' => l('Libelant-appellee'),
+    'lil' => l('Libelant'),
+    'lit' => l('Libelant-appellant'),
+    'lsa' => l('Landscape architect'),
+    'lse' => l('Licensee'),
+    'lso' => l('Licensor'),
+    'ltg' => l('Lithographer'),
+    'lyr' => l('Lyricist'),
+    'mcp' => l('Music copyist'),
+    'mdc' => l('Metadata contact'),
+    'mfp' => l('Manufacture place'),
+    'mfr' => l('Manufacturer'),
+    'mod' => l('Moderator'),
+    'mon' => l('Monitor'),
+    'mrb' => l('Marbler'),
+    'mrk' => l('Markup editor'),
+    'msd' => l('Musical director'),
+    'mte' => l('Metal-engraver'),
+    'mus' => l('Musician'),
+    'nrt' => l('Narrator'),
+    'opn' => l('Opponent'),
+    'org' => l('Originator'),
+    'orm' => l('Organizer of meeting'),
+    'oth' => l('Other'),
+    'own' => l('Owner'),
+    'pat' => l('Patron'),
+    'pbd' => l('Publishing director'),
+    'pbl' => l('Publisher'),
+    'pdr' => l('Project director'),
+    'pfr' => l('Proofreader'),
+    'pht' => l('Photographer'),
+    'plt' => l('Platemaker'),
+    'pma' => l('Permitting agency'),
+    'pmn' => l('Production manager'),
+    'pop' => l('Printer of plates'),
+    'ppm' => l('Papermaker'),
+    'ppt' => l('Puppeteer'),
+    'prc' => l('Process contact'),
+    'prd' => l('Production personnel'),
+    'prf' => l('Performer'),
+    'prg' => l('Programmer'),
+    'prm' => l('Printmaker'),
+    'pro' => l('Producer'),
+    'prp' => l('Production place'),
+    'prt' => l('Printer'),
+    'pta' => l('Patent applicant'),
+    'pte' => l('Plaintiff -appellee'),
+    'ptf' => l('Plaintiff'),
+    'pth' => l('Patent holder'),
+    'ptt' => l('Plaintiff-appellant'),
+    'pup' => l('Publication place'),
+    'rbr' => l('Rubricator'),
+    'rce' => l('Recording engineer'),
+    'rcp' => l('Recipient'),
+    'red' => l('Redactor'),
+    'ren' => l('Renderer'),
+    'res' => l('Researcher'),
+    'rev' => l('Reviewer'),
+    'rps' => l('Repository'),
+    'rpt' => l('Reporter'),
+    'rpy' => l('Responsible party'),
+    'rse' => l('Respondent-appellee'),
+    'rsg' => l('Restager'),
+    'rsp' => l('Respondent'),
+    'rst' => l('Respondent-appellant'),
+    'rth' => l('Research team head'),
+    'rtm' => l('Research team member'),
+    'sad' => l('Scientific advisor'),
+    'sce' => l('Scenarist'),
+    'scl' => l('Sculptor'),
+    'scr' => l('Scribe'),
+    'sds' => l('Sound designer'),
+    'sec' => l('Secretary'),
+    'sgn' => l('Signer'),
+    'sht' => l('Supporting host'),
+    'sng' => l('Singer'),
+    'spk' => l('Speaker'),
+    'spn' => l('Sponsor'),
+    'spy' => l('Second party'),
+    'srv' => l('Surveyor'),
+    'std' => l('Set designer'),
+    'stl' => l('Storyteller'),
+    'stm' => l('Stage manager'),
+    'stn' => l('Standards body'),
+    'str' => l('Stereotyper'),
+    'tcd' => l('Technical director'),
+    'tch' => l('Teacher'),
+    'ths' => l('Thesis advisor'),
+    'trc' => l('Transcriber'),
+    'trl' => l('Translator'),
+    'tyd' => l('Type designer'),
+    'tyg' => l('Typographer'),
+    'uvp' => l('University place'),
+    'vdg' => l('Videographer'),
+    'voc' => l('Vocalist'),
+    'wam' => l('Writer of accompanying material'),
+    'wdc' => l('Woodcutter'),
+    'wde' => l('Wood-engraver'),
+    'wit' => l('Witness'),
+} -%]
\ No newline at end of file
diff --git a/build/tools/relator_map b/build/tools/relator_map
new file mode 100644 (file)
index 0000000..7423482
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use XML::LibXML;
+use XML::LibXML::Reader;
+use LWP::Simple;
+use Data::Dumper;
+
+my %relator;
+my $code = 0;
+my $fullname = 0;
+my $code_v;
+
+my $content = get('http://www.loc.gov/marc/relators/relacode.html');
+
+$content =~ s{^<!DOCTYPE.*?>}{}s;
+$content =~ s{<head>.+?</head>}{}s;
+$content =~ s{<table .+?<table }{<table }s;
+$content =~ s{&raquo;}{}gs;
+$content =~ s{</table>.+?</table>}{</table>}s;
+
+my $reader = XML::LibXML::Reader->new(
+    string => $content,
+    recover => 2,
+    load_ext_dtd => 0
+);
+
+$reader->nextElement('table');
+$reader->nextElement('tr');
+while($reader->read) {
+    processNode($reader);
+}
+
+#print Dumper(\%relator);
+generateRelatorMap();
+
+sub processNode {
+    my $reader = shift;
+    if ($reader->nodeType == XML_READER_TYPE_ELEMENT && $reader->name eq 'td') {
+        if ($reader->getAttribute('class') && $reader->getAttribute('class') eq 'code') {
+            $code = 1;
+        } else {
+            $fullname = 1;
+        }
+    }
+    if ($reader->nodeType == XML_READER_TYPE_TEXT) {
+        if ($code) {
+            $code_v = $reader->value();
+
+            # Treat deprecated codes as valid
+            $code_v =~ s/^-//;
+            $code = 0;
+        } elsif ($fullname) {
+            $relator{$code_v} = $reader->value();
+            $fullname = 0;
+        }
+    }
+}
+
+sub generateRelatorMap {
+    print <<"HEAD";
+[%-  
+# Generated from http://www.loc.gov/marc/relators/relacode.html
+# using the build/tools/relator_map script
+HEAD
+    print 'relators = {' . "\n";
+    foreach (sort keys %relator) {
+        print "    '$_' => l('$relator{$_}'),\n";
+    }
+    print '} -%]';
+}