0 votes
ago in Datenauswertung by s297490 (125 points)

Sehr geehrtes SoSci-Support-Team,

ich habe in SoSci Survey ein komplexeres Experiment (Flanker-Test mit Musikbedingungen) vollständig über HTML und JavaScript realisiert und in eine Frage eingebettet. Dabei funktioniert der Ablauf grundsätzlich wie gewünscht – allerdings habe ich derzeit Unsicherheiten hinsichtlich der zuverlässigen Speicherung der Ergebnisdaten.

Zum Hintergrund meines Projekts:
Testart: Flanker-Test (visuelle Aufmerksamkeitsprüfung mit Tasteneingabe)

Dauer: ca. 3 × 2 Minuten pro Teilnehmer (3 Bedingungen à 30 Trials)

Bedingungen (randomisiert):

Alpha-Wellen-Musik

Klassische Musik (Bach)

Keine Musik

Reiz-Reaktion-Zyklus:
Fixationskreuz → Reiz (←/→) → Tasteneingabe → Feedback → nächste Runde

Technische Umsetzung:

Die Musik startet automatisiert beim ersten Stimulus jeder Bedingung (per JS-Audio-Element).

Es erfolgt eine automatische Berechnung zentraler Kennwerte am Ende:

Genauigkeit

RTs für kongruente und inkongruente Reize

Flanker-Effekt

Reihenfolge der Bedingungen

Die Ergebnisse werden als kompakte JSON-Zusammenfassung gespeichert:

Mein Anliegen:
Ich möchte sicherstellen, dass die Datenübertragung zu SoSci Survey absolut zuverlässig funktioniert, damit ich die erfassten Daten später vollständig analysieren kann. Nach dem Pretest lassen sich aktuell die Ergebnisse des Flanker-Tests nicht in den Daten finden.

Daher bitte ich um Prüfung und Unterstützung. ich wäre Ihnen sehr dankbar, wenn Sie mein Setup prüfen und mir mitteilen könnten, ob ich alle Aspekte korrekt umgesetzt habe – insbesondere mit Blick auf Datensicherheit, Kompatibilität und technische Grenzen in SoSci Survey.

Vielen herzlichen Dank für Ihre Unterstützung!

Elmas Coskun

1 Answer

0 votes
ago by SoSci Survey (345k points)
selected ago by s297490
 
Best answer

Ich möchte sicherstellen, dass die Datenübertragung zu SoSci Survey absolut zuverlässig funktioniert, damit ich die erfassten Daten später vollständig analysieren kann.

Absolute Zuverlässigkeit gibt es im Internet nicht. Es kann immer mal passieren, dass die WiFi-Verbindung verloren geht, dass der Browser abstürzt, u.s.w.

Nach dem Pretest lassen sich aktuell die Ergebnisse des Flanker-Tests nicht in den Daten finden.

Dann beschreiben Sie doch als nächsten Schritt bitte einmal, wie Sie das JSON bisher an SoSci Survey übergeben haben? Klassisch würden Sie eine interne Variable auf die Fragebogen-Seite ziehen und den String dort hineinschreiben.

Falls Ihr JavaScript den String im Verlauf gelegentlich aktualisiert, können Sie (optional) für die interne Variable einstellen, dass der Inhalt periodisch im Hintergrund auf den Server übertragen wird.

Beachten Sie bitte die Längenbegrenzung von knapp 16 kB für den JSON-String.

ich wäre Ihnen sehr dankbar, wenn Sie mein Setup prüfen und mir mitteilen könnten

Posten Sie gerne entsprechende Auszüge aus Ihrem Code, dann kann ich gerne einen Blick darauf werfen. Auch Pretest-Links (direkt zur betroffenen Seite) sind sehr hilfreich.

Sie können auch überlegen, ob Sie den Test vielleicht einfach in lab.js bauen und dieses Experiment in SoSci Survey einbinden möchten.

ago by s297490 (125 points)
Vielen Dank für Ihre schnelle Rückmeldung!

