Data Quality & Validation

Forsta HX Platform - API Scripting Guide

Data Quality & Validation

Speeder Detection:

/**
 * questionTimes - Object storing time spent on each question
 * @type {Object.}
 * @description Keys are question IDs, values are seconds spent on each.
 *              Used for speeder detection and data quality analysis.
 */
var questionTimes = {};

/**
 * currentQuestionStart - Timestamp when current question was displayed
 * @type {number}
 * @description Stores Date.now() value when question becomes active.
 *              Used to calculate elapsed time when moving to next question.
 */
var currentQuestionStart = Date.now();

/**
 * trackQuestionTime - Records time spent on a question and flags speeders
 * @param {string} qid - The question ID to track time for
 * @description Calculates elapsed seconds since currentQuestionStart,
 *              stores in questionTimes object, and logs warning if
 *              respondent spent less than 2 seconds (potential speeder).
 */
function trackQuestionTime(qid) {
    var elapsed = (Date.now() - currentQuestionStart) / 1000;
    questionTimes[qid] = elapsed;
    
    // Flag if too fast (less than 2 seconds)
    if(elapsed < 2) {
        console.warn('Speeding detected on ' + qid);
        // Could trigger warning or flag response
    }
    
    currentQuestionStart = Date.now();
}

// Call on next button click
$('.next-button').on('click', function() {
    trackQuestionTime(getCurrentQuestionId());
});

Straight-lining Detection:

/**
 * detectStraightLining - Detects if respondent gives same answer repeatedly
 * @param {Array} questions - Array of question IDs to analyze
 * @param {number} threshold - Percentage threshold to flag (e.g., 80 = 80%)
 * @returns {Object} - Object with detected (boolean), percentage, and mostCommon answer
 * @description Analyzes response patterns across multiple questions.
 *              If the same answer appears more than threshold% of times,
 *              flags as potential straight-lining (low quality response).
 */
function detectStraightLining(questions, threshold) {
    // Get all responses for the specified questions
    var responses = questions.map(function(qid) {
        return f(qid).val();
    });
    
    // Count most common response
    var counts = {};
    responses.forEach(function(val) {
        counts[val] = (counts[val] || 0) + 1;
    });
    
    var maxCount = Math.max(...Object.values(counts));
    var percentage = (maxCount / responses.length) * 100;
    
    if(percentage >= threshold) {
        return {
            detected: true,
            percentage: percentage,
            mostCommon: Object.keys(counts).find(key => 
                counts[key] === maxCount
            )
        };
    }
    return {detected: false};
}

// Usage
var scaleQuestions = ['BQ1', 'BQ2', 'BQ3', 'BQ4', 'BQ5'];
var result = detectStraightLining(scaleQuestions, 80);

if(result.detected) {
    alert('Please vary your responses');
}

Response Quality Scoring:

/**
 * calculateQualityScore - Computes an overall quality score for the response
 * @returns {number} - Quality score from 0-100+ (higher = better quality)
 * @description Starts with 100 points and applies penalties/bonuses:
 *              - Penalty for speeding (< 2 seconds per question)
 *              - Penalty for straight-lining responses
 *              - Penalty for short open-end answers
 *              - Bonus for passing attention checks
 *              Minimum score is clamped to 0.
 */
function calculateQualityScore() {
    var score = 100; // Start with perfect score
    
    // Penalize for speeding
    Object.values(questionTimes).forEach(function(time) {
        if(time < 2) score -= 5;
    });
    
    // Penalize for straight-lining
    if(detectStraightLining(['BQ1','BQ2','BQ3','BQ4'], 75).detected) {
        score -= 20;
    }
    
    // Penalize for incomplete open-ends
    $('textarea').each(function() {
        var words = countWords($(this).val());
        if(words < 5) score -= 10;
    });
    
    // Reward for consistency checks passed
    if(passedAttentionCheck()) score += 10;
    
    return Math.max(0, score);
}

// Flag low quality responses
var quality = calculateQualityScore();
if(quality < 50) {
    console.warn('Low quality response detected');
    // Could trigger review or rejection
}