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>"; include("tableHeader2nd.php"); echo "<tbody><tr><td id='question' class='firstRow'>"; echo $questions[$counter][1]; echo ". </td>"; for ($item = 1; $item <= 9; $item++) { echo "<td> </td>"; } echo "<td class='unscored'> </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> <div id="second"> <p id="firstPlot" class="titlePlot"></p> </div> <div id="third"> <p id="secondPlot" class="titlePlot"></p> </div> </div>
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:
if ($_SERVER["REQUEST_METHOD"] == "POST") { $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 } // END: if($_SERVER["REQUEST_METHOD"]
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 = ""; if ($_SERVER["REQUEST_METHOD"] == "POST") { $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; } $conn->close(); header("Location: http://localhost/~mp/part2/delphi2_2.php"); exit; } 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>"; } $conn->close(); } 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 […]
LikeLike
[…] Said that, building round II was much more exciting than round one, because I built visual displays of the votes of each single item. You can check how I did it here. […]
LikeLike