Aktuelle Umsetzung der JSON-Speicherung:
Einbettung des HTML-/JavaScript-Codes erfolgt direkt in eine HTML-Frage im Fragebogen (über „HTML-Code eingeben“).

Im JavaScript-Skript wird am Ende des Experiments eine kompakte JSON-Zusammenfassung erstellt (u. a. Accuracy, RTs, Bedingungsreihenfolge):

const jsonString = JSON.stringify(compactData);
sosci.setVar('flanker_data', jsonString); // Haupt-Variable
sosci.setVar('flanker_total_trials', results.length); // Zusatzvariable
sosci.setVar('flanker_completion', new Date().toISOString().slice(0,16)); // Zeitstempel

Im Fragebogen habe ich eine interne Textvariable flanker_data auf derselben Seite eingefügt und diese im JavaScript wie oben beschrieben per sosci.setVar befüllt.

Update-Verhalten:
Der JSON-String wird einmalig am Ende des letzten Durchgangs gespeichert (nicht wiederholt im Verlauf).

Die JSON-Zusammenfassung ist kompakt gehalten (i. d. R. < 4 KB), da nur aggregierte Werte pro Bedingung enthalten sind – nicht alle Einzel-Trials.

Frage dazu:
Ich habe gesehen, dass man bei internen Textvariablen eine periodische Hintergrundspeicherung aktivieren kann.
Diese Option habe ich bisher nicht aktiviert. Würden Sie empfehlen, diese Funktion zu nutzen, obwohl mein String erst am Ende geschrieben wird?

Anhang zur Veranschaulichung:
Falls hilfreich, sende ich Ihnen gerne nochmals:

Den Pretest-Link direkt zur betroffenen Seite: https://www.soscisurvey.de/MusikAufmerksamkeit/?act=nvXUy5dEpEsPHJmk0iXhNm6Q

Einen Screenshot der eingebetteten Variable im Fragebogen:
https://postimg.cc/gallery/HHm1cD7

Bitte lassen Sie mich wissen, ob mein Vorgehen aus Ihrer Sicht korrekt und stabil ist – oder ob Sie eine andere Empfehlung für eine zuverlässige Übergabe haben.

Vielen Dank für Ihre Unterstützung!

Mit freundlichen Grüßen
ago by s297490 (125 points)
<!-- CSS -->
<style>
/* Alle Input-Felder verstecken */
input[type="text"], textarea {
    display: none !important;
}

/* Layout für Instruktionsseiten */
.instructions, .results {
    text-align: center;
    font-family: Arial, sans-serif;
    padding: 20px;
    max-width: 800px;
    margin: 0 auto;
    line-height: 1.3;
    font-size: 14px;
    position: absolute;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    width: 90%;
    max-width: 800px;
}

.instructions h1, .instructions h2 {
    margin: 10px 0;
    font-size: 24px;
}

.instructions p {
    margin: 4px 0;
    padding: 0;
}

.results {
    background-color: #f8f9fa;
    border-radius: 8px;
    padding: 30px;
}

/* Layout für Stimuli */
.flanker-stimulus {
    font-size: 64px;
    font-family: 'Courier New', monospace;
    letter-spacing: 10px;
    text-align: center;
    margin: 0;
    position: absolute;
    top: 40%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
}

.fixation {
    font-size: 64px;
    color: #999;
    text-align: center;
    margin: 0;
    position: absolute;
    top: 40%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
}

.response-screen {
    font-size: 20px;
    text-align: center;
    color: #666;
    position: absolute;
    top: 40%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
}

.feedback-correct, .feedback-incorrect, .feedback-timeout {
    font-size: 28px;
    font-weight: bold;
    text-align: center;
    margin: 0;
    position: absolute;
    top: 40%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
}

