Separated out the graphing code into a separate js file.

This commit is contained in:
Peter Edmond 2025-05-03 19:06:42 +01:00
parent 4ec9b62d2c
commit 95574d6fd3
8 changed files with 590 additions and 0 deletions

43
checkCredentials.php Normal file
View File

@ -0,0 +1,43 @@
<?php
$apiToken="nCqnPnrt9HplICvOWhratTIrwkxqe7pILQ524GJG";
$dataCenter = "fra1"; // Example: "us-east-1"
$surveyId = "SV_3pyZVUNpxXm1PZI"; //Survey ID
$urveyId ="SV_bmiHoSHYWIgGM3I"; //Survey ID
//$surveyId = "SV_bmiHoSHYWIgGM3I"; //Survey ID
// API endpoint for getting survey details (simple GET request)
$surveyDetailsUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId";
// cURL request to fetch survey details
$headers = [
"X-API-TOKEN: $apiToken",
"Content-Type: application/json"
];
$ch = curl_init($surveyDetailsUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
// Check for errors
if (!$response) {
die("Error fetching survey details: " . curl_error($ch));
}
// Check the response code
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP Status Code: $httpCode\n";
// Decode the response to check if the API token is working
$surveyData = json_decode($response, true);
if (isset($surveyData['result'])) {
echo "Survey ID: " . $surveyData['result']['id'] . "\n";
} else {
echo "Error: " . json_encode($surveyData, JSON_PRETTY_PRINT) . "\n";
}
?>

111
draw3axis.js Normal file
View File

@ -0,0 +1,111 @@
function drawGraph() {
let ValA = 0.8;
let ValB = 0.4;
let ValC = 0.6;
createSVGgraphic("#Q1","FeatureA", "FeatureB", "FeatureC", ValA, ValB, ValC);
createSVGgraphic("#Q2","ConceptA", "ConceptB", "ConceptC", 0.2, 0.8, 0.8);
}
function createSVGgraphic(divID,FA,FB,FC,VA,VB,VC) {
const svg = d3.select(divID);
const width = +svg.attr("width");
const height = +svg.attr("height");
const radius = 150;
const center = { x: width / 2, y: height / 2 };
// Define axes with A at the top (rotate -90 degrees)
const axes = [
{ name: FA, angle: 0 },
{ name: FB, angle: 120 },
{ name: FC, angle: 240 }
];
axes.forEach(d => d.angle = (d.angle - 90) * Math.PI / 180);
// Sample data: [0-1] scale
const values = [VA, VB, VC];
// Draw axis lines
svg.append("g")
.attr("class", "axis")
.selectAll("line")
.data(axes)
.join("line")
.attr("x1", center.x)
.attr("y1", center.y)
.attr("x2", d => center.x + radius * Math.cos(d.angle))
.attr("y2", d => center.y + radius * Math.sin(d.angle));
// Draw grid rings (polygons)
const levels = 5;
for (let i = 1; i <= levels; i++) {
const r = (radius / levels) * i;
const points = axes.map(d => {
return [
center.x + r * Math.cos(d.angle),
center.y + r * Math.sin(d.angle)
];
});
svg.append("polygon")
.attr("class", "grid")
.attr("points", points.map(p => p.join(",")).join(" "));
}
// Draw axis ticks
const tickLength = 5;
axes.forEach(axis => {
for (let i = 1; i <= levels; i++) {
const r = (radius / levels) * i;
const angle = axis.angle;
const x1 = center.x + r * Math.cos(angle);
const y1 = center.y + r * Math.sin(angle);
// Perpendicular tick
const perpAngle = angle + Math.PI / 2;
const x2 = x1 + tickLength * Math.cos(perpAngle);
const y2 = y1 + tickLength * Math.sin(perpAngle);
svg.append("line")
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x2)
.attr("y2", y2)
.attr("class", "tick");
}
});
// Convert values to coordinates
const valuePoints = axes.map((d, i) => {
const r = radius * values[i];
return [
center.x + r * Math.cos(d.angle),
center.y + r * Math.sin(d.angle)
];
});
// Draw data area
svg.append("polygon")
.attr("class", "area")
.attr("points", valuePoints.map(p => p.join(",")).join(" "))
// Add axis labels
svg.selectAll(".label")
.data(axes)
.join("text")
.attr("class", "label")
.attr("x", d => center.x + (radius + 20) * Math.cos(d.angle))
.attr("y", (d,i) => {
if (i === 0) { //adjust for 1st access, rather than using d.name
return center.y + (radius + 20) * Math.sin(d.angle)
} else
return center.y + (radius + 20 + 30) * Math.sin(d.angle)
})
.text(d => d.name);
}

18
examineResponses.php Normal file
View File

@ -0,0 +1,18 @@
<?php
//Load JSON data
$jsonString = file_get_contents('survey_responses.json');
// Decode the JSON string into a PHP associative array
$data = json_decode($jsonString, true);
// Print the structure of the data
echo '<pre>';
print_r($data);
echo '</pre>';
?>

109
getData.php Normal file
View File

