Projekt

Allgemein

Profil

Fehler #1206 » learning_analytics.js

Avishan Sharafi, 26.06.2025 17:13

 
1
// unload-event?
2

    
3
// ------------------- globale Variablen --------------------------------------------------------------------------------------------------------------------
4
	var eventlog = "Learning-Analytics:\n"
5
	var letzteMausbewegung = Date.now();
6
	
7
	var aufg_mit_nummerierung = [];	// in diesem Array werden alle Aufgabennummern gespeichert, in denen die Inputs schon durchnummeriert wurden
8
	
9

    
10

    
11
// -------------------- logging ----------------------------------------------------------------------------------------------------------------------------
12
	
13
	// Ausgeben aller gespeicherten Aktionen in der Konsole
14
	function learning_analytics () {
15
		console.log(eventlog);
16
	}
17

    
18

    
19
	/* Logger
20
		eventtype: 	string		z.B. "click", "load"
21
		addtext: 	string		zusätzliche Informationen zur Aktion, z.B. Aufgabennummer
22
	*/
23
	function logger (eventtype, addtext) {
24
		var logeintrag = eventtype + ": " + addtext + "; Timestamp " + Date.now() + "\n";
25
		eventlog = eventlog + logeintrag;
26
		//console.log(logeintrag);
27
	}
28
	
29
	
30
	// Aktivität des Nutzers mitloggen: alle 10 Sekunden Scrollposition und Mausbewegungen erfassen
31
	function logactivity () {
32
		var scrollPos = $(window).scrollTop();
33
		logger("activityupdate", "Scrollposition " + scrollPos + "; letzte Mausbewegung " + letzteMausbewegung);
34
	}
35
	setInterval(logactivity,10000);
36
	
37
	
38
	
39
// -------------------- Funktionen rund um Aufgabennummer oder Input-Möglichkeiten je Aufgabe --------------------------------------------------------------
40

    
41
	/* 	Aufgabennummer eines HTML-Elements für den logger erhalten
42
		elmt:		html-Element
43
		return:		String für logger mit Aufgabennr, abgetrennt durch Semikolon	(falls elmt in Aufgaben-Umgebung)
44
					leerer String 													(falls elmt nicht in Aufgaben-Umgebung)
45
	*/
46
	function getaufgnr_elmt (elmt) {
47
		// mit ".last()" wird sichergestellt, dass auch bei aufgb- und aufgrahmen-Umgebungen alles funktioniert
48
		var exercise = $($(elmt).parents('.aufgabe').last());
49
		
50
		if (exercise.length != 0) {
51
			var aufgnr = 0;
52
			var zufallszusatz = "";
53
			// bei zufallsaufgabe muss Aufgnummer aus der original-Aufgaben-Umgebung gezogen werden, die Vorgänger der gefundenen exercise ist
54
			if (exercise.hasClass("zufallsaufgabe")) {
55
				aufgnr = getaufgnr_exercise(exercise.prev());
56
				zufallszusatz = " zufällig";
57
			} else {
58
				aufgnr = getaufgnr_exercise(exercise);
59
			}
60
			return "; Aufg. " + aufgnr + zufallszusatz;
61
		} else {
62
			return "";
63
		}
64
	}
65
	
66
	
67
	/* 	Aufgabennummer eines aufgaben-divs erhalten
68
		exercise:	aufgaben-div
69
		return:		aufgnr (als String)		(Fehlertext, falls Aufgaben-div undefiniert ist)
70
	*/
71
	function getaufgnr_exercise (exercise) {
72
		if ($(exercise).length != 0) {
73
			// Finde Element mit z.B. id "aufgabe_13" und lösche das "aufgabe_"
74
			return $(exercise).find("span[id^='aufgabe_']").attr("id").replace("aufgabe_","");
75
		} else {
76
			return "keine Aufgabennummer";
77
		}
78
	}
79
	
80
	