.feedback-correct { color: #28a745; }
.feedback-incorrect { color: #dc3545; }
.feedback-timeout { color: #fd7e14; }

#experiment-container {
    width: 100%;
    height: 100vh;
    position: relative;
    overflow: hidden;
}

#content {
    width: 100%;
    height: 100%;
    position: relative;
}

.progress-indicator {
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 12px;
    color: #999;
}

.save-status {
    background-color: #d4edda;
    border: 2px solid #28a745;
    border-radius: 8px;
    padding: 15px;
    margin: 15px 0;
    text-align: center;
    font-size: 14px;
}

.save-status.warning {
    background-color: #fff3cd;
    border-color: #ffc107;
}

.save-status.error {
    background-color: #f8d7da;
    border-color: #dc3545;
}
</style>

<!-- Audio -->
<audio id="background-music" preload="auto" loop></audio>

<!-- Container -->
<div id="experiment-container">
    <div id="content">Experiment wird geladen…</div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const musicConditions = ['alpha', 'klassik', 'keine'];
    const randomizedConditions = musicConditions.sort(() => Math.random() - 0.5);
    let currentConditionIndex = 0;

    const musicURLs = {
        alpha: 'https://www.soscisurvey.de/MusikAufmerksamkeit/Alpha_Waves_Aligned_to_Bach_FadeOut.mp3',
        klassik: 'https://www.soscisurvey.de/MusikAufmerksamkeit/Bach_Praeludium_C_Dur_BWV846.mp3',
        keine: null
    };

    const stimuli = [
        { arrows: '← ← ← ← ←', correct: 'ArrowLeft', type: 'congruent' },
        { arrows: '→ → → → →', correct: 'ArrowRight', type: 'congruent' },
        { arrows: '→ → ← → →', correct: 'ArrowLeft', type: 'incongruent' },
        { arrows: '← ← → ← ←', correct: 'ArrowRight', type: 'incongruent' }
    ];

    const FIXATION_DURATION = 500;
    const STIMULUS_DURATION = 175;
    const RESPONSE_DURATION = 1000;
    const FEEDBACK_DURATION = 600;
    const ITI_DURATION = 800;
    const totalTrials = 30;

    let currentTrial = 0;
    let trialOrder = [];
    let results = [];
    let currentKeyListener = null;
    let musicStarted = false;
    const contentDiv = document.getElementById('content');

    // KOMPAKTE SPEICHERFUNKTION - NUR WICHTIGSTE DATEN
    function saveCompactData(data) {
        console.log('{EM_FLOPPY_DISK} Starte kompakte Speicherung...');
        
        // NUR ZUSAMMENFASSUNG SPEICHERN (viel kürzer!)
        const summary = {};
        
        randomizedConditions.forEach(condition => {
            const condData = data.filter(r => r.condition === condition);
            const validTrials = condData.filter(r => r.response !== null);
            const correctTrials = condData.filter(r => r.correct);
            
            const accuracy = validTrials.length > 0 ?
                Math.round((correctTrials.length / validTrials.length) * 100) : 0;
            
            const congruentCorrect = correctTrials.filter(r => r.stimulus_type === 'congruent');
            const incongruentCorrect = correctTrials.filter(r => r.stimulus_type === 'incongruent');
            
            const congruentRT = congruentCorrect.length > 0 ?
                Math.round(congruentCorrect.reduce((sum, r) => sum + r.rt, 0) / congruentCorrect.length) : 0;
            
            const incongruentRT = incongruentCorrect.length > 0 ?
                Math.round(incongruentCorrect.reduce((sum, r) => sum + r.rt, 0) / incongruentCorrect.length) : 0;
            
            summary[condition] = {
                acc: accuracy,
                trials: condData.length,
                cong_rt: congruentRT,
                incong_rt: incongruentRT,
                flanker: incongruentRT - congruentRT
            };
        });

        // SEHR KURZE DATEN
        const compactData = {
            total: data.length,
            order: randomizedConditions.join(','),
            time: new Date().toISOString().slice(0,16), // Kurzes Datum
            summary: summary
        };

        const jsonString = JSON.stringify(compactData);
        console.log('{EM_BAR_CHART} Kompakte Daten:', jsonString.length, 'Zeichen');
        console.log('{EM_BAR_CHART} Inhalt:', compactData);

        if (typeof sosci !== 'undefined') {
            try {
                // NUR EINE VARIABLE MIT KOMPAKTEN DATEN
                sosci.setVar('flanker_data', jsonString);
                
                // ZUSÄTZLICH: Einzelne wichtige Werte
                sosci.setVar('flanker_total_trials', data.length);
                sosci.setVar('flanker_completion', new Date().toISOString().slice(0,16));
                
                console.log('{EM_WHITE_HEAVY_CHECK_MARK} Kompakte Speicherung erfolgreich');
                return true;
                
            } catch (error) {
                console.error('{EM_CROSS_MARK} Speicherfehler:', error);
                return false;
            }
        } else {
            console.warn('{EM_WARNING}️ SoSci Survey nicht verfügbar');
            return false;
        }
    }

    function showBreakScreen(callback) {
        let remaining = 10; // Kurz für Testing
        function updateTimer() {
            contentDiv.innerHTML = `
                <div class="instructions">
                    <h2>Pause</h2>
                    <p>Bitte machen Sie jetzt eine Pause.</p>
                    <p>Der nächste Durchgang startet in <strong>${remaining}</strong> Sekunden automatisch.</p>
                </div>
            `;
            remaining--;
            if (remaining >= 0) {
                setTimeout(updateTimer, 1000);
            } else {
                callback();
            }
        }
        updateTimer();
    }

    function startMusic() {
        const cond = randomizedConditions[currentConditionIndex];
        const audio = document.getElementById('background-music');
        if (musicURLs[cond] && !musicStarted) {
            audio.src = musicURLs[cond];
            audio.volume = 0.3;
            audio.loop = true;
            audio.load();
            audio.play().then(() => {
                console.log('{EM_MUSICAL_NOTE} Musik gestartet:', cond);
                musicStarted = true;
            }).catch((e) => {
                console.warn('{EM_PROHIBITED} Musikfehler:', e);
            });
        }
    }

    function stopMusic() {
        const audio = document.getElementById('background-music');
        if (audio) {
            audio.pause();
            musicStarted = false;
        }
    }

    function showWelcome() {
        const cond = randomizedConditions[currentConditionIndex];
        const conditionText = cond === 'alpha' ? 'Alpha-Wellen-Musik' : cond === 'klassik' ? 'Klassische Musik' : 'Keine Musik';
        contentDiv.innerHTML = `
            <div class="instructions">
                <h1>Flanker Test</h1>
                <p><strong>Bedingung: ${conditionText}</strong></p>
                <p>Sie werden fünf Pfeile sehen und sollen auf den <strong>mittleren Pfeil</strong> reagieren.</p>
                <p><strong>Tasten:</strong></p>
                <p>← Linke Pfeiltaste | Rechte Pfeiltaste →</p>
                <p><strong>Wichtig:</strong> Die Pfeile erscheinen nur sehr kurz!</p>
                <p>Reagieren Sie so schnell und genau wie möglich.</p>
                <p><strong>Drücken Sie eine beliebige Taste, um zu beginnen.</strong></p>
                ${cond !== 'keine' ? '<p><em>(Die Musik startet automatisch mit den ersten Stimuli)</em></p>' : ''}
            </div>
        `;
        addKeyListener(() => showInstructions());
    }
ago by s297490 (125 points)
function showInstructions() {
        contentDiv.innerHTML = `
            <div class="instructions">
                <h2>Anleitung</h2>
                <p>Ablauf: Fixationskreuz → Pfeile → Antwortfenster → Feedback</p>
                <div class="example" style="font-family: 'Courier New', monospace; font-size: 32px; margin: 15px 0;">
                    <p>→ → → → → &nbsp;&nbsp;&nbsp; ← ← ← ← ←</p>
                </div>
                <p><strong>Beispiele:</strong></p>
                <div class="example" style="font-family: 'Courier New', monospace; font-size: 16px;">
                    <p>→ → ← → → &nbsp;&nbsp;&nbsp; ← Linke Pfeiltaste drücken</p>
                    <p>← ← → ← ← &nbsp;&nbsp;&nbsp; → Rechte Pfeiltaste drücken</p>
                </div>
                <p style="color: red; font-weight: bold;">Konzentrieren Sie sich auf den mittleren Pfeil!</p>
                <p>Drücken Sie eine beliebige Taste, um zu beginnen.</p>
            </div>
        `;
        addKeyListener(() => {
            currentTrial = 0;
            trialOrder = [];
            for (let i = 0; i < totalTrials; i++) {
                trialOrder.push(stimuli[Math.floor(Math.random() * stimuli.length)]);
            }
            startTrial();
        });
    }

    function startTrial() {
        if (currentTrial >= totalTrials) {
            showResults();
            return;
        }
        removeKeyListener();
        if (currentTrial === 0) startMusic();
        const progress = Math.round(((currentTrial + 1) / totalTrials) * 100);
        contentDiv.innerHTML = `
            <div class="fixation">+</div>
            <div class="progress-indicator">Trial ${currentTrial + 1} von ${totalTrials} (${progress}%)</div>
        `;
        setTimeout(() => showStimulus(), FIXATION_DURATION);
    }

    function showStimulus() {
        const stimulus = trialOrder[currentTrial];
        contentDiv.innerHTML = `<div class="flanker-stimulus">${stimulus.arrows}</div>`;
        setTimeout(() => showResponseWindow(stimulus), STIMULUS_DURATION);
    }

    function showResponseWindow(stimulus) {
        const startTime = Date.now();
        let responded = false;
        contentDiv.innerHTML = `<div class="response-screen"><p>Antworten Sie jetzt!</p></div>`;
        addKeyListener(function (e) {
            if (responded) return;
            if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {
                responded = true;
                const rt = Date.now() - startTime;
                const correct = e.code === stimulus.correct;
                results.push({
                    condition: randomizedConditions[currentConditionIndex],
                    trial: currentTrial + 1,
                    stimulus_type: stimulus.type,
                    response: e.code,
                    correct: correct,
                    rt: rt
                });
                showFeedback(correct, false);
            }
        });
        setTimeout(() => {
            if (!responded) {
                responded = true;
                results.push({
                    condition: randomizedConditions[currentConditionIndex],
                    trial: currentTrial + 1,
                    stimulus_type: stimulus.type,
                    response: null,
                    correct: false,
                    rt: null
                });
                showFeedback(false, true);
            }
        }, RESPONSE_DURATION);
    }

    function showFeedback(correct, timeout) {
        removeKeyListener();
        const msg = timeout ? 'Zu spät!' : (correct ? 'Richtig!' : 'Falsch!');
        const css = timeout ? 'feedback-timeout' : (correct ? 'feedback-correct' : 'feedback-incorrect');
        contentDiv.innerHTML = `<div class="${css}">${msg}</div>`;
        setTimeout(() => {
            contentDiv.innerHTML = '';
            setTimeout(() => {
                currentTrial++;
                startTrial();
            }, ITI_DURATION);
        }, FEEDBACK_DURATION);
    }

    function showResults() {
        stopMusic();
        const validTrials = results.filter(r => r.response !== null);
        const correctTrials = results.filter(r => r.correct);
        const accuracy = validTrials.length > 0 ? Math.round((correctTrials.length / validTrials.length) * 100) : 0;
        const condition = randomizedConditions[currentConditionIndex];
        const conditionText = condition === 'alpha' ? 'Alpha-Wellen-Musik' : condition === 'klassik' ? 'Klassische Musik' : 'Keine Musik';
        
        contentDiv.innerHTML = `
            <div class="results">
                <h2>Durchgang abgeschlossen</h2>
                <p><strong>Bedingung:</strong> ${conditionText}</p>
                <p><strong>Genauigkeit:</strong> ${accuracy}%</p>
                <p>Vielen Dank für diesen Durchgang!</p>
            </div>
        `;
        
        currentConditionIndex++;
        if (currentConditionIndex < randomizedConditions.length) {
            showBreakScreen(() => {
                showWelcome();
            });
        } else {
            showFinalResults();
        }
    }

    function showFinalResults() {
        contentDiv.innerHTML += `
            <div class="save-status">
                <p>{EM_FLOPPY_DISK} <strong>Experiment abgeschlossen!</strong></p>
                <p>Kompakte Daten werden gespeichert...</p>
            </div>
        `;

        const success = saveCompactData(results);
        
        setTimeout(() => {
            if (success) {
                contentDiv.innerHTML += `
                    <div class="save-status">
                        <p>{EM_WHITE_HEAVY_CHECK_MARK} <strong>Kompakte Speicherung erfolgreich!</strong></p>
                        <p>Alle ${results.length} Trials wurden zusammengefasst gespeichert.</p>
                        <p><strong>Datenformat:</strong> Kompakte Zusammenfassung</p>
                        <p><em>Sie können nun zur nächsten Seite wechseln.</em></p>
                    </div>
                `;
            } else {
                contentDiv.innerHTML += `
                    <div class="save-status error">
                        <p>{EM_CROSS_MARK} <strong>Speicherfehler</strong></p>
                        <p>Bitte kontaktieren Sie den Versuchsleiter.</p>
                        <p>Öffnen Sie die Console (F12) für Details.</p>
                    </div>
                `;
            }
        }, 1000);
    }

    function addKeyListener(callback) {
        removeKeyListener();
        currentKeyListener = callback;
        document.addEventListener('keydown', currentKeyListener);
    }

    function removeKeyListener() {
        if (currentKeyListener) {
            document.removeEventListener('keydown', currentKeyListener);
            currentKeyListener = null;
        }
    }

    showWelcome();
});
</script>
ago by SoSci Survey (345k points)
Bitte haben Sie Verständnis, dass ich nicht den gesamten Code analysiere.

