Browse Source

Visualization code added.

Victor Hernandez 3 years ago
parent
commit
bdd1de45e9
8 changed files with 1322 additions and 67 deletions
  1. 192
    0
      categoryBreakdown.php
  2. 185
    0
      questionBreakdown.php
  3. 663
    64
      respuestas.php
  4. 1
    1
      siCategoria.php
  5. 1
    1
      siPreguntas.php
  6. 1
    1
      siSubCategoria.php
  7. 87
    0
      special5.php
  8. 192
    0
      subcategoryBreakdown.php

+ 192
- 0
categoryBreakdown.php View File

@@ -0,0 +1,192 @@
1
+<?php 
2
+
3
+
4
+// This script fetches how many people, for every category, have answered a given score
5
+// and spits out a json with an array of category ID, category name, score and count
6
+
7
+// 	require_once 'processes/config.php';
8
+	require_once 'processes/dbh.inc.php';
9
+// 	require_once 'processes/checkLogin.php';
10
+
11
+
12
+
13
+$experienceID = mysqli_real_escape_string($connection, trim($_POST['experienceID'])); // 26
14
+$momentID = mysqli_real_escape_string($connection, trim($_POST['momentID'])); // 196
15
+$studentID = mysqli_real_escape_string($connection, trim($_POST['studentID']));
16
+
17
+
18
+
19
+// Count the number of categories for the experience
20
+$sqlCountCategories = "SELECT DISTINCT C.id
21
+						FROM category AS C
22
+						JOIN question as Q
23
+						JOIN subquestionnair_question AS SQ
24
+						JOIN experience_subquestionnair AS ES
25
+							WHERE C.id = Q.id_category
26
+							AND SQ.id_question = Q.id
27
+							AND SQ.id_subquestionnair = ES.id_subquestionnair
28
+							AND ES.id_experience = '$experienceID'";
29
+$resultCountCategories = mysqli_query($connection, $sqlCountCategories);
30
+$countCategories = $resultCountCategories->num_rows;
31
+
32
+
33
+
34
+// Fetch all the results, depending if all students or a specific one
35
+if($studentID === "all") {
36
+
37
+	$sql = "SELECT C.id AS categoryID,
38
+		   	C.categoria AS categoryName,
39
+	       	A.value AS score,
40
+    	   	COUNT(id_student) AS `count`
41
+			FROM category AS C
42
+    	    LEFT JOIN question AS Q
43
+        		ON Q.id_category = C.id
44
+			JOIN answer AS A
45
+    	    JOIN subquestionnair_question AS SQ
46
+        	JOIN experience_subquestionnair AS ES
47
+				-- WHERE Q.id_category = C.id
48
+		        WHERE A.id_question = Q.id
49
+	    	    AND A.id_question = SQ.id_question
50
+    	    	AND A.id_subquestionnair = SQ.id_subquestionnair
51
+				AND SQ.id_subquestionnair = ES.id_subquestionnair
52
+				AND ES.id_experience = '$experienceID'
53
+    	        AND SQ.id_subquestionnair = '$momentID'
54
+					GROUP BY score, categoryID
55
+					ORDER BY score, categoryID";
56
+
57
+} else {
58
+
59
+	$sql = "SELECT C.id AS categoryID,
60
+		   	C.categoria AS categoryName,
61
+	       	A.value AS score,
62
+    	   	COUNT(id_student) AS `count`
63
+			FROM category AS C
64
+	        LEFT JOIN question AS Q
65
+    	    	ON Q.id_category = C.id
66
+			JOIN answer AS A
67
+	        JOIN subquestionnair_question AS SQ
68
+    	    JOIN experience_subquestionnair AS ES
69
+        		-- WHERE Q.id_category = C.id
70
+		        WHERE A.id_question = Q.id
71
+    		    AND A.id_question = SQ.id_question
72
+        		AND A.id_subquestionnair = SQ.id_subquestionnair
73
+				AND SQ.id_subquestionnair = ES.id_subquestionnair
74
+				AND ES.id_experience = '$experienceID' -- 10
75
+    	        AND SQ.id_subquestionnair = '$momentID' -- 105
76
+        	    AND A.id_student = '$studentID' -- 118
77
+					GROUP BY score, categoryID
78
+					ORDER BY score, categoryID";
79
+
80
+}
81
+
82
+$result = mysqli_query($connection, $sql);
83
+
84
+
85
+
86
+// Initialize Arrays
87
+$datasets = [];
88
+$dataLabels = [];
89
+$scoreToIndex = [];
90
+$i = 0;
91
+$categoryIDToIndex = [];
92
+$j = 0;
93
+
94
+
95
+
96
+// Prepare result
97
+while($row = mysqli_fetch_object($result)) {
98
+
99
+
100
+	// Parse score (cause if might come from an open question)
101
+	// or it might be a float, while we should expect an int
102
+	// then cast it to string to use it as a map key
103
+	
104
+	$pointLabel = ($studentID === "all") ? " # of people who answered '" : " # of times student answered '";
105
+	
106
+	if(is_numeric($row->score)) {
107
+		$score = $pointLabel . strval((int)$row->score) . "'"; // answers may be 5.0 or 5, so first we take out the .
108
+	} else {
109
+		continue;
110
+	}
111
+
112
+
113
+	// Create new label if new category is detected
114
+	if($categoryIDToIndex[$row->categoryID] || $categoryIDToIndex[$row->categoryID] === 0) { // index 0 should be skipped
115
+		
116
+		// nothing
117
+		
118
+	} else {
119
+	
120
+		// Create new category
121
+		$dataLabels[] = $row->categoryName;
122
+		
123
+		// Store category index for $data array (inside dataset)
124
+		$categoryIDToIndex[$row->categoryID] = $j;
125
+		$j++;
126
+		
127
+	}
128
+
129
+	
130
+	
131
+	// Create dataset for corresponding score
132
+	if($scoreToIndex[$score] || $scoreToIndex[$score] === 0) { // index 0 should be skipped
133
+	
134
+		// nothing
135
+	
136
+	} else {
137
+
138
+		// serves as a tally to check
139
+		// which scores have been already
140
+		// processed AND as a map of
141
+		// score to index of the
142
+		// $dataset array
143
+		$scoreToIndex[$score] = $i;
144
+		$i++;
145
+
146
+
147
+		// Create new dataset for this label
148
+		$datasets[] = [
149
+			'label' => $score,
150
+			'data' => array_fill(0, $countCategories, 0), // array of countCategories 0's
151
+			'backgroundColor' => '#' . substr(sha1($score), 0, 6),
152
+// 			'borderWidth' => 3,
153
+// 			'borderColor' => '#' . substr(sha1($score), 0, 6),
154
+			'stack' => $score
155
+// 			'fill' => false
156
+		];
157
+		
158
+	}
159
+
160
+
161
+	// Append data point to corresponding category
162
+	$datasets[$scoreToIndex[$score]]['data'][$categoryIDToIndex[$row->categoryID]] = (int)$row->count;	
163
+
164
+
165
+}
166
+
167
+
168
+
169
+
170
+/*
171
+	let data = {
172
+		labels: dataLabels,
173
+		datasets: [{
174
+			label: auxiliaries['myChart2'].label,
175
+			data: dataPoints,
176
+			backgroundColor: auxiliaries['myChart2'].backgroundColor,
177
+			borderColor: auxiliaries['myChart2'].borderColor,
178
+			fill: false
179
+		}]
180
+	};
181
+*/
182
+
183
+
184
+$arr = [
185
+	'datasets' => $datasets,
186
+	'dataLabels' => $dataLabels
187
+];
188
+
189
+
190
+// Spit out response JSON
191
+echo header("Content-Type: application/json; charset=utf-8");
192
+echo json_encode($arr);