81
	/* 	nummeriere inputs innerhalb einer Aufgabe, jeweils startend mit 1
82
				Ergebnis: jedes Element in array erhält eine id mit Input- und Aufgabennummer
83
		array:	array von html-Elementen (input oder select) in Aufg.-Umgebungen
84
	*/
85
	function nummeriere_inputs (array) {
86
		for (elmt of array) {
87
			// mit ".last()" wird sichergestellt, dass auch bei aufgb- und aufgrahmen-Umgebungen alles funktioniert
88
			var exercise = $(elmt).parents('.aufgabe').last();
89
			
90
			// die nächsten Schritte sind nur sinnvoll und ohne Fehler möglich, wenn das Element in einer Aufgaben-Umgebung steht
91
			if (exercise.length != 0) { 
92
				// bei zufallsaufgabe muss Aufgnummer aus der original-Aufgaben-Umgebung gezogen werden
93
				if (exercise.hasClass("zufallsaufgabe")) {
94
					var aufgnr = getaufgnr_exercise(exercise.prev()) + "zuf";
95
				} else {
96
					var aufgnr = getaufgnr_exercise(exercise);
97
				}
98
				
99
				// jede Aufgabe soll nur einmal durchnummeriert werden, auch wenn mehrere Eingabefelder o.ä. enthalten sind
100
				if (aufg_mit_nummerierung.indexOf(aufgnr) == -1) {
101
					aufg_mit_nummerierung.push(aufgnr);
102
					
103
					// Eingabefelder, Singlechoice-Optionen, Multiplechoice-Optionen (input) und Dropdowns-Elemente (select)
104
					var inputs = $(exercise).find("input, select");
105
					// input-/select-tags werden mit id inkl inputnr versehen
106
					for (i = 0; i < inputs.length; i++) {
107
						var j=i+1;
108
						inputs[i].setAttribute("id", "input" + j + "_aufg" + aufgnr);
109
					}
110
				}
111
			}
112
		}
113
	}
114
	
115
	
116
	/*	Inputnummer aus Input-Id generieren (Id stammt aus Funktion "nummeriere_inputs()")
117
		elmt:		html-Element ("input" oder "select")
118
		return:		String für logger mit Inputnummer
119
	*/
120
	function get_inputnr (elmt) {
121
		var inputid = "" + $(elmt).attr("id");
122
		var inputnr = 0;
123
		
124
		if(inputid.indexOf("input") != -1){
125
			inputnr = inputid.replace("input", "");
126
			inputnr = inputnr.substring(0,inputnr.indexOf("_"));
127
			return inputnr + ". Input-Element";
128
		} else {
129
			return "keine Inputnummer";
130
		}
131
	}
132
	
133
	
134
	/* 	Nr des Konrollbuttons innerhalb einer Aufgabe ausgeben, Zählung in jeder Aufgabe startet mit 1 
135
		kbutton:	Kontrollbutton-html-Element
136
		return:		String für logger mit Buttonnummer, abgetrennt mit Semikolon	(falls mehrere Buttons in der Aufgabe)
137
					leerer String 													(falls nur 1 Button in der Aufgabe)
138
	*/
139
	function get_kontrollbutton_nr (kbutton) {
140
		// mit ".last()" wird sichergestellt, dass auch bei aufgb- und aufgrahmen-Umgebungen alles funktioniert
141
		var exercise = $(kbutton).parents('.aufgabe').last();
142
		
143
		var kontrollbuttons = $(exercise).find(".control_button");
144
		if (kontrollbuttons.length == 1) {
145
			// bei einem Button in der Aufgabe muss man nicht durchzählen
146
			return "";
147
		} else {
148
			var buttonnr = $.inArray(kbutton, kontrollbuttons) + 1;
149
			return buttonnr + ". "
150
		}
151
	}
152
	
153
	