> sosci.setVar('flanker_completion', new Date().toISOString().slice(0,16)); // Zeitstempel

> Im Fragebogen habe ich eine interne Textvariable flanker_data auf derselben Seite
> eingefügt und diese im JavaScript wie oben beschrieben per sosci.setVar befüllt.

Wo haben Sie denn das Objekt sosci und die Methode setVar() definiert? In Ihrem Code finde ich dazu nichts. Und ich bin ziemlich sicher, dass dies auch von SoSci Survey nicht vordefiniert ist.

Falls Sie ChatGPT gefragt haben: Eine Studie zur Unterstützung beim Programmieren besagt, dass ca. 65% der Antworten con ChatGPT richtig sind. Und nachdem ChatGPT die Anleitung von SoSci Survey offenbar nicht gelesen hat, ist die Quote richtiger Antworten da näherungsweise 0.

Die Lösung laut Handbuch (https://www.soscisurvey.de/help/doku.php/de:create:questions:internal) wäre, dass Sie eine interne Variable (z.B. IV01) auf die Seite ziehen und dann Ihre Zeilen oben wie folgt ersetzen:

const jsonString = JSON.stringify(compactData);
input.value = document.getElementById("IV01_01");

> Den Pretest-Link direkt zur betroffenen Seite

Das ist kein gültiger Pretest-Link, vgl. https://www.soscisurvey.de/help/doku.php/de:survey:pretest

> Bitte lassen Sie mich wissen, ob mein Vorgehen aus Ihrer Sicht korrekt und stabil ist

Eher nicht, würde ich sagen :)

Starten Sie mal mit der JavaScript-Fehlerkonsole Ihres Browsers und beobachten sie, was dort an Meldungen kommt: https://www.soscisurvey.de/help/doku.php/de:general:browser-tools

Willkommen im Online-Support von SoSci Survey.

Hier bekommen Sie schnelle und fundierte Antworten von anderen Projektleitern und direkt von SoSci Survey.

→ Eine Frage stellen


Welcome to the SoSci Survey online support.

Simply ask a question to quickly get answers from other professionals, and directly from SoSci Survey.

→ Ask a Question

...