Web-interface for Delphi Method II

Delphi method, round 2

This post describes the web-interface I built for the second round of the Delphi method study on functional magnetic resonance imaging and tinnitus. In the second round, the experts who participated in the first round saw the responses that they gave to the first round side-by-side with the responses of all the other experts. As represented in the figure above, in round 2 the Delphi interface displayed one question and two bar plots showing the distribution of experts’ responses for the given question in round 1. To keep the post short and to the point, I divided the description of the interface for round 2 into two parts. This post describes the first part, which displays the questionnaire’s items on the web page. I will describe the second part in a next post, showing how to build the bar plots to provide feedback to the participants. The web interface is here.

The display of the questionnaire items was simpler in round 2 than in round 1 because the interface presented one question at the time with the relative bar plots for the groups’ feedback. As in round 1, I presented the question and radio buttons within an HTML table. However, the code displaying the items’ text in round 2 is stripped down in comparison to round 1 because there was no section’s header.

// later on I will refer to this as 'php code for the question'
echo "<table>";
echo "<tbody><tr><td id='question' class='firstRow'>";
echo $questions[$counter][1];
echo ". </td>";
for ($item = 1; $item <= 9; $item++) {
    echo "<td> <center> <input type='radio' name='q1'"; 
    if (isset($q1) && $q1=="1") echo "checked";
    echo " value='" . $item . "'> </center> </td>";
echo "<td class='unscored'> <center> <input type='radio' name='q1' ";
if (isset($q1) && $q1=='10') echo 'checked';
echo "value='10'> </center> </td>";
echo "<td class='asterisk'> <span class='error'>"; 
echo $q1Err;
echo "    </span> </td></tr></tbody></table>"; 

To keep the question’s text and bar plots aligned I wrapped them into an HTML div. Inside of this div `wrapper', there were three div: one for the table with the questions and one for each bar plot with the responses of the experts’ groups.

<div id="wrapper">
    <div id="first">
        <!-- php code for the question -->
    <div id="second">
        <p id="firstPlot" class="titlePlot"></p>
        <svg width="300" height="150"></svg> 
    <div id="third">
        <p id="secondPlot" class="titlePlot"></p>
        <svg width="300" height="150"></svg> 

The CSS code styling the presentation is straightforward. The style sheet is in the github repository of the project.

In round 2 we included new questions and rephrased or changed words in questions which were unclear in round 1. To simplify the management of old and new questions and of the bar plot that went with them I used a two-dimensional array. I stored the question’s text in the first dimension of the array. In the second dimension I stored the indexes to identify the question in the MySQL database. This allowed keeping the order of the questions presentation coherent with the information displayed in the bar plots. So, for example, if a new question was introduced in place 13th of round 2 it was stored on slot 12 of the PHP array (which starts with 0) and with index 41 in the MySQL database since round 1 presented 40 questions. The use of the index instead of the array order allows the retrieval of the responses given to round 1 independently from the fact that the text of the question was changed, clarified, or move forward or later in the list of questions displayed in round 2. Moreover, new questions were not accompanied by a bar plot. Therefore, numeric indexes allowed a simple way of determining whether a bar plot had to be displayed or not. In fact, since only the first 40 questions had been previously answered, an index lower than 41 implied drawing the bar plots and otherwise not.

The feedback process required updating the page with new text and bar plots every response. I controlled the updating of text and plots with a counter stored in the current PHP session of the webpage. The session counter increased after the response was entered into the MySQL database. Updating of the counter was embedded in the server POST request:

    $resp1 = FALSE;
    if (empty($_POST['q1'])){ 
        $q1Err = "*"; 
    } else { 
        $q1 = test_input($_POST["q1"]); $resp1 = TRUE;}
    if ($resp1){
        $qNn = 'q' . ($questions[$counter][0]);
        $sql = "UPDATE `{$tableName}` SET `{$qNn}` = '{$q1}' 
            WHERE uniqueID =  '{$_SESSION['ppid']}' AND session = 2";
        if (!mysqli_query($conn, $sql)){ 
            die('Error: ' . mysqli_error($conn)); }
        $q1 = $q1Err =  "";
        if (!isset($_SESSION['count'])) {
            $_SESSION['count'] = 0;
        } else {$_SESSION['count']++;}
    } else { 
        $requiredFields = "* required fields"; }// END: if ($resp1

Sanity checks

The second round of the Delphi questionnaire is useless if a participant does not receive feedback from his/her previous session. Therefore, the first sanity check prevented participants from continuing without a unique identifier. A warning text displayed on a red background advised users with no identifier that they should have gotten a unique identifier before continuing. The PHP $SESSION also stored the unique identifier.

if (!array_key_exists('ppid',$_SESSION) || empty($_SESSION['ppid'])){
    echo "<p>
      <span style='background-color:#d73027; color:white; font-size:x-large;'>
    You do not have an ID to submit the responses!! Please go to this ";
    echo "<a href='http://localhost/~mp/part2/delphi2.php'>";}
    echo "page</a>, fill in your first and last name, and get an ID!";
    echo "</span></p>";

Moreover, participants with a unique identifier were matched on the database containing the responses from round 1 to determine whether their responses were present or not:

$ppName = $ppSurname = "";
$ppNameErr = $ppSurnameErr = "";
$requiredFields = "";
  $ppResp  = $surnameResp = FALSE; 
  if (empty($_POST['ppName'])) { $ppNameErr = "*"; } 
  else { $ppName = test_input($_POST["ppName"]); $ppResp = TRUE;}
  if (empty($_POST['ppSurname'])) { $ppSurnameErr = "*"; } 
  else { $ppSurname = test_input($_POST["ppSurname"]); $surnameResp = TRUE;}
  $_SESSION['ppid'] = $ppID;
  if( $ppResp && $surnameResp) {
     require '../includeDatabase.php';
     $conn = new mysqli($servername, $username, $password, $dbname);
     if ($conn->connect_error) {die("Connection failed: " . 
         $conn->connect_error);	}
     $sql = "SELECT uniqueID FROM `{$tableName}` 
         WHERE uniqueID = '{$ppID}'";
     $result = $conn->query($sql);
     if ($result->num_rows > 0) {
        $sql = "INSERT IGNORE INTO mriTIN (`uniqueID`, `session`) 
                VALUES ('{$ppID}', 2);";
        if ($conn->query($sql) === FALSE) { echo "Error: " . 
             $sql . "<br>" . $conn->error; }
	header("Location: http://localhost/~mp/part2/delphi2_2.php");
      } else {
	echo "<p style='background-color:#d73027; 
              color:white; font-size:x-large;'>We could not find a user 
              matching " . $ppName . " " . $ppSurname . "<br>";
	echo "If you did participate in Round one of this questionnaire 
              then you might be using an ID we do not recognize. Please 
              get in touch with Paolo (" ;
	echo "<a href='mailto:p.toffanin@umcg.nl'>p.toffanin@umcg.nl</a>) 
              to sort out the issue. Apologies for the inconveniences.</p>";
  } else {$requiredFields = "* required fields";} // END: if( $ppResp && ...
} // END: if ($_SERVER["REQUEST_METHOD"] == "POST") {

Participants for whom a unique identifier was not found were asked to mail me for help or to complete round 1 before completing round 2.

Sanity checks include also form validation. Here I will not discuss form validation since I treated it in the post for round 1 (see here). Moreover, since most of this form’s inputs are radio buttons, form validation is superfluous.

In the next post, I will address the Javascript code for the bar plot, the XMLHTTP request retrieving the data and MySQL commands to retrieve the counts for the responses of each question.

Web-interface for Delphi Method II

Uploading data to the server [wbwit II]

Uploading the data gathered from the web-based version of the word identification task (i.e., lexical decision – see this post) to a MySQL database was more challenging than expected. The problem is that there is a limit on the size of data which could be transfered with an ajax XMLHttpRequest (ajax call from now on) from the web-page to the host server. It took quite a bit of trial-and-error to discover that the problem was Continue reading “Uploading data to the server [wbwit II]”

Uploading data to the server [wbwit II]