close

  
<div><p> /**
 * @fileOverview Plots the distribution of votes for each question
 * i.e.
 * - for each question
 *   - display the count of the number of votes for each judgment type
 */

var marginLeft = 120;
var marginBottom = 12;

var maxSegmentsPerGroup = 5; // up to 5 votes for each kind of judgment
var groupRecords = [
  {
    label: "Skip",
    answer: "skip"
  },
  {
    label: "Broken",
    answer: "broken"
  },
  {
    label: "Poor",
    answer: "bad"
  },
  {
    label: "Good",
    answer: "good"
  }
];
var maxGroup = groupRecords.length;
var users = [];
  
//prepare the data to be plotted
function prepareData(data) {
  var userMap = {};
  for (var i = 0; i &lt; data.length; i++) {
    var question = data[i];
    var nonZeroCounts = 0;
    var votes = question.aggregate_votes;
    for (var k in votes) {
      if (votes.hasOwnProperty(k)) {
        maxSegmentsPerGroup = Math.max(maxSegmentsPerGroup, votes[k]);
      }
    }
    question.concordance = nonZeroCounts &lt; 2;
    
    for (var userid in question.judgment_map) {
      if (question.judgment_map.hasOwnProperty(userid)) {
        if (!(userid in userMap)) {
          userMap[userid] = true;
          users.push({ id: userid });
        }
      }
    }
  }
  data.sort(function(a, b) { return b.assertion.meta.score - a.assertion.meta.score; });
  users.sort(function(a, b) { return a.id.localeCompare(b.id); });
  
  computeConcordance(data, concordanceSchemes[0]);
}

function computeConcordance(data, f) {
  for (var i = 0; i &lt; data.length; i++) {
    var question = data[i];
    f(question);
  }
  
  for (var g = 0; g &lt; groupRecords.length; g++) {
    var groupRecord = groupRecords[g];
    groupRecord.count = 0;
    
    for (var i = 0; i &lt; data.length; i++) {
      var question = data[i];
      if (question.concordanceAnswer == groupRecord.answer) {
        groupRecord.count++;
      }
    }
  }
}

function concordanceByAbsoluteAgreement(question) {
  var nonZeroCounts = 0;
  var answer = '';
  var votes = question.aggregate_votes;
  for (var k in votes) {
    if (votes.hasOwnProperty(k)) {
      if (votes[k] &gt; 0) {
        nonZeroCounts++;
        answer = k;
      }
    }
  }
  question.concordance = nonZeroCounts &lt; 2;
  question.concordanceAnswer = question.concordance ? answer : '';
}
  
function concordanceByOneOff(question) {
  var totalVotes = 0;
  var maxVote = 0;
  var maxAnswer = '';
  
  var votes = question.aggregate_votes;
  for (var k in votes) {
    if (votes.hasOwnProperty(k)) {
      var vote = votes[k];
      totalVotes += vote;
      if (maxVote &lt; vote) {
        maxVote = vote;
        maxAnswer = k;
      }
    }
  }
  question.concordance = maxVote &gt;= totalVotes - 1;
  question.concordanceAnswer = question.concordance ? maxAnswer : '';
}

function makeConcordanceByAgreementByRatio(ratio) {
  return function(question) {
    var totalVotes = 0;
    var maxVote = 0;
    var maxAnswer = '';
    
    var votes = question.aggregate_votes;
    for (var k in votes) {
      if (votes.hasOwnProperty(k)) {
        var vote = votes[k];
        totalVotes += vote;
        if (maxVote &lt; vote) {
          maxVote = vote;
          maxAnswer = k;
        }
      }
    }
    question.concordance = maxVote &gt; totalVotes * ratio;
    question.concordanceAnswer = question.concordance ? maxAnswer : '';
  }
}

var concordanceSchemes = [
  concordanceByAbsoluteAgreement,
  makeConcordanceByAgreementByRatio(0.5),
  makeConcordanceByAgreementByRatio(0.6),
  makeConcordanceByAgreementByRatio(0.7),
  makeConcordanceByAgreementByRatio(0.8),
  makeConcordanceByAgreementByRatio(0.9),
  concordanceByOneOff
];