@ -0,0 +1,109 @@
<?php
// Set up constants:
$apiToken="nCqnPnrt9HplICvOWhratTIrwkxqe7pILQ524GJG";
$dataCenter = "fra1"; // Example: "us-east-1"
$surveyId = "SV_3pyZVUNpxXm1PZI"; //Survey ID
$urveyId ="SV_bmiHoSHYWIgGM3I"; //Survey ID
//$surveyId = "SV_bmiHoSHYWIgGM3I"; //Survey ID
// API endpoint URLs
$exportUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId/export-responses";
$statusUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId/export-responses/";
$fileUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId/export-responses/";
// Step 1: Start Export
$headers = [
"X-API-TOKEN: $apiToken",
"Content-Type: application/json"
];
$ch = curl_init($exportUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["format" => "json"]));
$response = curl_exec($ch);
curl_close($ch);
if (!$response) {
die("Error starting export: " . curl_error($ch));
}
$exportData = json_decode($response, true);
$progressId = $exportData['result']['progressId'];
// Step 2: Check Export Status
$status = 'inProgress';
while ($status != 'complete') {
// Sleep for 5 seconds before checking again
sleep(5);
$statusCh = curl_init($statusUrl . $progressId);
curl_setopt($statusCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($statusCh, CURLOPT_HTTPHEADER, $headers);
$statusResponse = curl_exec($statusCh);
curl_close($statusCh);
if (!$statusResponse) {
die("Error checking status: " . curl_error($statusCh));
}
//print_r(json_decode($statusResponse, true));
//exit(0);
$statusData = json_decode($statusResponse, true);
$status = $statusData['result']['status'];
if ($status == 'failed') {
die("Export failed!");
}
echo "Waiting for export to complete... Status: $status\n";
}
// Step 3: Download the Exported File
$fileId = $statusData['result']['fileId'];
$fileCh = curl_init($fileUrl . $fileId . '/file');
curl_setopt($fileCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($fileCh, CURLOPT_HTTPHEADER, $headers);
$fileContent = curl_exec($fileCh);
curl_close($fileCh);
if (!$fileContent) {
die("Error downloading the file: " . curl_error($fileCh));
}
// Save the file locally
file_put_contents("survey_responses.zip", $fileContent);
echo "Survey data has been downloaded successfully!\n";
// Path to the ZIP file
$zipFile = './survey_responses.zip'; // Specify the path to your ZIP file
$extractTo = 'survey_responses.json'; // Specify the folder to extract the contents to
// Create a new ZipArchive object
$zip = new ZipArchive();
// Open the ZIP file
if ($zip->open($zipFile) === TRUE) {
// Extract all the contents to the specified folder
$zip->extractTo($extractTo);
$zip->close(); // Close the ZIP file
echo "ZIP file extracted successfully!";
} else {
echo "Failed to open the ZIP file.";
}
?>

94
getDataJSON.php Normal file
View File

@ -0,0 +1,94 @@
<?php
// Set up constants:
$apiToken="nCqnPnrt9HplICvOWhratTIrwkxqe7pILQ524GJG";
$dataCenter = "fra1"; // Example: "us-east-1"
$surveyId = "SV_3pyZVUNpxXm1PZI"; //Survey ID
$urveyId ="SV_bmiHoSHYWIgGM3I"; //Survey ID
//$surveyId = "SV_bmiHoSHYWIgGM3I"; //Survey ID
// API endpoint URLs
$exportUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId/export-responses";
$statusUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId/export-responses/";
$fileUrl = "https://$dataCenter.qualtrics.com/API/v3/surveys/$surveyId/export-responses/";
// Step 1: Start Export
$headers = [
"X-API-TOKEN: $apiToken",
"Content-Type: application/json"
];
$ch = curl_init($exportUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["format" => "json", "compress" => "false"]));
$response = curl_exec($ch);
curl_close($ch);
if (!$response) {
die("Error starting export: " . curl_error($ch));
}
$exportData = json_decode($response, true);
$progressId = $exportData['result']['progressId'];
// Step 2: Check Export Status
$status = 'inProgress';
while ($status != 'complete') {
// Sleep for 5 seconds before checking again
sleep(5);
$statusCh = curl_init($statusUrl . $progressId);
curl_setopt($statusCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($statusCh, CURLOPT_HTTPHEADER, $headers);
$statusResponse = curl_exec($statusCh);
curl_close($statusCh);
if (!$statusResponse) {
die("Error checking status: " . curl_error($statusCh));
}
//print_r(json_decode($statusResponse, true));
//exit(0);
$statusData = json_decode($statusResponse, true);
$status = $statusData['result']['status'];
if ($status == 'failed') {
die("Export failed!");
}
echo "Waiting for export to complete... Status: $status\n";
}
// Step 3: Download the Exported File
$fileId = $statusData['result']['fileId'];
$fileCh = curl_init($fileUrl . $fileId . '/file');
curl_setopt($fileCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($fileCh, CURLOPT_HTTPHEADER, $headers);
$fileContent = curl_exec($fileCh);
curl_close($fileCh);
if (!$fileContent) {
die("Error downloading the file: " . curl_error($fileCh));
}
// Save the file locally
file_put_contents("survey_responses.json", $fileContent);
echo "Survey data has been downloaded successfully!\n";
// No need to create a new ZipArchive object as imported without compression
?>

62
index.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>3-Axis Radar Chart with Ticks</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="./draw3axis.js"></script>
<style>
body {
font-family: sans-serif;
margin: 0;
padding: 0;
background-color: white;
}
svg {
background: white;
display: block;
margin: auto;
}
.axis line {
stroke: black;
stroke-width: 1.5px;
}
line.tick {
stroke: black;
stroke-width: 1;
}
polygon.grid {
fill: none;
stroke: rgba(200, 200, 200, 0.5); /* light grey with 50% opacity */
stroke-dasharray: 2,2;
}
.area {
fill: rgba(70, 130, 180, 0.3); /* steelblue with transparency */
stroke: rgba(70, 130, 180, 0.6);
stroke-width: 2;
}
.label {
font-size: 12px;
font-weight: bold;
text-anchor: middle;
}
</style>
</head>
<body onload="drawGraph()">
<h1>Concepts</h1>
<svg id="Q1" width="400" height="400"></svg>
<h1>Features</h1>
<svg id="Q2" width="400" height="400"></svg>
</body>
</html>

152
index3axis.html Normal file
View File

@ -0,0 +1,152 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>3-Axis Radar Chart with Ticks</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
font-family: sans-serif;
margin: 0;
padding: 0;
background-color: white;
}
svg {
background: white;
display: block;
margin: auto;
}
.axis line {
stroke: black;
stroke-width: 1.5px;
}
line.tick {
stroke: black;
stroke-width: 1;
}
polygon.grid {
fill: none;
stroke: rgba(200, 200, 200, 0.5); /* light grey with 50% opacity */
stroke-dasharray: 2,2;
}
.area {
fill: rgba(70, 130, 180, 0.3); /* steelblue with transparency */
stroke: rgba(70, 130, 180, 0.6);
stroke-width: 2;
}
.label {
font-size: 12px;
font-weight: bold;
text-anchor: middle;
}
</style>
</head>
<body>
<svg width="500" height="500"></svg>
<script>
const svg = d3.select("svg");
const width = +svg.attr("width");
const height = +svg.attr("height");
const radius = 200;
const center = { x: width / 2, y: height / 2 };
// Define axes with A at the top (rotate -90 degrees)
const axes = [
{ name: "Feature A", angle: 0 },
{ name: "Feature B", angle: 120 },
{ name: "Feature C", angle: 240 }
];
axes.forEach(d => d.angle = (d.angle - 90) * Math.PI / 180);
// Sample data: [0-1] scale
const values = [0.8, 0.4, 0.6];
// Draw axis lines
svg.append("g")
.attr("class", "axis")
.selectAll("line")
.data(axes)
.join("line")
.attr("x1", center.x)
.attr("y1", center.y)
.attr("x2", d => center.x + radius * Math.cos(d.angle))
.attr("y2", d => center.y + radius * Math.sin(d.angle));
// Draw grid rings (polygons)
const levels = 5;
for (let i = 1; i <= levels; i++) {
const r = (radius / levels) * i;
const points = axes.map(d => {
return [
center.x + r * Math.cos(d.angle),
center.y + r * Math.sin(d.angle)
];
});
svg.append("polygon")
.attr("class", "grid")
.attr("points", points.map(p => p.join(",")).join(" "));
}
// Draw axis ticks
const tickLength = 5;
axes.forEach(axis => {
for (let i = 1; i <= levels; i++) {
const r = (radius / levels) * i;
const angle = axis.angle;
const x1 = center.x + r * Math.cos(angle);
const y1 = center.y + r * Math.sin(angle);
// Perpendicular tick
const perpAngle = angle + Math.PI / 2;
const x2 = x1 + tickLength * Math.cos(perpAngle);
const y2 = y1 + tickLength * Math.sin(perpAngle);
svg.append("line")
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x2)
.attr("y2", y2)
.attr("class", "tick");
}
});
// Convert values to coordinates
const valuePoints = axes.map((d, i) => {
const r = radius * values[i];
return [
center.x + r * Math.cos(d.angle),
center.y + r * Math.sin(d.angle)
];
});
// Draw data area
svg.append("polygon")
.attr("class", "area")
.attr("points", valuePoints.map(p => p.join(",")).join(" "))
// Add axis labels
svg.selectAll(".label")
.data(axes)
.join("text")
.attr("class", "label")
.attr("x", d => center.x + (radius + 20) * Math.cos(d.angle))
.attr("y", (d,i) => {
if (i === 0) { //adjust for 1st access, rather than using d.name
return center.y + (radius + 20) * Math.sin(d.angle)
} else
return center.y + (radius + 20 + 30) * Math.sin(d.angle)
})
.text(d => d.name);
</script>
</body>
</html>

1
survey_responses.json Normal file

File diff suppressed because one or more lines are too long