+ 185
- 0
questionBreakdown.php View File

@@ -0,0 +1,185 @@
1
+<?php 
2
+
3
+
4
+// This script fetches how many people, for every question, have answered a given score
5
+// and spits out a json with an array of question ID, question premise, score and count
6
+
7
+// 	require_once 'processes/config.php';
8
+	require_once 'processes/dbh.inc.php';
9
+// 	require_once 'processes/checkLogin.php';
10
+
11
+
12
+
13
+$experienceID = mysqli_real_escape_string($connection, trim($_POST['experienceID'])); // 26
14
+$momentID = mysqli_real_escape_string($connection, trim($_POST['momentID'])); // 196
15
+$studentID = mysqli_real_escape_string($connection, trim($_POST['studentID']));
16
+
17
+
18
+// WARNING MAYBE AÑADIR/QUITAR momentID
19
+// Count the number of questions for the experience
20
+$sqlCountQuestion = "SELECT DISTINCT Q.id
21
+						FROM question as Q
22
+						JOIN subquestionnair_question AS SQ
23
+						JOIN experience_subquestionnair AS ES
24
+							WHERE SQ.id_question = Q.id
25
+							AND SQ.id_subquestionnair = ES.id_subquestionnair
26
+							AND SQ.id_subquestionnair = '$momentID'
27
+							AND ES.id_experience = '$experienceID'";
28
+$resultCountQuestion = mysqli_query($connection, $sqlCountQuestion);
29
+$countQuestions = $resultCountQuestion->num_rows;
30
+
31
+
32
+
33
+// Fetch all the results, depending if all students or a specific one
34
+if($studentID === "all") {
35
+
36
+	$sql = "SELECT Q.id AS questionID,
37
+		   	Q.premise AS questionPremise,
38
+	       	A.value AS score,
39
+    	   	COUNT(id_student) AS `count`
40
+			FROM question AS Q
41
+			JOIN answer AS A
42
+    	    JOIN subquestionnair_question AS SQ
43
+        	JOIN experience_subquestionnair AS ES
44
+		        WHERE A.id_question = Q.id
45
+	    	    AND A.id_question = SQ.id_question
46
+    	    	AND A.id_subquestionnair = SQ.id_subquestionnair
47
+				AND SQ.id_subquestionnair = ES.id_subquestionnair
48
+				AND ES.id_experience = '$experienceID'
49
+    	        AND SQ.id_subquestionnair = '$momentID'
50
+					GROUP BY score, questionID
51
+					ORDER BY score, questionID";
52
+
53
+} else {
54
+
55
+	$sql = "SELECT Q.id AS questionID,
56
+		   	Q.premise AS questionPremise,
57
+	       	A.value AS score,
58
+    	   	COUNT(id_student) AS `count`
59
+			FROM question AS Q
60
+			JOIN answer AS A
61
+    	    JOIN subquestionnair_question AS SQ
62
+        	JOIN experience_subquestionnair AS ES
63
+		        WHERE A.id_question = Q.id
64
+	    	    AND A.id_question = SQ.id_question
65
+    	    	AND A.id_subquestionnair = SQ.id_subquestionnair
66
+				AND SQ.id_subquestionnair = ES.id_subquestionnair
67
+				AND ES.id_experience = '$experienceID'
68
+    	        AND SQ.id_subquestionnair = '$momentID'
69
+        	    AND A.id_student = '$studentID'
70
+					GROUP BY score, questionID
71
+					ORDER BY score, questionID";
72
+
73
+}
74
+
75
+$result = mysqli_query($connection, $sql);
76
+
77
+
78
+
79
+// Initialize Arrays
80
+$datasets = [];
81
+$dataLabels = [];
82
+$scoreToIndex = [];
83
+$i = 0;
84
+$questionIDToIndex = [];
85
+$j = 0;
86
+
87
+
88
+
89
+// Prepare result
90
+while($row = mysqli_fetch_object($result)) {
91
+
92
+
93
+	// Parse score (cause if might come from an open question)
94
+	// or it might be a float, while we should expect an int
95
+	// then cast it to string to use it as a map key
96
+	
97
+	$pointLabel = ($studentID === "all") ? " # of people who answered '" : " # of times student answered '";
98
+	
99
+	if(is_numeric($row->score)) {
100
+		$score = $pointLabel . strval((int)$row->score) . "'"; // answers may be 5.0 or 5, so first we take out the .
101
+	} else {
102
+		continue;
103
+	}
104
+
105
+
106
+	// Create new label if new question is detected
107
+	if($questionIDToIndex[$row->questionID] || $questionIDToIndex[$row->questionID] === 0) { // index 0 should be skipped
108
+		
109
+		// nothing
110
+		
111
+	} else {
112
+	
113
+		// Create new question
114
+		$dataLabels[] = $row->questionID; //substr($row->questionPremise, 0, 30);//questionPremise;
115
+		
116
+		// Store question index for $data array (inside dataset)
117
+		$questionIDToIndex[$row->questionID] = $j;
118
+		$j++;
119
+		
120
+	}
121
+
122
+	
123
+	
124
+	// Create dataset for corresponding score
125
+	if($scoreToIndex[$score] || $scoreToIndex[$score] === 0) { // index 0 should be skipped
126
+	
127
+		// nothing
128
+	
129
+	} else {
130
+
131
+		// serves as a tally to check
132
+		// which scores have been already
133
+		// processed AND as a map of
134
+		// score to index of the
135
+		// $dataset array
136
+		$scoreToIndex[$score] = $i;
137
+		$i++;
138
+
139
+
140
+		// Create new dataset for this label
141
+		$datasets[] = [
142
+			'label' => $score,
143
+			'data' => array_fill(0, $countQuestions, 0), // array of countQuestions 0's
144
+			'backgroundColor' => '#' . substr(sha1($score), 0, 6),
145
+// 			'borderWidth' => 3,
146
+// 			'borderColor' => '#' . substr(sha1($score), 0, 6),
147
+			'stack' => $score
148
+// 			'fill' => false
149
+		];
150
+		
151
+	}
152
+
153
+
154
+	// Append data point to corresponding question
155
+	$datasets[$scoreToIndex[$score]]['data'][$questionIDToIndex[$row->questionID]] = (int)$row->count;	
156
+
157
+
158
+}
159
+
160
+
161
+
162
+
163
+/*
164
+	let data = {
165
+		labels: dataLabels,
166
+		datasets: [{
167
+			label: auxiliaries['myChart2'].label,
168
+			data: dataPoints,
169
+			backgroundColor: auxiliaries['myChart2'].backgroundColor,
170
+			borderColor: auxiliaries['myChart2'].borderColor,
171
+			fill: false
172
+		}]
173
+	};
174
+*/
175
+
176
+
177
+$arr = [
178
+	'datasets' => $datasets,
179
+	'dataLabels' => $dataLabels
180
+];
181
+
182
+
183
+// Spit out response JSON
184
+echo header("Content-Type: application/json; charset=utf-8");
185
+echo json_encode($arr);

+ 663
- 64
respuestas.php View File

@@ -4,27 +4,36 @@
4 4
 	require_once 'processes/dbh.inc.php';
5 5
 	require_once 'processes/checkLogin.php';
6 6
 	include_once 'header.php';
7
-	
8
-	//include("conection.php");
9 7
 	include("funciones.php");
10 8
 	
9
+	
10
+	
11 11
 	//SELECT a.id_question, q.premise, a.id_subquestionnair , q.id_category,q.id_subcategory  FROM `answer` a, question q WHERE a.`id_student` = 1860  and a.id_question=q.id ORDER BY `a`.`id_question` ASC