154
	/*	KV-Links: 	Nr des Links auf der Seite ausgeben, Zählung startet mit 1 
155
		elmt:		HTML-Element 	Link zu anderem KV-Bereich (HTML-Element)
156
		classname:	String			Name der Klasse des Zielbereichs (z.B. "bottom_link_math_content1")
157
		return:		String für logger mit Linknummer, abgetrennt mit Semikolon	(falls mehrere Links auf Seite)
158
					leerer String 												(falls nur 1 Link auf der Seite)
159
		
160
	*/
161
	function get_linknr_kv (elmt, classname) {
162
		var anzlinks = $("." + classname).length;
163
		if (anzlinks == 1) {
164
			// bei einem Link auf der Seite muss man nicht durchzählen
165
			return "";
166
		} else {
167
			var linknr = $.inArray(elmt,$('.' + classname)) + 1;
168
			return "; " + linknr + ". Link"
169
		}
170
	}
171

    
172

    
173

    
174

    
175
// ------------------- logging-Events --------------------------------------------------------------------------------------------------------------------
176

    
177
$(document).ready(function() {
178
	// Startposition loggen
179
	logactivity();
180
	
181
	
182
	// Laden der Seite loggen
183
	window.addEventListener('load', function(){
184
		logger('load', "Seite geladen")
185
	}, false)
186
	
187
	
188
	// Zeitpunkt der letzten Mausbewegung fortlaufend aktualisieren
189
	window.addEventListener('mousemove', function() {
190
		letzteMausbewegung = Date.now();
191
	}, false)
192
	
193
	
194
	
195
	// ------------------- Download PDF/GGB/... --------------------------
196
		// PDF
197
		var downloadpdfs = document.getElementsByClassName('pdf_download');
198
		for (let elmt of downloadpdfs) {
199
			elmt.addEventListener('click', function() {
200
				var pdfname = " '" + elmt.getAttribute("href").slice(10) + "'";	// "../../pdf/" muss im Dateinamen entfernt werden 
201
				logger('click', "PDF-Download" + pdfname);
202
			}, false)
203
		};
204
		
205
		// QR-Codes
206
		var downloadqrs = document.querySelectorAll('[id^="qrcode_"]');
207
		for (let elmt of downloadqrs) {
208
			elmt.addEventListener('click', function() {
209
				var qrid = " '" + elmt.getAttribute("id") + "'";
210
				logger('click', "QR-Code-Download" + qrid);
211
			}, false)
212
		};
213
		
214
		// Element-Links (direkt neben QR-Codes)
215
		var elmtlinks = document.getElementsByClassName('qrcode_link');
216
		for (let elmt of elmtlinks) {
217
			elmt.addEventListener('click', function() {
218
				var linkname = " '" + elmt.previousSibling.getAttribute("id").replace("qrcode_","") + "'";
219
				logger('click', "Element-Link" + linkname);
220
			}, false)
221
		};
222
		
223
		// GGBs
224
		var downloadggbs = document.getElementsByClassName('ggb_download');
225
		for (let elmt of downloadggbs) {
226
			elmt.addEventListener('click', function() {
227
				var ggbname = " '" + $(elmt).parents().prev().attr("id") + "'";
228
				logger('click', "Geogebra-Download" + ggbname);
229
			}, false)
230
		};
231
		
232
		
233
		
234
		// ggb-delayed anzeigen
235
		var ggb_starter = document.getElementsByClassName('ggb_starter');
236
		for (let elmt of ggb_starter) {
237
			elmt.addEventListener('click', function() {
238
				var ggbid = " '" + elmt.nextElementSibling.getAttribute("id") + "'";
239
				logger('click', "Anzeigen von GGB-Delayed" + ggbid)
240
			}, false)
241
		};
242
		
243
	// ------------------- Videos ----------------------------------------
244
		var videos =  document.getElementsByTagName("video");
245
		for (let elmt of videos) {
246
			elmt.addEventListener('timeupdate', function() {
247
				var videoname = " '" + $(elmt).attr("id") + "'";
248
				var position = ", currentTime " + elmt.currentTime;
249
				logger('timeupdate', "Video" + videoname + position)
250
			}, false)
251
			
252
			// mit timeupdate ist dieses 'play'-Event streng genommen nicht nötig, aber macht für den Menschen das Log-Protokoll besser lesbar
253
			elmt.addEventListener('play', function() { //
254
				var videoname = " '" + $(elmt).attr("id") + "'";
255
				var position = ", currentTime " + elmt.currentTime;
256
				logger('play', "Video" + videoname + position)
257
			}, false)
258
			
259
			/* Liefert wegen des timeupdate-Events falsche Informationen (currentTime wird etwas verzögert erfasst, also beim Springen zu anderem Zeitpunkt im Video wird 'Pause' fälschlicherweise zu neuem Zeitpunkt notiert statt zur Startzeit des Sprungs...)
260
			elmt.addEventListener('pause', function() {		
261
				var videoname = " '" + $(elmt).attr("id") + "'";
262
				var position = ", currentTime " + elmt.currentTime;
263
				logger('pause', "Video" + videoname + position)
264
			}, false)*/
265
			
266
			elmt.addEventListener('ended', function() {
267
				var videoname = " '" + $(elmt).attr("id") + "'";
268
				logger('ended', "Video" + videoname)
269
			}, false)
270
		};
271

    
272
	// ------------------- spezielle Aufgaben-Formate --------------------
273
		// Multiplechoice
274
		var multiplechoices = document.getElementsByClassName('multiplechoice_aufgabe');
275
		nummeriere_inputs(multiplechoices);
276
		for (let elmt of multiplechoices) {
277
			var inputs = $(elmt).find("input");
278
			for (let input of inputs) {
279
				input.addEventListener('change', function() {
280
					var aufgnr = getaufgnr_elmt(elmt);
281
					var nrmultiplechoice = ", " + get_inputnr(input);
282
					var aktion = "; unchecked";
283
					if ($(input).is(':checked')) {
284
						aktion = "; checked";
285
					}
286
					logger('change', "Multiplechoice" + aufgnr + nrmultiplechoice + aktion);
287
				}, false)
288
			}
289
		};
290
		
291
		// Singlechoice
292
		var singlechoices = document.getElementsByClassName('singlechoice_aufgabe');
293
		nummeriere_inputs(singlechoices);
294
		for (let elmt of singlechoices) {
295
			var inputs = $(elmt).find("input");
296
			for (let input of inputs) {
297
				input.addEventListener('change', function() {
298
					var aufgnr = ", " + getaufgnr_elmt(elmt);
299
					var nrsinglechoice = get_inputnr(input);
300
					var aktion = "; checked";
301
					logger('change', "Singlechoice" + aufgnr + nrsinglechoice + aktion);
302
				}, false)
303
			}
304
		};
305
		
306
		// Dropdown
307
		var dropdowns =  document.getElementsByTagName("select");
308
		nummeriere_inputs(dropdowns);
309
		for (let elmt of dropdowns) {
310
			elmt.addEventListener('change', function() {
311
				var aufgnr = getaufgnr_elmt(elmt);
312
				var nrdropdown = ", " + get_inputnr(elmt);
313
				var value = "; '" + $(this).val() + "'";
314
				logger('change', "Dropdown" + aufgnr + nrdropdown + value);
315
			}, false)
316
		};
317
		
318
		// Eingabefeld
319
		var eingabefelder = document.querySelectorAll('[type="text"]');
320
		nummeriere_inputs(eingabefelder);
321
		for (let elmt of eingabefelder) {
322
			elmt.addEventListener('change', function() {
323
				var aufgnr = getaufgnr_elmt(elmt);
324
				var nrinput = ", " + get_inputnr(elmt);
325
				var value = "; '" + $(this).val() + "'";
326
				logger('change', "Eingabefeld" + aufgnr + nrinput + value);
327
			}, false)
328
		};
329
		
330
		
331
	// ------------------- weitere Aufgaben-Aktionen ---------------------
332
		// Lösung aufklappen (oder andere Klapptexte)
333
		var loesungaufklapper = document.getElementsByClassName('loesung_zeigen');
334
		for (let elmt of loesungaufklapper) {
335
			elmt.addEventListener('click', function() {
336
				var aufgnr = getaufgnr_elmt(elmt);
337
				var linktext = " '" + elmt.innerHTML + "'";
338
				var klappid = "; id: " + elmt.nextSibling.getAttribute("id");
339
				logger('click', "Klapptext öffnen" + linktext + klappid + aufgnr);
340
			}, false)
341
		};
342
		
343
		// Lösung zuklappen (oder andere Klapptexte)
344
		var loesungzuklapper = document.getElementsByClassName('loesung_verbergen');
345
		for (let elmt of loesungzuklapper) {
346
			elmt.addEventListener('click', function() {
347
				var aufgnr = getaufgnr_elmt(elmt);
348
				var linktext = " '" + elmt.innerHTML + "'";//parentNode.previousSibling.innerHTML;
349
				var klappid = "; id: " + elmt.parentNode.getAttribute("id");
350
				logger('click', "Klapptext schließen" + linktext + klappid + aufgnr);
351
			}, false)
352
		};
353
		
354
		// Kontrollbutton
355
		var controlbuttons = document.getElementsByClassName('control_button');
356
		for (let elmt of controlbuttons) {
357
			elmt.addEventListener('click', function() {
358
				var aufgnr = getaufgnr_elmt(elmt);
359
				var buttonnr = get_kontrollbutton_nr(elmt);
360
								
361
				setTimeout(function(){ 
362
					/* Ob korrekt gelöst oder nicht, wird nicht wie in dynamic_exercises.js bestimmt, 
363
					   sondern einfach über das angezeigte Bild mit grünem Haken/rotem Kreuz, das aber erst geladen werden muss (daher Timeout nötig) */
364
					var korrekt = "; wrong answer"
365
					if ($(elmt).next().children(":nth-child(1)").is(":visible")) {
366
						korrekt = "; right answer"
367
					}
368
					logger('click', buttonnr + "Kontrollbutton" + aufgnr + korrekt);
369
				}, 1);
370
			}, false)
371
		};
372
		
373
		// Zufallsbuttons
374
		var zufallsbuttons = document.getElementsByClassName('zufallsbutton');
375
		for (let elmt of zufallsbuttons) {
376
			elmt.addEventListener('click', function() {
377
				var aufgnr = getaufgnr_elmt(elmt);
378
				logger('click', "Zufall erzeugen" + aufgnr);
379
			}, false)
380
		};
381
		var zufallsbuttons_renew = document.getElementsByClassName('zufallsbutton_renew');
382
		for (let elmt of zufallsbuttons_renew) {
383
			elmt.addEventListener('click', function() {
384
				var aufgnr = getaufgnr_elmt(elmt);
385
				logger('click', "Zufall erneuern" + aufgnr);
386
			}, false)
387
		};
388
		var zufallsbuttons_return = document.getElementsByClassName('zufallsbutton_return');
389
		for (let elmt of zufallsbuttons_return) {
390
			elmt.addEventListener('click', function() {
391
				var aufgnr = getaufgnr_elmt(elmt);
392
				logger('click', "Zufall beenden" + aufgnr);
393
			}, false)
394
		};
395
	
396
	
397
	
398
	
399
	
400
	// ------------------- KV-Specials -----------------------------------
401
		// link Einstiegsaufgabe
402
		var linkseinstieg = document.getElementsByClassName('bottom_link_example_exercise');
403
		for (let elmt of linkseinstieg) {
404
			elmt.addEventListener('click', function() {
405
				var linknr = get_linknr_kv(elmt, 'bottom_link_example_exercise');
406
				logger('click', "Link Einstiegsaufgabe" + linknr);
407
			}, false)
408
		};
409
		
410
		// link FachinhaltA
411
		var linksfacha = document.getElementsByClassName('bottom_link_math_content1');
412
		for (let elmt of linksfacha) {
413
			elmt.addEventListener('click', function() {
414
				var linknr = get_linknr_kv(elmt, 'bottom_link_math_content1');
415
				var bereichname = " '" + elmt.innerHTML + "'";
416
				logger('click', "Link Fachinhalt A" + bereichname + linknr);
417
			}, false)
418
		};
419
		
420
		// link FachinhaltB
421
		var linksfachb = document.getElementsByClassName('bottom_link_math_content2');
422
		for (let elmt of linksfachb) {
423
			elmt.addEventListener('click', function() {
424
				var linknr = get_linknr_kv(elmt, 'bottom_link_math_content2');
425
				var bereichname = " '" + elmt.innerHTML + "'";
426
				logger('click', "Link Fachinhalt B" + bereichname + linknr);
427
			}, false)
428
		};
429
		
430
		// link FachinhaltC
431
		var linksfachc = document.getElementsByClassName('bottom_link_math_content3');
432
		for (let elmt of linksfachc) {
433
			elmt.addEventListener('click', function() {
434
				var linknr = get_linknr_kv(elmt, 'bottom_link_math_content3');
435
				var bereichname = " '" + elmt.innerHTML + "'";
436
				logger('click', "Link Fachinhalt C" + bereichname + linknr);
437
			}, false)
438
		};
439
		
440
		// link Ausblick
441
		// var linksausblick = document.getElementsByClassName('bottom_link_score');
442
		// for (let elmt of linksausblick) {
443
		// 	elmt.addEventListener('click', function() {
444
		// 		var linknr = get_linknr_kv(elmt, 'bottom_link_score');
445
		// 		logger('click', "Link Ausblick" + linknr);
446
		// 	}, false)
447
		// };
448
		
449
		
450

    
451
		// link Ausblick  Avishan made changes here.
452
		var linksausblick = document.getElementsByClassName('bottom_link_score');
453
		for (let elmt of linksausblick) {
454
			elmt.addEventListener('click', function() {
455
				var linknr = get_linknr_kv(elmt, 'bottom_link_score');
456
				logger('click', "Link Ausblick" + linknr);
457

    
458
				// ✅ Redirect to Ausblick tab
459
				window.location.href = "https://moodle.math.uni-paderborn.de/course/view.php?id=7&section=4#tabs-tree-start";
460
			}, false);
461
}
462

    
463

    
464
		
465
		// Nächsten Abschnitt aufklappen
466
		var button_next = document.getElementById('button_next');
467
		if (button_next != null) {
468
			button_next.addEventListener('click', function() {
469
				var step_current = document.getElementById("step_current").innerHTML;
470
				var step_to = parseInt(step_current) + 1;
471
				var logseitenzahlen = "; von Seite " + step_current + " zu Seite " + step_to;
472
				logger('click', "Button 'Nächster Abschnitt'" + logseitenzahlen);
473
			}, false)
474
		}
475
		
476
		// letzten Abschnitt einklappen (Abschnitt zurück)
477
		var button_prev = document.getElementById('button_prev');
478
		if (button_prev != null) {
479
			button_prev.addEventListener('click', function() {
480
				var step_current = document.getElementById("step_current").innerHTML;
481
				var step_to = step_current - 1;
482
				var logseitenzahlen = "; von Seite " + step_current + " zu Seite " + step_to;
483
				logger('click', "Button 'Schritt zurück'" + logseitenzahlen);
484
			}, false)
485
		}
486
})
(4-4/4)