Robust wikidata infocard with documentation
authorDan Scott <dan@coffeecode.net>
Fri, 11 Aug 2017 11:07:28 +0000 (07:07 -0400)
committerDan Scott <dan@coffeecode.net>
Fri, 11 Aug 2017 11:07:28 +0000 (07:07 -0400)
Given that other sites might be interested in applying the wikidata infocard to
their own catalogues, document about how it works and its current limitations.

Refactor the process for adding data to the wikicard by using a more generic
function, and then add Twitter, Facebook, and Musicbrainz now that it is so
much easier.

Signed-off-by: Dan Scott <dan@coffeecode.net>
Open-ILS/web/js/ui/default/opac/wikidata_music_card.js

index 2d05f63..dbf6114 100644 (file)
@@ -1,12 +1,30 @@
 ;(function () {
-  var performer;
+  /**
+   * Display an infocard with data pulled from Wikidata
+   *
+   * Based on the primary contributor's name, search Wikidata via SPARQL for
+   * musicians or bands using an exact string match of the label or alias,
+   * and retrieve the data of interest.
+   *
+   * Current limitations:
+   * * Only activated for musical recordings, to avoid ambiguous results
+   * * Only activated for the first performer in the list
+   * * Hard-coded for English
+   *
+   * To use this in your catalogue, you will need to modify a few of the
+   * CSS queries to find the performer's name and identify appropriate
+   * locations for inserting the clickable note icon and the infocard
+   */
+  var icon_node;
   var entity_name;
   var note;
   var wd;
 
+  /* Ensure this is a musical recording */
   if (document.getElementById('canvas_main').getAttribute('typeof').indexOf('MusicAlbum') > -1) {
-    performer = document.querySelector('span[resource="#schemacontrib1"] span[property="description"]');
-    entity_name = performer.textContent.trim();
+    /* Performer's name - used to find a matching item in wikidata */
+    var performer_node = document.querySelector('span[resource="#schemacontrib1"] span[property="name"]');
+    entity_name = performer_node.textContent.trim();
     var lastchar = entity_name[entity_name.length - 1];
     if (lastchar === '.' || lastchar === ',') {
       entity_name = entity_name.slice(0, -1);
     if (inverse.length === 2) {
       entity_name = inverse[1].trim() + " " + inverse[0].trim();
     }
+    /* Insert clickable icon here */
+    icon_node = document.querySelector('span[resource="#schemacontrib1"] span[property="description"]');
     note = document.createElement('span');
-    note.setAttribute('class', 'wikidata');
+    note.class = 'wikidata';
     note.innerText = ' ♪';
     note.addEventListener('click', perform, { once: true });
-    performer.insertBefore(note, null);
+    icon_node.insertBefore(note, null);
   }
 
   function perform(e) {
-    findPerformer(performer, entity_name);
+    findPerformer(icon_node, entity_name);
     e.preventDefault();
     e.stopPropagation();
   }
     e.stopPropagation();
   }
 
-  function findPerformer(performer, entity_name) {
+  function findPerformer(icon_node, entity_name) {
     var url = 'https://query.wikidata.org/sparql';
-    var query = 'SELECT DISTINCT ?item ?itemLabel ?itemDescription ?image ?website ?songKick WHERE {' +
+    var query = 'SELECT DISTINCT ?item ?itemLabel ?itemDescription ?image ?birthPlace ?birthPlaceLabel ' +
+        '  ?website ?musicbrainz ?songKick ?twitter ?facebook WHERE {' +
         '?item rdfs:label|skos:altLabel|wdt:P1449 "' + entity_name + '"@en . ' +
         '{ ?item wdt:P31 wd:Q215380 . } ' + // band
         'UNION ' +
         'UNION ' +
         '{ ?item wdt:P106/wdt:P279* wd:Q639669 . } ' +
         'OPTIONAL { ?item wdt:P3478 ?songKick . ' +
+        '  ?item wdt:P19 ?birthPlace . ' +
         '  ?item wdt:P856 ?website . ' +
+        '  ?item wdt:P434 ?musicbrainz . ' +
+        '  ?item wdt:P2002 ?twitter . ' +
+        '  ?item wdt:P2013 ?facebook . ' +
         '  ?item wdt:P18 ?image } . ' +
         'SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } ' +
     '} ' +
@@ -70,7 +95,7 @@
       req.onload = function (evt) {
         var r = req.response.results.bindings[0];
         if (r !== undefined) {
-          generateCard(performer, r);
+          generateCard(icon_node, r);
           toggleMode(note);
           // console.log(r);
         }
       req.onload = function (evt) {
         var r = JSON.parse(req.responseText).results.bindings[0];
         if (r !== undefined) {
-          generateCard(performer, r);
+          generateCard(icon_node, r);
           toggleMode(note);
           // console.log(r);
         }
     req.send();
   }
 
-  function generateCard(performer, r) {
+  function generateCard(icon_node, r) {
     var auth_div = document.querySelector('div[class="rdetail_authors_div"]');
     var musician = document.createElement('div');
     musician.id = 'magic_musician';
       musician.appendChild(wdd);
     }
 
+    function addWDValue(property, propertyLabel, label, isLink, linkFormatter) {
+      var value = '';
+      if (r.hasOwnProperty(property)) {
+        value = r[property].value;
+        var valueDiv = document.createElement('div');
+        var labelText = value;
+        if (propertyLabel) {
+          var valueLabel = document.createElement('label');
+          valueLabel.innerText = propertyLabel;
+          valueDiv.appendChild(valueLabel);
+        }
+        if (label) {
+          if (r.hasOwnProperty(label)) {
+          labelText = r[label].value;
+          } else {
+            labelText = label;
+          }
+        }
+        if (isLink) {
+          var valueLink = document.createElement('a');
+          if (linkFormatter) {
+            valueLink.href = linkFormatter(value);
+          } else {
+            valueLink.href = value;
+          }
+          valueLink.innerText = labelText;
+          valueDiv.appendChild(valueLink);
+        }
+        musician.appendChild(valueDiv);
+      }
+    }
+
+    addWDValue('birthPlace', 'Birth place: ', 'birthPlaceLabel', true);
+    addWDValue('website', 'Web site: ', null, true);
+    addWDValue('musicbrainz', null, 'Discography (Musicbrainz)', true, function(value) { return 'https://musicbrainz.org/artist/' + value });
+    addWDValue('songKick', null, 'Tour dates (Songkick)', true, function(value) { return 'http://www.songkick.com/artists/' + value });
+    addWDValue('twitter', 'Twitter: ', null, true, function(value) { return 'https://twitter.com/' + value });
+    addWDValue('facebook', 'Facebook: ', null, true, function(value) { return 'https://www.facebook.com/' + value });
+
     var uri = r.item.value;
     var wd = document.createElement('div');
     var wdl = document.createElement('label');
-    wdl.innerText = 'Wikidata ID: ';
+    wdl.innerText = 'Edit on Wikidata: ';
     var wdv = document.createElement('a');
     wdv.href = uri;
     wdv.innerText = uri.substr(uri.lastIndexOf('/') + 1);
     wd.appendChild(wdv);
     musician.appendChild(wd);
 
-    var website = '';
-    if (r.hasOwnProperty('website')) {
-      website = r.website.value;
-      var ws = document.createElement('div');
-      var wsl = document.createElement('label');
-      wsl.innerText = 'Web site: ';
-      var wsv = document.createElement('a');
-      wsv.href = website;
-      wsv.innerText = website;
-      ws.appendChild(wsl);
-      ws.appendChild(wsv);
-      musician.appendChild(ws);
-    }
-
-    var songkick = '';
-    if (r.hasOwnProperty('songKick')) {
-      songkick = 'http://www.songkick.com/artists/' + r.songKick.value;
-      var sk = document.createElement('div');
-      var skl = document.createElement('label');
-      skl.innerText = 'Songkick ID: ';
-      var skv = document.createElement('a');
-      skv.href = songkick;
-      skv.innerText = songkick.substr(songkick.lastIndexOf('/') +1);
-      sk.appendChild(skl);
-      sk.appendChild(skv);
-      musician.appendChild(sk);
-    }
-
     auth_div.appendChild(musician);
+    /* Append the Wikidata infocard to the page */
     document.getElementById('rdetail_image_div').style.clear = 'both';
   }
 })()