12
-	$id_experiencia = mysqli_real_escape_string($connection, trim($_GET['exp']));
12
+	$experienceID = mysqli_real_escape_string($connection, trim($_GET['exp']));
13 13
 	//$id_student = 1860;
14 14
 
15 15
 	///////Busca cantidad de sub cuestionarios - Empieza////////////////
16
-	$sqlSelect = sprintf("SELECT  count(*) FROM experience_questionnair eq, subquestionnair qs where eq.id_questionnair=qs.id_questionnair and eq.id_experience = %s",
17
-		GetSQLValueString($id_experiencia, "int")
16
+	$sqlSelect = sprintf("SELECT * FROM experience_questionnair AS EQ JOIN subquestionnair AS SQ WHERE EQ.id_questionnair = SQ.id_questionnair AND EQ.id_experience = %s ORDER BY SQ.date_to_administer ASC",
17
+		GetSQLValueString($experienceID, "int")
18 18
 	);
19
-	$dbresultSel = mysqli_query($connection, $sqlSelect);		
20
-	$row = mysqli_fetch_array($dbresultSel);
21
-	$cant_sub_subquestionnair = $row[0];
22
-	//print($cant_sub_subquestionnair);
19
+	
20
+	$dbresultSel = mysqli_query($connection, $sqlSelect);
21
+	
22
+	$moments = [];
23
+	while($row = mysqli_fetch_array($dbresultSel)) {
24
+		$moments[] = $row; // warning: includes first (pre) and last (post)
25
+	}
26
+	
27
+// 	print("<pre>");
28
+// 	var_dump($moments);
29
+// 	exit();
30
+
31
+	//print(count($moments));
23 32
 	///////Termina - Busca cantidad de sub cuestionarios - ////////////////
24 33
 
25 34
 	///////Busca Preguntas - Empieza////////////////
26
-	$sqlSelect = sprintf("SELECT distinct q.* FROM `experience_questionnair` eq, questionnair_question qq, question q WHERE eq.id_questionnair=qq.`id_questionnair` and qq.id_question=q.id  and id_experience = %s order by q.id",
27
-		GetSQLValueString($id_experiencia, "int")
35
+	$sqlSelect = sprintf("SELECT DISTINCT Q.* FROM `experience_questionnair` AS EQ JOIN questionnair_question AS QQ JOIN question AS Q WHERE EQ.id_questionnair = QQ.`id_questionnair` AND QQ.id_question = Q.id AND EQ.id_experience = %s ORDER BY Q.id",
36
+		GetSQLValueString($experienceID, "int")
28 37
 	);
29 38
 	$dbresultSel = mysqli_query($connection, $sqlSelect);
30 39
 	$resultados = array();
@@ -37,7 +46,7 @@
37 46
 		$emas[] = "    "; // PRE test
38 47
 		
39 48
 		// SUBQUESTIONNAIRES
40
-		for($i = 1; $i <= $cant_sub_subquestionnair-2; $i++) {
49
+		for($i = 1; $i <= count($moments) - 2; $i++) {
41 50
 			$emas[] = "    ";
42 51
 		}
43 52
 		
@@ -55,12 +64,29 @@
55 64
 
56 65
 	///////Busca Estudiantes - Empieza////////////////
57 66
 	$sqlSelectEst = sprintf("SELECT s.id id_stu FROM student s, student_participate_experience se WHERE s.id=se.id_student and se.id_experience = %s order by id_stu",
58
-		GetSQLValueString($id_experiencia, "int")
67
+		GetSQLValueString($experienceID, "int")
59 68
 	);
60 69
 	$dbresultEst = mysqli_query($connection,$sqlSelectEst);
61 70
 	///////Termina - Busca Estudiantes - ////////////////
71
+	
72
+	
73
+	$queryExperience = "SELECT * FROM experience WHERE id = '$experienceID'";
74
+	$resultExperience = mysqli_query($connection, $queryExperience);
75
+	$experience = mysqli_fetch_assoc($resultExperience);
62 76
 
63 77
 ?>
78
+
79
+	<style>
80
+		.table-header {
81
+			width: 30px;
82
+			vertical-align: middle;
83
+			text-align: center;
84
+		}
85
+		
86
+		.table-header.clickable {
87
+			cursor: pointer;
88
+		}
89
+	</style>
64 90
 	
65 91
 	<!--START OF respuestas.php-->
66 92
 	<body>
@@ -71,21 +97,61 @@
71 97
             	<img src="./img/pen_800x800.png" alt="tania logo pen" width="25" height="25">
72 98
         	</a>
73 99
         	<div id="account">
100
+        		<a class="nav-link" style="margin-right: 1rem;" href="<?= $_SERVER['HTTP_REFERER'] ?>"><i class="fas fa-arrow-left"></i> Back</a>
74 101
             	<a class="sign-out" href="./processes/logout.php">Sign Out</a>
75 102
         	</div>
76 103
     	</header>
77 104
     	
78
-    	<?php if($id_experiencia == 1): ?>
79
-    	<div class="container" style="margin-top: 12vh;"><div class="row"><div class="col"><h4>Warning: Este script no funciona para esta experiencia en particular (la parte de escoger los resultados por cada estudiante).</h4></div></div></div>
105
+    	<?php if($experienceID === 1): ?>
106
+    	<div class="container" style="margin-top: 8rem;">
107
+    		<div class="row">
108
+    			<div class="col">
109
+    				<h4>Warning: Este script no funciona para esta experiencia en particular (la parte de escoger los resultados por cada estudiante).</h4>
110
+  				</div>
111
+			</div>
112
+    	</div>
80 113
     	<?php endif; ?>
81 114
 	
82
-		<div class="container" style="margin-top: 12vh;">
115
+		<div class="container" style="margin-top: 8rem;">
116
+		
117
+			<!-- CHART -->
118
+			<div class="row">
119
+				<div class="col-sm-10">
120
+					<h2>
121
+						Results – <span class="text-muted"><?php echo $experience['title'] ?></span>
122
+						<button type="button" class="btn btn-link btn-lg" data-toggle="popover" title="Table Tips" data-content="Toggle between one student or all students. Also, you can view results with respect to categories and subcategories. Click on each moment to get a breakdown of their details." data-placement="bottom">
123
+							<span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span>
124
+						</button>
125
+					</h2>
126
+				</div>
127
+				<div class="col-sm-2">
128
+					<br>
129
+					<form id="exportForm" method="POST" action="processes/export.php">
130
+						<input type="hidden" name="studentID" value="all">
131
+						<input type="hidden" name="id_exp" value="<?php echo $experienceID; ?>">
132
+						<input type="hidden" name="res_type" value="0">
133
+						<button type="submit" name="export" class="btn btn-primary">Export Raw</button>
134
+					</form>
135
+				</div>
136
+			</div>
137
+
138
+			<!-- CHART 2 -->
139
+			<div class="row">
140
+				<div class="col-md-12">
141
+					<canvas onclick="alert('holis brodel')" id="myChart2" style="margin: 2rem auto;">
142
+						<p>Your browser does not support the canvas element.</p>
143
+					</canvas>
144
+				</div>
145
+			</div>		
146
+		
83 147
 			<div class="row">
84 148
 				<div class="col">
85 149
 				
86 150
 				
87 151
 				
88 152
 				
153
+		<div class="table-responsive">
154
+		
89 155
 		<table class="table table-bordered">
90 156
 			<thead>
91 157
 				<tr>
@@ -93,25 +159,43 @@
93 159
 					<th scope="col">
94 160
 					
95 161
 						<form>
96
-							<select class="form-control" id="id_student" style="text-align-last: center;">
162
+							<select class="form-control" id="studentID" style="text-align-last: center;">
97 163
 								<option disabled selected>Select Student</option>
98 164
 								<option selected value="all">All Students</option>
99
-<?php while($rowEst = mysqli_fetch_array($dbresultEst)): ?>
165
+								<?php while($rowEst = mysqli_fetch_array($dbresultEst)): ?>
100 166
 								<option value='<?php echo $rowEst['id_stu']; ?>'><?php echo $rowEst['id_stu']; ?></option>
101
-<?php endwhile; ?>
167
+								<?php endwhile; ?>
102 168
 							</select>
103
-							<input type="hidden" value="<?php echo $id_experiencia;?>" id="id_experiencia">
169
+							<input type="hidden" value="<?php echo $experienceID;?>" id="experienceID">
104 170
 						</form>
105 171
 
106 172
 					</th>
107
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">Pre</th>
108
-					<th colspan="<?php echo $cant_sub_subquestionnair - 2;?>" style="vertical-align: middle; text-align: center;">Moments</th>
109
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">Post</th>
110
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">x&#772</th>
111
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">&sigma;</th>
112
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">% &Delta;<br><small>Ema - Pre</small></th>
113
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">% &Delta;<br><small>Post - Ema</small></th>
114
-					<th scope="col" rowspan="2" style="width:30px; vertical-align: middle; text-align: center;">% &Delta;<br><small>Post - Pre</small></th>
173
+					<th
174
+						scope="col"
175
+						rowspan="2"
176
+						class="table-header clickable"
177
+						data-momentid="<?= $moments[0]['id'] ?>"
178
+						onclick="$('#breakdownModal').modal('show')"
179
+						data-momentnumber="PRE"
180
+					>
181
+						Pre
182
+					</th>
183
+					<th colspan="<?php echo count($moments) - 2; ?>" style="vertical-align: middle; text-align: center;">Moments</th>
184
+					<th
185
+						scope="col"
186
+						rowspan="2"
187
+						class="table-header clickable"
188
+						data-momentid="<?= $moments[count($moments) - 1]['id'] ?>"
189
+						onclick="$('#breakdownModal').modal('show')"
190
+						data-momentnumber="POST"
191
+					>
192
+						Post
193
+					</th>
194
+					<th scope="col" rowspan="2" class="table-header">&mu;</th><!--x&#772-->
195
+					<th scope="col" rowspan="2" class="table-header">&sigma;</th>
196
+					<th scope="col" rowspan="2" class="table-header">%&Delta;<br><small>Moments - Pre</small></th>
197
+					<th scope="col" rowspan="2" class="table-header">%&Delta;<br><small>Post - Moments</small></th>
198
+					<th scope="col" rowspan="2" class="table-header">%&Delta;<br><small>Post - Pre</small></th>
115 199
 				</tr>
116 200
 				<tr>
117 201
 					<th>   
@@ -122,80 +206,595 @@
122 206
 								<option value="1">Subcategories</option>
123 207
 								<option value="2">Categories</option>
124 208
 							</select>
125
-							<input type="hidden" value="<?php echo $id_experiencia;?>" id="id_experiencia">
209
+							<input type="hidden" value="<?php echo $experienceID;?>" id="experienceID">
126 210
 						</form>
127 211
 	
128 212
 					</th>
129
-<?php for($i = 1; $i <= $cant_sub_subquestionnair-2; $i++): ?>
130
-					<th scope="col" style="width:30px vertical-align: middle; text-align: center;""><?php echo $i; ?></th>
131
-<?php endfor; ?>
213
+					<?php for($i = 1; $i <= count($moments) - 2; $i++): ?>
214
+					<th
215
+						class="table-header clickable"
216
+						scope="col"
217
+						data-momentid="<?= $moments[$i]['id'] ?>"
218
+						onclick="$('#breakdownModal').modal('show')"
219
+						data-momentnumber="<?= '#' . $i ?>"
220
+					>
221
+						<?php echo $i; ?>
222
+					</th>
223
+					<?php endfor; ?>
132 224
 				</tr>
133 225
 			</thead>
134 226
 			<tbody id="loqueseve">
135
-<?php foreach($resultados as $pregunta): ?>
227
+				<?php foreach($resultados as $pregunta): ?>
136 228
 				<tr>
137
-<?php foreach($pregunta as $item): ?>
229
+					<?php foreach($pregunta as $item): ?>
138 230
 					<td style="vertical-align: middle; text-align: center;"><?php echo $item ?></td>
139
-<?php endforeach; ?>
231
+					<?php endforeach; ?>
140 232
 				</tr>
141
-<?php endforeach; ?>
233
+				<?php endforeach; ?>
142 234
 			</tbody>
143 235
 		</table>
144 236
 		
145
-				</div><!--col-->
146
-			</div><!--row-->
147 237
 		
148
-
149
-			<div class="row">
150
-				<div class="col">
151
-					<form id="exportForm" method="POST" action="processes/export.php">
152
-						<input type="hidden" name="id_student" value="all">
153
-						<input type="hidden" name="id_exp" value="<?php echo $id_experiencia; ?>">
154
-						<input type="hidden" name="res_type" value="0">
155
-						<button type="submit" name="export" class="btn btn-info">Export</button>
156
-					</form>
238
+		</div><!--table-responsive-->
239
+		
240
+		
157 241
 				</div><!--col-->
158 242
 			</div><!--row-->
159 243
 
160 244
 		</div><!--container-->
161 245
 		
162 246
 		
247
+		<!-- CATEGORY CHART MODAL -->
248
+		<div class='modal' id='breakdownModal' tabindex='-1' role='dialog' aria-labelledby='breakdownModalLabel' aria-hidden='true'>
249
+			<div class='modal-dialog modal-lg' role='document'>
250
+ 				<div class='modal-content'>	
251
+						
252
+					<div class='modal-header'>
253
+						<button type='button' class='close' data-dismiss='modal' aria-label='Close'>
254
+							<span aria-hidden='true'>&times;</span>
255
+						</button>
256
+				 	  	<h3 class='modal-title' id='breakdownModalLabel'>Breakdown by <span id="breakdown-type">Category</span> <small id="moment-number">(Moment #)</small></h3>
257
+   					</div>
258
+ 						
259
+ 					<div class='modal-body'>
260
+ 						
261
+						<div class="row">
262
+ 							<div class="col-md-12">
263
+ 								<canvas id="categoryChart">
264
+									<p>Your browser does not support the canvas element.</p>
265
+								</canvas>
266
+ 								<canvas id="subcategoryChart">
267
+									<p>Your browser does not support the canvas element.</p>
268
+								</canvas>
269
+ 								<canvas id="questionChart">
270
+									<p>Your browser does not support the canvas element.</p>
271
+								</canvas>
272
+ 							</div>
273
+ 						</div>
274
+
275
+      				</div><!--modal-body-->
276
+     					
277
+     				<div class='modal-footer'>
278
+       					<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>
279
+      				</div>  					
280
+    			
281
+    			</div><!--modal-content-->
282
+  			</div><!--modal-dialog-->
283
+		</div><!--modal-->
284
+		
285
+		
286
+		
287
+		<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
288
+<!-- 		<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.bundle.min.js"></script> -->
289
+		
290
+		
291
+		
163 292
 <script>
164
-window.onload = function() {
165
-$("#tipo_resumen").change(function() {
166 293
 
294
+// Enable popovers
295
+$(function () {
296
+  $('[data-toggle="popover"]').popover();
297
+});
298
+
299
+
300
+// Global Map of charts
301
+let myCharts = {};
302
+
303
+
304
+function fetchData(elementID = '#studentID') {
305
+
306
+	// Define a map from elementID to element index
307
+	let idToIndex = {
308
+		'#studentID': 0,
309
+		'#tipo_resumen': 2
310
+	};
311
+	
312
+	// Fetch corresponding element index
313
+	let index = idToIndex[elementID];
314
+	
315
+	// Manually change the studentID and respuestaType for the export form
167 316
 	let inputs = document.querySelector('#exportForm').children;
168
-	inputs[2].value = $("#tipo_resumen").val();
317
+	inputs[index].value = $(elementID).val();
318
+
319
+	// Fetch data from database
320
+	$.post('respuestasData.php',
321
+		{
322
+			id_student: $('#studentID').val(),
323
+			id_exp: $('#experienceID').val(),
324
+			res_type: $('#tipo_resumen').val()
325
+		},
326
+		function(data, status) {
327
+			$('#loqueseve').html(data);
328
+		}
329
+	);
169 330
 	
170
-	$.post("respuestasData.php",
331
+}
332
+
333
+function fetchAnswerRate() {
334
+
335
+	let experienceID = $("#experienceID").val()
336
+
337
+	$.post("special5.php",
171 338
 		{
172
-			id_student: $("#id_student").val(),
173
-			id_exp: $("#id_experiencia").val(),
174
-			res_type: $("#tipo_resumen").val()
339
+			experienceID: experienceID
175 340
 		},
176 341
 		function(data, status) {
177
-			$("#loqueseve").html(data);
342
+		
343
+			console.log(data);
344
+		
345
+			// Fetch Data
346
+			let payload = JSON.parse(data);
347
+			let dataSet = payload.data; // array of {count, momentID, date} objects
348
+			let maxScore = payload.maxScore // the maximum 'count' value from the data array
349
+			
350
+			// Initialize Data Containers
351
+			let dataPoints = []; // [10, 20, 50, 70, 30];
352
+			let dataLabels = []; // ['1', '2', '3', '4', '5'];
353
+			
354
+			// Create momentID -> momentNumber map for styling purposes
355
+			let momentIDToNumber = {};
356
+			let number = 1; // start at 1
357
+			
358
+			// Fill Data Containers
359
+			dataSet.forEach(function(point) {
360
+			
361
+				// If given ID not in map, create it and increment number by 1
362
+				if(momentIDToNumber[point.momentID] === undefined) {
363
+					momentIDToNumber[point.momentID] = number;
364
+					number++;
365
+				}
366
+			
367
+				dataPoints.push(point.count);
368
+				dataLabels.push(`${point.momentTitle} (${point.date})`);
369
+				
370
+			});
371
+			
372
+			// Render Chart
373
+			renderStaticChart(dataPoints, dataLabels, maxScore);
374
+			
178 375
 		}
179 376
 	);
180
-});
181 377
 
182
-$("#id_student").change(function() {
378
+}
183 379
 
184
-	let inputs = document.querySelector('#exportForm').children;
185
-	inputs[0].value = $("#id_student").val();
186 380
 
187
-	$.post("respuestasData.php",
381
+function fetchBreakdownData(momentID, momentNumber, breakdownType) {
382
+
383
+	
384
+	// Fetch Necessary Data
385
+	let experienceID = $('#experienceID').val();
386
+	let studentID = $('#studentID').val();
387
+	
388
+	
389
+	
390
+	// Set Metadata Maps (breakdownType -> metadata)
391
+	let breakdownChart = {
392
+		'0': 'questionChart',
393
+		'1': 'subcategoryChart',
394
+		'2': 'categoryChart'
395
+	};
396
+	
397
+	let breakdownText = {
398
+		'0': 'Questions',
399
+		'1': 'Subcategories',
400
+		'2': 'Categories'
401
+	};
402
+	
403
+	let breakdownScripts = {
404
+		'0': 'questionBreakdown.php',
405
+		'1': 'subcategoryBreakdown.php',
406
+		'2': 'categoryBreakdown.php'
407
+	};
408
+	
409
+	
410
+	
411
+	// Change Breakdown Type Text
412
+	$('#breakdownModal #breakdown-type').text(breakdownText[breakdownType]);
413
+	
414
+	
415
+	
416
+	// Change Moment Number
417
+	let metaText = momentNumber.includes('#') ? `(Moment  ${momentNumber}` : `(${momentNumber} TEST`;
418
+	if(studentID !== 'all') {
419
+		metaText += ` of student ${studentID}`; // if a specific student is selected, say so
420
+	}
421
+	metaText += ')';
422
+	$('#breakdownModal #moment-number').text(metaText);
423
+	
424
+	
425
+	
426
+	// Set Loading State on modal
427
+	$('#breakdownModal canvas').hide();
428
+	let loader = $('<div class="text-center" style="margin: 5rem 0"><h3>Loading Chart...</h3><span class="loader loader-lg"></span></div>').insertAfter('#breakdownModal canvas:last-child');
429
+	
430
+	
431
+	
432
+	// Make Request & Update Selected Chart
433
+	$.post(breakdownScripts[breakdownType],
188 434
 		{
189
-			id_student: $("#id_student").val(),
190
-			id_exp: $("#id_experiencia").val(),
191
-			res_type: $("#tipo_resumen").val()
435
+			experienceID: experienceID,
436
+			momentID: momentID,
437
+			studentID: studentID
192 438
 		},
193 439
 		function(data, status) {
194
-			$("#loqueseve").html(data);
440
+		
441
+			console.log(data);
442
+			
443
+			// Fetch Data
444
+			let payload = data; //JSON.parse(data); // aparently already an object
445
+			let datasets = payload.datasets; // array of objects (datasets)
446
+			let dataLabels = payload.dataLabels; // array of labels ["ResearchSelfEfficacy", "SourcesSelfEfficacy"]
447
+
448
+			// Render Chart
449
+			updateBreakdownChart(datasets, dataLabels, breakdownChart[breakdownType]);
450
+			
451
+			// Stop Loading State and Show Chart
452
+			$(loader).remove();
453
+			$('#' + breakdownChart[breakdownType]).show();
454
+			
455
+		
195 456
 		}
196 457
 	);
458
+
459
+}
460
+
461
+
462
+
463
+
464
+window.addEventListener('load', function() {
465
+
466
+	// 1. Fetch Data for when changing select menus
467
+	fetchData(); // can be "#tipo_resumen" too
468
+	
469
+	// 2. Set Event Listeners for menu change
470
+	$("#studentID").change(function() {
471
+		fetchData("#studentID");
472
+	});
473
+	$("#tipo_resumen").change(function() {
474
+		fetchData("#tipo_resumen");
475
+	});
476
+	
477
+	// 3. Set Event Listeners for table clicks
478
+	$("th.table-header.clickable").on("click", function() {
479
+		let momentID = $(this).data("momentid");
480
+		let momentNumber = $(this).data("momentnumber");
481
+		let breakdownType = $("#tipo_resumen").val(); // 0 for questions, 1 for subcategories, 2 for categories
482
+		fetchBreakdownData(momentID, momentNumber, breakdownType);
483
+	});
484
+	
485
+	// 4. Fetch and Render Answer Rate Chart
486
+	fetchAnswerRate();
487
+	
488
+	// 5. Create Breakdown Charts
489
+	['categoryChart', 'subcategoryChart', 'questionChart'].forEach(function(chartName) {
490
+		createBreakdownChart(chartName);
491
+	});
492
+	
197 493
 });
494
+
495
+
496
+
497
+function createBreakdownChart(chartName) {
498
+
499
+
500
+	// Set Metadata Maps
501
+	let auxiliaries = {
502
+		'categoryChart': {
503
+			titleText: 'Distribution of Answers per Category',
504
+			xLabel: 'Category',
505
+			yLabel: 'Amount of people'
506
+		},
507
+		'subcategoryChart': {
508
+			titleText: 'Distribution of Answers per Subcategory',
509
+			xLabel: 'Subcategory',
510
+			yLabel: 'Amount of people'
511
+		},
512
+		'questionChart': {
513
+			titleText: 'Distribution of Answers per Question (not done yet)',
514
+			xLabel: 'Question',
515
+			yLabel: 'Amount of people'
516
+		},
517
+		
518
+	};
519
+
520
+
521
+
522
+	// Initialize Data
523
+	let data = {
524
+		labels: [],
525
+		datasets: []
526
+	};
527
+	
528
+	
529
+	// Initialize Options
530
+	let options = {
531
+		title: {
532
+			display: true,
533
+			text: auxiliaries[chartName].titleText
534
+		},
535
+		legend: false,
536
+		tooltips: {
537
+			position: 'average',
538
+			mode: 'index',
539
+			intersect: false
540
+		},
541
+		scales: {
542
+			xAxes: [{
543
+				stacked: true, // for vertical bars
544
+				display: true,
545
+				scaleLabel: {
546
+					display: true,
547
+					labelString: auxiliaries[chartName].xLabel
548
+				}
549
+			}],
550
+			yAxes: [{
551
+				stacked: false, // for vertical bars
552
+				display: true,
553
+				scaleLabel: {
554
+					display: true,
555
+					labelString: auxiliaries[chartName].yLabel
556
+				},
557
+				ticks: {
558
+					suggestedMin: 0
559
+				}
560
+			}]
561
+		},
562
+		layout: {
563
+			padding: 30
564
+		}
565
+	};
566
+	
567
+	
568
+	// Set Chart Configuration
569
+	let config = {
570
+		type: 'bar',
571
+		data: data,
572
+		options: options
573
+	};
574
+	
575
+	
576
+	// Create Chart
577
+	let chart = new Chart(chartName, config);
578
+	
579
+	
580
+	// Store Chart Reference in global dict
581
+	myCharts[chartName] = chart;
582
+
583
+}
584
+
585
+
586
+function updateBreakdownChart(datasets, dataLabels, chartName) {
587
+
588
+	// Store chart
589
+	let chart = myCharts[chartName];
590
+	
591
+	// Remove all labels
592
+	while(chart.data.labels.pop());
593
+	
594
+	// Insert new labels
595
+	dataLabels.forEach(function(label) {
596
+		chart.data.labels.push(label);
597
+	});
598
+	
599
+	// Remove all datasets
600
+	while(chart.data.datasets.pop());
601
+	
602
+	// Insert new datasets (with their respective data points)
603
+	datasets.forEach(function(dataset) {
604
+		chart.data.datasets.push(dataset);
605
+	});
606
+	
607
+	// Update chart
608
+	chart.update();
609
+	console.log(chart.config);
610
+
198 611
 }
612
+
613
+
614
+
615
+function renderStaticChart(dataPoints, dataLabels, maxScore, elementID) {
616
+
617
+
618
+	// Map from elementID to corresponding chart metadata
619
+	let auxiliaries = {
620
+		'myChart2': {
621
+			label: ' # of people who answered',
622
+			backgroundColor: '#A4262C',
623
+			borderColor: '#A4262C',
624
+			titleText: 'Answer Rate',
625
+			xAxisLabel: 'Moment Date & Number',
626
+			yAxisLabel: 'Answers'
627
+		}
628
+	};
629
+	
630
+	// Initialize Data
631
+	let data = {
632
+		labels: dataLabels,
633
+		datasets: [{
634
+			label: auxiliaries['myChart2'].label,
635
+			data: dataPoints,
636
+			backgroundColor: auxiliaries['myChart2'].backgroundColor,
637
+			borderColor: auxiliaries['myChart2'].borderColor,
638
+			fill: false
639
+		}]
640
+	};
641
+	
642
+	// Initialize Options
643
+	let options = {
644
+		title: {
645
+			display: true,
646
+			text: auxiliaries['myChart2'].titleText
647
+		},
648
+		legend: false,
649
+		tooltips: {
650
+			position: 'average',
651
+			mode: 'index',
652
+			intersect: false
653
+		},
654
+		scales: {
655
+			xAxes: [{
656
+				stacked: true, // for vertical bars
657
+				display: true,
658
+				scaleLabel: {
659
+					display: true,
660
+					labelString: auxiliaries['myChart2'].xAxisLabel
661
+				}
662
+			}],
663
+			yAxes: [{
664
+				stacked: false, // for vertical bars
665
+				display: true,
666
+				scaleLabel: {
667
+					display: true,
668
+					labelString: auxiliaries['myChart2'].yAxisLabel
669
+				},
670
+				ticks: {
671
+					suggestedMin: 0, // substitute with data.min or something
672
+					suggestedMax: maxScore // substitute with data.max or something
673
+				}
674
+			}]
675
+		}
676
+	};
677
+	
678
+	// Set Chart Configuration
679
+	let config = {
680
+		type: 'bar',
681
+		data: data,
682
+		options: options
683
+	};
684
+	
685
+	let myBarChart = new Chart('myChart2', config);	
686
+
687
+}
688
+
689
+
690
+// Globals for chart timeline
691
+let pointCount = 1;
692
+
693
+function renderMovingChart(dataPoints, dataLabels, maxScore) {
694
+	
695
+	// Initialize Data
696
+	let data = {
697
+			labels: [], // initially empty, because it will be filled slowly for the timeline
698
+			datasets: [{
699
+				label: 'Moments answered per day and moment',
700
+				data: [], // initially empty, because it will be filled slowly for the timeline
701
+				backgroundColor: 'red',
702
+    	        borderColor: 'red',
703
+// 	            borderWidth: 1,
704
+	            fill: false
705
+			}]
706
+		};
707
+	
708
+	// Initialize Options
709
+	let options = {
710
+			legend: false,
711
+			tooltips: {
712
+				position: 'average',
713
+				mode: 'index',
714
+				intersect: false
715
+			},
716
+			scales: {
717
+				xAxes: [{
718
+					display: true,
719
+					scaleLabel: {
720
+						display: true,
721
+						labelString: 'Moment Date (#ID)'
722
+					}
723
+				}],
724
+				yAxes: [{
725
+					display: true,
726
+					scaleLabel: {
727
+						display: true,
728
+						labelString: 'Moments Answered'
729
+					},
730
+					ticks: {
731
+						suggestedMin: 0, // substitute with data.min or something
732
+						suggestedMax: maxScore // substitute with data.max or something
733
+					}
734
+				}]
735
+			}
736
+		};
737
+	
738
+	// Set Chart Configuration
739
+	let config = {
740
+		type: 'line',
741
+		data: data,
742
+		options: options
743
+	}
744
+	
745
+	// config2
746
+	let config2 = {
747
+		type: 'bar',
748
+		data: data,
749
+		options: options
750
+	};
751
+	
752
+	// Render Chart
753
+// 	let myLineChart = new Chart('myChart', config);
754
+	let myBarChart = new Chart('myChart2', config2);	
755
+
756
+
757
+	// Set Timeline
758
+	setInterval(function() {
759
+
760
+		// Modularly increment 'index'/'counter'
761
+		pointCount = pointCount % dataPoints.length + 1;
762
+	
763
+		if(pointCount !== 1) {
764
+		
765
+			// Create the label for that ith point
766
+			config.data.labels.push(dataLabels[pointCount - 1]);
767
+	
768
+			// Overwrite existing
769
+			config.data.datasets.forEach(function(dataset) {
770
+				dataset.data.push(dataPoints[pointCount - 1]);
771
+			});
772
+	
773
+		} else {
774
+	
775
+			// Remove all labels except the first
776
+			config.data.labels.splice(0, dataPoints.length);
777
+		
778
+			// Remove all data points except the first (for each dataset)
779
+			config.data.datasets.forEach(function(dataset) {
780
+				for(let k = 0; k < dataPoints.length; k++) {
781
+					dataset.data.pop();
782
+				}
783
+			});
784
+		
785
+		}
786
+	
787
+		// Render chart
788
+// 		myLineChart.update();
789
+		myBarChart.update();
790
+	
791
+
792
+	}, 1500);
793
+
794
+}	
795
+
796
+
797
+
199 798
 </script>
200 799
 
201 800
 

+ 1
- 1
siCategoria.php View File

@@ -2,7 +2,7 @@
2 2
 	$tipo_resumen="Categoria";
3 3
 	//print($id_student);
4 4
 //////////Busca cantidad de Sub Cuestionarios - Empieza////////////
5
-	$sqlSelect = sprintf("SELECT qs.* FROM `experience_questionnair` eq, subquestionnair qs where eq.id_questionnair=qs.id_questionnair and eq.id_experience = %s",
5
+	$sqlSelect = sprintf("SELECT SQ.* FROM `experience_questionnair` AS EQ JOIN subquestionnair AS SQ WHERE EQ.id_questionnair = SQ.id_questionnair AND EQ.id_experience = %s ORDER BY SQ.date_to_administer",
6 6
 	GetSQLValueString($id_experiencia, "int")
7 7
 	);
8 8
 	$dbresultSel = mysqli_query($connection,$sqlSelect);

+ 1
- 1
siPreguntas.php View File

@@ -2,7 +2,7 @@
2 2
 	$tipo_resumen="Preguntas";
3 3
 	//print($id_student);
4 4
 //////////Busca cantidad de Sub Cuestionarios - Empieza////////////
5
-	$sqlSelect = sprintf("SELECT qs.* FROM `experience_questionnair` eq, subquestionnair qs where eq.id_questionnair=qs.id_questionnair and eq.id_experience = %s",
5
+	$sqlSelect = sprintf("SELECT SQ.* FROM `experience_questionnair` AS EQ JOIN subquestionnair AS SQ WHERE EQ.id_questionnair = SQ.id_questionnair AND EQ.id_experience = %s ORDER BY SQ.date_to_administer",
6 6
 	GetSQLValueString($id_experiencia, "int")
7 7
 	);
8 8
 	$dbresultSel = mysqli_query($connection,$sqlSelect);

+ 1
- 1
siSubCategoria.php View File

@@ -2,7 +2,7 @@
2 2
 	$tipo_resumen="Sub Categoria";
3 3
 	//print($id_student);
4 4
 //////////Busca cantidad de Sub Cuestionarios - Empieza////////////
5
-	$sqlSelect = sprintf("SELECT qs.* FROM `experience_questionnair` eq, subquestionnair qs where eq.id_questionnair=qs.id_questionnair and eq.id_experience = %s",
5
+	$sqlSelect = sprintf("SELECT SQ.* FROM `experience_questionnair` AS EQ JOIN subquestionnair AS SQ WHERE EQ.id_questionnair = SQ.id_questionnair AND EQ.id_experience = %s ORDER BY SQ.date_to_administer",
6 6
 	GetSQLValueString($id_experiencia, "int")
7 7
 	);
8 8
 	$dbresultSel = mysqli_query($connection,$sqlSelect);

+ 87
- 0
special5.php View File

@@ -0,0 +1,87 @@
1
+<?php
2
+
3
+// This script fetches how many people have answered a given moment
4
+// and spits out a json with an array of people count, moment ID, and date
5
+
6
+
7
+	require_once 'processes/config.php';
8
+	require_once 'processes/dbh.inc.php';
9
+	require_once 'processes/checkLogin.php';
10
+
11
+
12
+$experienceID = mysqli_real_escape_string($connection, trim($_POST['experienceID']));
13
+
14
+// Old Query
15
+// (Note: only the tuple (momentID, date) is unique, so same moments yet different dates appear separated from each other)
16
+/*
17
+$sql = "SELECT COUNT(id_student) AS `count`, id_subquestionnair AS momentID, DATE(answered_date) AS `date`
18
+		FROM `student_subquestionnair`
19
+		WHERE id_subquestionnair IN
20
+				(SELECT id_subquestionnair
21
+				 FROM experience_subquestionnair
22
+				 WHERE id_experience = '$experienceID')
23
+		GROUP BY id_subquestionnair, DATE(answered_date)
24
+		ORDER BY DATE(answered_date) ASC";
25
+*/
26
+		
27
+// Fixed Query
28
+// (Note: used date_to_administer instead of date_answered to avoid splitting (momentID, date) tuples)
29
+/*
30
+$sql = "SELECT COUNT(SS.id_student) AS `count`, SS.id_subquestionnair AS momentID, DATE(SQ.date_to_administer) AS `date`
31
+		FROM `student_subquestionnair` AS SS RIGHT JOIN `subquestionnair` AS SQ
32
+	    WHERE SS.id_subquestionnair = SQ.id
33
+		AND SS.id_subquestionnair IN
34
+				(SELECT id_subquestionnair
35
+				 FROM experience_subquestionnair
36
+				 WHERE id_experience = '$experienceID')
37
+		GROUP BY SS.id_subquestionnair, DATE(SQ.date_to_administer)
38
+		ORDER BY DATE(SQ.date_to_administer) ASC";
39
+*/
40
+
41
+// Re-fixed Query
42
+// (Note: display 0 for those moments that haven't been answered as of "today")
43
+$sql = "SELECT COUNT(SS.id_student) AS `count`, SQ.id AS momentID, DATE(SQ.date_to_administer) AS `date`, SQ.title AS momentTitle
44
+		FROM `student_subquestionnair` AS SS RIGHT JOIN `subquestionnair` AS SQ
45
+	    ON SS.id_subquestionnair = SQ.id
46
+		WHERE SQ.id IN
47
+				(SELECT id_subquestionnair
48
+				 FROM experience_subquestionnair
49
+				 WHERE id_experience = '$experienceID')
50
+        AND DATE(SQ.date_to_administer) <= CURRENT_DATE
51
+		GROUP BY SQ.id, DATE(SQ.date_to_administer)
52
+		ORDER BY DATE(SQ.date_to_administer) ASC";
53
+
54
+$result = mysqli_query($connection, $sql);
55
+
56
+
57
+// Prepare Result (fetch max score at once)
58
+$maxScore = 0;
59
+while($row = mysqli_fetch_object($result)) {
60
+	
61
+	// Fetch Max Score (for formating chart purposes)
62
+	if($maxScore < $row->count) {
63
+		$maxScore = $row->count;
64
+	}
65
+	
66
+	// Format Date as "<abbreviated month> <day of the month without leading zero>"
67
+	$row->date = date('M j', strtotime($row->date));
68
+	
69
+	// Append to array	
70
+	$data[] = $row;
71
+}
72
+
73
+// EXAMPLE:
74
+// $temp = [
75
+// 	[
76
+// 		'count' => 1,
77
+// 		'momentID' => 133,
78
+// 		'date' => '2020-03-12'
79
+// 	]
80
+// ];
81
+
82
+$payload = [
83
+	'maxScore' => $maxScore,
84
+	'data' => $data
85
+];
86
+
87
+echo json_encode($payload);

+ 192
- 0
subcategoryBreakdown.php View File

@@ -0,0 +1,192 @@
1
+<?php 
2
+
3
+
4
+// This script fetches how many people, for every subcategory, have answered a given score
5
+// and spits out a json with an array of subcategory ID, subcategory name, score and count
6
+
7
+// 	require_once 'processes/config.php';
8
+	require_once 'processes/dbh.inc.php';
9
+// 	require_once 'processes/checkLogin.php';
10
+
11
+
12
+
13
+$experienceID = mysqli_real_escape_string($connection, trim($_POST['experienceID'])); // 26
14
+$momentID = mysqli_real_escape_string($connection, trim($_POST['momentID'])); // 196
15
+$studentID = mysqli_real_escape_string($connection, trim($_POST['studentID']));
16
+
17
+
18
+
19
+// Count the number of subcategories for the experience
20
+$sqlCountSubcategories = "SELECT DISTINCT S.id
21
+						FROM subcategory AS S
22
+						JOIN question as Q
23
+						JOIN subquestionnair_question AS SQ
24
+						JOIN experience_subquestionnair AS ES
25
+							WHERE S.id = Q.id_subcategory
26
+							AND SQ.id_question = Q.id
27
+							AND SQ.id_subquestionnair = ES.id_subquestionnair
28
+							AND ES.id_experience = '$experienceID';";
29
+$resultCountSubcategories = mysqli_query($connection, $sqlCountSubcategories);
30
+$countSubcategories = $resultCountSubcategories->num_rows;
31
+
32
+
33
+
34
+// Fetch all the results, depending if all students or a specific one
35
+if($studentID === "all") {
36
+
37
+	$sql = "SELECT S.id AS subcategoryID,
38
+		   	S.subcategoria AS subcategoryName,
39
+	       	A.value AS score,
40
+    	   	COUNT(id_student) AS `count`
41
+			FROM subcategory AS S
42
+    	    LEFT JOIN question AS Q
43
+        		ON Q.id_subcategory = S.id
44
+			JOIN answer AS A
45
+    	    JOIN subquestionnair_question AS SQ
46
+        	JOIN experience_subquestionnair AS ES
47
+				-- WHERE Q.id_subcategory = S.id
48
+		        WHERE A.id_question = Q.id
49
+	    	    AND A.id_question = SQ.id_question
50
+    	    	AND A.id_subquestionnair = SQ.id_subquestionnair
51
+				AND SQ.id_subquestionnair = ES.id_subquestionnair
52
+				AND ES.id_experience = '$experienceID'
53
+    	        AND SQ.id_subquestionnair = '$momentID'
54
+					GROUP BY score, subcategoryID
55
+					ORDER BY score, subcategoryID";
56
+
57
+} else {
58
+
59
+	$sql = "SELECT S.id AS subcategoryID,
60
+		   	S.subcategoria AS subcategoryName,
61
+	       	A.value AS score,
62
+    	   	COUNT(id_student) AS `count`
63
+			FROM subcategory AS S
64
+    	    LEFT JOIN question AS Q
65
+        		ON Q.id_subcategory = S.id
66
+			JOIN answer AS A
67
+    	    JOIN subquestionnair_question AS SQ
68
+        	JOIN experience_subquestionnair AS ES
69
+				-- WHERE Q.id_subcategory = S.id
70
+		        WHERE A.id_question = Q.id
71
+	    	    AND A.id_question = SQ.id_question
72
+    	    	AND A.id_subquestionnair = SQ.id_subquestionnair
73
+				AND SQ.id_subquestionnair = ES.id_subquestionnair
74
+				AND ES.id_experience = '$experienceID'
75
+    	        AND SQ.id_subquestionnair = '$momentID'
76
+        	    AND A.id_student = '$studentID' -- 118
77
+					GROUP BY score, subcategoryID
78
+					ORDER BY score, subcategoryID";
79
+
80
+}
81
+
82
+$result = mysqli_query($connection, $sql);
83
+
84
+
85
+
86
+// Initialize Arrays
87
+$datasets = [];
88
+$dataLabels = [];
89
+$scoreToIndex = [];
90
+$i = 0;
91
+$subcategoryIDToIndex = [];
92
+$j = 0;
93
+
94
+
95
+
96
+// Prepare result
97
+while($row = mysqli_fetch_object($result)) {
98
+
99
+
100
+	// Parse score (cause if might come from an open question)
101
+	// or it might be a float, while we should expect an int
102
+	// then cast it to string to use it as a map key
103
+	
104
+	$pointLabel = ($studentID === "all") ? " # of people who answered '" : " # of times student answered '";
105
+	
106
+	if(is_numeric($row->score)) {
107
+		$score = $pointLabel . strval((int)$row->score) . "'"; // answers may be 5.0 or 5, so first we take out the .
108
+	} else {
109
+		continue;
110
+	}
111
+
112
+
113
+	// Create new label if new subcategory is detected
114
+	if($subcategoryIDToIndex[$row->subcategoryID] || $subcategoryIDToIndex[$row->subcategoryID] === 0) { // index 0 should be skipped
115
+		
116
+		// nothing
117
+		
118
+	} else {
119
+	
120
+		// Create new subcategory
121
+		$dataLabels[] = $row->subcategoryName;
122
+		
123
+		// Store subcategory index for $data array (inside dataset)
124
+		$subcategoryIDToIndex[$row->subcategoryID] = $j;
125
+		$j++;
126
+		
127
+	}
128
+
129
+	
130
+	
131
+	// Create dataset for corresponding score
132
+	if($scoreToIndex[$score] || $scoreToIndex[$score] === 0) { // index 0 should be skipped
133
+	
134
+		// nothing
135
+	
136
+	} else {
137
+
138
+		// serves as a tally to check
139
+		// which scores have been already
140
+		// processed AND as a map of
141
+		// score to index of the
142
+		// $dataset array
143
+		$scoreToIndex[$score] = $i;
144
+		$i++;
145
+
146
+
147
+		// Create new dataset for this label
148
+		$datasets[] = [
149
+			'label' => $score,
150
+			'data' => array_fill(0, $countSubcategories, 0), // array of countSubcategories 0's
151
+			'backgroundColor' => '#' . substr(sha1($score), 0, 6),
152
+// 			'borderWidth' => 3,
153
+// 			'borderColor' => '#' . substr(sha1($score), 0, 6),
154
+			'stack' => $score
155
+// 			'fill' => false
156
+		];
157
+		
158
+	}
159
+
160
+
161
+	// Append data point to corresponding subcategory
162
+	$datasets[$scoreToIndex[$score]]['data'][$subcategoryIDToIndex[$row->subcategoryID]] = (int)$row->count;	
163
+
164
+
165
+}
166
+
167
+
168
+
169
+
170
+/*
171
+	let data = {
172
+		labels: dataLabels,
173
+		datasets: [{
174
+			label: auxiliaries['myChart2'].label,
175
+			data: dataPoints,
176
+			backgroundColor: auxiliaries['myChart2'].backgroundColor,
177
+			borderColor: auxiliaries['myChart2'].borderColor,
178
+			fill: false
179
+		}]
180
+	};
181
+*/
182
+
183
+
184
+$arr = [
185
+	'datasets' => $datasets,
186
+	'dataLabels' => $dataLabels
187
+];
188
+
189
+
190
+// Spit out response JSON
191
+echo header("Content-Type: application/json; charset=utf-8");
192
+echo json_encode($arr);