//plot
function visualize(data) {
  var numberOfQuestions = data.length;
  
  var totalSegments = maxSegmentsPerGroup * maxGroup;
  var barSegmentHeight = totalSegments &lt;= 10 ? 7 : (totalSegments &lt;= 20 ? 5 : 3); // pixels
      
  var width = $(document.body).innerWidth() - $('#user-facet').outerWidth() - 30;
  var height = barSegmentHeight * totalSegments + marginBottom + 1;
  var plotViewPortWidth = width - marginLeft;
  var barWidth = Math.max(6, Math.floor(plotViewPortWidth / numberOfQuestions));
  var plotWidth = barWidth * numberOfQuestions;
  
  var gap = numberOfQuestions &gt; 100 ? 1 : 2;
  var actualBarWidth = Math.max(2, barWidth - gap);
  
  var answerToGroupBottom = {};
  for (var i = 0; i &lt; groupRecords.length; i++) {
    answerToGroupBottom[groupRecords[i].answer] = marginBottom + i * (barSegmentHeight * maxSegmentsPerGroup);
  }
  
  var activeQuestion = -1;
  var activeUserIndex = -1;
  
  var vis = new pv.Panel()
    .canvas('plot')
    .width(width)
    .height(height)
    .event("mousemove", pv.Behavior.point(Infinity));

  // group labels on left margin
  var labelGroup = function(groupRecord) {
    var percent = Math.round(groupRecord.count * 100 / data.length);
    return groupRecord.label + ' (' + percent + '%)'
  };
  vis.add(pv.Label)
    .data(pv.range(groupRecords.length))
    .left(marginLeft - 5)
    .bottom(function(d) { return marginBottom + (d + 0.25) * (barSegmentHeight * maxSegmentsPerGroup); })
    .textAlign("right")
    .text(function(d) { return labelGroup(groupRecords[d]); })
    .font("10pt sans-serif");

  var innerScrollPanel = vis.add(pv.Panel)
    .overflow("hidden")
    .left(marginLeft)
    .width(plotViewPortWidth)
    .top(0)
    .height(height)
    .events("all")
    .event("mousedown", pv.Behavior.pan())
    .event("pan", function() {
      var t = this.transform();
      var x = Math.min(0, Math.max(t.x, plotViewPortWidth - plotWidth))
      if (x != t.x || t.y != 0) {
        var t2 = new pv.Transform().translate(x, 0);
        this.transform(t2);
        innerScrollPanel.render();
      }
    });
  
  var pointHandler = function(d) {
    activeQuestion = this.index;
    
    var answerSpans = $('.user-facet-choice-answer').text('');
    if (activeQuestion &gt;= 0) {
      var question = data[activeQuestion];
      var judgmentMap = question.judgment_map;
      for (var i = 0; i &lt; users.length; i++) {
        var userid = users[i].id;
        if (userid in judgmentMap) {
          $(answerSpans[i]).text(judgmentMap[userid]);
        }
      }
    }
    
    return vis;
  };
  var clickHandler = function(d) {
    var questionId = d.id;
    var url = '/question?queue=' + escape(queue_id) + '&amp;question=' + escape(questionId);
    $('#question-link').attr('href', url).css('visibility', 'visible');
    
    var iframe = $('#question-view')[0];
    iframe.src = 'about:blank';
    window.setTimeout(function() {
      iframe.contentDocument.write(
        '</p>Rendering question ' + questionId + ' ...');
      iframe.src = url;
    }, 100);
  };
  
  // Background highlighters
  innerScrollPanel.add(pv.Bar)
    .data(data)
    .bottom(marginBottom)
    .width(actualBarWidth)
    .height(totalSegments * barSegmentHeight)
    .left(function() { return this.index * barWidth; })
    .fillStyle(function(d) { return (activeQuestion == this.index) ? '#eee' : 'white'; })
    .strokeStyle('none')
    .event("click", clickHandler)
    .event("mouseover", pointHandler)
    .anchor("bottom")
      .add(pv.Label)
      .text(function(d) {return '#' + this.index + ' score=' + data[this.index].assertion.meta.score; })
      .textAlign(function(d) { return this.index * 2 &lt; numberOfQuestions ? 'left' : 'right'; })
      .textBaseline("top")
      .visible(function(d) { return activeQuestion == this.index; });
  
  var getActiveUserJudgmentForQuestion = function(questionIndex) {
    if (activeUserIndex &gt;= 0) {
      var userid = users[activeUserIndex].id;
      var question = data[questionIndex];
      var judgmentMap = question.judgment_map;
      if (userid in judgmentMap) {
        return judgmentMap[userid];
      }
    }
    return '';
  };
  
  // vertical rules
  innerScrollPanel.add(pv.Rule)
    .data(pv.range(data.length+1))
    .left(function() { return this.index * (barWidth); })
    .bottom(marginBottom)
    .strokeStyle("#eee")
    .visible(function() { return (this.index % 5) == 0; });
  
  // horizontal rules
  innerScrollPanel.add(pv.Panel)
    .data(pv.range(maxGroup))
    .bottom(function(d) { return marginBottom + d * barSegmentHeight * maxSegmentsPerGroup; })
      .add(pv.Rule)
      .data(pv.range(maxSegmentsPerGroup))
      .bottom(function(d) { return d * barSegmentHeight; })
      .left(0)
      .width(plotWidth)
      .strokeStyle(function(d) { return d == 0 ? "#444" : (d % 5 == 0 ? "#ccc" : "#eee"); });
          
  var makeLabel = function(d) {
    return (activeQuestion == this.index) ? (d.assertion.sub + " : " + d.assertion.obj) : null;
  };
  var makeGroup = function(index, groupRecord) {
    innerScrollPanel.add(pv.Bar)
      .data(data)
      .bottom(marginBottom + index * maxSegmentsPerGroup * barSegmentHeight)
      .width(actualBarWidth)
      .height(function(d) { return d.aggregate_votes[groupRecord.answer] * barSegmentHeight; })
      .left(function() { return this.index * barWidth; })
      .fillStyle(function(d) { return (d.concordance) ? '#77A0E4' : '#f44'; })
      .strokeStyle('none')
      .event("mouseover", pointHandler)
      .event("click", clickHandler);
  }
  for (var i = 0; i &lt; groupRecords.length; i++) {
    makeGroup(i, groupRecords[i]);
  }

  // Answer dotters
  innerScrollPanel.add(pv.Dot)
    .data(data)
    .size(actualBarWidth)
    .left(function() { return this.index * barWidth + Math.round(actualBarWidth / 2); })
    .bottom(function() {
      var judgment = getActiveUserJudgmentForQuestion(this.index);
      return judgment == '' ? 0 : answerToGroupBottom[judgment];
    })
    .fillStyle('#000')
    .strokeStyle('none')
    .visible(function() { return getActiveUserJudgmentForQuestion(this.index) != ''; });
  

  vis.render();
  

  $('#loading-message').hide();
  $('#header-controls').show();
  
  $('#concordance-scheme-select').change(function(evt) {
    computeConcordance(data, concordanceSchemes[parseInt(this.value)]);
    vis.render();
  });
  
  var userFacet = $('#user-facet')
    .empty()
    .css('top', (5 + $('#plot').position().top) + 'px')
    .css('height', height + 'px');
  var renderUser = function(index, userRecord) {
    var div = $('<div>')
        .addClass('user-facet-choice')
        .attr('user-index', index)
        .appendTo(userFacet);
    
    $('<span>')
      .addClass('user-facet-choice-answer')
      .appendTo(div);
    $('<span>')
      .addClass('user-facet-choice-label')
      .text(userRecord.id)
      .appendTo(div);
    
    div.mouseenter(function() {
      activeUserIndex = index;
      vis.render();
    });
  };
  for (var i = 0; i &lt; users.length; i++) {
    renderUser(i, users[i]);
  }
}

$(function() {
  var allAnswers = [];
  var done = false;
  
  var baseTitle = document.title;
  var updateProgress = function(expected) {
    $('#loading-message').text('Loaded ' + allAnswers.length + ' of ' + expected + ' answers ...');
    document.title = '(' + allAnswers.length + ' of ' + expected + ') - ' + baseTitle;
  };
  var getNextBatch = function(offset) {
    $.getJSON(
      '/get_answers_with_judgments?queue_id=' + escape(queue_id) +
        '&amp;offset=' + allAnswers.length +
        '&amp;limit=10&amp;callback=?',
      null,
      function(data) {
        if ((data.answers) &amp;&amp; data.answers.length &gt; 0) {
          allAnswers = allAnswers.concat(data.answers);
          updateProgress(data.size);
          if (allAnswers.length &lt; data.size) {
            getNextBatch(data.cursor);
            return;
          }
        }
        //console.log(data);
        prepareData(allAnswers);
        visualize(allAnswers);
        document.title = baseTitle;
        done = true;
      },
      'jsonp'
    );
  };
  getNextBatch();
  
  var timerId = null;
  $(window).resize(function() {
    if (done) {
      if (!timerId) {
        timerId = window.setTimeout(function() {
          timerId = null;
          $('#plot').empty();
          visualize(allAnswers);
        }, 1000);
      }
    }
  });
});
 </span></span></div></div>

Comments

Hide